@payez/next-mvp 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +782 -0
- package/dist/api/auth-handler.d.ts +67 -0
- package/dist/api/auth-handler.js +397 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +19 -0
- package/dist/api-handlers/account/change-password.d.ts +9 -0
- package/dist/api-handlers/account/change-password.js +112 -0
- package/dist/api-handlers/account/masked-info.d.ts +2 -0
- package/dist/api-handlers/account/masked-info.js +41 -0
- package/dist/api-handlers/account/profile.d.ts +3 -0
- package/dist/api-handlers/account/profile.js +63 -0
- package/dist/api-handlers/account/recovery/initiate.d.ts +2 -0
- package/dist/api-handlers/account/recovery/initiate.js +26 -0
- package/dist/api-handlers/account/recovery/send-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/send-code.js +28 -0
- package/dist/api-handlers/account/recovery/verify-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/verify-code.js +28 -0
- package/dist/api-handlers/account/reset-password.d.ts +2 -0
- package/dist/api-handlers/account/reset-password.js +26 -0
- package/dist/api-handlers/account/send-code.d.ts +24 -0
- package/dist/api-handlers/account/send-code.js +60 -0
- package/dist/api-handlers/account/update-phone.d.ts +27 -0
- package/dist/api-handlers/account/update-phone.js +64 -0
- package/dist/api-handlers/account/validate-password.d.ts +17 -0
- package/dist/api-handlers/account/validate-password.js +81 -0
- package/dist/api-handlers/account/verify-email.d.ts +26 -0
- package/dist/api-handlers/account/verify-email.js +106 -0
- package/dist/api-handlers/account/verify-sms.d.ts +26 -0
- package/dist/api-handlers/account/verify-sms.js +106 -0
- package/dist/api-handlers/admin/analytics.d.ts +20 -0
- package/dist/api-handlers/admin/analytics.js +379 -0
- package/dist/api-handlers/admin/audit.d.ts +20 -0
- package/dist/api-handlers/admin/audit.js +214 -0
- package/dist/api-handlers/admin/index.d.ts +21 -0
- package/dist/api-handlers/admin/index.js +41 -0
- package/dist/api-handlers/admin/redis-sessions.d.ts +36 -0
- package/dist/api-handlers/admin/redis-sessions.js +204 -0
- package/dist/api-handlers/admin/sessions.d.ts +21 -0
- package/dist/api-handlers/admin/sessions.js +284 -0
- package/dist/api-handlers/admin/site-logs.d.ts +46 -0
- package/dist/api-handlers/admin/site-logs.js +318 -0
- package/dist/api-handlers/admin/users.d.ts +20 -0
- package/dist/api-handlers/admin/users.js +222 -0
- package/dist/api-handlers/admin/vibe-data.d.ts +80 -0
- package/dist/api-handlers/admin/vibe-data.js +268 -0
- package/dist/api-handlers/anon/preferences.d.ts +37 -0
- package/dist/api-handlers/anon/preferences.js +96 -0
- package/dist/api-handlers/auth/jwks.d.ts +2 -0
- package/dist/api-handlers/auth/jwks.js +24 -0
- package/dist/api-handlers/auth/login.d.ts +42 -0
- package/dist/api-handlers/auth/login.js +178 -0
- package/dist/api-handlers/auth/refresh.d.ts +74 -0
- package/dist/api-handlers/auth/refresh.js +635 -0
- package/dist/api-handlers/auth/signout.d.ts +37 -0
- package/dist/api-handlers/auth/signout.js +187 -0
- package/dist/api-handlers/auth/status.d.ts +8 -0
- package/dist/api-handlers/auth/status.js +26 -0
- package/dist/api-handlers/auth/update-session.d.ts +37 -0
- package/dist/api-handlers/auth/update-session.js +95 -0
- package/dist/api-handlers/auth/validate.d.ts +6 -0
- package/dist/api-handlers/auth/validate.js +43 -0
- package/dist/api-handlers/auth/verify-code.d.ts +43 -0
- package/dist/api-handlers/auth/verify-code.js +94 -0
- package/dist/api-handlers/session/refresh-viability.d.ts +14 -0
- package/dist/api-handlers/session/refresh-viability.js +39 -0
- package/dist/api-handlers/session/viability.d.ts +13 -0
- package/dist/api-handlers/session/viability.js +146 -0
- package/dist/api-handlers/test/force-expire.d.ts +23 -0
- package/dist/api-handlers/test/force-expire.js +65 -0
- package/dist/auth/auth-decision.d.ts +39 -0
- package/dist/auth/auth-decision.js +182 -0
- package/dist/auth/auth-options.d.ts +57 -0
- package/dist/auth/auth-options.js +213 -0
- package/dist/auth/callbacks/index.d.ts +6 -0
- package/dist/auth/callbacks/index.js +12 -0
- package/dist/auth/callbacks/jwt.d.ts +45 -0
- package/dist/auth/callbacks/jwt.js +305 -0
- package/dist/auth/callbacks/session.d.ts +60 -0
- package/dist/auth/callbacks/session.js +170 -0
- package/dist/auth/callbacks/signin.d.ts +23 -0
- package/dist/auth/callbacks/signin.js +44 -0
- package/dist/auth/events/index.d.ts +4 -0
- package/dist/auth/events/index.js +8 -0
- package/dist/auth/events/signout.d.ts +17 -0
- package/dist/auth/events/signout.js +32 -0
- package/dist/auth/providers/credentials.d.ts +32 -0
- package/dist/auth/providers/credentials.js +223 -0
- package/dist/auth/providers/index.d.ts +5 -0
- package/dist/auth/providers/index.js +21 -0
- package/dist/auth/providers/oauth.d.ts +26 -0
- package/dist/auth/providers/oauth.js +105 -0
- package/dist/auth/route-config.d.ts +66 -0
- package/dist/auth/route-config.js +190 -0
- package/dist/auth/types/auth-types.d.ts +417 -0
- package/dist/auth/types/auth-types.js +53 -0
- package/dist/auth/types/index.d.ts +6 -0
- package/dist/auth/types/index.js +22 -0
- package/dist/auth/unauthenticated-routes.d.ts +1 -0
- package/dist/auth/unauthenticated-routes.js +19 -0
- package/dist/auth/utils/idp-client.d.ts +94 -0
- package/dist/auth/utils/idp-client.js +383 -0
- package/dist/auth/utils/index.d.ts +5 -0
- package/dist/auth/utils/index.js +21 -0
- package/dist/auth/utils/token-utils.d.ts +84 -0
- package/dist/auth/utils/token-utils.js +219 -0
- package/dist/client/AuthContext.d.ts +19 -0
- package/dist/client/AuthContext.js +112 -0
- package/dist/client/fetch-with-auth.d.ts +11 -0
- package/dist/client/fetch-with-auth.js +44 -0
- package/dist/client/fetchWithSession.d.ts +3 -0
- package/dist/client/fetchWithSession.js +24 -0
- package/dist/client/index.d.ts +9 -0
- package/dist/client/index.js +20 -0
- package/dist/client/useAnonSession.d.ts +36 -0
- package/dist/client/useAnonSession.js +99 -0
- package/dist/components/SessionSync.d.ts +13 -0
- package/dist/components/SessionSync.js +119 -0
- package/dist/components/SignalRHealthCheck.d.ts +10 -0
- package/dist/components/SignalRHealthCheck.js +97 -0
- package/dist/components/account/UserAvatarMenu.d.ts +20 -0
- package/dist/components/account/UserAvatarMenu.js +80 -0
- package/dist/components/account/index.d.ts +7 -0
- package/dist/components/account/index.js +10 -0
- package/dist/components/admin/AlertSettingsTab.d.ts +48 -0
- package/dist/components/admin/AlertSettingsTab.js +351 -0
- package/dist/components/admin/AnalyticsTab.d.ts +22 -0
- package/dist/components/admin/AnalyticsTab.js +167 -0
- package/dist/components/admin/DataBrowserTab.d.ts +19 -0
- package/dist/components/admin/DataBrowserTab.js +252 -0
- package/dist/components/admin/LoggingSettingsTab.d.ts +73 -0
- package/dist/components/admin/LoggingSettingsTab.js +339 -0
- package/dist/components/admin/SessionsTab.d.ts +37 -0
- package/dist/components/admin/SessionsTab.js +165 -0
- package/dist/components/admin/StatsTab.d.ts +53 -0
- package/dist/components/admin/StatsTab.js +161 -0
- package/dist/components/admin/VibeAdminContext.d.ts +32 -0
- package/dist/components/admin/VibeAdminContext.js +38 -0
- package/dist/components/admin/VibeAdminLayout.d.ts +11 -0
- package/dist/components/admin/VibeAdminLayout.js +69 -0
- package/dist/components/admin/index.d.ts +29 -0
- package/dist/components/admin/index.js +44 -0
- package/dist/components/auth/FederatedAuthSection.d.ts +8 -0
- package/dist/components/auth/FederatedAuthSection.js +45 -0
- package/dist/components/auth/ModeAwareLoginPage.d.ts +10 -0
- package/dist/components/auth/ModeAwareLoginPage.js +42 -0
- package/dist/components/auth/ModeAwareSignupPage.d.ts +9 -0
- package/dist/components/auth/ModeAwareSignupPage.js +78 -0
- package/dist/components/auth/TraditionalAuthSection.d.ts +14 -0
- package/dist/components/auth/TraditionalAuthSection.js +20 -0
- package/dist/components/recovery/CompleteStep.d.ts +5 -0
- package/dist/components/recovery/CompleteStep.js +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.d.ts +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.js +20 -0
- package/dist/components/recovery/SelectMethodStep.d.ts +8 -0
- package/dist/components/recovery/SelectMethodStep.js +8 -0
- package/dist/components/recovery/SetPasswordStep.d.ts +6 -0
- package/dist/components/recovery/SetPasswordStep.js +20 -0
- package/dist/components/recovery/VerifyCodeStep.d.ts +10 -0
- package/dist/components/recovery/VerifyCodeStep.js +24 -0
- package/dist/components/reserved/ReservedRecoveryWarning.d.ts +38 -0
- package/dist/components/reserved/ReservedRecoveryWarning.js +92 -0
- package/dist/components/reserved/ReservedStatusBox.d.ts +30 -0
- package/dist/components/reserved/ReservedStatusBox.js +71 -0
- package/dist/components/ui/BetaBadge.d.ts +29 -0
- package/dist/components/ui/BetaBadge.js +38 -0
- package/dist/components/ui/Footer.d.ts +37 -0
- package/dist/components/ui/Footer.js +41 -0
- package/dist/config/env.d.ts +66 -0
- package/dist/config/env.js +57 -0
- package/dist/config/logger.d.ts +57 -0
- package/dist/config/logger.js +73 -0
- package/dist/config/logging-config.d.ts +30 -0
- package/dist/config/logging-config.js +122 -0
- package/dist/config/unauthenticated-routes.d.ts +17 -0
- package/dist/config/unauthenticated-routes.js +24 -0
- package/dist/config/vibe-log-transport.d.ts +79 -0
- package/dist/config/vibe-log-transport.js +203 -0
- package/dist/edge/internal-api-url.d.ts +53 -0
- package/dist/edge/internal-api-url.js +63 -0
- package/dist/edge/middleware.d.ts +14 -0
- package/dist/edge/middleware.js +32 -0
- package/dist/hooks/useAuth.d.ts +23 -0
- package/dist/hooks/useAuth.js +81 -0
- package/dist/hooks/useAuthSettings.d.ts +59 -0
- package/dist/hooks/useAuthSettings.js +93 -0
- package/dist/hooks/useAvailableProviders.d.ts +45 -0
- package/dist/hooks/useAvailableProviders.js +108 -0
- package/dist/hooks/usePasswordValidation.d.ts +27 -0
- package/dist/hooks/usePasswordValidation.js +102 -0
- package/dist/hooks/useProfile.d.ts +15 -0
- package/dist/hooks/useProfile.js +59 -0
- package/dist/hooks/usePublicAuthSettings.d.ts +56 -0
- package/dist/hooks/usePublicAuthSettings.js +131 -0
- package/dist/hooks/useSessionExpiration.d.ts +57 -0
- package/dist/hooks/useSessionExpiration.js +72 -0
- package/dist/hooks/useViabilitySession.d.ts +75 -0
- package/dist/hooks/useViabilitySession.js +268 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +54 -0
- package/dist/lib/anon-session.d.ts +74 -0
- package/dist/lib/anon-session.js +169 -0
- package/dist/lib/api-handler.d.ts +123 -0
- package/dist/lib/api-handler.js +478 -0
- package/dist/lib/app-slug.d.ts +95 -0
- package/dist/lib/app-slug.js +172 -0
- package/dist/lib/demo-mode.d.ts +6 -0
- package/dist/lib/demo-mode.js +16 -0
- package/dist/lib/geolocation.d.ts +64 -0
- package/dist/lib/geolocation.js +235 -0
- package/dist/lib/idp-client-config.d.ts +75 -0
- package/dist/lib/idp-client-config.js +351 -0
- package/dist/lib/idp-fetch.d.ts +14 -0
- package/dist/lib/idp-fetch.js +91 -0
- package/dist/lib/internal-api.d.ts +87 -0
- package/dist/lib/internal-api.js +122 -0
- package/dist/lib/jwt-decode-client.d.ts +10 -0
- package/dist/lib/jwt-decode-client.js +46 -0
- package/dist/lib/jwt-decode.d.ts +48 -0
- package/dist/lib/jwt-decode.js +57 -0
- package/dist/lib/nextauth-secret.d.ts +10 -0
- package/dist/lib/nextauth-secret.js +104 -0
- package/dist/lib/rate-limit-service.d.ts +23 -0
- package/dist/lib/rate-limit-service.js +6 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.js +28 -0
- package/dist/lib/refresh-token-validator.d.ts +13 -0
- package/dist/lib/refresh-token-validator.js +117 -0
- package/dist/lib/roles.d.ts +145 -0
- package/dist/lib/roles.js +168 -0
- package/dist/lib/secret-validation.d.ts +4 -0
- package/dist/lib/secret-validation.js +14 -0
- package/dist/lib/session-store.d.ts +166 -0
- package/dist/lib/session-store.js +537 -0
- package/dist/lib/session.d.ts +21 -0
- package/dist/lib/session.js +26 -0
- package/dist/lib/site-logger.d.ts +214 -0
- package/dist/lib/site-logger.js +210 -0
- package/dist/lib/standardized-client-api.d.ts +161 -0
- package/dist/lib/standardized-client-api.js +786 -0
- package/dist/lib/startup-init.d.ts +40 -0
- package/dist/lib/startup-init.js +261 -0
- package/dist/lib/test-aware-get-token.d.ts +2 -0
- package/dist/lib/test-aware-get-token.js +81 -0
- package/dist/lib/token-expiry.d.ts +14 -0
- package/dist/lib/token-expiry.js +39 -0
- package/dist/lib/token-lifecycle.d.ts +52 -0
- package/dist/lib/token-lifecycle.js +398 -0
- package/dist/lib/types/api-responses.d.ts +128 -0
- package/dist/lib/types/api-responses.js +171 -0
- package/dist/lib/user-agent-parser.d.ts +50 -0
- package/dist/lib/user-agent-parser.js +220 -0
- package/dist/logging/api/admin-analytics.d.ts +3 -0
- package/dist/logging/api/admin-analytics.js +45 -0
- package/dist/logging/api/audit-log.d.ts +3 -0
- package/dist/logging/api/audit-log.js +52 -0
- package/dist/logging/components/AdminAnalyticsLayout.d.ts +10 -0
- package/dist/logging/components/AdminAnalyticsLayout.js +11 -0
- package/dist/logging/components/AuditLogViewer.d.ts +7 -0
- package/dist/logging/components/AuditLogViewer.js +51 -0
- package/dist/logging/components/ErrorMetricsCard.d.ts +7 -0
- package/dist/logging/components/ErrorMetricsCard.js +16 -0
- package/dist/logging/components/HealthMetricsCard.d.ts +7 -0
- package/dist/logging/components/HealthMetricsCard.js +19 -0
- package/dist/logging/hooks/useAdminAnalytics.d.ts +24 -0
- package/dist/logging/hooks/useAdminAnalytics.js +22 -0
- package/dist/logging/hooks/useAuditLog.d.ts +6 -0
- package/dist/logging/hooks/useAuditLog.js +25 -0
- package/dist/logging/hooks/useErrorMetrics.d.ts +6 -0
- package/dist/logging/hooks/useErrorMetrics.js +38 -0
- package/dist/logging/hooks/useHealthMetrics.d.ts +6 -0
- package/dist/logging/hooks/useHealthMetrics.js +41 -0
- package/dist/logging/index.d.ts +11 -0
- package/dist/logging/index.js +40 -0
- package/dist/logging/types/analytics.d.ts +68 -0
- package/dist/logging/types/analytics.js +3 -0
- package/dist/logging/types/audit.d.ts +29 -0
- package/dist/logging/types/audit.js +2 -0
- package/dist/logging/types/index.d.ts +2 -0
- package/dist/logging/types/index.js +19 -0
- package/dist/middleware/auth-decision.d.ts +33 -0
- package/dist/middleware/auth-decision.js +65 -0
- package/dist/middleware/create-middleware.d.ts +100 -0
- package/dist/middleware/create-middleware.js +445 -0
- package/dist/middleware/rbac-check.d.ts +44 -0
- package/dist/middleware/rbac-check.js +191 -0
- package/dist/middleware/twofa-presets.d.ts +134 -0
- package/dist/middleware/twofa-presets.js +175 -0
- package/dist/models/DecodedAccessToken.d.ts +17 -0
- package/dist/models/DecodedAccessToken.js +2 -0
- package/dist/models/SessionModel.d.ts +122 -0
- package/dist/models/SessionModel.js +136 -0
- package/dist/pages/admin-login/page.d.ts +31 -0
- package/dist/pages/admin-login/page.js +83 -0
- package/dist/pages/admin-roles/RolesAdminPage.d.ts +15 -0
- package/dist/pages/admin-roles/RolesAdminPage.js +78 -0
- package/dist/pages/admin-roles/index.d.ts +8 -0
- package/dist/pages/admin-roles/index.js +15 -0
- package/dist/pages/admin-roles/modals.d.ts +72 -0
- package/dist/pages/admin-roles/modals.js +154 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.d.ts +79 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.js +177 -0
- package/dist/pages/client-admin/index.d.ts +32 -0
- package/dist/pages/client-admin/index.js +37 -0
- package/dist/pages/login/page.d.ts +22 -0
- package/dist/pages/login/page.js +239 -0
- package/dist/pages/profile/EnhancedProfilePage.d.ts +13 -0
- package/dist/pages/profile/EnhancedProfilePage.js +150 -0
- package/dist/pages/profile/index.d.ts +8 -0
- package/dist/pages/profile/index.js +16 -0
- package/dist/pages/profile/page.d.ts +19 -0
- package/dist/pages/profile/page.js +47 -0
- package/dist/pages/profile/profile-patch.d.ts +1 -0
- package/dist/pages/profile/profile-patch.js +281 -0
- package/dist/pages/recovery/page.d.ts +1 -0
- package/dist/pages/recovery/page.js +142 -0
- package/dist/pages/roles/MyRolesPage.d.ts +24 -0
- package/dist/pages/roles/MyRolesPage.js +71 -0
- package/dist/pages/roles/components.d.ts +63 -0
- package/dist/pages/roles/components.js +108 -0
- package/dist/pages/roles/index.d.ts +8 -0
- package/dist/pages/roles/index.js +19 -0
- package/dist/pages/security/EnhancedSecurityPage.d.ts +14 -0
- package/dist/pages/security/EnhancedSecurityPage.js +248 -0
- package/dist/pages/security/index.d.ts +8 -0
- package/dist/pages/security/index.js +16 -0
- package/dist/pages/security/page.d.ts +21 -0
- package/dist/pages/security/page.js +212 -0
- package/dist/pages/security/security-patch.d.ts +1 -0
- package/dist/pages/security/security-patch.js +302 -0
- package/dist/pages/settings/EnhancedSettingsPage.d.ts +46 -0
- package/dist/pages/settings/EnhancedSettingsPage.js +231 -0
- package/dist/pages/settings/index.d.ts +8 -0
- package/dist/pages/settings/index.js +16 -0
- package/dist/pages/settings/page.d.ts +7 -0
- package/dist/pages/settings/page.js +26 -0
- package/dist/pages/showcase/ShowcasePage.d.ts +13 -0
- package/dist/pages/showcase/ShowcasePage.js +140 -0
- package/dist/pages/showcase/index.d.ts +12 -0
- package/dist/pages/showcase/index.js +17 -0
- package/dist/pages/test-env/EmergencyLogoutPage.d.ts +14 -0
- package/dist/pages/test-env/EmergencyLogoutPage.js +98 -0
- package/dist/pages/test-env/JwtInspectPage.d.ts +14 -0
- package/dist/pages/test-env/JwtInspectPage.js +114 -0
- package/dist/pages/test-env/RefreshTokenPage.d.ts +15 -0
- package/dist/pages/test-env/RefreshTokenPage.js +91 -0
- package/dist/pages/test-env/TestEnvPage.d.ts +13 -0
- package/dist/pages/test-env/TestEnvPage.js +49 -0
- package/dist/pages/test-env/index.d.ts +24 -0
- package/dist/pages/test-env/index.js +32 -0
- package/dist/pages/verify-code/page.d.ts +30 -0
- package/dist/pages/verify-code/page.js +408 -0
- package/dist/routes/account/index.d.ts +28 -0
- package/dist/routes/account/index.js +71 -0
- package/dist/routes/account/masked-info.d.ts +33 -0
- package/dist/routes/account/masked-info.js +39 -0
- package/dist/routes/account/send-code.d.ts +37 -0
- package/dist/routes/account/send-code.js +42 -0
- package/dist/routes/account/update-phone.d.ts +13 -0
- package/dist/routes/account/update-phone.js +17 -0
- package/dist/routes/account/verify-email.d.ts +38 -0
- package/dist/routes/account/verify-email.js +43 -0
- package/dist/routes/account/verify-sms.d.ts +38 -0
- package/dist/routes/account/verify-sms.js +43 -0
- package/dist/routes/auth/index.d.ts +19 -0
- package/dist/routes/auth/index.js +64 -0
- package/dist/routes/auth/logout.d.ts +31 -0
- package/dist/routes/auth/logout.js +113 -0
- package/dist/routes/auth/nextauth.d.ts +19 -0
- package/dist/routes/auth/nextauth.js +72 -0
- package/dist/routes/auth/refresh.d.ts +30 -0
- package/dist/routes/auth/refresh.js +51 -0
- package/dist/routes/auth/session.d.ts +72 -0
- package/dist/routes/auth/session.js +180 -0
- package/dist/routes/auth/settings.d.ts +25 -0
- package/dist/routes/auth/settings.js +55 -0
- package/dist/routes/auth/viability.d.ts +52 -0
- package/dist/routes/auth/viability.js +201 -0
- package/dist/routes/index.d.ts +12 -0
- package/dist/routes/index.js +54 -0
- package/dist/routes/session/index.d.ts +6 -0
- package/dist/routes/session/index.js +10 -0
- package/dist/routes/session/refresh-viability.d.ts +16 -0
- package/dist/routes/session/refresh-viability.js +20 -0
- package/dist/services/signalrActivityService.d.ts +44 -0
- package/dist/services/signalrActivityService.js +257 -0
- package/dist/stores/authStore.d.ts +154 -0
- package/dist/stores/authStore.js +1531 -0
- package/dist/theme/ThemeProvider.d.ts +14 -0
- package/dist/theme/ThemeProvider.js +28 -0
- package/dist/theme/default.d.ts +8 -0
- package/dist/theme/default.js +33 -0
- package/dist/theme/index.d.ts +15 -0
- package/dist/theme/index.js +25 -0
- package/dist/theme/types.d.ts +56 -0
- package/dist/theme/types.js +8 -0
- package/dist/theme/useTheme.d.ts +60 -0
- package/dist/theme/useTheme.js +63 -0
- package/dist/theme/utils.d.ts +13 -0
- package/dist/theme/utils.js +39 -0
- package/dist/types/api.d.ts +134 -0
- package/dist/types/api.js +44 -0
- package/dist/types/auth.d.ts +19 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/logging.d.ts +42 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/recovery.d.ts +48 -0
- package/dist/types/recovery.js +2 -0
- package/dist/types/security.d.ts +1 -0
- package/dist/types/security.js +2 -0
- package/dist/utils/api.d.ts +85 -0
- package/dist/utils/api.js +287 -0
- package/dist/utils/circuitBreaker.d.ts +43 -0
- package/dist/utils/circuitBreaker.js +91 -0
- package/dist/utils/error-message.d.ts +1 -0
- package/dist/utils/error-message.js +103 -0
- package/dist/utils/layout/reservedSpace.d.ts +59 -0
- package/dist/utils/layout/reservedSpace.js +102 -0
- package/dist/utils/logout.d.ts +14 -0
- package/dist/utils/logout.js +32 -0
- package/dist/vibe/client.d.ts +261 -0
- package/dist/vibe/client.js +445 -0
- package/dist/vibe/errors.d.ts +83 -0
- package/dist/vibe/errors.js +146 -0
- package/dist/vibe/generic.d.ts +234 -0
- package/dist/vibe/generic.js +369 -0
- package/dist/vibe/hooks/index.d.ts +169 -0
- package/dist/vibe/hooks/index.js +252 -0
- package/dist/vibe/index.d.ts +23 -0
- package/dist/vibe/index.js +67 -0
- package/dist/vibe/sessions.d.ts +161 -0
- package/dist/vibe/sessions.js +391 -0
- package/dist/vibe/types.d.ts +353 -0
- package/dist/vibe/types.js +315 -0
- package/package.json +855 -0
- package/scripts/check-internal-url-usage.sh +73 -0
- package/scripts/dev-broker.ps1 +35 -0
- package/scripts/dev-local.ps1 +45 -0
- package/src/api/auth-handler.ts +550 -0
- package/src/api/index.ts +18 -0
- package/src/api-handlers/account/change-password.ts +145 -0
- package/src/api-handlers/account/masked-info.ts +45 -0
- package/src/api-handlers/account/profile.ts +80 -0
- package/src/api-handlers/account/recovery/initiate.ts +23 -0
- package/src/api-handlers/account/recovery/send-code.ts +25 -0
- package/src/api-handlers/account/recovery/verify-code.ts +25 -0
- package/src/api-handlers/account/reset-password.ts +23 -0
- package/src/api-handlers/account/send-code.ts +76 -0
- package/src/api-handlers/account/update-phone.ts +79 -0
- package/src/api-handlers/account/validate-password.ts +118 -0
- package/src/api-handlers/account/verify-email.ts +125 -0
- package/src/api-handlers/account/verify-sms.ts +125 -0
- package/src/api-handlers/admin/analytics.ts +445 -0
- package/src/api-handlers/admin/audit.ts +225 -0
- package/src/api-handlers/admin/index.ts +59 -0
- package/src/api-handlers/admin/redis-sessions.ts +253 -0
- package/src/api-handlers/admin/sessions.ts +320 -0
- package/src/api-handlers/admin/site-logs.ts +367 -0
- package/src/api-handlers/admin/users.ts +244 -0
- package/src/api-handlers/admin/vibe-data.ts +326 -0
- package/src/api-handlers/anon/preferences.ts +123 -0
- package/src/api-handlers/auth/jwks.ts +20 -0
- package/src/api-handlers/auth/login.ts +240 -0
- package/src/api-handlers/auth/refresh.ts +687 -0
- package/src/api-handlers/auth/signout.ts +212 -0
- package/src/api-handlers/auth/status.ts +23 -0
- package/src/api-handlers/auth/update-session.ts +125 -0
- package/src/api-handlers/auth/validate.ts +44 -0
- package/src/api-handlers/auth/verify-code.ts +129 -0
- package/src/api-handlers/session/refresh-viability.ts +36 -0
- package/src/api-handlers/session/viability.ts +166 -0
- package/src/api-handlers/test/force-expire.ts +67 -0
- package/src/auth/auth-decision.ts +230 -0
- package/src/auth/auth-options.ts +237 -0
- package/src/auth/callbacks/index.ts +7 -0
- package/src/auth/callbacks/jwt.ts +382 -0
- package/src/auth/callbacks/session.ts +243 -0
- package/src/auth/callbacks/signin.ts +56 -0
- package/src/auth/events/index.ts +5 -0
- package/src/auth/events/signout.ts +33 -0
- package/src/auth/providers/credentials.ts +256 -0
- package/src/auth/providers/index.ts +6 -0
- package/src/auth/providers/oauth.ts +114 -0
- package/src/auth/route-config.ts +220 -0
- package/src/auth/types/auth-types.ts +555 -0
- package/src/auth/types/index.ts +7 -0
- package/src/auth/unauthenticated-routes.ts +3 -0
- package/src/auth/utils/idp-client.ts +444 -0
- package/src/auth/utils/index.ts +6 -0
- package/src/auth/utils/token-utils.ts +244 -0
- package/src/client/AuthContext.tsx +140 -0
- package/src/client/fetch-with-auth.ts +48 -0
- package/src/client/fetchWithSession.ts +21 -0
- package/src/client/index.ts +13 -0
- package/src/client/useAnonSession.ts +131 -0
- package/src/components/SessionSync.tsx +137 -0
- package/src/components/SignalRHealthCheck.tsx +131 -0
- package/src/components/account/UserAvatarMenu.tsx +217 -0
- package/src/components/account/index.ts +8 -0
- package/src/components/admin/AlertSettingsTab.tsx +728 -0
- package/src/components/admin/AnalyticsTab.tsx +703 -0
- package/src/components/admin/DataBrowserTab.tsx +505 -0
- package/src/components/admin/LoggingSettingsTab.tsx +665 -0
- package/src/components/admin/SessionsTab.tsx +414 -0
- package/src/components/admin/StatsTab.tsx +379 -0
- package/src/components/admin/VibeAdminContext.tsx +87 -0
- package/src/components/admin/VibeAdminLayout.tsx +185 -0
- package/src/components/admin/index.ts +59 -0
- package/src/components/auth/FederatedAuthSection.tsx +95 -0
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -0
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -0
- package/src/components/auth/TraditionalAuthSection.tsx +99 -0
- package/src/components/recovery/CompleteStep.tsx +36 -0
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -0
- package/src/components/recovery/SelectMethodStep.tsx +73 -0
- package/src/components/recovery/SetPasswordStep.tsx +97 -0
- package/src/components/recovery/VerifyCodeStep.tsx +90 -0
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -0
- package/src/components/reserved/ReservedStatusBox.tsx +118 -0
- package/src/components/ui/BetaBadge.tsx +58 -0
- package/src/components/ui/Footer.tsx +93 -0
- package/src/config/env.ts +57 -0
- package/src/config/logger.ts +62 -0
- package/src/config/logging-config.ts +82 -0
- package/src/config/unauthenticated-routes.ts +19 -0
- package/src/config/vibe-log-transport.ts +250 -0
- package/src/edge/internal-api-url.ts +65 -0
- package/src/edge/middleware.ts +42 -0
- package/src/hooks/useAuth.ts +115 -0
- package/src/hooks/useAuthSettings.ts +97 -0
- package/src/hooks/useAvailableProviders.ts +118 -0
- package/src/hooks/usePasswordValidation.ts +127 -0
- package/src/hooks/useProfile.ts +75 -0
- package/src/hooks/usePublicAuthSettings.ts +149 -0
- package/src/hooks/useSessionExpiration.ts +102 -0
- package/src/hooks/useViabilitySession.ts +335 -0
- package/src/index.ts +63 -0
- package/src/lib/anon-session.ts +213 -0
- package/src/lib/api-handler.ts +625 -0
- package/src/lib/app-slug.ts +178 -0
- package/src/lib/demo-mode.ts +13 -0
- package/src/lib/geolocation.ts +265 -0
- package/src/lib/idp-client-config.ts +442 -0
- package/src/lib/idp-fetch.ts +101 -0
- package/src/lib/internal-api.ts +171 -0
- package/src/lib/jwt-decode-client.ts +45 -0
- package/src/lib/jwt-decode.ts +83 -0
- package/src/lib/nextauth-secret.ts +126 -0
- package/src/lib/rate-limit-service.ts +9 -0
- package/src/lib/redis.ts +27 -0
- package/src/lib/refresh-token-validator.ts +64 -0
- package/src/lib/roles.ts +177 -0
- package/src/lib/secret-validation.ts +8 -0
- package/src/lib/session-store.ts +637 -0
- package/src/lib/session.ts +34 -0
- package/src/lib/site-logger.ts +245 -0
- package/src/lib/standardized-client-api.ts +896 -0
- package/src/lib/startup-init.ts +247 -0
- package/src/lib/test-aware-get-token.ts +30 -0
- package/src/lib/token-expiry.ts +40 -0
- package/src/lib/token-lifecycle.ts +477 -0
- package/src/lib/types/api-responses.ts +336 -0
- package/src/lib/user-agent-parser.ts +252 -0
- package/src/logging/api/admin-analytics.ts +51 -0
- package/src/logging/api/audit-log.ts +53 -0
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -0
- package/src/logging/components/AuditLogViewer.tsx +125 -0
- package/src/logging/components/ErrorMetricsCard.tsx +98 -0
- package/src/logging/components/HealthMetricsCard.tsx +70 -0
- package/src/logging/hooks/useAdminAnalytics.ts +22 -0
- package/src/logging/hooks/useAuditLog.ts +24 -0
- package/src/logging/hooks/useErrorMetrics.ts +40 -0
- package/src/logging/hooks/useHealthMetrics.ts +44 -0
- package/src/logging/index.ts +18 -0
- package/src/logging/types/analytics.ts +81 -0
- package/src/logging/types/audit.ts +31 -0
- package/src/logging/types/index.ts +3 -0
- package/src/middleware/auth-decision.ts +43 -0
- package/src/middleware/create-middleware.ts +626 -0
- package/src/middleware/rbac-check.ts +244 -0
- package/src/middleware/twofa-presets.ts +224 -0
- package/src/models/DecodedAccessToken.ts +17 -0
- package/src/models/SessionModel.ts +258 -0
- package/src/pages/admin-login/page.tsx +229 -0
- package/src/pages/admin-roles/RolesAdminPage.tsx +357 -0
- package/src/pages/admin-roles/index.ts +9 -0
- package/src/pages/admin-roles/modals.tsx +469 -0
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +380 -0
- package/src/pages/client-admin/index.ts +33 -0
- package/src/pages/login/page.tsx +463 -0
- package/src/pages/profile/EnhancedProfilePage.tsx +479 -0
- package/src/pages/profile/index.ts +9 -0
- package/src/pages/profile/page.tsx +166 -0
- package/src/pages/recovery/page.tsx +234 -0
- package/src/pages/roles/MyRolesPage.tsx +211 -0
- package/src/pages/roles/components.tsx +294 -0
- package/src/pages/roles/index.ts +17 -0
- package/src/pages/security/EnhancedSecurityPage.tsx +574 -0
- package/src/pages/security/index.ts +9 -0
- package/src/pages/security/page.tsx +507 -0
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -0
- package/src/pages/settings/index.ts +9 -0
- package/src/pages/settings/page.tsx +47 -0
- package/src/pages/showcase/ShowcasePage.tsx +530 -0
- package/src/pages/showcase/index.ts +13 -0
- package/src/pages/test-env/EmergencyLogoutPage.tsx +179 -0
- package/src/pages/test-env/JwtInspectPage.tsx +418 -0
- package/src/pages/test-env/RefreshTokenPage.tsx +155 -0
- package/src/pages/test-env/TestEnvPage.tsx +116 -0
- package/src/pages/test-env/index.ts +25 -0
- package/src/pages/verify-code/page.tsx +648 -0
- package/src/routes/account/index.ts +32 -0
- package/src/routes/account/masked-info.ts +37 -0
- package/src/routes/account/send-code.ts +40 -0
- package/src/routes/account/update-phone.ts +13 -0
- package/src/routes/account/verify-email.ts +41 -0
- package/src/routes/account/verify-sms.ts +41 -0
- package/src/routes/auth/index.ts +23 -0
- package/src/routes/auth/logout.ts +127 -0
- package/src/routes/auth/nextauth.ts +71 -0
- package/src/routes/auth/refresh.ts +54 -0
- package/src/routes/auth/session.ts +193 -0
- package/src/routes/auth/settings.ts +75 -0
- package/src/routes/auth/viability.ts +220 -0
- package/src/routes/index.ts +18 -0
- package/src/routes/session/index.ts +7 -0
- package/src/routes/session/refresh-viability.ts +17 -0
- package/src/services/signalrActivityService.ts +258 -0
- package/src/stores/authStore.ts +1904 -0
- package/src/templates/instrumentation.ts +41 -0
- package/src/theme/ThemeProvider.tsx +39 -0
- package/src/theme/default.ts +33 -0
- package/src/theme/index.ts +31 -0
- package/src/theme/types.ts +69 -0
- package/src/theme/useTheme.ts +57 -0
- package/src/theme/utils.ts +40 -0
- package/src/types/api.ts +13 -0
- package/src/types/auth.d.ts +15 -0
- package/src/types/auth.ts +22 -0
- package/src/types/logging.ts +11 -0
- package/src/types/next-auth.d.ts +15 -0
- package/src/types/recovery.ts +54 -0
- package/src/types/security.ts +1 -0
- package/src/utils/api.ts +353 -0
- package/src/utils/circuitBreaker.ts +40 -0
- package/src/utils/error-message.ts +108 -0
- package/src/utils/layout/reservedSpace.ts +124 -0
- package/src/utils/logout.ts +30 -0
- package/src/vibe/client.ts +590 -0
- package/src/vibe/errors.ts +185 -0
- package/src/vibe/generic.ts +429 -0
- package/src/vibe/hooks/index.ts +367 -0
- package/src/vibe/index.ts +121 -0
- package/src/vibe/sessions.ts +551 -0
- package/src/vibe/types.ts +577 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Admin Login Page for @payez/next-mvp
|
|
4
|
+
*
|
|
5
|
+
* A standalone username/password login page for admin access.
|
|
6
|
+
* NOT linked from any navigation - only accessible via direct URL.
|
|
7
|
+
*
|
|
8
|
+
* USAGE:
|
|
9
|
+
* 1. Create app/account-auth/admin-login/page.tsx in your Next.js app
|
|
10
|
+
* 2. Re-export this component:
|
|
11
|
+
* export { default } from '@payez/next-mvp/pages/admin-login';
|
|
12
|
+
*
|
|
13
|
+
* CUSTOMIZATION:
|
|
14
|
+
* - Override styles via CSS variables or wrap with your own component
|
|
15
|
+
* - Provide custom branding via ThemeProvider
|
|
16
|
+
*/
|
|
17
|
+
'use client';
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.default = AdminLoginPage;
|
|
20
|
+
exports.AdminLoginForm = AdminLoginForm;
|
|
21
|
+
exports.AdminLoginFallback = AdminLoginFallback;
|
|
22
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
23
|
+
const react_1 = require("react");
|
|
24
|
+
const react_2 = require("next-auth/react");
|
|
25
|
+
const navigation_1 = require("next/navigation");
|
|
26
|
+
const react_3 = require("react");
|
|
27
|
+
const useTheme_1 = require("../../theme/useTheme");
|
|
28
|
+
function AdminLoginForm({ title = 'Admin Login', subtitle = 'Authorized personnel only', callbackUrl: propCallbackUrl, logo, }) {
|
|
29
|
+
const searchParams = (0, navigation_1.useSearchParams)();
|
|
30
|
+
const callbackUrl = propCallbackUrl || searchParams?.get('callbackUrl') || '/dashboard';
|
|
31
|
+
const branding = (0, useTheme_1.useBranding)();
|
|
32
|
+
const colors = (0, useTheme_1.useColors)();
|
|
33
|
+
const [email, setEmail] = (0, react_1.useState)('');
|
|
34
|
+
const [password, setPassword] = (0, react_1.useState)('');
|
|
35
|
+
const [showPassword, setShowPassword] = (0, react_1.useState)(false);
|
|
36
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
37
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
38
|
+
const handleSubmit = async (e) => {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
setIsLoading(true);
|
|
41
|
+
setError(null);
|
|
42
|
+
try {
|
|
43
|
+
const result = await (0, react_2.signIn)('credentials', {
|
|
44
|
+
email,
|
|
45
|
+
password,
|
|
46
|
+
redirect: false,
|
|
47
|
+
callbackUrl,
|
|
48
|
+
});
|
|
49
|
+
if (result?.error) {
|
|
50
|
+
// Parse structured error if available
|
|
51
|
+
try {
|
|
52
|
+
const errorData = JSON.parse(result.error);
|
|
53
|
+
setError(errorData.message || errorData.error?.message || 'Invalid credentials');
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
if (result.error === 'CredentialsSignin') {
|
|
57
|
+
setError('Invalid email or password');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
setError(result.error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (result?.ok) {
|
|
65
|
+
// Redirect to verify-code for 2FA or directly to callback
|
|
66
|
+
window.location.href = `/account-auth/verify-code?callbackUrl=${encodeURIComponent(callbackUrl)}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
setError('An unexpected error occurred');
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-700 via-slate-800 to-slate-900 p-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-white rounded-2xl shadow-2xl p-8 max-w-md w-full", children: [logo && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-center mb-6", children: logo })), (0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-center mb-2 text-slate-900", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "text-center mb-8 text-slate-600", children: subtitle }), error && ((0, jsx_runtime_1.jsx)("div", { className: "mb-6 px-4 py-3 rounded-lg bg-red-500 text-white text-center text-sm", children: error })), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { htmlFor: "admin-email", className: "block text-sm font-medium mb-2 text-slate-700", children: "Email" }), (0, jsx_runtime_1.jsx)("input", { id: "admin-email", type: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true, disabled: isLoading, autoComplete: "email", className: "w-full px-4 py-3 rounded-lg border border-slate-300 bg-white text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed", placeholder: "admin@example.com" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { htmlFor: "admin-password", className: "block text-sm font-medium mb-2 text-slate-700", children: "Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { id: "admin-password", type: showPassword ? 'text' : 'password', value: password, onChange: (e) => setPassword(e.target.value), required: true, disabled: isLoading, autoComplete: "current-password", className: "w-full px-4 py-3 pr-12 rounded-lg border border-slate-300 bg-white text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed", placeholder: "Enter your password" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-slate-500 hover:text-slate-700", "aria-label": showPassword ? 'Hide password' : 'Show password', children: showPassword ? ((0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", className: "w-5 h-5", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L6.464 6.464m7.535 7.535l3.415 3.414M3 3l3.464 3.464M21 21l-3.415-3.414" }) })) : ((0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", className: "w-5 h-5", children: [(0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" }), (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" })] })) })] })] }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: isLoading, className: "w-full py-3 px-4 rounded-lg font-semibold text-white transition-colors bg-slate-700 hover:bg-slate-800 disabled:bg-slate-400 disabled:cursor-not-allowed", children: isLoading ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center", children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin -ml-1 mr-3 h-5 w-5 text-white", fill: "none", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), "Signing in..."] })) : ('Sign In') })] }), (0, jsx_runtime_1.jsxs)("p", { className: "mt-6 text-center text-xs text-slate-500", children: ["This login is for authorized administrators only.", (0, jsx_runtime_1.jsx)("br", {}), "All access attempts are logged."] })] }) }));
|
|
77
|
+
}
|
|
78
|
+
function AdminLoginFallback() {
|
|
79
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-700 via-slate-800 to-slate-900", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin h-10 w-10 mx-auto text-white", fill: "none", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), (0, jsx_runtime_1.jsx)("p", { className: "mt-4 text-slate-400", children: "Loading..." })] }) }));
|
|
80
|
+
}
|
|
81
|
+
function AdminLoginPage(props) {
|
|
82
|
+
return ((0, jsx_runtime_1.jsx)(react_3.Suspense, { fallback: (0, jsx_runtime_1.jsx)(AdminLoginFallback, {}), children: (0, jsx_runtime_1.jsx)(AdminLoginForm, { ...props }) }));
|
|
83
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roles Admin Page for @payez/next-mvp
|
|
3
|
+
*
|
|
4
|
+
* Read-only admin interface for viewing roles and permissions (/admin/roles).
|
|
5
|
+
* MVP scope: View IDP roles and their page permissions only.
|
|
6
|
+
* Role creation/editing deferred to post-MVP.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/specs/ROLES_MANAGEMENT_SPEC.md
|
|
9
|
+
*/
|
|
10
|
+
interface RolesAdminPageProps {
|
|
11
|
+
rolesEndpoint?: string;
|
|
12
|
+
matrixEndpoint?: string;
|
|
13
|
+
}
|
|
14
|
+
export default function RolesAdminPage({ rolesEndpoint, matrixEndpoint, }: RolesAdminPageProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Roles Admin Page for @payez/next-mvp
|
|
4
|
+
*
|
|
5
|
+
* Read-only admin interface for viewing roles and permissions (/admin/roles).
|
|
6
|
+
* MVP scope: View IDP roles and their page permissions only.
|
|
7
|
+
* Role creation/editing deferred to post-MVP.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/specs/ROLES_MANAGEMENT_SPEC.md
|
|
10
|
+
*/
|
|
11
|
+
'use client';
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.default = RolesAdminPage;
|
|
14
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
15
|
+
const react_1 = require("react");
|
|
16
|
+
const useTheme_1 = require("../../theme/useTheme");
|
|
17
|
+
const components_1 = require("../roles/components");
|
|
18
|
+
function RolesAdminPage({ rolesEndpoint = '/api/v1/admin/roles', matrixEndpoint = '/api/v1/admin/permissions-matrix', }) {
|
|
19
|
+
const layout = (0, useTheme_1.useLayout)();
|
|
20
|
+
const colors = (0, useTheme_1.useColors)();
|
|
21
|
+
const isDark = colors?.background?.includes('slate-9') ||
|
|
22
|
+
colors?.background?.includes('gray-9') ||
|
|
23
|
+
colors?.card?.includes('slate-8');
|
|
24
|
+
// State
|
|
25
|
+
const [viewMode, setViewMode] = (0, react_1.useState)('list');
|
|
26
|
+
const [roles, setRoles] = (0, react_1.useState)([]);
|
|
27
|
+
const [matrixData, setMatrixData] = (0, react_1.useState)(null);
|
|
28
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
29
|
+
const [expandedRole, setExpandedRole] = (0, react_1.useState)(null);
|
|
30
|
+
// Theme colors
|
|
31
|
+
const bgColor = isDark ? 'bg-slate-900' : 'bg-gray-50';
|
|
32
|
+
const cardBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
33
|
+
const borderColor = isDark ? 'border-slate-700' : 'border-gray-200';
|
|
34
|
+
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
35
|
+
const textMuted = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
36
|
+
const hoverBg = isDark ? 'hover:bg-slate-700' : 'hover:bg-gray-50';
|
|
37
|
+
// Fetch data
|
|
38
|
+
const fetchRoles = (0, react_1.useCallback)(async () => {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch(rolesEndpoint, { credentials: 'include' });
|
|
41
|
+
if (res.ok) {
|
|
42
|
+
const data = await res.json();
|
|
43
|
+
setRoles(data.roles || data.idp_roles || []);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
console.error('Failed to fetch roles:', err);
|
|
48
|
+
}
|
|
49
|
+
}, [rolesEndpoint]);
|
|
50
|
+
const fetchMatrix = (0, react_1.useCallback)(async () => {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(matrixEndpoint, { credentials: 'include' });
|
|
53
|
+
if (res.ok) {
|
|
54
|
+
const data = await res.json();
|
|
55
|
+
setMatrixData(data);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error('Failed to fetch matrix:', err);
|
|
60
|
+
}
|
|
61
|
+
}, [matrixEndpoint]);
|
|
62
|
+
(0, react_1.useEffect)(() => {
|
|
63
|
+
setLoading(true);
|
|
64
|
+
Promise.all([fetchRoles(), fetchMatrix()])
|
|
65
|
+
.finally(() => setLoading(false));
|
|
66
|
+
}, [fetchRoles, fetchMatrix]);
|
|
67
|
+
const toggleRoleExpanded = (roleName) => {
|
|
68
|
+
setExpandedRole(expandedRole === roleName ? null : roleName);
|
|
69
|
+
};
|
|
70
|
+
if (loading) {
|
|
71
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgColor} flex items-center justify-center`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center space-y-4", children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin h-8 w-8 text-blue-500", viewBox: "0 0 24 24", fill: "none", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })] }), (0, jsx_runtime_1.jsx)("p", { className: textMuted, children: "Loading roles..." })] }) }));
|
|
72
|
+
}
|
|
73
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgColor}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `max-w-6xl mx-auto ${layout?.padding || 'p-6'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: `text-3xl font-bold ${textPrimary}`, children: "Role Permissions" }), (0, jsx_runtime_1.jsx)("p", { className: `mt-1 ${textMuted}`, children: "View IDP roles and their page access permissions" })] }), (0, jsx_runtime_1.jsx)("a", { href: "/admin", className: `text-sm hover:underline ${textMuted}`, children: "Back to Admin" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `flex border-b ${borderColor} mb-6`, children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setViewMode('list'), className: `px-4 py-3 font-medium text-sm border-b-2 transition-colors ${viewMode === 'list'
|
|
74
|
+
? 'border-blue-500 text-blue-500'
|
|
75
|
+
: `border-transparent ${textMuted} hover:text-blue-400`}`, children: "Role List" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setViewMode('matrix'), className: `px-4 py-3 font-medium text-sm border-b-2 transition-colors ${viewMode === 'matrix'
|
|
76
|
+
? 'border-blue-500 text-blue-500'
|
|
77
|
+
: `border-transparent ${textMuted} hover:text-blue-400`}`, children: "Permissions Matrix" })] }), viewMode === 'list' && ((0, jsx_runtime_1.jsxs)("div", { className: `${cardBg} border ${borderColor} rounded-lg overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-4 py-3 border-b ${borderColor}`, children: (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: "Click a role to see what pages it grants access to" }) }), (0, jsx_runtime_1.jsx)("div", { className: `divide-y ${borderColor}`, children: roles.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: `p-8 text-center ${textMuted}`, children: "No roles found" })) : (roles.map((role) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => toggleRoleExpanded(role.role_name), className: `w-full px-4 py-4 flex items-center justify-between ${hoverBg} transition-colors`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("svg", { className: `w-5 h-5 ${textMuted} transition-transform ${expandedRole === role.role_name ? 'rotate-90' : ''}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-left", children: [(0, jsx_runtime_1.jsx)("div", { className: `font-medium ${textPrimary}`, children: role.display_name || role.role_name }), (0, jsx_runtime_1.jsx)("div", { className: `text-sm ${textMuted}`, children: role.role_name })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(components_1.RoleBadge, { roleName: "IDP", source: "idp", size: "sm", isDark: isDark }), (0, jsx_runtime_1.jsxs)("span", { className: `text-sm ${textMuted}`, children: [role.permission_count ?? role.permissions?.length ?? 0, " pages"] })] })] }), expandedRole === role.role_name && ((0, jsx_runtime_1.jsx)("div", { className: `px-4 pb-4 ${isDark ? 'bg-slate-800/50' : 'bg-gray-50'}`, children: (0, jsx_runtime_1.jsx)("div", { className: "pl-8", children: role.permissions && role.permissions.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-2 pt-2", children: role.permissions.map((perm) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between py-2 px-3 rounded ${isDark ? 'bg-slate-700/50' : 'bg-white'} border ${borderColor}`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: `font-mono text-sm ${textPrimary}`, children: perm.route_pattern }), perm.display_name && ((0, jsx_runtime_1.jsxs)("span", { className: `ml-2 text-sm ${textMuted}`, children: ["- ", perm.display_name] }))] }), perm.requires_2fa && ((0, jsx_runtime_1.jsx)("span", { className: "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800", children: "2FA Required" }))] }, perm.page_permission_id))) })) : ((0, jsx_runtime_1.jsx)("p", { className: `py-4 text-sm ${textMuted}`, children: "No page permissions assigned to this role" })) }) }))] }, role.role_name)))) }), (0, jsx_runtime_1.jsxs)("div", { className: `px-4 py-3 border-t ${borderColor} ${textMuted} text-sm`, children: [roles.length, " roles total"] })] })), viewMode === 'matrix' && matrixData && ((0, jsx_runtime_1.jsxs)("div", { className: `${cardBg} border ${borderColor} rounded-lg overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-4 py-3 border-b ${borderColor}`, children: (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: "Read-only view of which roles have access to which pages" }) }), (0, jsx_runtime_1.jsx)("div", { className: "overflow-x-auto", children: (0, jsx_runtime_1.jsxs)("table", { className: "w-full", children: [(0, jsx_runtime_1.jsx)("thead", { className: isDark ? 'bg-slate-700/50' : 'bg-gray-50', children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { className: `px-4 py-3 text-left text-sm font-medium ${textMuted} sticky left-0 ${isDark ? 'bg-slate-700' : 'bg-gray-50'}`, children: "Page" }), matrixData.roles.map((role) => ((0, jsx_runtime_1.jsx)("th", { className: `px-4 py-3 text-center text-sm font-medium ${textMuted} whitespace-nowrap`, children: role }, role))), (0, jsx_runtime_1.jsx)("th", { className: `px-4 py-3 text-center text-sm font-medium ${textMuted}`, children: "2FA" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { className: `divide-y ${borderColor}`, children: matrixData.pages.map((page) => ((0, jsx_runtime_1.jsxs)("tr", { className: hoverBg, children: [(0, jsx_runtime_1.jsx)("td", { className: `px-4 py-3 ${textPrimary} sticky left-0 ${cardBg}`, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "font-mono text-sm", children: page.route_pattern }), page.display_name && ((0, jsx_runtime_1.jsx)("span", { className: `block text-xs ${textMuted}`, children: page.display_name }))] }) }), matrixData.roles.map((role) => ((0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 text-center", children: page.role_access[role] ? ((0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 mx-auto text-green-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })) : ((0, jsx_runtime_1.jsx)("svg", { className: `w-5 h-5 mx-auto ${textMuted}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })) }, role))), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 text-center", children: page.requires_2fa && ((0, jsx_runtime_1.jsx)("span", { className: "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800", children: "2FA" })) })] }, page.page_permission_id))) })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: `px-4 py-3 border-t ${borderColor} ${textMuted} text-sm flex items-center gap-4`, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-4 h-4 text-green-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }), "Access granted"] }), (0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: `w-4 h-4 ${textMuted}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }), "No access"] })] })] })), viewMode === 'matrix' && !matrixData && ((0, jsx_runtime_1.jsx)("div", { className: `${cardBg} border ${borderColor} rounded-lg p-8 text-center ${textMuted}`, children: "Unable to load permissions matrix" })), (0, jsx_runtime_1.jsx)("div", { className: `mt-6 p-4 rounded-lg ${isDark ? 'bg-blue-900/20 border border-blue-700' : 'bg-blue-50 border border-blue-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-3", children: [(0, jsx_runtime_1.jsx)("svg", { className: `w-5 h-5 mt-0.5 ${isDark ? 'text-blue-400' : 'text-blue-600'}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${isDark ? 'text-blue-300' : 'text-blue-800'}`, children: "Roles are managed by your Identity Provider" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm mt-1 ${isDark ? 'text-blue-400' : 'text-blue-600'}`, children: "Role assignments are controlled through your organization's IDP. Contact your system administrator to request role changes." })] })] }) })] }) }));
|
|
78
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Admin Roles Page exports
|
|
4
|
+
*
|
|
5
|
+
* - RolesAdminPage: Read-only admin interface for viewing roles and permissions (/admin/roles)
|
|
6
|
+
*
|
|
7
|
+
* MVP scope: Role creation/editing deferred to post-MVP.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.RolesAdminPage = void 0;
|
|
14
|
+
var RolesAdminPage_1 = require("./RolesAdminPage");
|
|
15
|
+
Object.defineProperty(exports, "RolesAdminPage", { enumerable: true, get: function () { return __importDefault(RolesAdminPage_1).default; } });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Role Modals for @payez/next-mvp
|
|
3
|
+
*
|
|
4
|
+
* - RoleEditorModal: Create/edit roles with permissions
|
|
5
|
+
* - UserRoleAssignerModal: Assign roles to users
|
|
6
|
+
*
|
|
7
|
+
* Security Addendum implemented:
|
|
8
|
+
* - Role name validation (3-50 chars, lowercase alphanumeric + underscore, no payez_ prefix)
|
|
9
|
+
* - Last admin warning
|
|
10
|
+
* - IDP roles read-only
|
|
11
|
+
* - Self-assignment warning
|
|
12
|
+
*
|
|
13
|
+
* @see docs/specs/ROLES_MANAGEMENT_SPEC.md
|
|
14
|
+
*/
|
|
15
|
+
interface PagePermission {
|
|
16
|
+
page_permission_id: number;
|
|
17
|
+
route_pattern: string;
|
|
18
|
+
display_name: string;
|
|
19
|
+
requires_2fa: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface Role {
|
|
22
|
+
role_id?: number;
|
|
23
|
+
role_name: string;
|
|
24
|
+
display_name?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
page_permissions?: string[];
|
|
27
|
+
requires_2fa?: boolean;
|
|
28
|
+
is_system_role?: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface User {
|
|
31
|
+
user_id: number;
|
|
32
|
+
email: string;
|
|
33
|
+
full_name?: string;
|
|
34
|
+
roles: {
|
|
35
|
+
idp: string[];
|
|
36
|
+
app: string[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* RoleEditorModal - Create or edit a role
|
|
41
|
+
*/
|
|
42
|
+
export declare function RoleEditorModal({ role, pagePermissions, adminAccessiblePages, onSave, onClose, isDark, }: {
|
|
43
|
+
role?: Role;
|
|
44
|
+
pagePermissions: PagePermission[];
|
|
45
|
+
adminAccessiblePages?: string[];
|
|
46
|
+
onSave: (data: {
|
|
47
|
+
role_name: string;
|
|
48
|
+
display_name?: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
page_permissions: string[];
|
|
51
|
+
requires_2fa: boolean;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
onClose: () => void;
|
|
54
|
+
isDark?: boolean;
|
|
55
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* UserRoleAssignerModal - Assign roles to a user
|
|
58
|
+
*/
|
|
59
|
+
export declare function UserRoleAssignerModal({ user, availableRoles, isLastAdmin, currentUserId, onSave, onClose, isDark, }: {
|
|
60
|
+
user: User;
|
|
61
|
+
availableRoles: {
|
|
62
|
+
role_name: string;
|
|
63
|
+
display_name?: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
}[];
|
|
66
|
+
isLastAdmin?: boolean;
|
|
67
|
+
currentUserId?: number;
|
|
68
|
+
onSave: (userId: number, appRoles: string[]) => Promise<void>;
|
|
69
|
+
onClose: () => void;
|
|
70
|
+
isDark?: boolean;
|
|
71
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Admin Role Modals for @payez/next-mvp
|
|
4
|
+
*
|
|
5
|
+
* - RoleEditorModal: Create/edit roles with permissions
|
|
6
|
+
* - UserRoleAssignerModal: Assign roles to users
|
|
7
|
+
*
|
|
8
|
+
* Security Addendum implemented:
|
|
9
|
+
* - Role name validation (3-50 chars, lowercase alphanumeric + underscore, no payez_ prefix)
|
|
10
|
+
* - Last admin warning
|
|
11
|
+
* - IDP roles read-only
|
|
12
|
+
* - Self-assignment warning
|
|
13
|
+
*
|
|
14
|
+
* @see docs/specs/ROLES_MANAGEMENT_SPEC.md
|
|
15
|
+
*/
|
|
16
|
+
'use client';
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.RoleEditorModal = RoleEditorModal;
|
|
19
|
+
exports.UserRoleAssignerModal = UserRoleAssignerModal;
|
|
20
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// VALIDATION
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Role name validation (Security Addendum)
|
|
26
|
+
function validateRoleName(name) {
|
|
27
|
+
if (!name)
|
|
28
|
+
return { valid: false, error: 'Role name is required' };
|
|
29
|
+
if (name.length < 3)
|
|
30
|
+
return { valid: false, error: 'Role name must be at least 3 characters' };
|
|
31
|
+
if (name.length > 50)
|
|
32
|
+
return { valid: false, error: 'Role name must be at most 50 characters' };
|
|
33
|
+
if (!/^[a-z0-9_]+$/.test(name))
|
|
34
|
+
return { valid: false, error: 'Only lowercase letters, numbers, and underscores allowed' };
|
|
35
|
+
if (name.startsWith('payez_'))
|
|
36
|
+
return { valid: false, error: 'Role name cannot start with "payez_"' };
|
|
37
|
+
return { valid: true };
|
|
38
|
+
}
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// ROLE EDITOR MODAL
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* RoleEditorModal - Create or edit a role
|
|
44
|
+
*/
|
|
45
|
+
function RoleEditorModal({ role, pagePermissions, adminAccessiblePages, onSave, onClose, isDark = true, }) {
|
|
46
|
+
const isEditing = !!role?.role_id;
|
|
47
|
+
// Form state
|
|
48
|
+
const [roleName, setRoleName] = (0, react_1.useState)(role?.role_name || '');
|
|
49
|
+
const [displayName, setDisplayName] = (0, react_1.useState)(role?.display_name || '');
|
|
50
|
+
const [description, setDescription] = (0, react_1.useState)(role?.description || '');
|
|
51
|
+
const [selectedPermissions, setSelectedPermissions] = (0, react_1.useState)(role?.page_permissions || []);
|
|
52
|
+
const [requires2FA, setRequires2FA] = (0, react_1.useState)(role?.requires_2fa || false);
|
|
53
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
54
|
+
const [saving, setSaving] = (0, react_1.useState)(false);
|
|
55
|
+
// Role name validation
|
|
56
|
+
const [nameValidation, setNameValidation] = (0, react_1.useState)({ valid: true });
|
|
57
|
+
(0, react_1.useEffect)(() => {
|
|
58
|
+
if (!isEditing && roleName) {
|
|
59
|
+
setNameValidation(validateRoleName(roleName));
|
|
60
|
+
}
|
|
61
|
+
}, [roleName, isEditing]);
|
|
62
|
+
// Filter permissions based on admin access (Security Addendum)
|
|
63
|
+
const availablePermissions = adminAccessiblePages
|
|
64
|
+
? pagePermissions.filter((p) => adminAccessiblePages.some((ap) => p.route_pattern.startsWith(ap.replace('*', ''))))
|
|
65
|
+
: pagePermissions;
|
|
66
|
+
const handleSubmit = async (e) => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
setError('');
|
|
69
|
+
if (!isEditing) {
|
|
70
|
+
const validation = validateRoleName(roleName);
|
|
71
|
+
if (!validation.valid) {
|
|
72
|
+
setError(validation.error || 'Invalid role name');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
setSaving(true);
|
|
78
|
+
await onSave({
|
|
79
|
+
role_name: roleName,
|
|
80
|
+
display_name: displayName || undefined,
|
|
81
|
+
description: description || undefined,
|
|
82
|
+
page_permissions: selectedPermissions,
|
|
83
|
+
requires_2fa: requires2FA,
|
|
84
|
+
});
|
|
85
|
+
onClose();
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
setError(err instanceof Error ? err.message : 'Failed to save role');
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
setSaving(false);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const togglePermission = (pattern) => {
|
|
95
|
+
setSelectedPermissions((prev) => prev.includes(pattern) ? prev.filter((p) => p !== pattern) : [...prev, pattern]);
|
|
96
|
+
};
|
|
97
|
+
// Theme colors
|
|
98
|
+
const modalBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
99
|
+
const borderColor = isDark ? 'border-slate-700' : 'border-gray-200';
|
|
100
|
+
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
101
|
+
const textMuted = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
102
|
+
const inputBg = isDark ? 'bg-slate-700' : 'bg-white';
|
|
103
|
+
const inputBorder = isDark ? 'border-slate-600' : 'border-gray-300';
|
|
104
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: (0, jsx_runtime_1.jsxs)("div", { className: `w-full max-w-lg mx-4 rounded-lg shadow-xl ${modalBg} border ${borderColor} max-h-[90vh] overflow-hidden flex flex-col`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-6 py-4 border-b ${borderColor} flex items-center justify-between`, children: [(0, jsx_runtime_1.jsx)("h3", { className: `text-lg font-semibold ${textPrimary}`, children: isEditing ? `Edit Role: ${role?.role_name}` : 'Create New Role' }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: textMuted, children: (0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex-1 overflow-y-auto", children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4 space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("label", { className: `block text-sm font-medium mb-1 ${textMuted}`, children: ["Role Name ", (0, jsx_runtime_1.jsx)("span", { className: "text-red-500", children: "*" })] }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: roleName, onChange: (e) => setRoleName(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '')), disabled: isEditing, className: `w-full px-3 py-2 rounded-md border ${inputBorder} ${inputBg} ${textPrimary} ${isEditing ? 'opacity-50 cursor-not-allowed' : ''} font-mono`, placeholder: "e.g., content_editor" }), !isEditing && roleName && !nameValidation.valid && ((0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-red-500", children: nameValidation.error })), !isEditing && ((0, jsx_runtime_1.jsx)("p", { className: `mt-1 text-xs ${textMuted}`, children: "3-50 characters, lowercase letters, numbers, underscores only" }))] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-1 ${textMuted}`, children: "Display Name" }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), className: `w-full px-3 py-2 rounded-md border ${inputBorder} ${inputBg} ${textPrimary}`, placeholder: "e.g., Content Editor" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-1 ${textMuted}`, children: "Description" }), (0, jsx_runtime_1.jsx)("textarea", { value: description, onChange: (e) => setDescription(e.target.value), rows: 2, className: `w-full px-3 py-2 rounded-md border ${inputBorder} ${inputBg} ${textPrimary}`, placeholder: "What does this role do?" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textMuted}`, children: "Page Permissions" }), (0, jsx_runtime_1.jsx)("div", { className: `border ${borderColor} rounded-md max-h-48 overflow-y-auto`, children: availablePermissions.length === 0 ? ((0, jsx_runtime_1.jsx)("p", { className: `p-3 text-sm ${textMuted}`, children: "No page permissions available" })) : (availablePermissions.map((perm) => ((0, jsx_runtime_1.jsxs)("label", { className: `flex items-center px-3 py-2 border-b last:border-b-0 ${borderColor} cursor-pointer hover:${isDark ? 'bg-slate-700' : 'bg-gray-50'}`, children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedPermissions.includes(perm.route_pattern), onChange: () => togglePermission(perm.route_pattern), className: "mr-3" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: `font-mono text-sm ${textPrimary}`, children: perm.route_pattern }), perm.display_name && ((0, jsx_runtime_1.jsx)("span", { className: `block text-xs ${textMuted}`, children: perm.display_name }))] })] }, perm.page_permission_id)))) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `border-t ${borderColor} pt-4`, children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textMuted}`, children: "Security Options" }), (0, jsx_runtime_1.jsxs)("label", { className: "flex items-center cursor-pointer", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: requires2FA, onChange: (e) => setRequires2FA(e.target.checked), className: "mr-3" }), (0, jsx_runtime_1.jsx)("span", { className: textPrimary, children: "Requires 2FA for all pages" })] })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "bg-red-900/30 border border-red-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-400 text-sm", children: error }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `px-6 py-4 border-t ${borderColor} flex justify-end gap-3`, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClose, className: `px-4 py-2 rounded-md border ${borderColor} ${textMuted}`, children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: saving || (!isEditing && !nameValidation.valid), className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50", children: saving ? 'Saving...' : isEditing ? 'Save Changes' : 'Create Role' })] })] })] }) }));
|
|
105
|
+
}
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// USER ROLE ASSIGNER MODAL
|
|
108
|
+
// ============================================================================
|
|
109
|
+
/**
|
|
110
|
+
* UserRoleAssignerModal - Assign roles to a user
|
|
111
|
+
*/
|
|
112
|
+
function UserRoleAssignerModal({ user, availableRoles, isLastAdmin, currentUserId, onSave, onClose, isDark = true, }) {
|
|
113
|
+
const [selectedRoles, setSelectedRoles] = (0, react_1.useState)(user.roles.app);
|
|
114
|
+
const [saving, setSaving] = (0, react_1.useState)(false);
|
|
115
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
116
|
+
const handleSubmit = async (e) => {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
setError('');
|
|
119
|
+
// Security: Self-assignment warning
|
|
120
|
+
if (user.user_id === currentUserId) {
|
|
121
|
+
if (!confirm('You are modifying your own roles. Changes take effect on next login. Continue?')) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Security Addendum: Last admin warning
|
|
126
|
+
const hadAdmin = user.roles.app.some((r) => r.includes('admin'));
|
|
127
|
+
const hasAdmin = selectedRoles.some((r) => r.includes('admin'));
|
|
128
|
+
if (hadAdmin && !hasAdmin && isLastAdmin) {
|
|
129
|
+
if (!confirm('Warning: This is the last administrator. Removing admin access will lock out admin access. Continue?')) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
setSaving(true);
|
|
135
|
+
await onSave(user.user_id, selectedRoles);
|
|
136
|
+
onClose();
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
setError(err instanceof Error ? err.message : 'Failed to update roles');
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
setSaving(false);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const toggleRole = (roleName) => {
|
|
146
|
+
setSelectedRoles((prev) => prev.includes(roleName) ? prev.filter((r) => r !== roleName) : [...prev, roleName]);
|
|
147
|
+
};
|
|
148
|
+
// Theme colors
|
|
149
|
+
const modalBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
150
|
+
const borderColor = isDark ? 'border-slate-700' : 'border-gray-200';
|
|
151
|
+
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
152
|
+
const textMuted = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
153
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: (0, jsx_runtime_1.jsxs)("div", { className: `w-full max-w-md mx-4 rounded-lg shadow-xl ${modalBg} border ${borderColor}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-6 py-4 border-b ${borderColor} flex items-center justify-between`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: `text-lg font-semibold ${textPrimary}`, children: "Edit Roles" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: user.full_name || user.email })] }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: textMuted, children: (0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4 space-y-4", children: [user.roles.idp.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("label", { className: `block text-sm font-medium mb-2 ${textMuted}`, children: ["IDP Roles (read-only)", (0, jsx_runtime_1.jsx)("svg", { className: "inline w-4 h-4 ml-1", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-1", children: user.roles.idp.map((role) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center px-3 py-2 rounded ${isDark ? 'bg-slate-700/50' : 'bg-gray-100'}`, children: [(0, jsx_runtime_1.jsx)("span", { className: textMuted, children: role }), (0, jsx_runtime_1.jsx)("span", { className: `ml-auto text-xs ${textMuted}`, children: "assigned by IDP" })] }, role))) })] })), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textMuted}`, children: "Application Roles" }), (0, jsx_runtime_1.jsx)("div", { className: `border ${borderColor} rounded-md`, children: availableRoles.map((role) => ((0, jsx_runtime_1.jsxs)("label", { className: `flex items-start px-3 py-2 border-b last:border-b-0 ${borderColor} cursor-pointer hover:${isDark ? 'bg-slate-700' : 'bg-gray-50'}`, children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedRoles.includes(role.role_name), onChange: () => toggleRole(role.role_name), className: "mt-1 mr-3" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: textPrimary, children: role.display_name || role.role_name }), role.description && ((0, jsx_runtime_1.jsx)("span", { className: `block text-xs ${textMuted}`, children: role.description }))] })] }, role.role_name))) })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "bg-red-900/30 border border-red-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-400 text-sm", children: error }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `px-6 py-4 border-t ${borderColor} flex justify-end gap-3`, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClose, className: `px-4 py-2 rounded-md border ${borderColor} ${textMuted}`, children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: saving, className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50", children: saving ? 'Saving...' : 'Save Changes' })] })] })] }) }));
|
|
154
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* CLIENT SITE ADMIN - Your App's Admin Dashboard
|
|
4
|
+
* =============================================================================
|
|
5
|
+
*
|
|
6
|
+
* This is the foundation for YOUR app-specific admin area.
|
|
7
|
+
* The MVP admin (/admin) handles membership - this handles YOUR data.
|
|
8
|
+
*
|
|
9
|
+
* SEPARATION OF CONCERNS:
|
|
10
|
+
* ┌─────────────────────────────────────────────────────────────────────┐
|
|
11
|
+
* │ /admin (MVP Admin) │ /admin/[your-area] (This) │
|
|
12
|
+
* │ ───────────────── │ ──────────────────────── │
|
|
13
|
+
* │ • User authentication │ • Your app's collections │
|
|
14
|
+
* │ • Sessions management │ • Your app's features │
|
|
15
|
+
* │ • Role assignments │ • Content moderation │
|
|
16
|
+
* │ • Tier/subscription mgmt │ • App-specific analytics │
|
|
17
|
+
* │ • Site-wide analytics │ • Custom admin tools │
|
|
18
|
+
* │ • vibe_app collection │ • your_app collection │
|
|
19
|
+
* └─────────────────────────────────────────────────────────────────────┘
|
|
20
|
+
*
|
|
21
|
+
* USAGE:
|
|
22
|
+
* ```tsx
|
|
23
|
+
* // app/admin/your-area/page.tsx
|
|
24
|
+
* import { ClientSiteAdminPage } from '@payez/next-mvp/pages/client-admin';
|
|
25
|
+
*
|
|
26
|
+
* export default function YourAdminPage() {
|
|
27
|
+
* return (
|
|
28
|
+
* <ClientSiteAdminPage
|
|
29
|
+
* title="Your Admin"
|
|
30
|
+
* subtitle="Manage your app data"
|
|
31
|
+
* collectionName="your_collection"
|
|
32
|
+
* tabs={[
|
|
33
|
+
* { id: 'overview', label: 'Overview', icon: '📊' },
|
|
34
|
+
* { id: 'items', label: 'Items', icon: '📦', table: 'items' },
|
|
35
|
+
* ]}
|
|
36
|
+
* renderTabContent={(tab, data) => <YourCustomContent />}
|
|
37
|
+
* />
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* =============================================================================
|
|
43
|
+
*/
|
|
44
|
+
import React from 'react';
|
|
45
|
+
export interface ClientAdminTab {
|
|
46
|
+
id: string;
|
|
47
|
+
label: string;
|
|
48
|
+
icon: string;
|
|
49
|
+
table?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface TableStats {
|
|
52
|
+
table: string;
|
|
53
|
+
count: number;
|
|
54
|
+
label: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ClientSiteAdminProps {
|
|
57
|
+
/** Admin area title */
|
|
58
|
+
title: string;
|
|
59
|
+
/** Subtitle shown under title */
|
|
60
|
+
subtitle?: string;
|
|
61
|
+
/** Vibe collection name for this admin area */
|
|
62
|
+
collectionName: string;
|
|
63
|
+
/** Tabs to display */
|
|
64
|
+
tabs: ClientAdminTab[];
|
|
65
|
+
/** Roles that can access (defaults to vibe_app_admin) */
|
|
66
|
+
allowedRoles?: string[];
|
|
67
|
+
/** Custom overview content renderer */
|
|
68
|
+
renderOverview?: (stats: TableStats[]) => React.ReactNode;
|
|
69
|
+
/** Custom tab content renderer */
|
|
70
|
+
renderTabContent?: (tab: ClientAdminTab, data: any[], loading: boolean) => React.ReactNode;
|
|
71
|
+
/** Theme detection function (optional) */
|
|
72
|
+
isDark?: boolean;
|
|
73
|
+
/** Back link URL (defaults to /) */
|
|
74
|
+
backUrl?: string;
|
|
75
|
+
/** Back link label (defaults to "Back to Site") */
|
|
76
|
+
backLabel?: string;
|
|
77
|
+
}
|
|
78
|
+
export declare function ClientSiteAdminPage({ title, subtitle, collectionName, tabs, allowedRoles, renderOverview, renderTabContent, isDark: isDarkProp, backUrl, backLabel, }: ClientSiteAdminProps): import("react/jsx-runtime").JSX.Element | null;
|
|
79
|
+
export default ClientSiteAdminPage;
|