From cc4e174880b0051d6c0d75ad08d960619b98d3db Mon Sep 17 00:00:00 2001 From: Giuseppe Naponiello Date: Mon, 22 Jun 2026 14:31:44 +0200 Subject: [PATCH] auth tests --- .env.example | 21 +- Makefile | 21 +- backend/app/Actions/Fortify/CreateNewUser.php | 43 + .../Fortify/PasswordValidationRules.php | 19 + .../app/Actions/Fortify/ResetUserPassword.php | 32 + .../Actions/Fortify/UpdateUserPassword.php | 35 + .../Fortify/UpdateUserProfileInformation.php | 61 + backend/app/Actions/PurgeUserAction.php | 82 + .../Commands/PurgeAnonymizableUsers.php | 54 + .../CannotDeleteSystemUserException.php | 17 + .../app/Http/Controllers/AuthController.php | 72 + .../app/Http/Controllers/UserController.php | 85 + .../Http/Controllers/UserRoleController.php | 78 + .../Http/Middleware/EnsureSetupComplete.php | 40 + .../app/Http/Middleware/ForceJsonResponse.php | 22 + backend/app/Http/Middleware/IsAdmin.php | 25 + .../app/Http/Requests/IndexUserRequest.php | 31 + .../Http/Requests/StoreUserRoleRequest.php | 28 + .../Http/Requests/UpdateUserRoleRequest.php | 36 + backend/app/Http/Traits/ApiResponse.php | 130 ++ backend/app/Models/Lists/UserRole.php | 57 + backend/app/Models/User.php | 92 +- .../ResetPasswordNotification.php | 54 + .../app/Notifications/WelcomeNotification.php | 53 + backend/app/Providers/AppServiceProvider.php | 7 +- .../app/Providers/FortifyServiceProvider.php | 56 + backend/bootstrap/app.php | 30 +- backend/bootstrap/providers.php | 2 + backend/composer.json | 2 +- backend/config/audit.php | 204 +++ backend/config/cors.php | 45 + backend/config/fortify.php | 160 ++ backend/config/messages.php | 37 + backend/config/sanctum.php | 102 ++ .../factories/Lists/UserRoleFactory.php | 25 + ...026_06_18_162047_add_users_soft_delete.php | 28 + ..._06_18_162204_create_users_roles_table.php | 29 + ...6_18_162324_add_role_id_to_users_table.php | 27 + ...dd_must_change_password_to_users_table.php | 28 + ...s_system_and_anonymized_to_users_table.php | 30 + ..._add_two_factor_columns_to_users_table.php | 42 + .../2026_06_19_073346_create_audits_table.php | 48 + ...add_setup_2fa_completed_to_users_table.php | 27 + backend/database/seeders/DatabaseSeeder.php | 12 +- backend/database/seeders/SystemUserSeeder.php | 41 + backend/database/seeders/UserRoleSeeder.php | 42 + backend/phpunit.xml | 34 +- backend/routes/api.php | 36 + backend/routes/api/user-roles.php | 18 + backend/routes/api/users.php | 11 + backend/routes/console.php | 4 + backend/tests/Concerns/CreatesUsers.php | 66 + backend/tests/Feature/Auth/AccessTierTest.php | 73 + .../tests/Feature/Auth/AuthControllerTest.php | 86 ++ .../tests/Feature/EnvironmentSafetyTest.php | 24 + backend/tests/Feature/ExampleTest.php | 19 - .../PurgeAnonymizableUsersCommandTest.php | 77 + backend/tests/Feature/PurgeUserActionTest.php | 55 + backend/tests/Feature/SeederTest.php | 73 + backend/tests/Feature/UserControllerTest.php | 170 ++ .../tests/Feature/UserRoleControllerTest.php | 163 ++ .../Support/UnsafeTestDatabaseException.php | 21 + backend/tests/TestCase.php | 24 +- backend/tests/Unit/ExampleTest.php | 16 - docker-compose.override.yml | 35 + docker-compose.yml | 11 +- frontend/.stylelintrc.json | 26 + frontend/package-lock.json | 1361 +++++++++++++++++ frontend/package.json | 4 + frontend/public/logo/logo_dyncoll.png | Bin 101684 -> 88989 bytes frontend/src/config/menuTypes.ts | 104 +- frontend/src/config/ui.ts | 25 +- frontend/src/styles/main.css | 73 +- frontend/template.html.example | 24 - frontend/vite.config.ts | 2 - 75 files changed, 4679 insertions(+), 168 deletions(-) create mode 100644 backend/app/Actions/Fortify/CreateNewUser.php create mode 100644 backend/app/Actions/Fortify/PasswordValidationRules.php create mode 100644 backend/app/Actions/Fortify/ResetUserPassword.php create mode 100644 backend/app/Actions/Fortify/UpdateUserPassword.php create mode 100644 backend/app/Actions/Fortify/UpdateUserProfileInformation.php create mode 100644 backend/app/Actions/PurgeUserAction.php create mode 100644 backend/app/Console/Commands/PurgeAnonymizableUsers.php create mode 100644 backend/app/Exceptions/CannotDeleteSystemUserException.php create mode 100644 backend/app/Http/Controllers/AuthController.php create mode 100644 backend/app/Http/Controllers/UserController.php create mode 100644 backend/app/Http/Controllers/UserRoleController.php create mode 100644 backend/app/Http/Middleware/EnsureSetupComplete.php create mode 100644 backend/app/Http/Middleware/ForceJsonResponse.php create mode 100644 backend/app/Http/Middleware/IsAdmin.php create mode 100644 backend/app/Http/Requests/IndexUserRequest.php create mode 100644 backend/app/Http/Requests/StoreUserRoleRequest.php create mode 100644 backend/app/Http/Requests/UpdateUserRoleRequest.php create mode 100644 backend/app/Http/Traits/ApiResponse.php create mode 100644 backend/app/Models/Lists/UserRole.php create mode 100644 backend/app/Notifications/ResetPasswordNotification.php create mode 100644 backend/app/Notifications/WelcomeNotification.php create mode 100644 backend/app/Providers/FortifyServiceProvider.php create mode 100644 backend/config/audit.php create mode 100644 backend/config/cors.php create mode 100644 backend/config/fortify.php create mode 100644 backend/config/messages.php create mode 100644 backend/config/sanctum.php create mode 100644 backend/database/factories/Lists/UserRoleFactory.php create mode 100644 backend/database/migrations/2026_06_18_162047_add_users_soft_delete.php create mode 100644 backend/database/migrations/2026_06_18_162204_create_users_roles_table.php create mode 100644 backend/database/migrations/2026_06_18_162324_add_role_id_to_users_table.php create mode 100644 backend/database/migrations/2026_06_18_162420_add_must_change_password_to_users_table.php create mode 100644 backend/database/migrations/2026_06_18_162631_add_is_system_and_anonymized_to_users_table.php create mode 100644 backend/database/migrations/2026_06_19_071116_add_two_factor_columns_to_users_table.php create mode 100644 backend/database/migrations/2026_06_19_073346_create_audits_table.php create mode 100644 backend/database/migrations/2026_06_19_080000_add_setup_2fa_completed_to_users_table.php create mode 100644 backend/database/seeders/SystemUserSeeder.php create mode 100644 backend/database/seeders/UserRoleSeeder.php create mode 100644 backend/routes/api.php create mode 100644 backend/routes/api/user-roles.php create mode 100644 backend/routes/api/users.php create mode 100644 backend/tests/Concerns/CreatesUsers.php create mode 100644 backend/tests/Feature/Auth/AccessTierTest.php create mode 100644 backend/tests/Feature/Auth/AuthControllerTest.php create mode 100644 backend/tests/Feature/EnvironmentSafetyTest.php delete mode 100644 backend/tests/Feature/ExampleTest.php create mode 100644 backend/tests/Feature/PurgeAnonymizableUsersCommandTest.php create mode 100644 backend/tests/Feature/PurgeUserActionTest.php create mode 100644 backend/tests/Feature/SeederTest.php create mode 100644 backend/tests/Feature/UserControllerTest.php create mode 100644 backend/tests/Feature/UserRoleControllerTest.php create mode 100644 backend/tests/Support/UnsafeTestDatabaseException.php delete mode 100644 backend/tests/Unit/ExampleTest.php create mode 100644 frontend/.stylelintrc.json delete mode 100644 frontend/template.html.example diff --git a/.env.example b/.env.example index 7505ebe..cc63413 100644 --- a/.env.example +++ b/.env.example @@ -30,21 +30,18 @@ LOG_LEVEL=debug # warning/error in produzione # Usati sia da Laravel sia dal container mysql (mappati nel compose). DB_CONNECTION=mysql DB_HOST=db # nome del servizio docker -DB_PORT=3306 +DB_PORT=3306 # porta di connessione INTERNA (mysql nel container) +DB_HOST_PORT=3310 # porta pubblicata sull'host; ≠ 3306 per non collidere DB_DATABASE=dyncoll DB_USERNAME=dyncoll DB_PASSWORD= # segreto: compilare DB_ROOT_PASSWORD= # segreto: solo per il container mysql (root) -# --- Database legacy (v1) — sorgente per l'ETL `php artisan v1:import` -------- -# Connessione in SOLA LETTURA verso il MySQL di dyncoll.v1. -# Attiva solo durante l'import; richiede rete docker condivisa o tunnel. -DB_LEGACY_CONNECTION=mysql -DB_LEGACY_HOST=dyncoll_v1_db # container/host del DB v1 -DB_LEGACY_PORT=3306 -DB_LEGACY_DATABASE=lund -DB_LEGACY_USERNAME=readonly -DB_LEGACY_PASSWORD= # segreto: compilare +# NB: le variabili DB_LEGACY_* (sorgente in SOLA LETTURA per l'ETL +# `php artisan v1:import`) NON stanno qui di proposito: servono solo durante la +# migrazione una-tantum da dyncoll.v1, non nella configurazione standard. Al +# momento del cutover aggiungerle a mano al .env con i valori corretti +# dell'ambiente (host/porta/credenziali possono differire dal locale). # --- Redis (cache, sessioni, code, Horizon) ---------------------------------- REDIS_HOST=redis @@ -62,6 +59,10 @@ FILESYSTEM_DISK=local # --- Autenticazione (Sanctum / Fortify) -------------------------------------- SANCTUM_STATEFUL_DOMAINS=dyncoll-dev.local,localhost,localhost:8080 SESSION_DOMAIN=.dyncoll-dev.local +# Sicurezza del cookie di sessione (app servita in HTTPS via Traefik). +SESSION_SECURE_COOKIE=true +SESSION_SAME_SITE=lax +SESSION_HTTP_ONLY=true # --- Mail (SMTP) ------------------------------------------------------------- # DEV: Mailpit (servizio nel docker-compose.override.yml). UI: http://localhost:8025 diff --git a/Makefile b/Makefile index 716755d..9a66a0b 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ DC := docker compose EXEC := $(DC) exec -u $(UID):$(GID) backend .DEFAULT_GOAL := help -.PHONY: help up down build logs composer be-install be-update artisan migrate import tinker fe-install fe-add fe-rebuild fe-dev fe-build permissions +.PHONY: help up down build logs composer be-install be-update artisan migrate import tinker test fe-install fe-add fe-rebuild fe-dev fe-build fe-lint-css fe-lint-css-fix permissions help: ## Mostra questo aiuto @grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-14s\033[0m %s\n",$$1,$$2}' @@ -42,6 +42,20 @@ import: ## ETL da v1 (container) $(EXEC) php artisan v1:import tinker: ## REPL artisan (container) $(EXEC) php artisan tinker +test: ## Suite di test sul DB dedicato db-test (es: make test c="--filter=AuthTest") + # Le -e iniettano VERE env nel processo di test → finiscono in $$_SERVER, che + # l'env() di Laravel legge per primo. Così vincono sul container (DB_HOST=db, + # DB_DATABASE=dyncoll) SENZA dipendere dai force di phpunit.xml. DB_HOST=db-test + # = isolamento fisico: la suite non può nemmeno raggiungere il db di sviluppo. + $(DC) exec -u $(UID):$(GID) \ + -e APP_ENV=testing \ + -e DB_HOST=db-test \ + -e DB_DATABASE=dyncoll_test \ + -e CACHE_STORE=array \ + -e SESSION_DRIVER=array \ + -e QUEUE_CONNECTION=sync \ + -e MAIL_MAILER=array \ + backend php artisan test $(c) ## --- Frontend / Node --- # In dev Vite gira NEL container (make up): HMR via bind-mount, node_modules musl @@ -58,7 +72,10 @@ fe-dev: ## ALTERNATIVA: vite dev server su host senza Docker (conflitto porta 51 cd frontend && npm run dev fe-build: ## build di produzione su host (smoke test locale) cd frontend && npm run build - +fe-lint-css: ## Stylelint (check) nel container — gira anche nel pre-commit + $(DC) exec -T frontend npm run lint:css +fe-lint-css-fix: ## Stylelint con --fix (container come UID host: scrive sul bind-mount) + $(DC) exec -u $(UID):$(GID) -T frontend npm run lint:css:fix ## --- Manutenzione --- permissions: ## Permessi storage scrivibili da php-fpm (www-data) — richiede sudo, una tantum sudo chgrp -R www-data backend/storage backend/bootstrap/cache diff --git a/backend/app/Actions/Fortify/CreateNewUser.php b/backend/app/Actions/Fortify/CreateNewUser.php new file mode 100644 index 0000000..ee2d712 --- /dev/null +++ b/backend/app/Actions/Fortify/CreateNewUser.php @@ -0,0 +1,43 @@ + $input + * + * @throws ValidationException + */ + public function create(array $input): User + { + Validator::make($input, [ + 'name' => ['required', 'string', 'max:255'], + 'email' => [ + 'required', + 'string', + 'email', + 'max:255', + Rule::unique(User::class), + ], + 'password' => $this->passwordRules(), + ])->validate(); + + return User::create([ + 'name' => $input['name'], + 'email' => $input['email'], + 'password' => Hash::make($input['password']), + ]); + } +} diff --git a/backend/app/Actions/Fortify/PasswordValidationRules.php b/backend/app/Actions/Fortify/PasswordValidationRules.php new file mode 100644 index 0000000..3678865 --- /dev/null +++ b/backend/app/Actions/Fortify/PasswordValidationRules.php @@ -0,0 +1,19 @@ +|string> + */ + protected function passwordRules(): array + { + return ['required', 'string', Password::default(), 'confirmed']; + } +} diff --git a/backend/app/Actions/Fortify/ResetUserPassword.php b/backend/app/Actions/Fortify/ResetUserPassword.php new file mode 100644 index 0000000..667651f --- /dev/null +++ b/backend/app/Actions/Fortify/ResetUserPassword.php @@ -0,0 +1,32 @@ + $input + * + * @throws ValidationException + */ + public function reset(User $user, array $input): void + { + Validator::make($input, [ + 'password' => $this->passwordRules(), + ])->validate(); + + $user->forceFill([ + 'password' => Hash::make($input['password']), + ])->save(); + } +} diff --git a/backend/app/Actions/Fortify/UpdateUserPassword.php b/backend/app/Actions/Fortify/UpdateUserPassword.php new file mode 100644 index 0000000..4a0306d --- /dev/null +++ b/backend/app/Actions/Fortify/UpdateUserPassword.php @@ -0,0 +1,35 @@ + $input + * + * @throws ValidationException + */ + public function update(User $user, array $input): void + { + Validator::make($input, [ + 'current_password' => ['required', 'string', 'current_password:web'], + 'password' => $this->passwordRules(), + ], [ + 'current_password.current_password' => __('The provided password does not match your current password.'), + ])->validateWithBag('updatePassword'); + + $user->forceFill([ + 'password' => Hash::make($input['password']), + ])->save(); + } +} diff --git a/backend/app/Actions/Fortify/UpdateUserProfileInformation.php b/backend/app/Actions/Fortify/UpdateUserProfileInformation.php new file mode 100644 index 0000000..62f58fa --- /dev/null +++ b/backend/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -0,0 +1,61 @@ + $input + * + * @throws ValidationException + */ + public function update(User $user, array $input): void + { + Validator::make($input, [ + 'name' => ['required', 'string', 'max:255'], + + 'email' => [ + 'required', + 'string', + 'email', + 'max:255', + Rule::unique('users')->ignore($user->id), + ], + ])->validateWithBag('updateProfileInformation'); + + if ($input['email'] !== $user->email && + $user instanceof MustVerifyEmail) { + $this->updateVerifiedUser($user, $input); + } else { + $user->forceFill([ + 'name' => $input['name'], + 'email' => $input['email'], + ])->save(); + } + } + + /** + * Update the given verified user's profile information. + * + * @param array $input + */ + protected function updateVerifiedUser(User $user, array $input): void + { + $user->forceFill([ + 'name' => $input['name'], + 'email' => $input['email'], + 'email_verified_at' => null, + ])->save(); + + $user->sendEmailVerificationNotification(); + } +} diff --git a/backend/app/Actions/PurgeUserAction.php b/backend/app/Actions/PurgeUserAction.php new file mode 100644 index 0000000..4a1e523 --- /dev/null +++ b/backend/app/Actions/PurgeUserAction.php @@ -0,0 +1,82 @@ + + */ + private const OWNED_MODELS = []; + + public function execute(User $user): void + { + if ($user->is_system) { + throw CannotDeleteSystemUserException::make(); + } + + if ($user->anonymized_at !== null) { + return; // già anonimizzato: idempotente + } + + $system = User::where('is_system', true)->firstOrFail(); + + DB::transaction(function () use ($user, $system) { + $this->reassignContent($user, $system); + $this->anonymize($user); + }); + } + + /** + * Riassegna all'utente di sistema tutti i contenuti posseduti, inclusi + * quelli a loro volta nel cestino. + */ + private function reassignContent(User $user, User $system): void + { + foreach (self::OWNED_MODELS as $model) { + $model::withTrashed() + ->where('user_id', $user->id) + ->update(['user_id' => $system->id]); + } + } + + /** + * Azzera i dati personali mantenendo la riga per l'integrità referenziale. + */ + private function anonymize(User $user): void + { + $user->forceFill([ + 'name' => 'Deleted user', + 'email' => "deleted-{$user->id}@anonymized.invalid", + 'password' => Hash::make(Str::random(60)), + 'two_factor_secret' => null, + 'two_factor_recovery_codes' => null, + 'two_factor_confirmed_at' => null, + 'two_factor_setup_completed_at' => null, + 'email_verified_at' => null, + 'remember_token' => null, + 'must_change_password' => false, + 'anonymized_at' => now(), + ])->save(); + } +} diff --git a/backend/app/Console/Commands/PurgeAnonymizableUsers.php b/backend/app/Console/Commands/PurgeAnonymizableUsers.php new file mode 100644 index 0000000..c431a12 --- /dev/null +++ b/backend/app/Console/Commands/PurgeAnonymizableUsers.php @@ -0,0 +1,54 @@ +option('days'); + $dryRun = (bool) $this->option('dry-run'); + + $users = User::onlyTrashed() + ->where('is_system', false) + ->whereNull('anonymized_at') + ->where('deleted_at', '<=', now()->subDays($days)) + ->get(); + + if ($users->isEmpty()) { + $this->info('No users to anonymize.'); + + return self::SUCCESS; + } + + foreach ($users as $user) { + if ($dryRun) { + $this->line(sprintf(' [dry-run] #%d %s', $user->id, $user->email)); + + continue; + } + + $purge->execute($user); + $this->line(sprintf(' ✓ anonymized user #%d', $user->id)); + } + + $this->info(($dryRun ? '[dry-run] ' : '').'Processed users: '.$users->count()); + + return self::SUCCESS; + } +} diff --git a/backend/app/Exceptions/CannotDeleteSystemUserException.php b/backend/app/Exceptions/CannotDeleteSystemUserException.php new file mode 100644 index 0000000..3520904 --- /dev/null +++ b/backend/app/Exceptions/CannotDeleteSystemUserException.php @@ -0,0 +1,17 @@ +json($request->user()); + } + + /** + * Logout: chiude la sessione stateful e ripulisce i cookie. + * Sovrascrive la rotta omonima di Fortify (registrata dopo → ha precedenza). + */ + public function logout(Request $request): JsonResponse + { + $this->tearDownSession($request); + + return $this->forgetAuthCookies($this->messageResponse('auth.logout_success')); + } + + /** + * Cancellazione self-service dell'account: soft-delete + logout. + * L'anonimizzazione definitiva (GDPR) la fa l'Admin o la retention. + */ + public function destroyAccount(Request $request): JsonResponse + { + $user = $request->user(); + abort_if($user->is_system, 403); + + $user->delete(); + $this->tearDownSession($request); + + return $this->forgetAuthCookies($this->messageResponse('account.deleted')); + } + + /** + * Invalida la sessione corrente e rigenera il token CSRF. + */ + private function tearDownSession(Request $request): void + { + Auth::guard('web')->logout(); + + if ($request->hasSession()) { + $request->session()->invalidate(); + $request->session()->regenerateToken(); + } + } + + /** + * Marca per la cancellazione i cookie di sessione e XSRF lato client. + */ + private function forgetAuthCookies(JsonResponse $response): JsonResponse + { + return $response + ->withCookie(Cookie::forget(config('session.cookie'))) + ->withCookie(Cookie::forget('XSRF-TOKEN')); + } +} diff --git a/backend/app/Http/Controllers/UserController.php b/backend/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..3a859c3 --- /dev/null +++ b/backend/app/Http/Controllers/UserController.php @@ -0,0 +1,85 @@ +with('role') + ->orderBy('name'); + + $this->applyTrashed($query, $request->input('trashed')); + $this->applySearch($query, $request->input('search')); + + if ($request->filled('role_id')) { + $query->where('role_id', $request->integer('role_id')); + } + + $users = $query->paginate($request->integer('per_page') ?: 20); + + return $this->paginatedCollectionResponse($users); + } + + /** + * Dettaglio di un singolo utente (anche cestinato/anonimizzato). + */ + public function show(User $user): JsonResponse + { + $user->load('role'); + + return $this->okResponse($user); + } + + /** + * Include i soft-deleted nell'elenco: `with` (tutti) o `only` (solo cestinati). + * + * @param Builder $query + */ + private function applyTrashed(Builder $query, ?string $trashed): void + { + match ($trashed) { + 'with' => $query->withTrashed(), + 'only' => $query->onlyTrashed(), + default => null, + }; + } + + /** + * Filtra per nome o email (ricerca parziale, case-insensitive). + * + * @param Builder $query + */ + private function applySearch(Builder $query, ?string $search): void + { + if (blank($search)) { + return; + } + + $term = '%'.$search.'%'; + + $query->where(function (Builder $q) use ($term): void { + $q->where('name', 'like', $term) + ->orWhere('email', 'like', $term); + }); + } +} diff --git a/backend/app/Http/Controllers/UserRoleController.php b/backend/app/Http/Controllers/UserRoleController.php new file mode 100644 index 0000000..8b27d84 --- /dev/null +++ b/backend/app/Http/Controllers/UserRoleController.php @@ -0,0 +1,78 @@ +collectionResponse(UserRole::all()); + } + + /** + * Crea un nuovo ruolo. + */ + public function store(StoreUserRoleRequest $request): JsonResponse + { + $role = UserRole::create($request->validated()); + + return $this->createdResponse($role); + } + + /** + * Dettaglio di un ruolo. + */ + public function show(UserRole $userRole): JsonResponse + { + return $this->okResponse($userRole); + } + + /** + * Aggiorna un ruolo. + */ + public function update(UpdateUserRoleRequest $request, UserRole $userRole): JsonResponse + { + $userRole->update($request->validated()); + + return $this->updatedResponse($userRole); + } + + /** + * Elimina un ruolo (se non di sistema e non in uso). + */ + public function destroy(UserRole $userRole): JsonResponse + { + if ($userRole->isSystemRole()) { + return $this->conflictResponse('Cannot delete a system role.'); + } + + if ($userRole->isInUse()) { + return $this->conflictResponse('Cannot delete: role assigned to at least one user.'); + } + + $userRole->delete(); + + return $this->deletedResponse(); + } + + /** + * Indica se il ruolo è bloccato per l'eliminazione (di sistema o in uso). + */ + public function usage(UserRole $userRole): JsonResponse + { + return response()->json([ + 'in_use' => $userRole->isSystemRole() || $userRole->isInUse(), + ]); + } +} diff --git a/backend/app/Http/Middleware/EnsureSetupComplete.php b/backend/app/Http/Middleware/EnsureSetupComplete.php new file mode 100644 index 0000000..1ba5131 --- /dev/null +++ b/backend/app/Http/Middleware/EnsureSetupComplete.php @@ -0,0 +1,40 @@ +user(); + + if (! $user) { + return $next($request); + } + + $setupStatus = null; + + if ($user->must_change_password) { + $setupStatus = 'password_required'; + } elseif (is_null($user->two_factor_setup_completed_at)) { + $setupStatus = '2fa_setup_required'; + } + + if ($setupStatus !== null) { + return response()->json([ + 'setup_status' => $setupStatus, + ], 403); + } + + return $next($request); + } +} diff --git a/backend/app/Http/Middleware/ForceJsonResponse.php b/backend/app/Http/Middleware/ForceJsonResponse.php new file mode 100644 index 0000000..35ea080 --- /dev/null +++ b/backend/app/Http/Middleware/ForceJsonResponse.php @@ -0,0 +1,22 @@ +headers->set('Accept', 'application/json'); + + return $next($request); + } +} diff --git a/backend/app/Http/Middleware/IsAdmin.php b/backend/app/Http/Middleware/IsAdmin.php new file mode 100644 index 0000000..6770bb0 --- /dev/null +++ b/backend/app/Http/Middleware/IsAdmin.php @@ -0,0 +1,25 @@ +role?->name !== 'Admin') { + abort(403); + } + + return $next($request); + } +} diff --git a/backend/app/Http/Requests/IndexUserRequest.php b/backend/app/Http/Requests/IndexUserRequest.php new file mode 100644 index 0000000..9193980 --- /dev/null +++ b/backend/app/Http/Requests/IndexUserRequest.php @@ -0,0 +1,31 @@ + + */ + public function rules(): array + { + return [ + 'search' => ['nullable', 'string', 'max:255'], + 'role_id' => ['nullable', 'integer', 'exists:user_roles,id'], + 'trashed' => ['nullable', Rule::in(['with', 'only'])], + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/backend/app/Http/Requests/StoreUserRoleRequest.php b/backend/app/Http/Requests/StoreUserRoleRequest.php new file mode 100644 index 0000000..fb82574 --- /dev/null +++ b/backend/app/Http/Requests/StoreUserRoleRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:255|unique:user_roles,name', + 'description' => 'nullable|string', + ]; + } +} diff --git a/backend/app/Http/Requests/UpdateUserRoleRequest.php b/backend/app/Http/Requests/UpdateUserRoleRequest.php new file mode 100644 index 0000000..9fb4e69 --- /dev/null +++ b/backend/app/Http/Requests/UpdateUserRoleRequest.php @@ -0,0 +1,36 @@ +|string> + */ + public function rules(): array + { + $id = $this->route('user_role')->id; + + return [ + 'name' => [ + 'required', + 'string', + 'max:255', + Rule::unique('user_roles', 'name')->ignore($id), + ], + 'description' => ['nullable', 'string'], + ]; + } +} diff --git a/backend/app/Http/Traits/ApiResponse.php b/backend/app/Http/Traits/ApiResponse.php new file mode 100644 index 0000000..0f00549 --- /dev/null +++ b/backend/app/Http/Traits/ApiResponse.php @@ -0,0 +1,130 @@ +json([ + 'message' => config($prefix ? "messages.{$prefix}.index" : 'messages.index'), + 'data' => $paginator->items(), + 'meta' => [ + 'current_page' => $paginator->currentPage(), + 'last_page' => $paginator->lastPage(), + 'per_page' => $paginator->perPage(), + 'total' => $paginator->total(), + ], + ], 200); + } + + /** + * 200 — Collezione di risorse. + */ + protected function collectionResponse(mixed $collection, ?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.index" : 'messages.index'), + 'data' => $collection, + ], 200); + } + + /** + * 200 — Singola risorsa. + */ + protected function okResponse(mixed $data, ?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.show" : 'messages.show'), + 'data' => $data, + ], 200); + } + + /** + * 201 — Risorsa creata. + */ + protected function createdResponse(mixed $model, ?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.created" : 'messages.created'), + 'data' => $model, + ], 201); + } + + /** + * 200 — Risorsa aggiornata. + */ + protected function updatedResponse(mixed $model, ?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.updated" : 'messages.updated'), + 'data' => $model, + ], 200); + } + + /** + * 200 — Risorsa eliminata. + */ + protected function deletedResponse(?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.deleted" : 'messages.deleted'), + ], 200); + } + + /** + * 200 — Risorsa ripristinata (soft delete restore). + */ + protected function restoredResponse(mixed $model, ?string $prefix = null): JsonResponse + { + return response()->json([ + 'message' => config($prefix ? "messages.{$prefix}.restored" : 'messages.restored'), + 'data' => $model, + ], 200); + } + + /** + * 409 — Conflitto: operazione bloccata (es. risorsa in uso). + */ + protected function conflictResponse(string $message): JsonResponse + { + return response()->json([ + 'message' => $message, + 'in_use' => true, + ], 409); + } + + /** + * Risposta con solo messaggio — chiave config es. "auth.login_success". + */ + protected function messageResponse(string $configKey, int $status = 200): JsonResponse + { + return response()->json([ + 'message' => config("messages.{$configKey}"), + ], $status); + } + + /** + * Risposta con messaggio e dati — chiave config es. "auth.login_success". + */ + protected function dataResponse(mixed $data, string $configKey, int $status = 200): JsonResponse + { + return response()->json([ + 'message' => config("messages.{$configKey}"), + 'data' => $data, + ], $status); + } +} diff --git a/backend/app/Models/Lists/UserRole.php b/backend/app/Models/Lists/UserRole.php new file mode 100644 index 0000000..79536f5 --- /dev/null +++ b/backend/app/Models/Lists/UserRole.php @@ -0,0 +1,57 @@ + + */ + public function users(): HasMany + { + return $this->hasMany(User::class, 'role_id'); + } + + /** + * Ruolo predefinito di sistema: non eliminabile. + */ + public function isSystemRole(): bool + { + return in_array($this->name, [self::ADMIN, self::SUPERVISOR, self::USER, self::GUEST], true); + } + + /** + * Il ruolo è assegnato ad almeno un utente. + */ + public function isInUse(): bool + { + return $this->users()->exists(); + } +} diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index f6ba1d2..47024e4 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -2,20 +2,59 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; -use Database\Factories\UserFactory; -use Illuminate\Database\Eloquent\Attributes\Fillable; -use Illuminate\Database\Eloquent\Attributes\Hidden; +use App\Models\Lists\UserRole; +use App\Notifications\ResetPasswordNotification; +use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Fortify\TwoFactorAuthenticatable; +use Laravel\Sanctum\HasApiTokens; +use OwenIt\Auditing\Contracts\Auditable; -#[Fillable(['name', 'email', 'password'])] -#[Hidden(['password', 'remember_token'])] -class User extends Authenticatable +class User extends Authenticatable implements Auditable, MustVerifyEmail { /** @use HasFactory */ - use HasFactory, Notifiable; + use HasApiTokens, HasFactory, Notifiable, SoftDeletes; + + use \OwenIt\Auditing\Auditable; + use TwoFactorAuthenticatable; + + /** + * The attributes that are mass assignable. + * + * @var list + */ + protected $appends = ['setup_status']; + + protected $auditExclude = ['password', 'remember_token', 'two_factor_secret']; + + protected $fillable = [ + 'name', + 'email', + 'password', + 'role_id', + 'is_system', + 'must_change_password', + 'two_factor_secret', + 'two_factor_recovery_codes', + 'two_factor_setup_completed_at', + 'email_verified_at', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var list + */ + protected $hidden = [ + 'password', + 'remember_token', + 'two_factor_secret', + 'two_factor_recovery_codes', + ]; /** * Get the attributes that should be cast. @@ -27,6 +66,43 @@ class User extends Authenticatable return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'role_id' => 'integer', + 'must_change_password' => 'boolean', + 'two_factor_setup_completed_at' => 'datetime', + 'two_factor_confirmed_at' => 'datetime', + 'is_system' => 'boolean', + 'anonymized_at' => 'datetime', ]; } + + /** + * Ruolo applicativo dell'utente. + * + * @return BelongsTo + */ + public function role(): BelongsTo + { + return $this->belongsTo(UserRole::class, 'role_id'); + } + + /** + * Send the password reset notification. + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new ResetPasswordNotification($token)); + } + + public function getSetupStatusAttribute(): string + { + if ($this->must_change_password) { + return 'password_required'; + } + + if (is_null($this->two_factor_setup_completed_at)) { + return '2fa_setup_required'; + } + + return 'complete'; + } } diff --git a/backend/app/Notifications/ResetPasswordNotification.php b/backend/app/Notifications/ResetPasswordNotification.php new file mode 100644 index 0000000..c762c54 --- /dev/null +++ b/backend/app/Notifications/ResetPasswordNotification.php @@ -0,0 +1,54 @@ + + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } +} diff --git a/backend/app/Notifications/WelcomeNotification.php b/backend/app/Notifications/WelcomeNotification.php new file mode 100644 index 0000000..b6cdf05 --- /dev/null +++ b/backend/app/Notifications/WelcomeNotification.php @@ -0,0 +1,53 @@ + + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } +} diff --git a/backend/app/Providers/AppServiceProvider.php b/backend/app/Providers/AppServiceProvider.php index 452e6b6..d0e0d8c 100644 --- a/backend/app/Providers/AppServiceProvider.php +++ b/backend/app/Providers/AppServiceProvider.php @@ -2,6 +2,9 @@ namespace App\Providers; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +22,8 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + // Rate limiter delle rotte pubbliche (gruppo `throttle:public` in routes/api.php). + // Nessun utente loggato → si limita per IP. Tarare il numero sul traffico reale. + RateLimiter::for('public', fn (Request $request) => Limit::perMinute(60)->by($request->ip())); } } diff --git a/backend/app/Providers/FortifyServiceProvider.php b/backend/app/Providers/FortifyServiceProvider.php new file mode 100644 index 0000000..e48ab1a --- /dev/null +++ b/backend/app/Providers/FortifyServiceProvider.php @@ -0,0 +1,56 @@ +input(Fortify::username())).'|'.$request->ip()); + + return Limit::perMinute(5)->by($throttleKey); + }); + + RateLimiter::for('two-factor', function (Request $request) { + return Limit::perMinute(5)->by($request->session()->get('login.id')); + }); + + RateLimiter::for('passkeys', function (Request $request) { + $credentialId = $request->input('credential.id'); + + return Limit::perMinute(10)->by( + ($credentialId ?: $request->session()->getId()).'|'.$request->ip() + ); + }); + } +} diff --git a/backend/bootstrap/app.php b/backend/bootstrap/app.php index 7a2848f..cd67ffb 100644 --- a/backend/bootstrap/app.php +++ b/backend/bootstrap/app.php @@ -1,18 +1,46 @@ withRouting( + api: __DIR__.'/../routes/api.php', // NOSONAR web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', + then: function (): void { + // Auto-discovery: ogni file in routes/api/ è caricato come gruppo sotto + // prefisso `api` e stack `api`. Aggiungere un modello = creare un file lì, + // senza toccare questo bootstrap. glob() ordina alfabeticamente → ordine + // di registrazione deterministico. (Dopo aver aggiunto un file, se usi la + // route cache rilancia `route:cache`, come per qualunque nuova rotta.) + foreach (glob(base_path('routes/api/*.php')) as $routeFile) { + Route::middleware('api')->prefix('api')->group($routeFile); + } + }, ) ->withMiddleware(function (Middleware $middleware): void { - // + // per Sanctum + $middleware->statefulApi(); + // Configurazione CORS + $middleware->preventRequestForgery(except: ['api/*']); + $middleware->alias([ + 'admin' => IsAdmin::class, + 'setup.complete' => EnsureSetupComplete::class, + ]); + // Tier riusabili nei file di routes/api/ — fonte di verità unica. + // (nomi `tier.*` per non confliggere con l'alias `admin`.) + $middleware->group('tier.app', ['auth:sanctum', 'verified', 'setup.complete']); + $middleware->group('tier.admin', ['auth:sanctum', 'verified', 'admin', 'setup.complete']); + $middleware->api(append: [ForceJsonResponse::class]); + $middleware->trustProxies(at: '*'); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->shouldRenderJsonWhen( diff --git a/backend/bootstrap/providers.php b/backend/bootstrap/providers.php index fc94ae6..5ffd769 100644 --- a/backend/bootstrap/providers.php +++ b/backend/bootstrap/providers.php @@ -1,7 +1,9 @@ env('AUDITING_ENABLED', true), + + /* + |-------------------------------------------------------------------------- + | Audit Implementation + |-------------------------------------------------------------------------- + | + | Define which Audit model implementation should be used. + | + */ + + 'implementation' => Audit::class, + + /* + |-------------------------------------------------------------------------- + | User Morph prefix & Guards + |-------------------------------------------------------------------------- + | + | Define the morph prefix and authentication guards for the User resolver. + | + */ + + 'user' => [ + 'morph_prefix' => 'user', + 'guards' => [ + 'web', + 'api', + ], + 'resolver' => UserResolver::class, + ], + + /* + |-------------------------------------------------------------------------- + | Audit Resolvers + |-------------------------------------------------------------------------- + | + | Define the IP Address, User Agent and URL resolver implementations. + | + */ + 'resolvers' => [ + 'ip_address' => IpAddressResolver::class, + 'user_agent' => UserAgentResolver::class, + 'url' => UrlResolver::class, + ], + + /* + |-------------------------------------------------------------------------- + | Audit Events + |-------------------------------------------------------------------------- + | + | The Eloquent events that trigger an Audit. + | + */ + + 'events' => [ + 'created', + 'updated', + 'deleted', + 'restored', + ], + + /* + |-------------------------------------------------------------------------- + | Strict Mode + |-------------------------------------------------------------------------- + | + | Enable the strict mode when auditing? + | + */ + + 'strict' => false, + + /* + |-------------------------------------------------------------------------- + | Global exclude + |-------------------------------------------------------------------------- + | + | Have something you always want to exclude by default? - add it here. + | Note that this is overwritten (not merged) with local exclude + | + */ + + 'exclude' => [], + + /* + |-------------------------------------------------------------------------- + | Empty Values + |-------------------------------------------------------------------------- + | + | Should Audit records be stored when the recorded old_values & new_values + | are both empty? + | + | Some events may be empty on purpose. Use allowed_empty_values to exclude + | those from the empty values check. For example when auditing + | model retrieved events which will never have new and old values. + | + | + */ + + 'empty_values' => true, + 'allowed_empty_values' => [ + 'retrieved', + ], + + /* + |-------------------------------------------------------------------------- + | Allowed Array Values + |-------------------------------------------------------------------------- + | + | Should the array values be audited? + | + | By default, array values are not allowed. This is to prevent performance + | issues when storing large amounts of data. You can override this by + | setting allow_array_values to true. + */ + 'allowed_array_values' => false, + + /* + |-------------------------------------------------------------------------- + | Audit Timestamps + |-------------------------------------------------------------------------- + | + | Should the created_at, updated_at and deleted_at timestamps be audited? + | + */ + + 'timestamps' => false, + + /* + |-------------------------------------------------------------------------- + | Audit Threshold + |-------------------------------------------------------------------------- + | + | Specify a threshold for the amount of Audit records a model can have. + | Zero means no limit. + | + */ + + 'threshold' => 0, + + /* + |-------------------------------------------------------------------------- + | Audit Driver + |-------------------------------------------------------------------------- + | + | The default audit driver used to keep track of changes. + | + */ + + 'driver' => 'database', + + /* + |-------------------------------------------------------------------------- + | Audit Driver Configurations + |-------------------------------------------------------------------------- + | + | Available audit drivers and respective configurations. + | + */ + + 'drivers' => [ + 'database' => [ + 'table' => 'audits', + 'connection' => null, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Audit Queue Configurations + |-------------------------------------------------------------------------- + | + | Available audit queue configurations. + | + */ + + 'queue' => [ + 'enable' => false, + 'connection' => 'sync', + 'queue' => 'default', + 'delay' => 0, + ], + + /* + |-------------------------------------------------------------------------- + | Audit Console + |-------------------------------------------------------------------------- + | + | Whether console events should be audited (eg. php artisan db:seed). + | + */ + + 'console' => false, +]; diff --git a/backend/config/cors.php b/backend/config/cors.php new file mode 100644 index 0000000..f9d780e --- /dev/null +++ b/backend/config/cors.php @@ -0,0 +1,45 @@ + ['api/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + // Origini autorizzate per le richieste cross-origin con credenziali (cookie). + // Con `supports_credentials => true` NON è ammesso il wildcard `*`: serve la + // lista esatta delle origini. In dev l'unica che porta il cookie di sessione è + // APP_URL, perché SESSION_DOMAIN è scopato a `.dyncoll-dev.local`. + // FRONTEND_URL (se valorizzato) fa da override e accetta più origini separate + // da virgola; altrimenti si usa APP_URL. + 'allowed_origins' => array_values(array_filter(array_map( + 'trim', + explode(',', (string) env('FRONTEND_URL', env('APP_URL', 'https://dyncoll-dev.local'))) + ))), + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => true, + +]; diff --git a/backend/config/fortify.php b/backend/config/fortify.php new file mode 100644 index 0000000..1a87537 --- /dev/null +++ b/backend/config/fortify.php @@ -0,0 +1,160 @@ + 'web', + + /* + |-------------------------------------------------------------------------- + | Fortify Password Broker + |-------------------------------------------------------------------------- + | + | Here you may specify which password broker Fortify can use when a user + | is resetting their password. This configured value should match one + | of your password brokers setup in your "auth" configuration file. + | + */ + + 'passwords' => 'users', + + /* + |-------------------------------------------------------------------------- + | Username / Email + |-------------------------------------------------------------------------- + | + | This value defines which model attribute should be considered as your + | application's "username" field. Typically, this might be the email + | address of the users but you are free to change this value here. + | + | Out of the box, Fortify expects forgot password and reset password + | requests to have a field named 'email'. If the application uses + | another name for the field you may define it below as needed. + | + */ + + 'username' => 'email', + + 'email' => 'email', + + /* + |-------------------------------------------------------------------------- + | Lowercase Usernames + |-------------------------------------------------------------------------- + | + | This value defines whether usernames should be lowercased before saving + | them in the database, as some database system string fields are case + | sensitive. You may disable this for your application if necessary. + | + */ + + 'lowercase_usernames' => true, + + /* + |-------------------------------------------------------------------------- + | Home Path + |-------------------------------------------------------------------------- + | + | Here you may configure the path where users will get redirected during + | authentication or password reset when the operations are successful + | and the user is authenticated. You are free to change this value. + | + */ + + 'home' => '/home', + + /* + |-------------------------------------------------------------------------- + | Fortify Routes Prefix / Subdomain + |-------------------------------------------------------------------------- + | + | Here you may specify which prefix Fortify will assign to all the routes + | that it registers with the application. If necessary, you may change + | subdomain under which all of the Fortify routes will be available. + | + */ + + 'prefix' => 'api', + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | Fortify Routes Middleware + |-------------------------------------------------------------------------- + | + | Here you may specify which middleware Fortify will assign to the routes + | that it registers with the application. If necessary, you may change + | these middleware but typically this provided default is preferred. + | + */ + + 'middleware' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Rate Limiting + |-------------------------------------------------------------------------- + | + | By default, Fortify will throttle logins to five requests per minute for + | every email and IP address combination. However, if you would like to + | specify a custom rate limiter to call then you may specify it here. + | + */ + + 'limiters' => [ + 'login' => 'login', + 'two-factor' => 'two-factor', + 'passkeys' => 'passkeys', + ], + + /* + |-------------------------------------------------------------------------- + | Register View Routes + |-------------------------------------------------------------------------- + | + | Here you may specify if the routes returning views should be disabled as + | you may not need them when building your own application. This may be + | especially true if you're writing a custom single-page application. + | + */ + + 'views' => false, + + /* + |-------------------------------------------------------------------------- + | Features + |-------------------------------------------------------------------------- + | + | Some of the Fortify features are optional. You may disable the features + | by removing them from this array. You're free to only remove some of + | these features or you can even remove all of these if you need to. + | + */ + + 'features' => [ + Features::registration(), + Features::resetPasswords(), + Features::emailVerification(), + Features::updateProfileInformation(), + Features::updatePasswords(), + Features::twoFactorAuthentication([ + 'confirm' => true, + 'confirmPassword' => true, + // 'window' => 0, + ]), + ], + +]; diff --git a/backend/config/messages.php b/backend/config/messages.php new file mode 100644 index 0000000..743e1b5 --- /dev/null +++ b/backend/config/messages.php @@ -0,0 +1,37 @@ + ['created' => '...']). + | + */ + + 'index' => 'List retrieved successfully.', + 'show' => 'Resource retrieved successfully.', + 'created' => 'Resource created successfully.', + 'updated' => 'Resource updated successfully.', + 'deleted' => 'Resource deleted successfully.', + 'restored' => 'Resource restored successfully.', + + /* + |-------------------------------------------------------------------------- + | Autenticazione / account + |-------------------------------------------------------------------------- + */ + + 'auth' => [ + 'logout_success' => 'Logged out successfully.', + ], + + 'account' => [ + 'deleted' => 'Account deleted successfully.', + ], + +]; diff --git a/backend/config/sanctum.php b/backend/config/sanctum.php new file mode 100644 index 0000000..02c25f6 --- /dev/null +++ b/backend/config/sanctum.php @@ -0,0 +1,102 @@ + explode( + ',', + env( + 'SANCTUM_STATEFUL_DOMAINS', + sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort(), + // Sanctum::currentRequestHost(), + ) + ) + ), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + 'sanctum' => [ + 'driver' => 'sanctum', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => AuthenticateSession::class, + 'encrypt_cookies' => EncryptCookies::class, + 'validate_csrf_token' => ValidateCsrfToken::class, + ], + +]; diff --git a/backend/database/factories/Lists/UserRoleFactory.php b/backend/database/factories/Lists/UserRoleFactory.php new file mode 100644 index 0000000..fa7f8b7 --- /dev/null +++ b/backend/database/factories/Lists/UserRoleFactory.php @@ -0,0 +1,25 @@ + + */ +class UserRoleFactory extends Factory +{ + protected $model = UserRole::class; + + /** + * @return array + */ + public function definition(): array + { + return [ + 'name' => ucfirst($this->faker->unique()->word()), + 'description' => $this->faker->sentence(), + ]; + } +} diff --git a/backend/database/migrations/2026_06_18_162047_add_users_soft_delete.php b/backend/database/migrations/2026_06_18_162047_add_users_soft_delete.php new file mode 100644 index 0000000..a7bda58 --- /dev/null +++ b/backend/database/migrations/2026_06_18_162047_add_users_soft_delete.php @@ -0,0 +1,28 @@ +softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +}; diff --git a/backend/database/migrations/2026_06_18_162204_create_users_roles_table.php b/backend/database/migrations/2026_06_18_162204_create_users_roles_table.php new file mode 100644 index 0000000..6bee80c --- /dev/null +++ b/backend/database/migrations/2026_06_18_162204_create_users_roles_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name')->unique(); + $table->string('description')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_roles'); + } +}; diff --git a/backend/database/migrations/2026_06_18_162324_add_role_id_to_users_table.php b/backend/database/migrations/2026_06_18_162324_add_role_id_to_users_table.php new file mode 100644 index 0000000..82d7c0d --- /dev/null +++ b/backend/database/migrations/2026_06_18_162324_add_role_id_to_users_table.php @@ -0,0 +1,27 @@ +unsignedBigInteger('role_id')->nullable()->after('id'); + $table->foreign('role_id')->references('id')->on('user_roles')->onDelete('set null'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['role_id']); + $table->dropColumn('role_id'); + }); + } +}; diff --git a/backend/database/migrations/2026_06_18_162420_add_must_change_password_to_users_table.php b/backend/database/migrations/2026_06_18_162420_add_must_change_password_to_users_table.php new file mode 100644 index 0000000..8895b48 --- /dev/null +++ b/backend/database/migrations/2026_06_18_162420_add_must_change_password_to_users_table.php @@ -0,0 +1,28 @@ +boolean('must_change_password')->default(true)->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('must_change_password'); + }); + } +}; diff --git a/backend/database/migrations/2026_06_18_162631_add_is_system_and_anonymized_to_users_table.php b/backend/database/migrations/2026_06_18_162631_add_is_system_and_anonymized_to_users_table.php new file mode 100644 index 0000000..35e2fa9 --- /dev/null +++ b/backend/database/migrations/2026_06_18_162631_add_is_system_and_anonymized_to_users_table.php @@ -0,0 +1,30 @@ +boolean('is_system')->default(false)->after('role_id'); + + // Traccia l'avvenuta anonimizzazione (GDPR). Il record resta ai fini + // di integrità referenziale ma esce dal cestino e dalle liste utenti. + $table->timestamp('anonymized_at')->nullable()->after('deleted_at'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['is_system', 'anonymized_at']); + }); + } +}; diff --git a/backend/database/migrations/2026_06_19_071116_add_two_factor_columns_to_users_table.php b/backend/database/migrations/2026_06_19_071116_add_two_factor_columns_to_users_table.php new file mode 100644 index 0000000..45739ef --- /dev/null +++ b/backend/database/migrations/2026_06_19_071116_add_two_factor_columns_to_users_table.php @@ -0,0 +1,42 @@ +text('two_factor_secret') + ->after('password') + ->nullable(); + + $table->text('two_factor_recovery_codes') + ->after('two_factor_secret') + ->nullable(); + + $table->timestamp('two_factor_confirmed_at') + ->after('two_factor_recovery_codes') + ->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn([ + 'two_factor_secret', + 'two_factor_recovery_codes', + 'two_factor_confirmed_at', + ]); + }); + } +}; diff --git a/backend/database/migrations/2026_06_19_073346_create_audits_table.php b/backend/database/migrations/2026_06_19_073346_create_audits_table.php new file mode 100644 index 0000000..1307f7d --- /dev/null +++ b/backend/database/migrations/2026_06_19_073346_create_audits_table.php @@ -0,0 +1,48 @@ +create($table, function (Blueprint $table) { + + $morphPrefix = config('audit.user.morph_prefix', 'user'); + + $table->bigIncrements('id'); + $table->string($morphPrefix.'_type')->nullable(); + $table->unsignedBigInteger($morphPrefix.'_id')->nullable(); + $table->string('event'); + $table->morphs('auditable'); + $table->text('old_values')->nullable(); + $table->text('new_values')->nullable(); + $table->text('url')->nullable(); + $table->ipAddress('ip_address')->nullable(); + $table->string('user_agent', 1023)->nullable(); + $table->string('tags')->nullable(); + $table->timestamps(); + + $table->index([$morphPrefix.'_id', $morphPrefix.'_type']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $connection = config('audit.drivers.database.connection', config('database.default')); + $table = config('audit.drivers.database.table', 'audits'); + + Schema::connection($connection)->drop($table); + } +}; diff --git a/backend/database/migrations/2026_06_19_080000_add_setup_2fa_completed_to_users_table.php b/backend/database/migrations/2026_06_19_080000_add_setup_2fa_completed_to_users_table.php new file mode 100644 index 0000000..2191a7f --- /dev/null +++ b/backend/database/migrations/2026_06_19_080000_add_setup_2fa_completed_to_users_table.php @@ -0,0 +1,27 @@ +timestamp('two_factor_setup_completed_at') + ->nullable() + ->after('two_factor_confirmed_at'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('two_factor_setup_completed_at'); + }); + } +}; diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php index 6b901f8..ba68f46 100644 --- a/backend/database/seeders/DatabaseSeeder.php +++ b/backend/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -11,15 +10,14 @@ class DatabaseSeeder extends Seeder use WithoutModelEvents; /** - * Seed the application's database. + * Seed dell'applicazione. L'ordine conta: i ruoli prima dell'utente di + * sistema (che richiede il ruolo Admin). */ public function run(): void { - // User::factory(10)->create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', + $this->call([ + UserRoleSeeder::class, + SystemUserSeeder::class, ]); } } diff --git a/backend/database/seeders/SystemUserSeeder.php b/backend/database/seeders/SystemUserSeeder.php new file mode 100644 index 0000000..3d1db1d --- /dev/null +++ b/backend/database/seeders/SystemUserSeeder.php @@ -0,0 +1,41 @@ +value('id'); + + User::updateOrCreate( + ['email' => self::EMAIL], + [ + 'name' => 'Dyncoll System', + 'password' => Hash::make(Str::random(60)), + 'role_id' => $adminRoleId, + 'is_system' => true, + 'must_change_password' => false, + 'email_verified_at' => now(), + 'two_factor_setup_completed_at' => now(), + ] + ); + + $this->command?->info('✅ System user ready: '.self::EMAIL); + } +} diff --git a/backend/database/seeders/UserRoleSeeder.php b/backend/database/seeders/UserRoleSeeder.php new file mode 100644 index 0000000..ac4cb47 --- /dev/null +++ b/backend/database/seeders/UserRoleSeeder.php @@ -0,0 +1,42 @@ + UserRole::ADMIN], + ['description' => 'Full access to all features.' + .' Manages users, configurations and global data supervision.' + .' Can create, read, update and delete any record.'] + ); + + UserRole::updateOrCreate( + ['name' => UserRole::SUPERVISOR], + ['description' => 'Limited access to certain features.' + .' Can manage users but not administrators.' + .' Can create, read, update and delete both own and other users\' records.'] + ); + + UserRole::updateOrCreate( + ['name' => UserRole::USER], + ['description' => 'Day-to-day operational use. Can access all records' + .' and create new ones, but can only update or delete their own.'] + ); + + UserRole::updateOrCreate( + ['name' => UserRole::GUEST], + ['description' => 'Very limited access. Can only view information' + .' not publicly available, with no editing capabilities.'] + ); + } +} diff --git a/backend/phpunit.xml b/backend/phpunit.xml index e7f0a48..bba6118 100644 --- a/backend/phpunit.xml +++ b/backend/phpunit.xml @@ -18,19 +18,25 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/backend/routes/api.php b/backend/routes/api.php new file mode 100644 index 0000000..dc41a62 --- /dev/null +++ b/backend/routes/api.php @@ -0,0 +1,36 @@ +group(function () { + // Solo endpoint GET (index/show) leggibili da chiunque. +}); + +// ----------------------------------------------------------------------- +// Tier "transitional": autenticato, MA setup eventualmente incompleto +// (password forzata da cambiare e/o 2FA non ancora configurata). +// +// ⚠️ NON aggiungere `setup.complete` qui: queste rotte devono restare +// raggiungibili PROPRIO durante il setup, altrimenti deadlock. In particolare +// il frontend legge GET /user per scoprire `setup_status` e decidere cosa +// mostrare. Le azioni di setup vero (cambio password, abilitazione 2FA) e la +// verifica email le registra Fortify, già auth-only e fuori da `setup.complete`. +// ----------------------------------------------------------------------- +Route::middleware($AUTH)->group(function () { + Route::get('/user', [AuthController::class, 'me']); + // Sovrascrive la rotta logout di Fortify (registrata dopo → precedenza). + Route::post('/logout', [AuthController::class, 'logout']); + Route::delete('/account', [AuthController::class, 'destroyAccount']); +}); diff --git a/backend/routes/api/user-roles.php b/backend/routes/api/user-roles.php new file mode 100644 index 0000000..3cec2cb --- /dev/null +++ b/backend/routes/api/user-roles.php @@ -0,0 +1,18 @@ +group(function () { + Route::apiResource('user-roles', UserRoleController::class)->only(['index', 'show']); + Route::get('user-roles/{user_role}/usage', [UserRoleController::class, 'usage']); +}); + +// Scrittura: solo Admin. +Route::middleware('tier.admin')->group(function () { + Route::apiResource('user-roles', UserRoleController::class)->only(['store', 'update', 'destroy']); +}); diff --git a/backend/routes/api/users.php b/backend/routes/api/users.php new file mode 100644 index 0000000..e89fcd3 --- /dev/null +++ b/backend/routes/api/users.php @@ -0,0 +1,11 @@ +group(function () { + Route::get('users', [UserController::class, 'index']); + // withTrashed: l'admin può ispezionare anche utenti cestinati/anonimizzati. + Route::get('users/{user}', [UserController::class, 'show'])->withTrashed(); +}); diff --git a/backend/routes/console.php b/backend/routes/console.php index 3c9adf1..d078d5d 100644 --- a/backend/routes/console.php +++ b/backend/routes/console.php @@ -2,7 +2,11 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Schedule; Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); + +// Anonimizzazione GDPR degli utenti oltre la retention (richiede lo scheduler attivo). +Schedule::command('users:purge')->daily(); diff --git a/backend/tests/Concerns/CreatesUsers.php b/backend/tests/Concerns/CreatesUsers.php new file mode 100644 index 0000000..6f79abc --- /dev/null +++ b/backend/tests/Concerns/CreatesUsers.php @@ -0,0 +1,66 @@ + $name], ['description' => $name.' role']); + } + + /** Utente pienamente operativo: verificato, setup completo, ruolo User. */ + protected function operationalUser(array $overrides = []): User + { + return User::factory()->create(array_merge([ + 'role_id' => $this->role(UserRole::USER)->id, + 'email_verified_at' => now(), + 'must_change_password' => false, + 'two_factor_setup_completed_at' => now(), + ], $overrides)); + } + + /** Operativo con ruolo Admin → supera tier.admin / IsAdmin. */ + protected function adminUser(array $overrides = []): User + { + return $this->operationalUser(array_merge([ + 'role_id' => $this->role(UserRole::ADMIN)->id, + ], $overrides)); + } + + /** Utente di sistema (destinatario dei contenuti riassegnati, non eliminabile). */ + protected function systemUser(array $overrides = []): User + { + return $this->operationalUser(array_merge([ + 'is_system' => true, + 'role_id' => $this->role(UserRole::ADMIN)->id, + ], $overrides)); + } + + /** Autenticato ma setup incompleto (deve cambiare password). */ + protected function setupIncompleteUser(array $overrides = []): User + { + return $this->operationalUser(array_merge([ + 'must_change_password' => true, + ], $overrides)); + } + + /** Setup completo ma email non verificata → blocco di `verified`. */ + protected function unverifiedUser(array $overrides = []): User + { + return $this->operationalUser(array_merge([ + 'email_verified_at' => null, + ], $overrides)); + } +} diff --git a/backend/tests/Feature/Auth/AccessTierTest.php b/backend/tests/Feature/Auth/AccessTierTest.php new file mode 100644 index 0000000..0a10e6b --- /dev/null +++ b/backend/tests/Feature/Auth/AccessTierTest.php @@ -0,0 +1,73 @@ +actingAs($this->operationalUser(), 'sanctum'); + + $this->getJson(self::APP_ROUTE)->assertOk(); + } + + public function test_tier_app_requires_authentication(): void + { + $this->getJson(self::APP_ROUTE)->assertUnauthorized(); + } + + public function test_tier_app_blocks_user_who_must_change_password(): void + { + $this->actingAs($this->setupIncompleteUser(), 'sanctum'); + + $this->getJson(self::APP_ROUTE) + ->assertForbidden() + ->assertJsonPath('setup_status', 'password_required'); + } + + public function test_tier_app_blocks_user_without_two_factor_setup(): void + { + $user = $this->operationalUser(['two_factor_setup_completed_at' => null]); + $this->actingAs($user, 'sanctum'); + + $this->getJson(self::APP_ROUTE) + ->assertForbidden() + ->assertJsonPath('setup_status', '2fa_setup_required'); + } + + public function test_tier_app_blocks_unverified_user(): void + { + $this->actingAs($this->unverifiedUser(), 'sanctum'); + + $this->getJson(self::APP_ROUTE)->assertForbidden(); + } + + public function test_tier_admin_allows_admin(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->getJson(self::ADMIN_ROUTE)->assertOk(); + } + + public function test_tier_admin_forbids_non_admin(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + + $this->getJson(self::ADMIN_ROUTE)->assertForbidden(); + } +} diff --git a/backend/tests/Feature/Auth/AuthControllerTest.php b/backend/tests/Feature/Auth/AuthControllerTest.php new file mode 100644 index 0000000..dc4552c --- /dev/null +++ b/backend/tests/Feature/Auth/AuthControllerTest.php @@ -0,0 +1,86 @@ +operationalUser(); + $this->actingAs($user, 'sanctum'); + + $response = $this->getJson('/api/user'); + + $response->assertOk() + ->assertJsonPath('id', $user->id) + ->assertJsonPath('email', $user->email) + ->assertJsonPath('setup_status', 'complete'); + + // Gli attributi sensibili non devono mai essere serializzati. + $this->assertArrayNotHasKey('password', $response->json()); + $this->assertArrayNotHasKey('two_factor_secret', $response->json()); + $this->assertArrayNotHasKey('two_factor_recovery_codes', $response->json()); + } + + public function test_me_is_reachable_during_setup(): void + { + // La rotta /user è nel tier "transitional": deve restare accessibile anche + // a setup incompleto, così il frontend può leggere setup_status. + $user = $this->setupIncompleteUser(); + $this->actingAs($user, 'sanctum'); + + $this->getJson('/api/user') + ->assertOk() + ->assertJsonPath('setup_status', 'password_required'); + } + + public function test_me_requires_authentication(): void + { + $this->getJson('/api/user')->assertUnauthorized(); + } + + public function test_logout_succeeds_and_forgets_cookies(): void + { + $user = $this->operationalUser(); + $this->actingAs($user, 'sanctum'); + + $response = $this->postJson('/api/logout'); + + $response->assertOk() + ->assertJsonPath('message', config('messages.auth.logout_success')) + ->assertCookieExpired('XSRF-TOKEN'); + } + + public function test_destroy_account_soft_deletes_the_user(): void + { + $user = $this->operationalUser(); + $this->actingAs($user, 'sanctum'); + + $this->deleteJson('/api/account') + ->assertOk() + ->assertJsonPath('message', config('messages.account.deleted')); + + $this->assertSoftDeleted('users', ['id' => $user->id]); + } + + public function test_destroy_account_is_forbidden_for_the_system_user(): void + { + $user = $this->systemUser(); + $this->actingAs($user, 'sanctum'); + + $this->deleteJson('/api/account')->assertForbidden(); + + $this->assertDatabaseHas('users', ['id' => $user->id, 'deleted_at' => null]); + } + + public function test_session_endpoints_require_authentication(): void + { + $this->postJson('/api/logout')->assertUnauthorized(); + $this->deleteJson('/api/account')->assertUnauthorized(); + } +} diff --git a/backend/tests/Feature/EnvironmentSafetyTest.php b/backend/tests/Feature/EnvironmentSafetyTest.php new file mode 100644 index 0000000..ac3e552 --- /dev/null +++ b/backend/tests/Feature/EnvironmentSafetyTest.php @@ -0,0 +1,24 @@ +assertSame('testing', app()->environment()); + $this->assertSame('mysql', config('database.default')); + $this->assertSame('db-test', config('database.connections.mysql.host')); + $this->assertSame('dyncoll_test', DB::connection()->getDatabaseName()); + } +} diff --git a/backend/tests/Feature/ExampleTest.php b/backend/tests/Feature/ExampleTest.php deleted file mode 100644 index 8364a84..0000000 --- a/backend/tests/Feature/ExampleTest.php +++ /dev/null @@ -1,19 +0,0 @@ -get('/'); - - $response->assertStatus(200); - } -} diff --git a/backend/tests/Feature/PurgeAnonymizableUsersCommandTest.php b/backend/tests/Feature/PurgeAnonymizableUsersCommandTest.php new file mode 100644 index 0000000..ac574cf --- /dev/null +++ b/backend/tests/Feature/PurgeAnonymizableUsersCommandTest.php @@ -0,0 +1,77 @@ +operationalUser(); + $user->delete(); + User::withTrashed()->whereKey($user->id)->update(['deleted_at' => now()->subDays($days)]); + + return $user->fresh(); + } + + public function test_it_anonymizes_users_past_the_retention_window(): void + { + $this->systemUser(); + $old = $this->trashedDaysAgo(40); + + $this->artisan('users:purge', ['--days' => 30])->assertSuccessful(); + + $old->refresh(); + $this->assertNotNull($old->anonymized_at); + $this->assertSame('Deleted user', $old->name); + } + + public function test_it_skips_users_still_within_retention(): void + { + $this->systemUser(); + $recent = $this->trashedDaysAgo(5); + + $this->artisan('users:purge', ['--days' => 30])->assertSuccessful(); + + $recent->refresh(); + $this->assertNull($recent->anonymized_at); + } + + public function test_dry_run_does_not_modify_anyone(): void + { + $this->systemUser(); + $old = $this->trashedDaysAgo(40); + + $this->artisan('users:purge', ['--days' => 30, '--dry-run' => true])->assertSuccessful(); + + $old->refresh(); + $this->assertNull($old->anonymized_at); + } + + public function test_it_never_touches_the_system_user(): void + { + $system = $this->systemUser(); + // Anche se (per assurdo) cestinato e vecchio, l'utente di sistema è escluso. + $system->delete(); + User::withTrashed()->whereKey($system->id)->update(['deleted_at' => now()->subDays(99)]); + + $this->artisan('users:purge', ['--days' => 30])->assertSuccessful(); + + $this->assertNull($system->fresh()->anonymized_at); + } + + public function test_it_reports_when_there_is_nothing_to_do(): void + { + $this->systemUser(); + + $this->artisan('users:purge', ['--days' => 30]) + ->expectsOutputToContain('No users to anonymize.') + ->assertSuccessful(); + } +} diff --git a/backend/tests/Feature/PurgeUserActionTest.php b/backend/tests/Feature/PurgeUserActionTest.php new file mode 100644 index 0000000..d4907b3 --- /dev/null +++ b/backend/tests/Feature/PurgeUserActionTest.php @@ -0,0 +1,55 @@ +systemUser(); + $user = $this->operationalUser(['name' => 'Mario Rossi']); + + $this->purge()->execute($user); + + $user->refresh(); + $this->assertSame('Deleted user', $user->name); + $this->assertSame("deleted-{$user->id}@anonymized.invalid", $user->email); + $this->assertNotNull($user->anonymized_at); + $this->assertNull($user->email_verified_at); + $this->assertNull($user->two_factor_setup_completed_at); + $this->assertFalse($user->must_change_password); + } + + public function test_it_is_idempotent_on_already_anonymized_users(): void + { + $this->systemUser(); + $user = $this->operationalUser(['name' => 'Keep Me', 'anonymized_at' => now()]); + + $this->purge()->execute($user); + + $user->refresh(); + // Già anonimizzato → uscita anticipata: nessuna riscrittura del nome. + $this->assertSame('Keep Me', $user->name); + } + + public function test_it_refuses_to_anonymize_the_system_user(): void + { + $system = $this->systemUser(); + + $this->expectException(CannotDeleteSystemUserException::class); + + $this->purge()->execute($system); + } +} diff --git a/backend/tests/Feature/SeederTest.php b/backend/tests/Feature/SeederTest.php new file mode 100644 index 0000000..cd70c43 --- /dev/null +++ b/backend/tests/Feature/SeederTest.php @@ -0,0 +1,73 @@ +seed(UserRoleSeeder::class); + + $this->assertSame(4, UserRole::count()); + foreach ([UserRole::ADMIN, UserRole::SUPERVISOR, UserRole::USER, UserRole::GUEST] as $name) { + $this->assertDatabaseHas('user_roles', ['name' => $name]); + } + } + + public function test_role_descriptions_are_in_english(): void + { + $this->seed(UserRoleSeeder::class); + + $this->assertStringContainsString( + 'Full access', + (string) UserRole::where('name', UserRole::ADMIN)->value('description') + ); + } + + public function test_user_role_seeder_is_idempotent(): void + { + $this->seed(UserRoleSeeder::class); + $this->seed(UserRoleSeeder::class); + + $this->assertSame(4, UserRole::count()); + } + + public function test_system_user_seeder_creates_a_locked_admin_account(): void + { + $this->seed(UserRoleSeeder::class); + $this->seed(SystemUserSeeder::class); + + $system = User::where('email', SystemUserSeeder::EMAIL)->firstOrFail(); + $this->assertTrue($system->is_system); + $this->assertFalse($system->must_change_password); + $this->assertSame(UserRole::ADMIN, $system->role->name); + $this->assertSame('complete', $system->setup_status); + } + + public function test_system_user_seeder_is_idempotent(): void + { + $this->seed(UserRoleSeeder::class); + $this->seed(SystemUserSeeder::class); + $this->seed(SystemUserSeeder::class); + + $this->assertSame(1, User::where('email', SystemUserSeeder::EMAIL)->count()); + } + + public function test_database_seeder_runs_the_full_chain(): void + { + $this->seed(DatabaseSeeder::class); + + $this->assertSame(4, UserRole::count()); + $this->assertDatabaseHas('users', ['email' => SystemUserSeeder::EMAIL, 'is_system' => true]); + } +} diff --git a/backend/tests/Feature/UserControllerTest.php b/backend/tests/Feature/UserControllerTest.php new file mode 100644 index 0000000..aa1007c --- /dev/null +++ b/backend/tests/Feature/UserControllerTest.php @@ -0,0 +1,170 @@ + id presenti in data della risposta. */ + private function idsFrom(TestResponse $response): array + { + return collect($response->json('data'))->pluck('id')->all(); + } + + // --- Gating --------------------------------------------------------------- + + public function test_index_requires_authentication(): void + { + $this->getJson('/api/users')->assertUnauthorized(); + } + + public function test_index_is_forbidden_for_non_admin(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + + $this->getJson('/api/users')->assertForbidden(); + } + + // --- Index ---------------------------------------------------------------- + + public function test_index_returns_paginated_envelope(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->getJson('/api/users') + ->assertOk() + ->assertJsonStructure([ + 'message', + 'data', + 'meta' => ['current_page', 'last_page', 'per_page', 'total'], + ]); + } + + public function test_index_filters_by_search_term(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $match = $this->operationalUser(['name' => 'Zzz Unique Marker']); + $other = $this->operationalUser(['name' => 'Someone Else']); + + $ids = $this->idsFrom($this->getJson('/api/users?search=Unique+Marker')->assertOk()); + + $this->assertContains($match->id, $ids); + $this->assertNotContains($other->id, $ids); + } + + public function test_index_filters_by_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::factory()->create(); + $inRole = $this->operationalUser(['role_id' => $role->id]); + $outRole = $this->operationalUser(); + + $ids = $this->idsFrom($this->getJson("/api/users?role_id={$role->id}")->assertOk()); + + $this->assertContains($inRole->id, $ids); + $this->assertNotContains($outRole->id, $ids); + } + + public function test_index_excludes_trashed_by_default(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $trashed = $this->operationalUser(); + $trashed->delete(); + + $ids = $this->idsFrom($this->getJson('/api/users')->assertOk()); + + $this->assertNotContains($trashed->id, $ids); + } + + public function test_index_can_include_trashed(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $active = $this->operationalUser(); + $trashed = $this->operationalUser(); + $trashed->delete(); + + $ids = $this->idsFrom($this->getJson('/api/users?trashed=with')->assertOk()); + + $this->assertContains($active->id, $ids); + $this->assertContains($trashed->id, $ids); + } + + public function test_index_can_return_only_trashed(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $active = $this->operationalUser(); + $trashed = $this->operationalUser(); + $trashed->delete(); + + $ids = $this->idsFrom($this->getJson('/api/users?trashed=only')->assertOk()); + + $this->assertContains($trashed->id, $ids); + $this->assertNotContains($active->id, $ids); + } + + public function test_index_respects_per_page(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->getJson('/api/users?per_page=5') + ->assertOk() + ->assertJsonPath('meta.per_page', 5); + } + + public function test_index_rejects_an_excessive_per_page(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->getJson('/api/users?per_page=500') + ->assertUnprocessable() + ->assertJsonValidationErrors('per_page'); + } + + public function test_index_rejects_an_unknown_trashed_value(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->getJson('/api/users?trashed=garbage') + ->assertUnprocessable() + ->assertJsonValidationErrors('trashed'); + } + + // --- Show ----------------------------------------------------------------- + + public function test_show_returns_a_user_with_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $user = $this->operationalUser(); + + $this->getJson("/api/users/{$user->id}") + ->assertOk() + ->assertJsonPath('data.id', $user->id) + ->assertJsonPath('data.setup_status', 'complete'); + } + + public function test_show_can_load_a_trashed_user(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $user = $this->operationalUser(); + $user->delete(); + + $this->getJson("/api/users/{$user->id}") + ->assertOk() + ->assertJsonPath('data.id', $user->id); + } + + public function test_show_is_forbidden_for_non_admin(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + $target = User::factory()->create(); + + $this->getJson("/api/users/{$target->id}")->assertForbidden(); + } +} diff --git a/backend/tests/Feature/UserRoleControllerTest.php b/backend/tests/Feature/UserRoleControllerTest.php new file mode 100644 index 0000000..d5a8397 --- /dev/null +++ b/backend/tests/Feature/UserRoleControllerTest.php @@ -0,0 +1,163 @@ +actingAs($this->operationalUser(), 'sanctum'); + UserRole::factory()->count(2)->create(); + + $this->getJson('/api/user-roles') + ->assertOk() + ->assertJsonStructure(['message', 'data' => [['id', 'name', 'description']]]); + } + + public function test_show_returns_a_single_role(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + $role = UserRole::factory()->create(); + + $this->getJson("/api/user-roles/{$role->id}") + ->assertOk() + ->assertJsonPath('data.id', $role->id) + ->assertJsonPath('data.name', $role->name); + } + + public function test_usage_flags_system_role_as_locked(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + $role = UserRole::create(['name' => UserRole::SUPERVISOR]); + + $this->getJson("/api/user-roles/{$role->id}/usage") + ->assertOk() + ->assertJsonPath('in_use', true); + } + + public function test_usage_flags_free_custom_role_as_unlocked(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + $role = UserRole::factory()->create(); + + $this->getJson("/api/user-roles/{$role->id}/usage") + ->assertOk() + ->assertJsonPath('in_use', false); + } + + // --- Scrittura (tier.admin) --------------------------------------------- + + public function test_admin_can_create_a_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->postJson('/api/user-roles', ['name' => 'Editor', 'description' => 'Edits stuff']) + ->assertCreated() + ->assertJsonPath('data.name', 'Editor'); + + $this->assertDatabaseHas('user_roles', ['name' => 'Editor']); + } + + public function test_create_requires_a_name(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + + $this->postJson('/api/user-roles', ['description' => 'no name']) + ->assertUnprocessable() + ->assertJsonValidationErrors('name'); + } + + public function test_create_rejects_a_duplicate_name(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + UserRole::factory()->create(['name' => 'Editor']); + + $this->postJson('/api/user-roles', ['name' => 'Editor']) + ->assertUnprocessable() + ->assertJsonValidationErrors('name'); + } + + public function test_admin_can_update_a_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::factory()->create(['name' => 'Old']); + + $this->putJson("/api/user-roles/{$role->id}", ['name' => 'New']) + ->assertOk() + ->assertJsonPath('data.name', 'New'); + + $this->assertDatabaseHas('user_roles', ['id' => $role->id, 'name' => 'New']); + } + + public function test_update_can_keep_the_same_name(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::factory()->create(['name' => 'Stable']); + + $this->putJson("/api/user-roles/{$role->id}", ['name' => 'Stable']) + ->assertOk(); + } + + public function test_update_rejects_a_name_taken_by_another_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + UserRole::factory()->create(['name' => 'Taken']); + $role = UserRole::factory()->create(['name' => 'Mine']); + + $this->putJson("/api/user-roles/{$role->id}", ['name' => 'Taken']) + ->assertUnprocessable() + ->assertJsonValidationErrors('name'); + } + + public function test_cannot_delete_a_system_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::create(['name' => UserRole::GUEST]); + + $this->deleteJson("/api/user-roles/{$role->id}") + ->assertStatus(409) + ->assertJsonPath('in_use', true); + + $this->assertDatabaseHas('user_roles', ['id' => $role->id]); + } + + public function test_cannot_delete_a_role_in_use(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::factory()->create(['name' => 'Busy']); + $this->operationalUser(['role_id' => $role->id]); + + $this->deleteJson("/api/user-roles/{$role->id}") + ->assertStatus(409) + ->assertJsonPath('in_use', true); + + $this->assertDatabaseHas('user_roles', ['id' => $role->id]); + } + + public function test_admin_can_delete_a_free_custom_role(): void + { + $this->actingAs($this->adminUser(), 'sanctum'); + $role = UserRole::factory()->create(['name' => 'Disposable']); + + $this->deleteJson("/api/user-roles/{$role->id}")->assertOk(); + + $this->assertDatabaseMissing('user_roles', ['id' => $role->id]); + } + + // --- Gating --------------------------------------------------------------- + + public function test_non_admin_cannot_write(): void + { + $this->actingAs($this->operationalUser(), 'sanctum'); + + $this->postJson('/api/user-roles', ['name' => 'Nope'])->assertForbidden(); + } +} diff --git a/backend/tests/Support/UnsafeTestDatabaseException.php b/backend/tests/Support/UnsafeTestDatabaseException.php new file mode 100644 index 0000000..6297fed --- /dev/null +++ b/backend/tests/Support/UnsafeTestDatabaseException.php @@ -0,0 +1,21 @@ +getDatabaseName(); + + if ($database !== 'dyncoll_test') { + throw UnsafeTestDatabaseException::for($database); + } + } } diff --git a/backend/tests/Unit/ExampleTest.php b/backend/tests/Unit/ExampleTest.php deleted file mode 100644 index 5773b0c..0000000 --- a/backend/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertTrue(true); - } -} diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 935f779..a5a6791 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -42,6 +42,41 @@ services: build: target: development command: ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"] + # db-test si avvia insieme al backend (merge col depends_on del compose base: + # db + redis). Così `make test` trova sempre il DB di test pronto senza doverlo + # accendere a mano. È in override → presente solo in dev/CI, mai in produzione. + depends_on: + db-test: + condition: service_healthy + + # DB dedicato ai test (SOLO dev/CI — sta qui in override, non nel compose base + # né in produzione). Isolamento FISICO dal db di sviluppo: la suite punta qui + # (DB_HOST=db-test, vedi target `make test`), così nessun RefreshDatabase può + # raggiungere e azzerare `dyncoll`. tmpfs = dati in RAM → effimeri e veloci. + # Non è una replica dei DATI: i test ricostruiscono lo schema dalle migration e + # vogliono un DB vuoto; serve solo lo stesso motore (mysql:8.4) del db di sviluppo. + db-test: + image: mysql:8.4 + container_name: dyncoll_db_test + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MYSQL_DATABASE: dyncoll_test + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} + tmpfs: + - /var/lib/mysql + networks: + - internal + healthcheck: + # Stessa logica del db dev (docker-compose.yml): $$ = $ letterale espanso a + # runtime dalla shell del container; password quotata per i caratteri speciali. + test: ["CMD-SHELL", "mysqladmin ping -h localhost -u \"$$MYSQL_USER\" -p\"$$MYSQL_PASSWORD\""] + interval: 10s + timeout: 5s + retries: 5 + labels: + - "com.docker.compose.project=dyncoll-project" # Mailpit: SMTP sink di sviluppo. Cattura tutte le mail (nessuna spedizione reale). # SMTP su 1025 (raggiungibile dal backend come host `mailpit`), Web UI su 8025. diff --git a/docker-compose.yml b/docker-compose.yml index 7e17031..53a7556 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,7 @@ services: APP_KEY: ${APP_KEY} APP_DEBUG: ${APP_DEBUG} APP_ENV: ${APP_ENV} - DB_CONNECTION: mysql + DB_procedCONNECTION: mysql DB_HOST: db DB_PORT: ${DB_PORT} DB_DATABASE: ${DB_DATABASE} @@ -91,7 +91,10 @@ services: MYSQL_USER: ${DB_USERNAME} MYSQL_PASSWORD: ${DB_PASSWORD} ports: - - "127.0.0.1:${DB_PORT}:3306" + # Porta PUBBLICATA sull'host (per client esterni) ≠ porta di connessione + # interna: i container raggiungono mysql su 3306 (DB_PORT), il forward host + # usa DB_HOST_PORT per non collidere con un MySQL nativo sulla 3306. + - "127.0.0.1:${DB_HOST_PORT}:3306" volumes: - mysql_data:/var/lib/mysql # Nessun import di dump qui: i dati v2 arrivano da migration Laravel + ETL @@ -126,7 +129,9 @@ services: redis: condition: service_healthy ports: - - ${APP_BACKEND_PORT}:8000 + # Solo loopback: in dev/prod l'accesso passa da Traefik/proxy, non serve + # esporre il backend sulla LAN. + - "127.0.0.1:${APP_BACKEND_PORT}:8000" networks: - internal volumes: diff --git a/frontend/.stylelintrc.json b/frontend/.stylelintrc.json new file mode 100644 index 0000000..cd46ad4 --- /dev/null +++ b/frontend/.stylelintrc.json @@ -0,0 +1,26 @@ +{ + "extends": "stylelint-config-standard", + "rules": { + "declaration-property-value-no-unknown": true, + "import-notation": null, + "at-rule-no-unknown": [ + true, + { + "ignoreAtRules": [ + "theme", + "apply", + "tailwind", + "config", + "plugin", + "source", + "utility", + "variant", + "custom-variant", + "reference" + ] + } + ], + "color-function-notation": null, + "alpha-value-notation": null + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8f66bd5..3efa448 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,12 +23,34 @@ "@vitest/coverage-v8": "^4.1.8", "daisyui": "^5.5.23", "rollup-plugin-visualizer": "^7.0.1", + "stylelint": "^17.13.0", + "stylelint-config-standard": "^40.0.0", "tailwindcss": "^4.3.1", "typescript": "^6.0.3", "vite": "^8.0.16", "vitest": "^4.1.8" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -84,6 +106,183 @@ "node": ">=18" } }, + "node_modules/@cacheable/memory": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.9.tgz", + "integrity": "sha512-HdMx6DoGywB30vacDbBsITbIX4pgFqj1zsrV58jZBUw3klzkNoXhj7qOqAgledhxG7YZI5rBSJg7Zp8/VG0DuA==", + "dev": true, + "dependencies": { + "@cacheable/utils": "^2.4.1", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==", + "dev": true, + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", + "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz", + "integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz", + "integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", + "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -176,6 +375,28 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true + }, "node_modules/@kurkle/color": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", @@ -199,6 +420,41 @@ "@emnapi/runtime": "^1.7.1" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@oxc-project/types": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", @@ -456,6 +712,18 @@ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -922,6 +1190,22 @@ "node": ">= 6.0.0" } }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -946,6 +1230,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -966,6 +1256,15 @@ "js-tokens": "^10.0.0" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -982,6 +1281,18 @@ "proxy-from-env": "^2.1.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -997,6 +1308,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacheable": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.5.tgz", + "integrity": "sha512-EQfaKe09tl615iNvq/TBRWTFf1AKJNXYQSsMx0Z3EI0nA+pVsVPS8wJhnRlkbdacKPh1d0qVIhwTc2zsQNFEEg==", + "dev": true, + "dependencies": { + "@cacheable/memory": "^2.0.8", + "@cacheable/utils": "^2.4.1", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1009,6 +1333,15 @@ "node": ">= 0.4" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -1051,6 +1384,30 @@ "node": ">=20" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1068,6 +1425,66 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/daisyui": { "version": "5.5.23", "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.23.tgz", @@ -1182,6 +1599,24 @@ "node": ">=10.13.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1256,6 +1691,62 @@ "node": ">=12.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1273,6 +1764,44 @@ } } }, + "node_modules/file-entry-cache": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.3.tgz", + "integrity": "sha512-oMbq0PD6VIiIwMF6LIa7MEwd/l9huKwmqRKXqmrkqIZv8CvRbfowL+L0ryAl8h//HfAS0zS+4SbYoRyAoA6BJA==", + "dev": true, + "dependencies": { + "flat-cache": "^6.1.22" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "6.1.22", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.22.tgz", + "integrity": "sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug==", + "dev": true, + "dependencies": { + "cacheable": "^2.3.4", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true + }, "node_modules/follow-redirects": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", @@ -1385,6 +1914,70 @@ "node": ">= 0.4" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globby": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz", + "integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -1436,6 +2029,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -1447,12 +2052,30 @@ "node": ">= 0.4" } }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html-tags": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", + "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==", + "dev": true, + "engines": { + "node": ">=20.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -1465,6 +2088,53 @@ "node": ">= 6" } }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -1480,6 +2150,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-in-ssh": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", @@ -1510,6 +2210,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -1525,6 +2246,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -1576,6 +2303,58 @@ "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", @@ -1830,6 +2609,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, "node_modules/lucide": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/lucide/-/lucide-1.18.0.tgz", @@ -1878,6 +2669,68 @@ "node": ">= 0.4" } }, + "node_modules/mathml-tag-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz", + "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true + }, + "node_modules/meow": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", + "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1920,6 +2773,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/obug": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", @@ -1953,6 +2815,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -2005,6 +2897,51 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/powershell-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", @@ -2025,6 +2962,72 @@ "node": ">=10" } }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rolldown": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", @@ -2100,6 +3103,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/semver": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", @@ -2118,6 +3144,62 @@ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -2180,6 +3262,128 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/stylelint": { + "version": "17.13.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.13.0.tgz", + "integrity": "sha512-G1WYzMerp7ihOaIe9VJCHLt12MoAD2QLf1AFerYP37+BCRBUK5UCpq8e/mN+zCIaJPKQcaxhE4WlPmqdiOx/gw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "dependencies": { + "@csstools/css-calc": "^3.2.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.4", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "@csstools/selector-resolve-nested": "^4.0.0", + "@csstools/selector-specificity": "^6.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.1", + "css-functions-list": "^3.3.3", + "css-tree": "^3.2.1", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.3", + "global-modules": "^2.0.0", + "globby": "^16.2.0", + "globjoin": "^0.1.4", + "html-tags": "^5.1.0", + "ignore": "^7.0.5", + "import-meta-resolve": "^4.2.0", + "mathml-tag-names": "^4.0.0", + "meow": "^14.1.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.15", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.1", + "postcss-value-parser": "^4.2.0", + "string-width": "^8.2.1", + "supports-hyperlinks": "^4.4.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^7.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-18.0.0.tgz", + "integrity": "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.0.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "40.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-40.0.0.tgz", + "integrity": "sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "dependencies": { + "stylelint-config-recommended": "^18.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.0.0" + } + }, + "node_modules/stylelint/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2192,6 +3396,109 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", + "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", + "dev": true, + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tailwindcss": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", @@ -2251,6 +3558,18 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2277,6 +3596,24 @@ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true }, + "node_modules/unicorn-magic": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", + "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", @@ -2443,6 +3780,18 @@ } } }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2476,6 +3825,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/write-file-atomic": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", + "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", + "dev": true, + "dependencies": { + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/wsl-utils": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 96d5023..66bbf9e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,11 +8,15 @@ "build": "vite build", "preview": "vite preview", "type-check": "tsc --noEmit", + "lint:css": "stylelint \"src/**/*.css\"", + "lint:css:fix": "stylelint --fix \"src/**/*.css\"", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest" }, "devDependencies": { "@tailwindcss/vite": "^4.3.1", + "stylelint": "^17.13.0", + "stylelint-config-standard": "^40.0.0", "@types/leaflet": "^1.9.21", "@types/node": "^25.9.3", "@vitest/coverage-v8": "^4.1.8", diff --git a/frontend/public/logo/logo_dyncoll.png b/frontend/public/logo/logo_dyncoll.png index c5e917053bd5debfe4e72b08695b0ddaf9f3f5c7..79e5730b265c025e99921bca5c7121557743980a 100644 GIT binary patch literal 88989 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

