Skip to content

Commit aac1dc4

Browse files
committed
Admin panel
1 parent 362495f commit aac1dc4

File tree

8 files changed

+455
-10
lines changed

8 files changed

+455
-10
lines changed

routes/admin.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Atomjoy\Apilogin\Http\Controllers\Admin\PasswordResetController as AdminPasswordResetController;
88
use Atomjoy\Apilogin\Http\Controllers\Admin\LoggedController as AdminLoggedController;
99
use Atomjoy\Apilogin\Http\Controllers\Admin\LogoutController as AdminLogoutController;
10+
use Atomjoy\Apilogin\Http\Controllers\Admin\PasswordChangeController as AdminPasswordChangeController;
11+
use Atomjoy\Apilogin\Http\Controllers\Admin\UploadAvatarController as AdminUploadAvatarController;
1012

1113
// Admin panel
1214
Route::prefix('web/api/admin')->name('web.api.admin')->middleware([
@@ -19,26 +21,41 @@
1921
Route::get('/logged', [AdminLoggedController::class, 'index'])->name('logged');
2022
Route::post('/f2a', [AdminF2aController::class, 'index'])->name('f2a');
2123

22-
// Private routes (guard admin)
24+
// Private admin, worker routes (guard admin)
2325
Route::middleware([
2426
'auth:admin', 'apilogin_is_admin',
2527
'role:' . config(
26-
'apilogin.allowed_admin_roles',
28+
'apilogin.allowed_worker_roles',
2729
'super_admin|admin|worker'
2830
) . ',admin'
2931
])->group(function () {
30-
// 2FA auth on/off
32+
// Admin, worker routes
33+
Route::post('/password/change', [AdminPasswordChangeController::class, 'index'])->name('change');
3134
Route::post('/f2a/enable', [AdminF2aController::class, 'enable'])->name('f2a.enable');
3235
Route::post('/f2a/disable', [AdminF2aController::class, 'disable'])->name('f2a.disable');
36+
Route::post('/upload/avatar', [AdminUploadAvatarController::class, 'index'])->name('upload.avatar');
37+
Route::post('/remove/avatar', [AdminUploadAvatarController::class, 'remove'])->name('remove.avatar');
3338

34-
// Admin panel routes
35-
// ...
36-
37-
// Test route
3839
Route::get('/test', function () {
3940
return response()->json([
4041
'message' => 'Authenticated.'
4142
]);
4243
})->middleware('throttle:20,1'); // 20/min
4344
});
45+
46+
// Private admin routes (guard admin)
47+
Route::middleware([
48+
'auth:admin', 'apilogin_is_admin',
49+
'role:' . config(
50+
'apilogin.allowed_admin_roles',
51+
'super_admin|admin'
52+
) . ',admin'
53+
])->group(function () {
54+
// Admin only routes
55+
Route::get('/test/admin', function () {
56+
return response()->json([
57+
'message' => 'Authenticated.'
58+
]);
59+
})->middleware('throttle:20,1'); // 20/min
60+
});
4461
});

src/Events/PasswordChange.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Atomjoy\Apilogin\Events;
44

55
use App\Models\User;
6+
use Atomjoy\Apilogin\Models\Admin;
67
use Illuminate\Broadcasting\Channel;
78
use Illuminate\Broadcasting\InteractsWithSockets;
89
use Illuminate\Broadcasting\PresenceChannel;
@@ -20,7 +21,7 @@ class PasswordChange
2021
*
2122
* @return void
2223
*/
23-
public function __construct(public User $user)
24+
public function __construct(public User|Admin $user)
2425
{
2526
}
2627

src/Events/UploadAvatar.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Atomjoy\Apilogin\Events;
44

