mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-10 00:34:50 +00:00
commit
d4d92187db
16 changed files with 585 additions and 236 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.1...dev)
|
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.1...dev)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Manual email verification requests. ([bc659387](https://github.com/pixelfed/pixelfed/commit/bc659387))
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
- Updated NotificationService, fix 500 bug. ([4a609dc3](https://github.com/pixelfed/pixelfed/commit/4a609dc3))
|
- Updated NotificationService, fix 500 bug. ([4a609dc3](https://github.com/pixelfed/pixelfed/commit/4a609dc3))
|
||||||
- Updated HttpSignatures, update instance actor headers. Fixes #2935. ([a900de21](https://github.com/pixelfed/pixelfed/commit/a900de21))
|
- Updated HttpSignatures, update instance actor headers. Fixes #2935. ([a900de21](https://github.com/pixelfed/pixelfed/commit/a900de21))
|
||||||
|
|
|
@ -75,7 +75,10 @@ class AccountController extends Controller
|
||||||
|
|
||||||
public function verifyEmail(Request $request)
|
public function verifyEmail(Request $request)
|
||||||
{
|
{
|
||||||
return view('account.verify_email');
|
$recentSent = EmailVerification::whereUserId(Auth::id())
|
||||||
|
->whereDate('created_at', '>', now()->subHours(12))->count();
|
||||||
|
|
||||||
|
return view('account.verify_email', compact('recentSent'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendVerifyEmail(Request $request)
|
public function sendVerifyEmail(Request $request)
|
||||||
|
|
|
@ -4,8 +4,12 @@ namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use Cache;
|
use Cache;
|
||||||
use App\Report;
|
use App\Report;
|
||||||
|
use App\User;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
use App\Services\AccountService;
|
||||||
|
use App\Services\StatusService;
|
||||||
|
|
||||||
trait AdminReportController
|
trait AdminReportController
|
||||||
{
|
{
|
||||||
|
@ -33,6 +37,7 @@ trait AdminReportController
|
||||||
$report = Report::findOrFail($id);
|
$report = Report::findOrFail($id);
|
||||||
|
|
||||||
$this->handleReportAction($report, $action);
|
$this->handleReportAction($report, $action);
|
||||||
|
Cache::forget('admin-dash:reports:list-cache');
|
||||||
|
|
||||||
return response()->json(['msg'=> 'Success']);
|
return response()->json(['msg'=> 'Success']);
|
||||||
}
|
}
|
||||||
|
@ -52,17 +57,20 @@ trait AdminReportController
|
||||||
$item->is_nsfw = true;
|
$item->is_nsfw = true;
|
||||||
$item->save();
|
$item->save();
|
||||||
$report->nsfw = true;
|
$report->nsfw = true;
|
||||||
|
StatusService::del($item->id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'unlist':
|
case 'unlist':
|
||||||
$item->visibility = 'unlisted';
|
$item->visibility = 'unlisted';
|
||||||
$item->save();
|
$item->save();
|
||||||
Cache::forget('profiles:private');
|
Cache::forget('profiles:private');
|
||||||
|
StatusService::del($item->id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
// Todo: fire delete job
|
// Todo: fire delete job
|
||||||
$report->admin_seen = null;
|
$report->admin_seen = null;
|
||||||
|
StatusService::del($item->id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'shadowban':
|
case 'shadowban':
|
||||||
|
@ -115,4 +123,55 @@ trait AdminReportController
|
||||||
];
|
];
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function reportMailVerifications(Request $request)
|
||||||
|
{
|
||||||
|
$ids = Redis::smembers('email:manual');
|
||||||
|
$ignored = Redis::smembers('email:manual-ignored');
|
||||||
|
$reports = [];
|
||||||
|
if($ids) {
|
||||||
|
$reports = collect($ids)
|
||||||
|
->filter(function($id) use($ignored) {
|
||||||
|
return !in_array($id, $ignored);
|
||||||
|
})
|
||||||
|
->map(function($id) {
|
||||||
|
$account = AccountService::get($id);
|
||||||
|
$user = User::whereProfileId($id)->first();
|
||||||
|
if(!$user) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$account['email'] = $user->email;
|
||||||
|
return $account;
|
||||||
|
})
|
||||||
|
->filter(function($res) {
|
||||||
|
return isset($res['id']);
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
}
|
||||||
|
return view('admin.reports.mail_verification', compact('reports', 'ignored'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reportMailVerifyIgnore(Request $request)
|
||||||
|
{
|
||||||
|
$id = $request->input('id');
|
||||||
|
Redis::sadd('email:manual-ignored', $id);
|
||||||
|
return redirect('/i/admin/reports');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reportMailVerifyApprove(Request $request)
|
||||||
|
{
|
||||||
|
$id = $request->input('id');
|
||||||
|
$user = User::whereProfileId($id)->firstOrFail();
|
||||||
|
Redis::srem('email:manual', $id);
|
||||||
|
Redis::srem('email:manual-ignored', $id);
|
||||||
|
$user->email_verified_at = now();
|
||||||
|
$user->save();
|
||||||
|
return redirect('/i/admin/reports');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reportMailVerifyClearIgnored(Request $request)
|
||||||
|
{
|
||||||
|
Redis::del('email:manual-ignored');
|
||||||
|
return [200];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use App\{
|
||||||
use DB, Cache;
|
use DB, Cache;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
use App\Http\Controllers\Admin\{
|
use App\Http\Controllers\Admin\{
|
||||||
AdminDiscoverController,
|
AdminDiscoverController,
|
||||||
AdminInstanceController,
|
AdminInstanceController,
|
||||||
|
@ -28,6 +29,7 @@ use App\Http\Controllers\Admin\{
|
||||||
};
|
};
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use App\Services\AdminStatsService;
|
use App\Services\AdminStatsService;
|
||||||
|
use App\Services\StatusService;
|
||||||
use App\Services\StoryService;
|
use App\Services\StoryService;
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
|
@ -54,9 +56,15 @@ class AdminController extends Controller
|
||||||
|
|
||||||
public function statuses(Request $request)
|
public function statuses(Request $request)
|
||||||
{
|
{
|
||||||
$statuses = Status::orderBy('id', 'desc')->simplePaginate(10);
|
$statuses = Status::orderBy('id', 'desc')->cursorPaginate(10);
|
||||||
|
$data = $statuses->map(function($status) {
|
||||||
return view('admin.statuses.home', compact('statuses'));
|
return StatusService::get($status->id, false);
|
||||||
|
})
|
||||||
|
->filter(function($s) {
|
||||||
|
return $s;
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
return view('admin.statuses.home', compact('statuses', 'data'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showStatus(Request $request, $id)
|
public function showStatus(Request $request, $id)
|
||||||
|
@ -69,17 +77,45 @@ class AdminController extends Controller
|
||||||
public function reports(Request $request)
|
public function reports(Request $request)
|
||||||
{
|
{
|
||||||
$filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
|
$filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
|
||||||
$reports = Report::whereHas('status')
|
$page = $request->input('page') ?? 1;
|
||||||
->whereHas('reportedUser')
|
|
||||||
->whereHas('reporter')
|
$ai = Cache::remember('admin-dash:reports:ai-count', 3600, function() {
|
||||||
->orderBy('created_at','desc')
|
return AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count();
|
||||||
->when($filter, function($q, $filter) {
|
});
|
||||||
return $filter == 'open' ?
|
|
||||||
$q->whereNull('admin_seen') :
|
$spam = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
|
||||||
$q->whereNotNull('admin_seen');
|
return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
|
||||||
})
|
});
|
||||||
->paginate(6);
|
|
||||||
return view('admin.reports.home', compact('reports'));
|
$mailVerifications = Redis::scard('email:manual');
|
||||||
|
|
||||||
|
if($filter == 'open' && $page == 1) {
|
||||||
|
$reports = Cache::remember('admin-dash:reports:list-cache', 300, function() use($page, $filter) {
|
||||||
|
return Report::whereHas('status')
|
||||||
|
->whereHas('reportedUser')
|
||||||
|
->whereHas('reporter')
|
||||||
|
->orderBy('created_at','desc')
|
||||||
|
->when($filter, function($q, $filter) {
|
||||||
|
return $filter == 'open' ?
|
||||||
|
$q->whereNull('admin_seen') :
|
||||||
|
$q->whereNotNull('admin_seen');
|
||||||
|
})
|
||||||
|
->paginate(6);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$reports = Report::whereHas('status')
|
||||||
|
->whereHas('reportedUser')
|
||||||
|
->whereHas('reporter')
|
||||||
|
->orderBy('created_at','desc')
|
||||||
|
->when($filter, function($q, $filter) {
|
||||||
|
return $filter == 'open' ?
|
||||||
|
$q->whereNull('admin_seen') :
|
||||||
|
$q->whereNotNull('admin_seen');
|
||||||
|
})
|
||||||
|
->paginate(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showReport(Request $request, $id)
|
public function showReport(Request $request, $id)
|
||||||
|
@ -143,7 +179,7 @@ class AdminController extends Controller
|
||||||
|
|
||||||
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
||||||
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
return redirect('/i/admin/reports/autospam');
|
return redirect('/i/admin/reports/autospam');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,8 +192,11 @@ class AdminController extends Controller
|
||||||
$appeal->appeal_handled_at = now();
|
$appeal->appeal_handled_at = now();
|
||||||
$appeal->save();
|
$appeal->save();
|
||||||
|
|
||||||
|
StatusService::del($status->id);
|
||||||
|
|
||||||
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
||||||
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
|
|
||||||
return redirect('/i/admin/reports/autospam');
|
return redirect('/i/admin/reports/autospam');
|
||||||
}
|
}
|
||||||
|
@ -176,7 +215,7 @@ class AdminController extends Controller
|
||||||
if($action == 'dismiss') {
|
if($action == 'dismiss') {
|
||||||
$appeal->appeal_handled_at = now();
|
$appeal->appeal_handled_at = now();
|
||||||
$appeal->save();
|
$appeal->save();
|
||||||
|
Cache::forget('admin-dash:reports:ai-count');
|
||||||
return redirect('/i/admin/reports/appeals');
|
return redirect('/i/admin/reports/appeals');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +240,8 @@ class AdminController extends Controller
|
||||||
|
|
||||||
$appeal->appeal_handled_at = now();
|
$appeal->appeal_handled_at = now();
|
||||||
$appeal->save();
|
$appeal->save();
|
||||||
|
StatusService::del($status->id);
|
||||||
|
Cache::forget('admin-dash:reports:ai-count');
|
||||||
|
|
||||||
return redirect('/i/admin/reports/appeals');
|
return redirect('/i/admin/reports/appeals');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\{
|
use App\{
|
||||||
AccountInterstitial,
|
AccountInterstitial,
|
||||||
|
Bookmark,
|
||||||
DirectMessage,
|
DirectMessage,
|
||||||
DiscoverCategory,
|
DiscoverCategory,
|
||||||
Hashtag,
|
Hashtag,
|
||||||
|
@ -19,6 +20,7 @@ use App\{
|
||||||
UserFilter,
|
UserFilter,
|
||||||
};
|
};
|
||||||
use Auth,Cache;
|
use Auth,Cache;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use App\Transformer\Api\{
|
use App\Transformer\Api\{
|
||||||
|
@ -345,14 +347,18 @@ class InternalApiController extends Controller
|
||||||
|
|
||||||
public function bookmarks(Request $request)
|
public function bookmarks(Request $request)
|
||||||
{
|
{
|
||||||
$statuses = Auth::user()->profile
|
$res = Bookmark::whereProfileId($request->user()->profile_id)
|
||||||
->bookmarks()
|
->orderByDesc('created_at')
|
||||||
->withCount(['likes','comments'])
|
->simplePaginate(10)
|
||||||
->orderBy('created_at', 'desc')
|
->map(function($bookmark) {
|
||||||
->simplePaginate(10);
|
$status = StatusService::get($bookmark->status_id);
|
||||||
|
$status['bookmarked_at'] = $bookmark->created_at->format('c');
|
||||||
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
|
return $status;
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
})
|
||||||
|
->filter(function($bookmark) {
|
||||||
|
return isset($bookmark['id']);
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
@ -456,4 +462,18 @@ class InternalApiController extends Controller
|
||||||
$template = $status->in_reply_to_id ? 'status.reply' : 'status.remote';
|
$template = $status->in_reply_to_id ? 'status.reply' : 'status.remote';
|
||||||
return view($template, compact('user', 'status'));
|
return view($template, compact('user', 'status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requestEmailVerification(Request $request)
|
||||||
|
{
|
||||||
|
$pid = $request->user()->profile_id;
|
||||||
|
$exists = Redis::sismember('email:manual', $pid);
|
||||||
|
return view('account.email.request_verification', compact('exists'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requestEmailVerificationStore(Request $request)
|
||||||
|
{
|
||||||
|
$pid = $request->user()->profile_id;
|
||||||
|
Redis::sadd('email:manual', $pid);
|
||||||
|
return redirect('/i/verify-email')->with(['status' => 'Successfully sent manual verification request!']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ class EmailVerificationCheck
|
||||||
is_null($request->user()->email_verified_at) &&
|
is_null($request->user()->email_verified_at) &&
|
||||||
!$request->is(
|
!$request->is(
|
||||||
'i/auth/*',
|
'i/auth/*',
|
||||||
'i/verify-email',
|
'i/verify-email*',
|
||||||
'log*',
|
'log*',
|
||||||
'site*',
|
'site*',
|
||||||
'i/confirm-email/*',
|
'i/confirm-email/*',
|
||||||
|
|
37
resources/views/account/email/request_verification.blade.php
Normal file
37
resources/views/account/email/request_verification.blade.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<p class="font-weight-bold mb-0">{{ session('status') }}</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if (session('error'))
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<p class="font-weight-bold mb-0">{{ session('error') }}</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div class="card shadow-none border">
|
||||||
|
<div class="card-header font-weight-bold bg-white">Request Manual Email Verification</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="">If you are experiencing issues receiving your email address confirmation code to the email address you registered with, you can request manual verification as a last resort. An administrator will review your request.</p>
|
||||||
|
|
||||||
|
<p class="font-weight-bold">If you request manual email verification, you still may experience issues recieving emails from our service, including password reset requests.</p>
|
||||||
|
|
||||||
|
<p>In the event you need to reset your password and do not receive the password reset email, please contact the administrators.</p>
|
||||||
|
|
||||||
|
@if(!$exists)
|
||||||
|
<form method="post">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-primary btn-block py-1 font-weight-bold">I understand, proceed with request</button>
|
||||||
|
</form>
|
||||||
|
@else
|
||||||
|
<button class="btn btn-primary btn-block py-1 font-weight-bold" disabled>Verification Request Sent</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -13,19 +13,35 @@
|
||||||
<p class="font-weight-bold mb-0">{{ session('error') }}</p>
|
<p class="font-weight-bold mb-0">{{ session('error') }}</p>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if(Auth::user()->email_verified_at)
|
||||||
|
<p class="lead text-center mt-5">Your email is already verified. <a href="/" class="font-weight-bold">Click here</a> to go home.</p>
|
||||||
|
@else
|
||||||
<div class="card shadow-none border">
|
<div class="card shadow-none border">
|
||||||
<div class="card-header font-weight-bold bg-white">Confirm Email Address</div>
|
<div class="card-header font-weight-bold bg-white">Confirm Email Address</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="lead">You need to confirm your email address (<span class="font-weight-bold">{{Auth::user()->email}}</span>) before you can proceed.</p>
|
<p class="lead text-break">You need to confirm your email address <span class="font-weight-bold">{{Auth::user()->email}}</span> before you can proceed.</p>
|
||||||
<p class="lead">You can change your email address <a href="/settings/email">here</a>.</p>
|
@if(!$recentSent)
|
||||||
<p class="small">If you don't recieve an email within 30 minutes, you can <a href="/site/contact">contact the administrator</a>.</p>
|
|
||||||
<hr>
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="btn btn-primary btn-block py-1 font-weight-bold">Send Confirmation Email</button>
|
<button type="submit" class="btn btn-primary btn-block py-1 font-weight-bold">Send Confirmation Email</button>
|
||||||
</form>
|
</form>
|
||||||
|
@else
|
||||||
|
<button class="btn btn-primary btn-block py-1 font-weight-bold" disabled>Confirmation Email Sent</button>
|
||||||
|
@endif
|
||||||
|
<p class="mt-3 mb-0 small text-muted"><a href="/settings/email" class="font-weight-bold">Click here</a> to change your email address.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if($recentSent)
|
||||||
|
<div class="card mt-3 border shadow-none">
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="mb-0 text-muted">If you are experiencing issues receiving your email confirmation, you can <a href="/i/verify-email/request" class="font-weight-bold">request manual verification</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -15,11 +15,14 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@php($ai = App\AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count())
|
|
||||||
@php($spam = App\AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count())
|
@if($ai || $spam || $mailVerifications)
|
||||||
@if($ai || $spam)
|
|
||||||
<div class="col-12 col-md-8 offset-md-2">
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
|
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/email-verifications">
|
||||||
|
<p class="font-weight-bold h4 mb-0">{{$mailVerifications}}</p>
|
||||||
|
Email Verify {{$mailVerifications == 1 ? 'Request' : 'Requests'}}
|
||||||
|
</a>
|
||||||
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/appeals">
|
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/appeals">
|
||||||
<p class="font-weight-bold h4 mb-0">{{$ai}}</p>
|
<p class="font-weight-bold h4 mb-0">{{$ai}}</p>
|
||||||
Appeal {{$ai == 1 ? 'Request' : 'Requests'}}
|
Appeal {{$ai == 1 ? 'Request' : 'Requests'}}
|
||||||
|
|
75
resources/views/admin/reports/mail_verification.blade.php
Normal file
75
resources/views/admin/reports/mail_verification.blade.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
@extends('admin.partial.template-full')
|
||||||
|
|
||||||
|
@section('section')
|
||||||
|
<div class="title mb-3 d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h3 class="font-weight-bold d-inline-block">Email Verification Requests</h3>
|
||||||
|
@if($ignored)
|
||||||
|
<p>
|
||||||
|
You are ignoring <strong>{{ count($ignored) }}</strong> mail verification requests. <a href="#" class="font-weight-bold clear-ignored">Clear ignored requests</a>
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="float-right">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
|
<div class="card shadow-none border">
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
@foreach($reports as $report)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="media align-items-center">
|
||||||
|
<img src="{{ $report['avatar'] }}" width="50" height="50" class="rounded-circle border mr-3">
|
||||||
|
<div class="media-body">
|
||||||
|
<p class="font-weight-bold mb-0">{{ $report['username'] }}</p>
|
||||||
|
<p class="text-muted mb-0">{{ $report['email'] }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="action-btn btn btn-light font-weight-bold mr-2" data-action="ignore" data-id="{{$report['id']}}">Ignore</button>
|
||||||
|
<button class="action-btn btn btn-primary font-weight-bold" data-action="approve" data-id="{{$report['id']}}"><i class="far fa-check-circle fa-lg mr-2"></i>Approve</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@if(count($reports) == 0)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<p class="font-weight-bold mb-0">No email verification requests found!</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('.clear-ignored').click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if(!window.confirm('Are you sure you want to clear all ignored requests?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.post('/i/admin/reports/email-verifications/clear-ignored')
|
||||||
|
.then(res => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.action-btn').click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let type = e.currentTarget.getAttribute('data-action');
|
||||||
|
let id = e.currentTarget.getAttribute('data-id');
|
||||||
|
if(!window.confirm(`Are you sure you want to ${type} this email verification request?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.post('/i/admin/reports/email-verifications/' + type, {
|
||||||
|
id: id
|
||||||
|
}).then(res => {
|
||||||
|
location.href = '/i/admin/reports';
|
||||||
|
}).catch(err => {
|
||||||
|
swal('Oops!', 'An error occured', 'error');
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
|
@ -1,36 +1,63 @@
|
||||||
@extends('settings.template')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('section')
|
@section('content')
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="alert alert-primary px-3 h6 text-center">
|
||||||
|
{{ session('status') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger px-3 h6 text-center">
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<p class="font-weight-bold mb-1">{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if (session('error'))
|
||||||
|
<div class="alert alert-danger px-3 h6 text-center">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="title">
|
<div class="container">
|
||||||
<h3 class="font-weight-bold">Email Settings</h3>
|
<div class="col-12">
|
||||||
|
<div class="card shadow-none border mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3 p-md-5">
|
||||||
|
<div class="title">
|
||||||
|
<h3 class="font-weight-bold">Email Settings</h3>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<form method="post" action="{{route('settings.email')}}">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" class="form-control" name="name" value="{{Auth::user()->profile->name}}">
|
||||||
|
<input type="hidden" class="form-control" name="username" value="{{Auth::user()->profile->username}}">
|
||||||
|
<input type="hidden" class="form-control" name="website" value="{{Auth::user()->profile->website}}">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="font-weight-bold">Email Address</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" placeholder="Email Address" value="{{Auth::user()->email}}">
|
||||||
|
<p class="help-text small text-muted font-weight-bold">
|
||||||
|
@if(Auth::user()->email_verified_at)
|
||||||
|
<span class="text-success">Verified</span> {{Auth::user()->email_verified_at->diffForHumans()}}
|
||||||
|
@else
|
||||||
|
<span class="text-danger">Unverified</span> You need to <a href="/i/verify-email">verify your email</a>.
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12 text-right">
|
||||||
|
<button type="submit" class="btn btn-primary font-weight-bold py-0 px-5">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
</div>
|
||||||
<form method="post" action="{{route('settings.email')}}">
|
|
||||||
@csrf
|
|
||||||
<input type="hidden" class="form-control" name="name" value="{{Auth::user()->profile->name}}">
|
|
||||||
<input type="hidden" class="form-control" name="username" value="{{Auth::user()->profile->username}}">
|
|
||||||
<input type="hidden" class="form-control" name="website" value="{{Auth::user()->profile->website}}">
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="email" class="col-sm-3 col-form-label font-weight-bold text-right">Email</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input type="email" class="form-control" id="email" name="email" placeholder="Email Address" value="{{Auth::user()->email}}">
|
|
||||||
<p class="help-text small text-muted font-weight-bold">
|
|
||||||
@if(Auth::user()->email_verified_at)
|
|
||||||
<span class="text-success">Verified</span> {{Auth::user()->email_verified_at->diffForHumans()}}
|
|
||||||
@else
|
|
||||||
<span class="text-danger">Unverified</span> You need to <a href="/i/verify-email">verify your email</a>.
|
|
||||||
@endif
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-12 text-right">
|
|
||||||
<button type="submit" class="btn btn-primary font-weight-bold py-0 px-5">Submit</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
|
@ -1,38 +1,66 @@
|
||||||
@extends('settings.template')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('section')
|
@section('content')
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="alert alert-primary px-3 h6 text-center">
|
||||||
|
{{ session('status') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger px-3 h6 text-center">
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<p class="font-weight-bold mb-1">{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if (session('error'))
|
||||||
|
<div class="alert alert-danger px-3 h6 text-center">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="title">
|
<div class="container">
|
||||||
<h3 class="font-weight-bold">Update Password</h3>
|
<div class="col-12">
|
||||||
|
<div class="card shadow-none border mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3 p-md-5">
|
||||||
|
<div class="title">
|
||||||
|
<h3 class="font-weight-bold">Update Password</h3>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<form method="post">
|
||||||
|
@csrf
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="existing" class="col-sm-3 col-form-label font-weight-bold">Current</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" name="current" placeholder="Your current password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="new" class="col-sm-3 col-form-label font-weight-bold">New</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" name="password" placeholder="Enter new password here">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="confirm" class="col-sm-3 col-form-label font-weight-bold">Confirm</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" name="password_confirmation" placeholder="Confirm new password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12 text-right">
|
||||||
|
<button type="submit" class="btn btn-primary font-weight-bold py-0 px-5">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
</div>
|
||||||
<form method="post">
|
|
||||||
@csrf
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="existing" class="col-sm-3 col-form-label font-weight-bold">Current</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input type="password" class="form-control" name="current" placeholder="Your current password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="new" class="col-sm-3 col-form-label font-weight-bold">New</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input type="password" class="form-control" name="password" placeholder="Enter new password here">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="confirm" class="col-sm-3 col-form-label font-weight-bold">Confirm</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input type="password" class="form-control" name="password_confirmation" placeholder="Confirm new password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-12 text-right">
|
|
||||||
<button type="submit" class="btn btn-primary font-weight-bold py-0 px-5">Submit</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
|
@ -1,33 +1,42 @@
|
||||||
@extends('settings.template')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('section')
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow-none border mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3 p-md-5">
|
||||||
|
<div class="title">
|
||||||
|
<h3 class="font-weight-bold">Edit Two-Factor Authentication</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="title">
|
<hr>
|
||||||
<h3 class="font-weight-bold">Edit Two-Factor Authentication</h3>
|
|
||||||
|
<p class="lead pb-3">
|
||||||
|
To register a new device, you have to remove any active devices.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-light font-weight-bold">
|
||||||
|
Authenticator App
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
|
<i class="fas fa-lock fa-3x text-success"></i>
|
||||||
|
<p class="font-weight-bold mb-0">
|
||||||
|
Added {{$user->{'2fa_setup_at'}->diffForHumans()}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer bg-white text-right">
|
||||||
|
<a class="btn btn-outline-secondary btn-sm px-4 font-weight-bold mr-3" href="{{route('settings.security.2fa.recovery')}}">View Recovery Codes</a>
|
||||||
|
<a class="btn btn-outline-danger btn-sm px-4 font-weight-bold remove-device" href="#">Remove</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<hr>
|
|
||||||
|
|
||||||
<p class="lead pb-3">
|
|
||||||
To register a new device, you have to remove any active devices.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header bg-light font-weight-bold">
|
|
||||||
Authenticator App
|
|
||||||
</div>
|
|
||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
|
||||||
<i class="fas fa-lock fa-3x text-success"></i>
|
|
||||||
<p class="font-weight-bold mb-0">
|
|
||||||
Added {{$user->{'2fa_setup_at'}->diffForHumans()}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer bg-white text-right">
|
|
||||||
<a class="btn btn-outline-secondary btn-sm px-4 font-weight-bold mr-3" href="{{route('settings.security.2fa.recovery')}}">View Recovery Codes</a>
|
|
||||||
<a class="btn btn-outline-danger btn-sm px-4 font-weight-bold remove-device" href="#">Remove</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
|
|
@ -1,32 +1,42 @@
|
||||||
@extends('settings.template')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('section')
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow-none border mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3 p-md-5">
|
||||||
|
<div class="title">
|
||||||
|
<h3 class="font-weight-bold">Two-Factor Authentication Recovery Codes</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="title">
|
<hr>
|
||||||
<h3 class="font-weight-bold">Two-Factor Authentication Recovery Codes</h3>
|
@if(count($codes) > 0)
|
||||||
</div>
|
<p class="lead pb-3">
|
||||||
|
Each code can only be used once.
|
||||||
<hr>
|
</p>
|
||||||
@if(count($codes) > 0)
|
<ul class="list-group">
|
||||||
<p class="lead pb-3">
|
@foreach($codes as $code)
|
||||||
Each code can only be used once.
|
<li class="list-group-item"><code>{{$code}}</code></li>
|
||||||
</p>
|
@endforeach
|
||||||
<ul class="list-group">
|
</ul>
|
||||||
@foreach($codes as $code)
|
@else
|
||||||
<li class="list-group-item"><code>{{$code}}</code></li>
|
<div class="pt-5">
|
||||||
@endforeach
|
<h4 class="font-weight-bold">You are out of recovery codes</h4>
|
||||||
</ul>
|
<p class="lead">Generate more recovery codes and store them in a safe place.</p>
|
||||||
@else
|
<p>
|
||||||
<div class="pt-5">
|
<form method="post">
|
||||||
<h4 class="font-weight-bold">You are out of recovery codes</h4>
|
@csrf
|
||||||
<p class="lead">Generate more recovery codes and store them in a safe place.</p>
|
<button type="submit" class="btn btn-primary font-weight-bold">Generate Recovery Codes</button>
|
||||||
<p>
|
</form>
|
||||||
<form method="post">
|
</p>
|
||||||
@csrf
|
</div>
|
||||||
<button type="submit" class="btn btn-primary font-weight-bold">Generate Recovery Codes</button>
|
@endif
|
||||||
</form>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
</div>
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
|
@ -1,85 +1,97 @@
|
||||||
@extends('settings.template')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('section')
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow-none border mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3 p-md-5">
|
||||||
|
<div class="title">
|
||||||
|
<h3 class="font-weight-bold">Setup Two-Factor Authentication</h3>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="alert alert-info font-weight-light mb-3">
|
||||||
|
We only support Two-Factor Authentication via TOTP mobile apps.
|
||||||
|
</div>
|
||||||
|
<section class="step-one pb-5">
|
||||||
|
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step1" aria-expanded="true" aria-controls="step1" data-step="1">
|
||||||
|
Step 1: Install compatible 2FA mobile app <i class="float-right fas fa-chevron-down"></i>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="collapse show" id="step1">
|
||||||
|
<p>You will need to install a compatible mobile app, we recommend the following apps:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://1password.com/downloads/" rel="nooopener nofollow">1Password</a></li>
|
||||||
|
<li><a href="https://authy.com/download/" rel="nooopener nofollow">Authy</a></li>
|
||||||
|
<li><a href="https://lastpass.com/auth/" rel="nooopener nofollow">LastPass Authenticator</a></li>
|
||||||
|
<li>
|
||||||
|
Google Authenticator
|
||||||
|
<a class="small" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_CA" rel="nooopener nofollow">
|
||||||
|
(android)
|
||||||
|
</a>
|
||||||
|
<a class="small" href="https://itunes.apple.com/ca/app/google-authenticator/id388497605?mt=8" rel="nooopener nofollow">
|
||||||
|
(iOS)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="https://www.microsoft.com/en-us/account/authenticator" rel="nooopener nofollow">Microsoft Authenticator</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="title">
|
<section class="step-two pb-5">
|
||||||
<h3 class="font-weight-bold">Setup Two-Factor Authentication</h3>
|
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step2" aria-expanded="false" aria-controls="step2" data-step="2">
|
||||||
|
Step 2: Scan QR Code and confirm <i class="float-right fas fa-chevron-down"></i>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="collapse" id="step2">
|
||||||
|
<p>Please scan the QR code and then enter the 6 digit code in the form below. Keep in mind the code changes every 30 seconds, and is only good for 1 minute.</p>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div class="pb-3">
|
||||||
|
<p class="font-weight-bold">QR Code</p>
|
||||||
|
<img src="data:image/png;base64,{{$qrcode}}" class="img-fluid" width="200px">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="font-weight-bold">OTP Secret</p>
|
||||||
|
<input type="text" class="form-control" value="{{ $user->{'2fa_secret'} }}" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form id="confirm-code">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="font-weight-bold small">Code</label>
|
||||||
|
<input type="text" name="code" id="verifyCode" class="form-control" placeholder="Code" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary font-weight-bold">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="step-three pb-5">
|
||||||
|
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step3" aria-expanded="true" aria-controls="step3" data-step="3">
|
||||||
|
Step 3: Download Backup Codes <i class="float-right fas fa-chevron-down"></i>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="collapse" id="step3">
|
||||||
|
<p>Please store the following codes in a safe place, each backup code can be used only once if you do not have access to your 2FA mobile app.</p>
|
||||||
|
|
||||||
|
<code>
|
||||||
|
@foreach($backups as $code)
|
||||||
|
<p class="mb-0">{{$code}}</p>
|
||||||
|
@endforeach
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
</div>
|
||||||
<div class="alert alert-info font-weight-light mb-3">
|
|
||||||
We only support Two-Factor Authentication via TOTP mobile apps.
|
|
||||||
</div>
|
|
||||||
<section class="step-one pb-5">
|
|
||||||
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step1" aria-expanded="true" aria-controls="step1" data-step="1">
|
|
||||||
Step 1: Install compatible 2FA mobile app <i class="float-right fas fa-chevron-down"></i>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="collapse show" id="step1">
|
|
||||||
<p>You will need to install a compatible mobile app, we recommend the following apps:</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://1password.com/downloads/" rel="nooopener nofollow">1Password</a></li>
|
|
||||||
<li><a href="https://authy.com/download/" rel="nooopener nofollow">Authy</a></li>
|
|
||||||
<li><a href="https://lastpass.com/auth/" rel="nooopener nofollow">LastPass Authenticator</a></li>
|
|
||||||
<li>
|
|
||||||
Google Authenticator
|
|
||||||
<a class="small" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_CA" rel="nooopener nofollow">
|
|
||||||
(android)
|
|
||||||
</a>
|
|
||||||
<a class="small" href="https://itunes.apple.com/ca/app/google-authenticator/id388497605?mt=8" rel="nooopener nofollow">
|
|
||||||
(iOS)
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li><a href="https://www.microsoft.com/en-us/account/authenticator" rel="nooopener nofollow">Microsoft Authenticator</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="step-two pb-5">
|
|
||||||
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step2" aria-expanded="false" aria-controls="step2" data-step="2">
|
|
||||||
Step 2: Scan QR Code and confirm <i class="float-right fas fa-chevron-down"></i>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="collapse" id="step2">
|
|
||||||
<p>Please scan the QR code and then enter the 6 digit code in the form below. Keep in mind the code changes every 30 seconds, and is only good for 1 minute.</p>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<div class="pb-3">
|
|
||||||
<p class="font-weight-bold">QR Code</p>
|
|
||||||
<img src="data:image/png;base64,{{$qrcode}}" class="img-fluid" width="200px">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="font-weight-bold">OTP Secret</p>
|
|
||||||
<input type="text" class="form-control" value="{{ $user->{'2fa_secret'} }}" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<form id="confirm-code">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="font-weight-bold small">Code</label>
|
|
||||||
<input type="text" name="code" id="verifyCode" class="form-control" placeholder="Code" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary font-weight-bold">Submit</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="step-three pb-5">
|
|
||||||
<div class="sub-title font-weight-bold h5" data-toggle="collapse" data-target="#step3" aria-expanded="true" aria-controls="step3" data-step="3">
|
|
||||||
Step 3: Download Backup Codes <i class="float-right fas fa-chevron-down"></i>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="collapse" id="step3">
|
|
||||||
<p>Please store the following codes in a safe place, each backup code can be used only once if you do not have access to your 2FA mobile app.</p>
|
|
||||||
|
|
||||||
<code>
|
|
||||||
@foreach($backups as $code)
|
|
||||||
<p class="mb-0">{{$code}}</p>
|
|
||||||
@endforeach
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
|
|
@ -14,6 +14,10 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
|
||||||
Route::get('reports/appeals', 'AdminController@appeals');
|
Route::get('reports/appeals', 'AdminController@appeals');
|
||||||
Route::get('reports/appeal/{id}', 'AdminController@showAppeal');
|
Route::get('reports/appeal/{id}', 'AdminController@showAppeal');
|
||||||
Route::post('reports/appeal/{id}', 'AdminController@updateAppeal');
|
Route::post('reports/appeal/{id}', 'AdminController@updateAppeal');
|
||||||
|
Route::get('reports/email-verifications', 'AdminController@reportMailVerifications');
|
||||||
|
Route::post('reports/email-verifications/ignore', 'AdminController@reportMailVerifyIgnore');
|
||||||
|
Route::post('reports/email-verifications/approve', 'AdminController@reportMailVerifyApprove');
|
||||||
|
Route::post('reports/email-verifications/clear-ignored', 'AdminController@reportMailVerifyClearIgnored');
|
||||||
Route::redirect('stories', '/stories/list');
|
Route::redirect('stories', '/stories/list');
|
||||||
Route::get('stories/list', 'AdminController@stories')->name('admin.stories');
|
Route::get('stories/list', 'AdminController@stories')->name('admin.stories');
|
||||||
Route::redirect('statuses', '/statuses/list');
|
Route::redirect('statuses', '/statuses/list');
|
||||||
|
@ -273,6 +277,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
||||||
|
|
||||||
Route::get('verify-email', 'AccountController@verifyEmail');
|
Route::get('verify-email', 'AccountController@verifyEmail');
|
||||||
Route::post('verify-email', 'AccountController@sendVerifyEmail');
|
Route::post('verify-email', 'AccountController@sendVerifyEmail');
|
||||||
|
Route::get('verify-email/request', 'InternalApiController@requestEmailVerification');
|
||||||
|
Route::post('verify-email/request', 'InternalApiController@requestEmailVerificationStore');
|
||||||
Route::get('confirm-email/{userToken}/{randomToken}', 'AccountController@confirmVerifyEmail');
|
Route::get('confirm-email/{userToken}/{randomToken}', 'AccountController@confirmVerifyEmail');
|
||||||
|
|
||||||
Route::get('auth/sudo', 'AccountController@sudoMode');
|
Route::get('auth/sudo', 'AccountController@sudoMode');
|
||||||
|
|
Loading…
Reference in a new issue