Dp_S!ii-a?@)&s! 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 QbpW)`(>By9Lq7`oAHW2l0ssI2 literal 101684 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
?NRqecp8awD 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 diff --git a/frontend/src/config/menuTypes.ts b/frontend/src/config/menuTypes.ts index 2a723e7..e27777a 100644 --- a/frontend/src/config/menuTypes.ts +++ b/frontend/src/config/menuTypes.ts @@ -9,7 +9,15 @@ type IconName = | 'log-out' | 'user' | 'settings' - | 'layout-dashboard'; + | 'layout-dashboard' + | 'pickaxe' + | 'box' + | 'landmark' + | 'book-user' + | 'history' + | 'list' + | 'mail-plus' + | 'images'; type Visibility = | 'always' @@ -41,7 +49,7 @@ const menu: MenuItem[] = [ { id: 'legalLink', href: '/policy', ico: 'shield', label: 'Legal', visibility: 'always' }, { id: 'docsLink', href: 'https://lunddarklab.github.io/adc/', ico: 'book-open', label: 'Docs', visibility: 'always' }, { id: 'loginLink', href: '/login', ico: 'log-in', label: 'Login', visibility: 'guest-only' }, - { id: 'userMenuToggle', href: '#', ico: 'menu', visibility: 'auth-only' }, + { id: 'user-menu-toggle', href: '#', ico: 'menu', visibility: 'auth-only' }, ]; @@ -55,9 +63,15 @@ export function getVisibleItems(isLoggedIn: boolean) { } // --- Menu utente (pannello laterale) ------------------------------------- -// Mostrato solo agli utenti autenticati. Le voci con `roles` sono visibili -// solo ai ruoli elencati; senza `roles` sono visibili a tutti i loggati. -// Per spostare/aggiungere una voce basta intervenire qui, senza toccare il DOM. +// Mostrato solo agli utenti autenticati. Le voci sono raggruppate in sezioni +// con titolo (come in dyncoll.v1/assets/menu.php). Sia i gruppi sia le singole +// voci possono dichiarare `roles`: se presente, sono visibili solo ai ruoli +// elencati; se assente, a tutti i loggati. Per spostare/aggiungere una voce +// basta intervenire qui, senza toccare il DOM. +// +// NB: le "main pages" di v1 (home/map/credits/legal/db model) non sono replicate +// qui: in v2 l'header-menu è sempre visibile, quindi quei link non servono nel +// pannello. Gli href sono rotte placeholder, da allineare al routing reale. type PanelAction = 'logout'; @@ -70,35 +84,55 @@ export type UserPanelItem = { roles?: RoleId[]; // assente = tutti gli autenticati }; -const userPanel: UserPanelItem[] = [ - { id: 'dashboardLink', label: 'Dashboard', ico: 'layout-dashboard', href: '/dashboard' }, - { id: 'profileLink', label: 'Profile', ico: 'user', href: '/profile' }, - { id: 'adminLink', label: 'Admin', ico: 'settings', href: '/admin', roles: [ROLE.ADMIN, ROLE.SUPERVISOR] }, - { id: 'logoutBtn', label: 'Logout', ico: 'log-out', action: 'logout' }, +export type UserPanelGroup = { + title?: string; // titolo di sezione; assente nel primo gruppo + roles?: RoleId[]; // assente = tutti gli autenticati + items: UserPanelItem[]; +}; + +const userPanel: UserPanelGroup[] = [ + { + items: [ + { id: 'dashboardLink', label: 'Dashboard', ico: 'layout-dashboard', href: '/dashboard' }, + ], + }, + { + title: 'Add resource', + items: [ + { id: 'artifactAddLink', label: 'Artifact', ico: 'pickaxe', href: '/artifacts/add' }, + { id: 'modelAddLink', label: 'Model', ico: 'box', href: '/models/add' }, + { id: 'institutionAddLink', label: 'Institution', ico: 'landmark', href: '/institutions/add' }, + { id: 'personAddLink', label: 'Person', ico: 'book-user', href: '/persons/add' }, + ], + }, + { + title: 'Admin', + roles: [ROLE.ADMIN, ROLE.SUPERVISOR], + items: [ + { id: 'timelineLink', label: 'Timeline', ico: 'history', href: '/timeline' }, + { id: 'vocabulariesLink', label: 'Vocabularies', ico: 'list', href: '/vocabularies' }, + { id: 'mailComposerLink', label: 'Compose email', ico: 'mail-plus', href: '/mail/compose' }, + ], + }, + { + title: 'My account', + items: [ + { id: 'settingsLink', label: 'Settings', ico: 'settings', href: '/settings' }, + { id: 'collectionsLink', label: 'My collections', ico: 'images', href: '#' }, + { id: 'logoutBtn', label: 'Logout', ico: 'log-out', action: 'logout' }, + ], + }, ]; -export function getUserPanelItems(roleId: number): UserPanelItem[] { - return userPanel.filter(item => !item.roles || item.roles.includes(roleId as RoleId)); -} - -// Tipi per sidebar -// type SidebarAction = 'logout'; -// type SidebarItem = { -// id?: string; -// label: string; -// ico: IconName; -// href?: string; // se presente, la voce è un link -// action?: SidebarAction; // se presente, la voce è un pulsante con comportamento -// }; - -// Gruppo di voci del menu laterale. `roles` elenca i ruoli che vedono il gruppo: -// per spostare/aggiungere un link basta intervenire qui, senza toccare il DOM. -// type SidebarGroup = { -// title?: string; // titolo di sezione (header colorato); assente nel primo gruppo -// roles: RoleId[]; -// items: SidebarItem[]; -// }; - -// Menu laterale (utenti autenticati). I gruppi sono filtrati per ruolo: -// sposta una voce in un altro gruppo per cambiarne la visibilità. -// const ALL_AUTH: RoleId[] = [ROLE.ADMIN, ROLE.SUPERVISOR, ROLE.USER]; \ No newline at end of file +// Ritorna i gruppi visibili al ruolo: filtra prima i gruppi, poi le singole voci, +// scartando infine i gruppi rimasti vuoti. +export function getUserPanelGroups(roleId: number): UserPanelGroup[] { + const role = roleId as RoleId; + return userPanel + .filter(group => !group.roles || group.roles.includes(role)) + .map(group => ({ + ...group, + items: group.items.filter(item => !item.roles || item.roles.includes(role)), + })) + .filter(group => group.items.length > 0); +} \ No newline at end of file diff --git a/frontend/src/config/ui.ts b/frontend/src/config/ui.ts index 780895a..a827aa5 100644 --- a/frontend/src/config/ui.ts +++ b/frontend/src/config/ui.ts @@ -1,11 +1,12 @@ import type { AuthUser } from "@/shared/auth"; import { logoutUser } from "@/shared/auth"; -import { getVisibleItems, getUserPanelItems, type UserPanelItem } from "./menuTypes"; +import { getVisibleItems, getUserPanelGroups, type UserPanelItem } from "./menuTypes"; import { getCurrentDate } from "@/shared/utils"; import { createIcons, House, MapPin, Award, Shield, BookOpen, LogIn, Menu, LogOut, User, Settings, LayoutDashboard, + Pickaxe, Box, Landmark, BookUser, History, List, MailPlus, Images, } from 'lucide'; type Img = { @@ -27,7 +28,11 @@ const footerImg: Img[] = [ ]; // Set unico di icone Lucide usate nelle isole (header, pannello, footer). -const icons = { House, MapPin, Award, Shield, BookOpen, LogIn, Menu, LogOut, User, Settings, LayoutDashboard }; +const icons = { + House, MapPin, Award, Shield, BookOpen, LogIn, Menu, + LogOut, User, Settings, LayoutDashboard, + Pickaxe, Box, Landmark, BookUser, History, List, MailPlus, Images, +}; export function renderUI(user: AuthUser | null): void { setLogoHeader(); @@ -92,7 +97,7 @@ function setHeaderMenu(user: AuthUser | null): void { function setFooterContent(user: AuthUser | null): void{ const footerMenu = document.getElementById('footer-menu'); if(footerMenu){ - buildMenu(footerMenu, getVisibleItems(user !== null), { exclude: ['userMenuToggle'] }); + buildMenu(footerMenu, getVisibleItems(user !== null), { exclude: ['user-menu-toggle'] }); } addFooterLogo(); } @@ -127,7 +132,7 @@ function buildMenu(dom: HTMLElement, links: MenuLink[], opts: { exclude?: string // --------------------------------------------------------------------------- // Pannello utente (solo loggati): pannello laterale da destra, sotto l'header, -// con backdrop leggero. L'hamburger (#userMenuToggle) resta sempre visibile e +// con backdrop leggero. L'hamburger (#user-menu-toggle) resta sempre visibile e // fa da toggle. Replica il comportamento di v1 senza coprire l'header. const PANEL_ID = 'user-panel'; const BACKDROP_ID = 'user-panel-backdrop'; @@ -139,7 +144,7 @@ function setUserPanel(user: AuthUser | null): void { document.getElementById(BACKDROP_ID)?.remove(); if (escHandler) { document.removeEventListener('keydown', escHandler); escHandler = null; } - const toggle = document.getElementById('userMenuToggle') as HTMLAnchorElement | null; + const toggle = document.getElementById('user-menu-toggle') as HTMLAnchorElement | null; if (!user || !toggle) return; // backdrop (parte sotto l'header, vedi CSS) @@ -156,8 +161,14 @@ function setUserPanel(user: AuthUser | null): void { const list = document.createElement('ul'); list.className = 'menu w-full p-4'; - getUserPanelItems(user.role_id).forEach(item => { - list.appendChild(buildPanelEntry(item)); + getUserPanelGroups(user.role_id).forEach(group => { + if (group.title) { + const title = document.createElement('li'); + title.className = 'menu-title'; + title.textContent = group.title; + list.appendChild(title); + } + group.items.forEach(item => list.appendChild(buildPanelEntry(item))); }); panel.appendChild(list); diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index c8019e6..3ef5233 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -3,7 +3,9 @@ @import '@fontsource/rajdhani/500.css'; @import '@fontsource/rajdhani/700.css'; /* display/titoli, coerente col logo */ @import "tailwindcss"; + @plugin "daisyui"; /* NOSONAR */ + @theme { --font-sans: "Titillium Web", ui-sans-serif, system-ui, sans-serif; --font-secondary: "Rajdhani", ui-sans-serif, system-ui, sans-serif; @@ -17,6 +19,7 @@ --dc-secondary-dark: rgb(241, 146, 4); --dc-white: rgb(255, 255, 255); --dc-dark-gray: rgb(52, 58, 64); + --dc-light-gray: rgb(77,76,68,1); } body{ @@ -48,6 +51,12 @@ header { justify-content: center; gap: 3px; } + +.user-panel li a, +.user-panel li button{ + align-items: center; +} + #header-menu a{ width:70px; height:100%; @@ -57,7 +66,7 @@ header { justify-content: center; } -#header-menu a:not(#userMenuToggle){ +#header-menu a:not(#user-menu-toggle){ border-bottom: 4px solid transparent; transition: all 500ms ease; } @@ -90,22 +99,16 @@ header { width:100%; height:150px; display: flex; - align-content: center; - justify-content: center; -} - -@media (hover: hover) and (pointer: fine) { - #header-menu a:not(#userMenuToggle):hover{ - border-bottom-color: var(--dc-white); - } + place-content: center center; } /* Sotto md (48rem): voci più strette e label più piccola, così il testo resta sotto le icone senza mandare in overflow l'header su schermi piccoli. */ -@media (max-width: 47.999rem) { +@media (width <= 47.999rem) { #header-menu a{ width: 3rem; } + #header-menu a span{ font-size: 0.6rem; line-height: 1.1; @@ -121,36 +124,72 @@ header { top: var(--dc-header-h); right: 0; bottom: 0; - width: min(20rem, 85vw); + width: min(15rem, 85vw); z-index: 910; - background-color: var(--dc-white); - color: var(--dc-dark-gray); + background-color: var(--dc-light-gray); + color: var(--dc-white); box-shadow: -4px 0 16px rgb(0 0 0 / 0.18); overflow-y: auto; transform: translateX(100%); transition: transform 300ms ease; + padding:0; } + .user-panel.is-open{ transform: translateX(0); } +ul.menu{ padding:0 } + +ul.menu > li > *{ + padding:10px 20px; + transition: all 300ms ease; +} + +.menu .menu-title{ + background-color: var(--dc-dark-gray); + color: var(--dc-white); + font-size: 1.2rem; + font-weight: normal; +} + +.menu li:not(.menu-title){ + border-bottom: 1px solid var(--dc-dark-gray); +} + +/* Voci del pannello utente: l'icona Lucide (svg, 24px di default) viene + dimensionata in em così segue il testo, e allineata verticalmente ad esso. */ + + +.user-panel li :is(a, button) svg{ + width: 1.25em; + height: 1.25em; + flex-shrink: 0; +} + .user-panel-backdrop{ position: fixed; - top: var(--dc-header-h); - left: 0; - right: 0; - bottom: 0; + inset: var(--dc-header-h) 0 0 0; z-index: 900; background-color: rgb(0 0 0 / 0.25); opacity: 0; pointer-events: none; transition: opacity 300ms ease; } + .user-panel-backdrop.is-open{ opacity: 1; pointer-events: auto; } +@media (hover: hover) and (pointer: fine) { + #header-menu a:not(#user-menu-toggle):hover{ + border-bottom-color: var(--dc-white); + } + + ul.menu > li > *:hover{ padding-left: 35px;} +} + @media (prefers-reduced-motion: reduce) { .user-panel, .user-panel-backdrop{ diff --git a/frontend/template.html.example b/frontend/template.html.example deleted file mode 100644 index 4de9ffa..0000000 --- a/frontend/template.html.example +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Dynamic Collection - Index - - -
-
- -
- -
- - - -
- - - \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 1ab055c..6943307 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,7 +3,6 @@ import type { Plugin } from 'vite' import { fileURLToPath } from 'node:url' import { readdirSync } from 'node:fs' import tailwindcss from '@tailwindcss/vite' -import { visualizer } from 'rollup-plugin-visualizer' // MPA: raccoglie automaticamente ogni *.html nella root di frontend/ come entry di build. // { 'index': '/app/index.html', 'scheda': '/app/scheda.html', ... } @@ -44,7 +43,6 @@ function mpaRewritePlugin(): Plugin { export default defineConfig({ plugins: [ tailwindcss(), - visualizer(), mpaRewritePlugin() ], resolve: {