55
use App\Models\User;
6+
use Atomjoy\Apilogin\Models\Admin;
67
use Illuminate\Broadcasting\Channel;
78
use Illuminate\Broadcasting\InteractsWithSockets;
89
use Illuminate\Broadcasting\PresenceChannel;
@@ -21,7 +22,7 @@ class UploadAvatar
2122
* @return void
2223
*/
2324
public function __construct(
24-
public User $user,
25+
public User|Admin $user,
2526
public $path
2627
) {
2728
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Atomjoy\Apilogin\Http\Controllers\Admin;
4+
5+
use App\Http\Controllers\Controller;
6+
use Atomjoy\Apilogin\Exceptions\JsonException;
7+
use Atomjoy\Apilogin\Http\Requests\PasswordChangeRequest;
8+
use Atomjoy\Apilogin\Events\PasswordChange;
9+
use Atomjoy\Apilogin\Events\PasswordChangeError;
10+
use Illuminate\Support\Facades\Auth;
11+
use Illuminate\Support\Facades\Hash;
12+
use Exception;
13+
14+
class PasswordChangeController extends Controller
15+
{
16+
function index(PasswordChangeRequest $request)
17+
{
18+
$valid = $request->validated();
19+
20+
if (Auth::guard('admin')->check()) {
21+
$user = Auth::guard('admin')->user();
22+
23+
if (Hash::check($valid['password_current'], $user->password)) {
24+
try {
25+
// Tests error
26+
$request->testDatabase();
27+
28+
$user->update([
29+
'password' => Hash::make($valid['password']),
30+
]);
31+
32+
PasswordChange::dispatch($user);
33+
return response()->json([
34+
'message' => __('apilogin.change.success')
35+
], 200);
36+
} catch (Exception $e) {
37+
report($e);
38+
PasswordChangeError::dispatch($valid);
39+
throw new JsonException(__('apilogin.change.error'), 422);
40+
}
41+
} else {
42+
PasswordChangeError::dispatch($valid);
43+
throw new JsonException(__('apilogin.change.invalid.current.password'), 422);
44+
}
45+
} else {
46+
PasswordChangeError::dispatch($valid);
47+
throw new JsonException(__('apilogin.change.unauthenticated.'), 422);
48+
}
49+
}
50+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
namespace Atomjoy\Apilogin\Http\Controllers\Admin;
4+
5+
use Exception;
6+
use App\Http\Controllers\Controller;
7+
use Atomjoy\Apilogin\Events\UploadAvatar;
8+
use Atomjoy\Apilogin\Exceptions\JsonException;
9+
use Atomjoy\Apilogin\Http\Requests\UploadAvatarRequest;
10+
use Illuminate\Http\Request;
11+
use Illuminate\Support\Facades\Auth;
12+
use Illuminate\Support\Facades\Storage;
13+
use Response;
14+
15+
class UploadAvatarController extends Controller
16+
{
17+
protected $disk = 's3';
18+
19+
function index(UploadAvatarRequest $request)
20+
{
21+
try {
22+
Auth::shouldUse('admin');
23+
24+
$user = Auth::user();
25+
26+
$filename = $user->id . '.webp';
27+
28+
// $path = $request->file('avatar')->storeAs('avatars/admin', $filename, 'public');
29+
30+
$path = Storage::disk($this->disk)
31+
->putFileAs(
32+
'avatars/admin',
33+
$request->file('avatar'),
34+
$filename
35+
);
36+
37+
$user->avatar = $path;
38+
$user->save();
39+
40+
UploadAvatar::dispatch($user, $path);
41+
42+
return response()->json([
43+
'message' => __('apilogin.upload.avatar.success'),
44+
'avatar' => $path,
45+
], 200);
46+
} catch (Exception $e) {
47+
report($e);
48+
throw new JsonException(__('apilogin.upload.avatar.error'), 422);
49+
}
50+
}
51+
52+
function remove(Request $request)
53+
{
54+
try {
55+
Auth::shouldUse('admin');
56+
57+
$filename = 'avatars/admin/' . Auth::id() . '.webp';
58+
59+
if (Storage::disk($this->disk)->exists($filename)) {
60+
Storage::disk($this->disk)->delete($filename);
61+
Auth::user()->update(['avatar' => null]);
62+
}
63+
64+
return response()->json([
65+
'message' => __('apilogin.remove.avatar.success'),
66+
], 200);
67+
} catch (Exception $e) {
68+
report($e);
69+
throw new JsonException(__('apilogin.remove.avatar.error'), 422);
70+
}
71+
}
72+
73+
public function show()
74+
{
75+
return $this->showAvatar();
76+
}
77+
78+
/**
79+
* Show avatar only for logged in users.
80+
*/
81+
public function showAvatar($default_avatar = 'js/components/input/profil/avatar.png')
82+
{
83+
try {
84+
Auth::shouldUse('admin');
85+
86+
$id = Auth::id() ?? 'error';
87+
88+
$filename = '/avatars/admin/' . $id . '.webp';
89+
90+
$exists = Storage::disk($this->disk)->exists($filename);
91+
92+
if ($exists) {
93+
$mime = Storage::disk($this->disk)->mimeType($filename);
94+
95+
$content = Storage::disk($this->disk)->get($filename);
96+
97+
$response = Response::make($content, 200);
98+
99+
$response->header("Content-Type", $mime);
100+
101+
return $response;
102+
} else {
103+
$default = resource_path($default_avatar);
104+
105+
if (!file_exists($default)) {
106+
$default = fake()->image(
107+
null,
108+
128,
109+
128,
110+
null,
111+
true,
112+
true,
113+
'avatar',
114+
true,
115+
'png'
116+
);
117+
}
118+
119+
return response(
120+
file_get_contents($default)
121+
)->header('Content-Type', 'image/png');
122+
}
123+
} catch (Exception $e) {
124+
report($e);
125+
throw new JsonException(__('apilogin.show.avatar.error'), 422);
126+
}
127+
}
128+
}

src/Http/Requests/PasswordChangeRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Exception;
66
use App\Models\User;
7+
use Atomjoy\Apilogin\Models\Admin;
78
use Illuminate\Contracts\Validation\Validator;
89
use Illuminate\Foundation\Http\FormRequest;
910
use Illuminate\Validation\ValidationException;
@@ -15,7 +16,7 @@ class PasswordChangeRequest extends FormRequest
1516

1617
public function authorize()
1718
{
18-
if (auth()->user() instanceof User) {
19+
if (auth()->user() instanceof User or auth()->user() instanceof Admin) {
1920
return true; // Allow logged
2021
}
2122

0 commit comments

Comments
 (0)