add notifications logic and add support of both JWT and Sanctum

This commit is contained in:
root
2026-03-11 01:20:31 -04:00
parent f6be51576c
commit 182036cc41
141 changed files with 8685 additions and 648 deletions
@@ -0,0 +1,115 @@
<?php
namespace App\Http\Controllers\Api\Auth;
use App\Http\Controllers\Api\BaseApiController;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
class AuthController extends BaseApiController
{
public function login(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'email' => ['required', 'email'],
'password' => ['required', 'string'],
]);
if ($validator->fails()) {
return $this->respondValidationError($validator->errors()->toArray());
}
$email = (string) $request->input('email');
$password = (string) $request->input('password');
$user = User::query()->where('email', $email)->first();
if (!$user || !ci_password_verify($password, (string) $user->password)) {
return $this->respondError('Invalid credentials.', 401);
}
if ((int) ($user->is_verified ?? 0) !== 1) {
return $this->respondError('Account not verified.', 403);
}
if (strcasecmp((string) ($user->status ?? ''), 'Active') !== 0) {
return $this->respondError('Account is inactive.', 403);
}
$jwtToken = JWTAuth::fromUser($user);
$sanctumToken = $user->createToken('api')->plainTextToken;
return $this->respondSuccess([
'user' => [
'id' => (int) $user->id,
'firstname' => $user->firstname,
'lastname' => $user->lastname,
'email' => $user->email,
],
'jwt' => [
'access_token' => $jwtToken,
'token_type' => 'bearer',
'expires_in' => (int) config('jwt.ttl') * 60,
],
'sanctum' => [
'access_token' => $sanctumToken,
'token_type' => 'bearer',
],
], 'Authenticated.');
}
public function refresh(Request $request): JsonResponse
{
try {
$newToken = JWTAuth::parseToken()->refresh();
} catch (\Throwable $e) {
return $this->respondError('Token refresh failed.', 401);
}
return $this->respondSuccess([
'access_token' => $newToken,
'token_type' => 'bearer',
'expires_in' => (int) config('jwt.ttl') * 60,
], 'Token refreshed.');
}
public function me(): JsonResponse
{
$user = Auth::user();
if (!$user) {
return $this->respondError('Unauthorized.', 401);
}
return $this->respondSuccess([
'id' => (int) $user->id,
'firstname' => $user->firstname,
'lastname' => $user->lastname,
'email' => $user->email,
], 'OK');
}
public function logout(Request $request): JsonResponse
{
$token = $request->bearerToken();
if ($token && substr_count($token, '.') === 2) {
try {
JWTAuth::setToken($token)->invalidate();
} catch (\Throwable $e) {
// ignore invalidation errors
}
}
$user = $request->user();
if ($user && method_exists($user, 'currentAccessToken')) {
$current = $user->currentAccessToken();
if ($current) {
$current->delete();
}
}
return $this->respondSuccess(null, 'Logged out.');
}
}
@@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers\Api\Notifications;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Notifications\NotificationActiveRequest;
use App\Http\Requests\Notifications\NotificationDeletedRequest;
use App\Http\Requests\Notifications\NotificationIndexRequest;
use App\Http\Requests\Notifications\NotificationSendRequest;
use App\Http\Requests\Notifications\NotificationUpdateRequest;
use App\Http\Resources\Notifications\NotificationResource;
use App\Http\Resources\Notifications\UserNotificationResource;
use App\Services\Notifications\NotificationActiveService;
use App\Services\Notifications\NotificationDeletedService;
use App\Services\Notifications\NotificationManagementService;
use App\Services\Notifications\NotificationReadService;
use App\Services\Notifications\NotificationSendService;
use App\Services\Notifications\NotificationShowService;
use App\Services\Notifications\NotificationUserListService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
class NotificationController extends BaseApiController
{
public function __construct(
private NotificationUserListService $listService,
private NotificationSendService $sendService,
private NotificationReadService $readService,
private NotificationActiveService $activeService,
private NotificationDeletedService $deletedService,
private NotificationManagementService $managementService,
private NotificationShowService $showService
) {
parent::__construct();
}
public function index(NotificationIndexRequest $request): JsonResponse
{
$userId = (int) (auth()->id() ?? 0);
if ($userId <= 0) {
return response()->json(['ok' => false, 'message' => 'Unauthorized.'], 401);
}
$payload = $request->validated();
$result = $this->listService->listForUser($userId, $payload);
return response()->json([
'ok' => true,
'notifications' => UserNotificationResource::collection($result['notifications']),
'pagination' => $result['pagination'],
]);
}
public function show(int $notificationId): JsonResponse
{
$userId = (int) (auth()->id() ?? 0);
if ($userId <= 0) {
return response()->json(['ok' => false, 'message' => 'Unauthorized.'], 401);
}
$row = $this->showService->getForUser($userId, $notificationId);
if (!$row) {
return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404);
}
return response()->json([
'ok' => true,
'notification' => new UserNotificationResource($row),
]);
}
public function store(NotificationSendRequest $request): JsonResponse
{
$payload = $request->validated();
$actorId = (int) (auth()->id() ?? 0);
try {
$result = $this->sendService->send($payload, $actorId);
} catch (\Throwable $e) {
Log::error('Notification send failed.', ['error' => $e->getMessage()]);
return response()->json(['ok' => false, 'message' => 'Failed to send notification.'], 500);
}
if (empty($result['ok'])) {
return response()->json(['ok' => false, 'message' => 'Failed to send notification.'], 422);
}
return response()->json([
'ok' => true,
'notification' => new NotificationResource($result['notification']),
'recipient_count' => $result['recipient_count'] ?? 0,
], 201);
}
public function update(NotificationUpdateRequest $request, int $notificationId): JsonResponse
{
$payload = $request->validated();
$result = $this->managementService->update($notificationId, $payload);
if (empty($result['ok'])) {
return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Failed to update notification.'], 404);
}
return response()->json([
'ok' => true,
'notification' => new NotificationResource($result['notification']),
]);
}
public function destroy(int $notificationId): JsonResponse
{
$deleted = $this->managementService->delete($notificationId);
if (!$deleted) {
return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404);
}
return response()->json(['ok' => true]);
}
public function restore(int $notificationId): JsonResponse
{
$restored = $this->managementService->restore($notificationId);
if (!$restored) {
return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404);
}
return response()->json(['ok' => true]);
}
public function markRead(int $notificationId): JsonResponse
{
$userId = (int) (auth()->id() ?? 0);
if ($userId <= 0) {
return response()->json(['ok' => false, 'message' => 'Unauthorized.'], 401);
}
$ok = $this->readService->markRead($userId, $notificationId);
if (!$ok) {
return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404);
}
return response()->json(['ok' => true]);
}
public function active(NotificationActiveRequest $request): JsonResponse
{
$payload = $request->validated();
$data = $this->activeService->list($payload['target_group'] ?? null);
return response()->json([
'ok' => true,
'notifications' => $data['notifications'],
]);
}
public function deleted(NotificationDeletedRequest $request): JsonResponse
{
$data = $this->deletedService->list();
return response()->json([
'ok' => true,
'notifications' => $data['notifications'],
]);
}
}