diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index c081dfd..bc4a975 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -2,6 +2,7 @@ import { Routes } from '@angular/router'; import { languageGuard } from './guards/language.guard'; import { projectEditorDirtyGuard } from './features/project-editor/guards/project-editor-dirty.guard'; import { adminAuthGuard } from './core/admin-auth/admin-auth.guard'; +import { authRoutes } from './core/auth/auth.routes'; import { adminCategoryDirtyGuard } from './features/admin/categories/guards/admin-category-dirty.guard'; import { AdminLayoutComponent } from './features/admin/shell/admin-layout.component'; import { environment } from '../environments/environment'; @@ -45,10 +46,12 @@ const coreRoutes: Routes = [ }, { path: 'edit', + canActivate: [adminAuthGuard], loadComponent: () => import('./features/project-editor/pages/builder-overview-page.component').then(m => m.BuilderOverviewPageComponent) }, { path: 'edit/:section', + canActivate: [adminAuthGuard], loadComponent: () => import('./features/project-editor/pages/project-editor-page.component').then(m => m.ProjectEditorPageComponent), canDeactivate: [projectEditorDirtyGuard] }, @@ -294,6 +297,7 @@ export const routes: Routes = [ path: '__diagnostics', loadComponent: () => import('./features/diagnostics/components/diagnostics-page.component').then(m => m.DiagnosticsPageComponent) }]), + ...authRoutes, { path: ':lang', canActivate: [languageGuard], diff --git a/src/app/core/auth/auth.routes.ts b/src/app/core/auth/auth.routes.ts new file mode 100644 index 0000000..901d13e --- /dev/null +++ b/src/app/core/auth/auth.routes.ts @@ -0,0 +1,19 @@ +import { Routes } from '@angular/router'; + +/** + * Ed25519 admin auth routes. Registered at top level (outside the `:lang` + * prefix, alongside `__diagnostics`) since login/error screens are + * infrastructure, not localized storefront content. Not linked from any + * live nav - reachable only by direct URL until the backend ships the + * challenge/verify endpoints and a guard is pointed here (see docs/AUTH.md). + */ +export const authRoutes: Routes = [ + { + path: 'admin-login', + loadComponent: () => import('./pages/admin-login-page.component').then(m => m.AdminLoginPageComponent) + }, + { + path: 'admin-login/error/:code', + loadComponent: () => import('./pages/auth-error-page.component').then(m => m.AuthErrorPageComponent) + } +]; diff --git a/src/app/core/auth/pages/admin-login-page.component.html b/src/app/core/auth/pages/admin-login-page.component.html new file mode 100644 index 0000000..279bc32 --- /dev/null +++ b/src/app/core/auth/pages/admin-login-page.component.html @@ -0,0 +1,27 @@ +
+ Sign in with your device's Ed25519 key. A challenge is requested from the server, signed locally, and + verified - your private key never leaves this device. +
+ + @if (!isEd25519Supported) { ++ This browser does not support Ed25519 (requires WebCrypto + IndexedDB). Use an up-to-date browser. +
+ } + + @if (lastError()) { +{{ lastError()?.message }}
+ } + + @if (phaseLabel()) { +{{ phaseLabel() }}
+ } + +