@payez/next-mvp 4.8.0 → 4.8.2
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/POST-INSTALL.md +408 -408
- package/README.md +859 -859
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/session-store.js +21 -21
- package/dist/lib/startup-init.js +19 -19
- package/dist/middleware/create-middleware.js +6 -5
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/dist/vibe/sessions.js +28 -3
- package/package.json +2 -2
- package/scripts/check-publish-eol.mjs +33 -15
- package/src/api/auth-handler.ts +550 -550
- package/src/api/index.ts +17 -17
- package/src/api-handlers/account/change-password.ts +170 -170
- package/src/api-handlers/account/masked-info.ts +46 -46
- package/src/api-handlers/account/profile.ts +99 -99
- package/src/api-handlers/account/recovery/forward.ts +117 -117
- package/src/api-handlers/account/recovery/initiate.ts +59 -59
- package/src/api-handlers/account/recovery/send-code.ts +21 -21
- package/src/api-handlers/account/recovery/verify-code.ts +20 -20
- package/src/api-handlers/account/reset-password.ts +87 -87
- package/src/api-handlers/account/send-code.ts +70 -70
- package/src/api-handlers/account/update-phone.ts +84 -84
- package/src/api-handlers/account/validate-password.ts +139 -139
- package/src/api-handlers/account/verify-email.ts +134 -134
- package/src/api-handlers/account/verify-sms.ts +134 -134
- package/src/api-handlers/admin/analytics.ts +452 -452
- package/src/api-handlers/admin/audit.ts +232 -232
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +259 -259
- package/src/api-handlers/admin/sessions.ts +327 -327
- package/src/api-handlers/admin/site-logs.ts +388 -388
- package/src/api-handlers/admin/stats.ts +260 -260
- package/src/api-handlers/admin/users.ts +251 -251
- package/src/api-handlers/admin/vibe-data.ts +333 -333
- package/src/api-handlers/anon/preferences.ts +123 -123
- package/src/api-handlers/auth/credential-login-email-code.ts +92 -92
- package/src/api-handlers/auth/credential-login.ts +948 -948
- package/src/api-handlers/auth/jwks.ts +31 -31
- package/src/api-handlers/auth/login.ts +252 -252
- package/src/api-handlers/auth/passkey-signin.ts +116 -116
- package/src/api-handlers/auth/refresh.ts +829 -829
- package/src/api-handlers/auth/signout.ts +201 -201
- package/src/api-handlers/auth/status.ts +23 -23
- package/src/api-handlers/auth/update-session.ts +144 -144
- package/src/api-handlers/auth/validate.ts +45 -45
- package/src/api-handlers/auth/verify-code.ts +116 -116
- package/src/api-handlers/mfa/backup-code-verify.ts +170 -170
- package/src/api-handlers/mfa/backup-codes.ts +42 -42
- package/src/api-handlers/mfa/methods.ts +47 -47
- package/src/api-handlers/mfa/totp-confirm.ts +43 -43
- package/src/api-handlers/mfa/totp-enroll.ts +43 -43
- package/src/api-handlers/mfa/totp-verify.ts +172 -172
- package/src/api-handlers/oauth/resume.ts +182 -182
- package/src/api-handlers/onboarding/send-email-code.ts +18 -18
- package/src/api-handlers/onboarding/set-password.ts +21 -21
- package/src/api-handlers/onboarding/verify-code.ts +20 -20
- package/src/api-handlers/onboarding/verify-welcome-email.ts +17 -17
- package/src/api-handlers/passkeys/list.ts +32 -32
- package/src/api-handlers/passkeys/manage.ts +55 -55
- package/src/api-handlers/passkeys/register-options.ts +43 -43
- package/src/api-handlers/passkeys/register-verify.ts +42 -42
- package/src/api-handlers/session/refresh-viability.ts +56 -56
- package/src/api-handlers/session/viability.ts +238 -238
- package/src/api-handlers/test/force-expire.ts +167 -167
- package/src/auth/auth-decision.ts +292 -292
- package/src/auth/better-auth.ts +1688 -1688
- package/src/auth/idp-oauth.ts +310 -310
- package/src/auth/password-reset-bridge.ts +98 -98
- package/src/auth/route-config.ts +236 -236
- package/src/auth/safe-callback.ts +199 -199
- package/src/auth/session-cookie-clear.ts +30 -30
- package/src/auth/session-reset-link.ts +159 -159
- package/src/auth/session-response-sanitizer.ts +151 -151
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/unauthenticated-routes.ts +2 -2
- package/src/auth/utils/idp-client.ts +364 -364
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +192 -192
- package/src/client/better-auth-client.ts +70 -70
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/client/fetchWithSession.ts +21 -21
- package/src/client/index.ts +13 -13
- package/src/client/useAnonSession.ts +131 -131
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +277 -277
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +508 -508
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +421 -421
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminContext.tsx +87 -87
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/CredentialSignInForm.tsx +240 -240
- package/src/components/auth/FederatedAuthSection.tsx +129 -129
- package/src/components/auth/ModeAwareLoginPage.tsx +146 -146
- package/src/components/auth/ModeAwareSignupPage.tsx +200 -200
- package/src/components/auth/PasswordResetBridge.tsx +30 -30
- package/src/components/auth/TraditionalAuthSection.tsx +103 -103
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/RequestedStep.tsx +30 -30
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +118 -118
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +55 -55
- package/src/config/logger.ts +62 -62
- package/src/config/logging-config.ts +82 -82
- package/src/config/unauthenticated-routes.ts +19 -19
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/edge/internal-api-url.ts +65 -65
- package/src/edge/middleware.ts +42 -42
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +109 -109
- package/src/hooks/useAvailableProviders.ts +143 -143
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/useProfile.ts +82 -82
- package/src/hooks/usePublicAuthSettings.ts +171 -171
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +337 -337
- package/src/index.ts +64 -64
- package/src/lib/anon-session.ts +213 -213
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +228 -228
- package/src/lib/authorize-resume.ts +172 -172
- package/src/lib/bearer-fetch.ts +31 -31
- package/src/lib/client-key.ts +88 -88
- package/src/lib/code-step-copy.ts +83 -83
- package/src/lib/credential-sign-in-client.ts +101 -101
- package/src/lib/demo-mode.ts +18 -18
- package/src/lib/device-id.ts +67 -67
- package/src/lib/ensure-fresh-access-token.ts +509 -509
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-client-config.ts +1005 -1005
- package/src/lib/idp-fetch.ts +169 -169
- package/src/lib/idp-url-guard.ts +37 -37
- package/src/lib/internal-api.ts +171 -171
- package/src/lib/jwt-decode-client.ts +45 -45
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/lock-window.ts +49 -49
- package/src/lib/login-error-message.ts +41 -41
- package/src/lib/never-in-response.ts +64 -64
- package/src/lib/nextauth-secret.ts +168 -168
- package/src/lib/onboarding-proxy.ts +57 -57
- package/src/lib/password-rules.ts +50 -50
- package/src/lib/rate-limit-service.ts +9 -9
- package/src/lib/redis.ts +105 -105
- package/src/lib/refresh-token-validator.ts +64 -64
- package/src/lib/roles.ts +177 -177
- package/src/lib/safe-error-response.ts +149 -149
- package/src/lib/secret-validation.ts +8 -8
- package/src/lib/session-cookie-shape.ts +185 -185
- package/src/lib/session-store.ts +1092 -1092
- package/src/lib/session.ts +34 -34
- package/src/lib/single-flight.ts +64 -64
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/startup-init.ts +276 -276
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/token-expiry.ts +40 -40
- package/src/lib/token-lifecycle.ts +546 -546
- package/src/lib/token-log-id.ts +69 -69
- package/src/lib/twofa-rejection.ts +91 -91
- package/src/lib/types/api-responses.ts +336 -336
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +1139 -1138
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/models/DecodedAccessToken.ts +16 -16
- package/src/models/SessionModel.ts +331 -331
- package/src/pages/admin-login/page.tsx +111 -111
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/index.ts +9 -9
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +338 -338
- package/src/pages/onboarding/index.ts +11 -11
- package/src/pages/onboarding/page.tsx +212 -212
- package/src/pages/profile/EnhancedProfilePage.tsx +514 -514
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +267 -267
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +660 -660
- package/src/pages/security/index.ts +12 -12
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +192 -192
- package/src/pages/test-env/JwtInspectPage.tsx +449 -449
- package/src/pages/test-env/RefreshTokenPage.tsx +178 -178
- package/src/pages/test-env/TestEnvPage.tsx +130 -130
- package/src/pages/test-env/guarded.tsx +81 -81
- package/src/pages/test-env/index.ts +77 -77
- package/src/pages/test-env/redact-credentials.ts +96 -96
- package/src/pages/verify-code/page.tsx +746 -746
- package/src/routes/account/index.ts +31 -31
- package/src/routes/account/masked-info.ts +36 -36
- package/src/routes/account/send-code.ts +39 -39
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/account/verify-email.ts +40 -40
- package/src/routes/account/verify-sms.ts +40 -40
- package/src/routes/auth/credential-login-email-code.ts +6 -6
- package/src/routes/auth/credential-login.ts +6 -6
- package/src/routes/auth/index.ts +22 -22
- package/src/routes/auth/logout.ts +111 -111
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/passkey-signin.ts +13 -13
- package/src/routes/auth/refresh.ts +77 -77
- package/src/routes/auth/session-reset.ts +37 -37
- package/src/routes/auth/session.ts +261 -261
- package/src/routes/auth/settings.ts +181 -181
- package/src/routes/auth/viability.ts +519 -519
- package/src/routes/index.ts +17 -17
- package/src/routes/mfa/backup-code-verify.ts +1 -1
- package/src/routes/mfa/backup-codes.ts +10 -10
- package/src/routes/mfa/methods.ts +22 -22
- package/src/routes/mfa/totp-confirm.ts +10 -10
- package/src/routes/mfa/totp-enroll.ts +10 -10
- package/src/routes/mfa/totp-verify.ts +14 -14
- package/src/routes/onboarding/index.ts +15 -15
- package/src/routes/onboarding/send-email-code.ts +2 -2
- package/src/routes/onboarding/set-password.ts +2 -2
- package/src/routes/onboarding/verify-code.ts +2 -2
- package/src/routes/onboarding/verify-welcome-email.ts +2 -2
- package/src/routes/passkeys/list.ts +10 -10
- package/src/routes/passkeys/manage.ts +23 -23
- package/src/routes/passkeys/register-options.ts +10 -10
- package/src/routes/passkeys/register-verify.ts +10 -10
- package/src/routes/session/index.ts +7 -7
- package/src/routes/session/refresh-viability.ts +17 -17
- package/src/server/auth-guard.ts +203 -203
- package/src/server/auth.ts +486 -486
- package/src/server/decode-session.ts +309 -309
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/verified-session-token.ts +103 -103
- package/src/server/with-auth.ts +93 -93
- package/src/services/signalrActivityService.ts +258 -258
- package/src/stores/authStore.ts +1900 -1900
- package/src/templates/instrumentation.ts +60 -60
- package/src/theme/ThemeProvider.tsx +39 -39
- package/src/theme/default.ts +33 -33
- package/src/theme/index.ts +31 -31
- package/src/theme/types.ts +69 -69
- package/src/theme/useTheme.ts +57 -57
- package/src/theme/utils.ts +40 -40
- package/src/types/api.ts +12 -12
- package/src/types/auth.d.ts +23 -23
- package/src/types/auth.ts +48 -48
- package/src/types/logging.ts +11 -11
- package/src/types/recovery.ts +54 -54
- package/src/utils/api.ts +401 -401
- package/src/utils/circuitBreaker.ts +40 -40
- package/src/utils/error-message.ts +108 -108
- package/src/utils/layout/reservedSpace.ts +124 -124
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +877 -877
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +209 -209
- package/src/vibe/generic.ts +339 -339
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +133 -133
- package/src/vibe/sessions.ts +925 -899
- package/src/vibe/types.ts +577 -577
|
@@ -58,24 +58,24 @@ function MobileNavDrawer({ isOpen, onClose, navItems, customSections, basePath =
|
|
|
58
58
|
const userEmail = session?.user?.email;
|
|
59
59
|
const displaySource = userName || userEmail;
|
|
60
60
|
const userInitial = displaySource?.charAt(0).toUpperCase() || '?';
|
|
61
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `
|
|
62
|
-
fixed inset-0 bg-black/50 backdrop-blur-sm z-40 lg:hidden
|
|
63
|
-
transition-opacity duration-300
|
|
64
|
-
${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}
|
|
65
|
-
`, onClick: onClose, "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", "aria-expanded": isOpen, className: `
|
|
66
|
-
fixed top-0 right-0 bottom-0 w-80 max-w-[85vw]
|
|
67
|
-
bg-white dark:bg-slate-900
|
|
68
|
-
shadow-[-8px_0_32px_rgba(0,0,0,0.15)]
|
|
69
|
-
dark:shadow-[-8px_0_32px_rgba(0,0,0,0.4)]
|
|
70
|
-
z-50 lg:hidden
|
|
71
|
-
overflow-y-auto
|
|
72
|
-
transition-transform duration-300 ease-out
|
|
73
|
-
${isOpen ? 'translate-x-0' : 'translate-x-full'}
|
|
74
|
-
`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-white/10", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Menu" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\
|
|
75
|
-
flex items-center gap-3 px-4 py-3.5 rounded-xl
|
|
76
|
-
transition-colors duration-200
|
|
61
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `
|
|
62
|
+
fixed inset-0 bg-black/50 backdrop-blur-sm z-40 lg:hidden
|
|
63
|
+
transition-opacity duration-300
|
|
64
|
+
${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}
|
|
65
|
+
`, onClick: onClose, "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", "aria-expanded": isOpen, className: `
|
|
66
|
+
fixed top-0 right-0 bottom-0 w-80 max-w-[85vw]
|
|
67
|
+
bg-white dark:bg-slate-900
|
|
68
|
+
shadow-[-8px_0_32px_rgba(0,0,0,0.15)]
|
|
69
|
+
dark:shadow-[-8px_0_32px_rgba(0,0,0,0.4)]
|
|
70
|
+
z-50 lg:hidden
|
|
71
|
+
overflow-y-auto
|
|
72
|
+
transition-transform duration-300 ease-out
|
|
73
|
+
${isOpen ? 'translate-x-0' : 'translate-x-full'}
|
|
74
|
+
`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-white/10", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Menu" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\n p-2 rounded-xl\n text-gray-400 hover:text-gray-900\n dark:hover:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", "aria-label": "Close menu", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-5 w-5" }) })] }), isAuthenticated && session?.user && ((0, jsx_runtime_1.jsx)("div", { className: "p-4 border-b border-gray-200 dark:border-white/10", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 48, height: 48, className: "w-12 h-12 rounded-full", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-lg", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-500 dark:text-slate-400 truncate", children: userEmail }))] })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "p-2", children: navItems.map((item) => ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: `
|
|
75
|
+
flex items-center gap-3 px-4 py-3.5 rounded-xl
|
|
76
|
+
transition-colors duration-200
|
|
77
77
|
${isActiveRoute(item.href)
|
|
78
78
|
? 'bg-blue-500/10 text-blue-500'
|
|
79
|
-
: 'text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-white/10'}
|
|
80
|
-
`, children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label }), isActiveRoute(item.href) && ((0, jsx_runtime_1.jsx)("span", { className: "ml-auto w-2 h-2 rounded-full bg-blue-500" }))] }, item.href))) }), customSections?.map((section, sectionIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: "p-2 border-t border-gray-200 dark:border-white/10", children: [section.title && ((0, jsx_runtime_1.jsx)("p", { className: "px-4 py-2 text-xs font-semibold text-gray-500 dark:text-slate-400 uppercase tracking-wider", children: section.title })), section.items.map((item, itemIndex) => item.href ? ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: "\
|
|
79
|
+
: 'text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-white/10'}
|
|
80
|
+
`, children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label }), isActiveRoute(item.href) && ((0, jsx_runtime_1.jsx)("span", { className: "ml-auto w-2 h-2 rounded-full bg-blue-500" }))] }, item.href))) }), customSections?.map((section, sectionIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: "p-2 border-t border-gray-200 dark:border-white/10", children: [section.title && ((0, jsx_runtime_1.jsx)("p", { className: "px-4 py-2 text-xs font-semibold text-gray-500 dark:text-slate-400 uppercase tracking-wider", children: section.title })), section.items.map((item, itemIndex) => item.href ? ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: "\n flex items-center gap-3 px-4 py-3 rounded-xl\n text-gray-900 dark:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)) : ((0, jsx_runtime_1.jsxs)("button", { onClick: () => handleSectionItemClick(item), className: "\n flex items-center gap-3 px-4 py-3 rounded-xl w-full text-left\n text-gray-900 dark:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)))] }, sectionIndex))), (0, jsx_runtime_1.jsx)("div", { className: "p-4 mt-auto border-t border-gray-200 dark:border-white/10", children: !isAuthenticated ? (unauthActions ?? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: (0, jsx_runtime_1.jsx)("button", { onClick: handleSignIn, className: "\n w-full px-4 py-3 rounded-xl\n text-blue-500 font-semibold\n border border-blue-500/30\n hover:bg-blue-500/10\n transition-colors\n ", children: "Login" }) }))) : (authFooter ?? ((0, jsx_runtime_1.jsx)(link_1.default, { href: basePath, onClick: onClose, className: "\n flex items-center justify-center gap-2\n w-full px-4 py-3 rounded-xl\n text-gray-500 dark:text-slate-400 font-medium\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: "Account Settings" }))) })] })] }));
|
|
81
81
|
}
|
|
@@ -92,7 +92,7 @@ function UserAvatarMenu({ basePath = '', showProfile = true, showSettings = true
|
|
|
92
92
|
router.push(item.href);
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "relative", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(!isOpen), className: "flex items-center justify-center h-10 w-10 rounded-full overflow-hidden bg-blue-500 text-white font-semibold text-lg hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900", "aria-label": "User menu", "aria-expanded": isOpen, "aria-haspopup": "true", children: session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 40, height: 40, className: "w-10 h-10 rounded-full object-cover", unoptimized: true })) : (userInitial) }), isOpen && ((0, jsx_runtime_1.jsxs)("div", { className: "absolute right-0 mt-2 w-56 rounded-md shadow-lg z-50\
|
|
95
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "relative", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(!isOpen), className: "flex items-center justify-center h-10 w-10 rounded-full overflow-hidden bg-blue-500 text-white font-semibold text-lg hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900", "aria-label": "User menu", "aria-expanded": isOpen, "aria-haspopup": "true", children: session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 40, height: 40, className: "w-10 h-10 rounded-full object-cover", unoptimized: true })) : (userInitial) }), isOpen && ((0, jsx_runtime_1.jsxs)("div", { className: "absolute right-0 mt-2 w-56 rounded-md shadow-lg z-50\n bg-white dark:bg-slate-900\n border border-gray-200 dark:border-slate-700", role: "menu", "aria-orientation": "vertical", children: [(0, jsx_runtime_1.jsx)("div", { className: "px-4 py-3 border-b border-gray-200 dark:border-slate-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 32, height: 32, className: "w-8 h-8 rounded-full flex-shrink-0", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-sm flex-shrink-0", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-700 dark:text-slate-200 truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400 truncate", children: userEmail })), !userName && !userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400", children: "Signed in" }))] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "py-1", children: [showProfile && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.User, { className: "h-4 w-4" }), label: "Profile", onClick: () => handleNavigation(`${basePath}/profile`) })), showSettings && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { className: "h-4 w-4" }), label: "Settings", onClick: () => handleNavigation(`${basePath}/settings`) })), showSecurity && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Shield, { className: "h-4 w-4" }), label: "Security", onClick: () => handleNavigation(`${basePath}/security`) })), customItems?.map((item, index) => ((0, jsx_runtime_1.jsx)(MenuItem, { icon: item.icon, label: item.label, onClick: () => handleItemClick(item) }, index)))] }), (0, jsx_runtime_1.jsx)("div", { className: "border-t border-gray-200 dark:border-slate-700 py-1", children: (0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "h-4 w-4" }), label: "Sign Out", onClick: handleSignOut, variant: "danger" }) })] }))] }));
|
|
96
96
|
}
|
|
97
97
|
function MenuItem({ icon, label, onClick, variant = 'default' }) {
|
|
98
98
|
const baseClasses = "flex items-center w-full px-4 py-2 text-sm cursor-pointer transition-colors";
|
|
@@ -277,22 +277,22 @@ function AlertSettingsTab({ isDark = true, apiBasePath = '/api/admin/alerts', on
|
|
|
277
277
|
e.preventDefault();
|
|
278
278
|
addRecipient();
|
|
279
279
|
}
|
|
280
|
-
}, placeholder: "Enter email address", className: `
|
|
281
|
-
w-full px-3 py-2 rounded-lg text-sm
|
|
282
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
283
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
284
|
-
${recipientError ? (isDark ? 'border-red-500' : 'border-red-400') : ''}
|
|
285
|
-
` }), recipientError && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-1 ${isDark ? 'text-red-400' : 'text-red-500'}`, children: recipientError }))] }), (0, jsx_runtime_1.jsxs)("button", { onClick: addRecipient, className: `
|
|
286
|
-
px-4 py-2 rounded-lg font-medium text-sm transition-colors
|
|
287
|
-
${isDark ? 'bg-indigo-600 hover:bg-indigo-700' : 'bg-indigo-500 hover:bg-indigo-600'}
|
|
288
|
-
text-white flex items-center gap-2
|
|
280
|
+
}, placeholder: "Enter email address", className: `
|
|
281
|
+
w-full px-3 py-2 rounded-lg text-sm
|
|
282
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
283
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
284
|
+
${recipientError ? (isDark ? 'border-red-500' : 'border-red-400') : ''}
|
|
285
|
+
` }), recipientError && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-1 ${isDark ? 'text-red-400' : 'text-red-500'}`, children: recipientError }))] }), (0, jsx_runtime_1.jsxs)("button", { onClick: addRecipient, className: `
|
|
286
|
+
px-4 py-2 rounded-lg font-medium text-sm transition-colors
|
|
287
|
+
${isDark ? 'bg-indigo-600 hover:bg-indigo-700' : 'bg-indigo-500 hover:bg-indigo-600'}
|
|
288
|
+
text-white flex items-center gap-2
|
|
289
289
|
`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16 }), "Add"] })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: isDark ? 'text-cyan-400' : 'text-cyan-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Delivery Mode" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "How alerts are delivered to recipients" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3", children: DELIVERY_MODES.map((mode) => {
|
|
290
290
|
const isSelected = editedMode === mode.value;
|
|
291
|
-
return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setEditedMode(mode.value), className: `
|
|
292
|
-
p-4 rounded-lg border-2 text-left transition-all
|
|
291
|
+
return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setEditedMode(mode.value), className: `
|
|
292
|
+
p-4 rounded-lg border-2 text-left transition-all
|
|
293
293
|
${isSelected
|
|
294
294
|
? 'border-indigo-500 ' + (isDark ? 'bg-indigo-500/10' : 'bg-indigo-50')
|
|
295
|
-
: (isDark ? 'border-slate-700 hover:border-slate-600' : 'border-gray-200 hover:border-gray-300')}
|
|
295
|
+
: (isDark ? 'border-slate-700 hover:border-slate-600' : 'border-gray-200 hover:border-gray-300')}
|
|
296
296
|
`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-1", children: [(0, jsx_runtime_1.jsx)("div", { className: `w-3 h-3 rounded-full border-2 flex items-center justify-center ${isSelected ? 'border-indigo-500' : (isDark ? 'border-slate-500' : 'border-gray-400')}`, children: isSelected && (0, jsx_runtime_1.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500" }) }), (0, jsx_runtime_1.jsx)("span", { className: `font-medium ${themeClasses.textPrimary}`, children: mode.label })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${themeClasses.textMuted} ml-5`, children: mode.description })] }, mode.value));
|
|
297
297
|
}) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Bell, { className: isDark ? 'text-amber-400' : 'text-amber-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Alert Types" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Configure which alerts to receive and their thresholds" })] }), (0, jsx_runtime_1.jsx)("div", { className: "divide-y divide-slate-700/50", children: ALERT_TYPES.map((alertType) => {
|
|
298
298
|
const config = editedAlerts[alertType.id];
|
|
@@ -305,47 +305,47 @@ function AlertSettingsTab({ isDark = true, apiBasePath = '/api/admin/alerts', on
|
|
|
305
305
|
else {
|
|
306
306
|
updateAlertConfig(alertType.id, { threshold: val });
|
|
307
307
|
}
|
|
308
|
-
}, disabled: !config.enabled && !alertType.alwaysSent, className: `
|
|
309
|
-
w-16 px-2 py-1.5 rounded text-sm text-center
|
|
310
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
311
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
312
|
-
disabled:opacity-50
|
|
313
|
-
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs ${themeClasses.textMuted} whitespace-nowrap`, children: alertType.thresholdUnit })] })), !alertType.alwaysSent && ((0, jsx_runtime_1.jsx)("button", { onClick: () => updateAlertConfig(alertType.id, { enabled: !config.enabled }), className: `
|
|
314
|
-
relative w-11 h-6 rounded-full transition-colors
|
|
308
|
+
}, disabled: !config.enabled && !alertType.alwaysSent, className: `
|
|
309
|
+
w-16 px-2 py-1.5 rounded text-sm text-center
|
|
310
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
311
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
312
|
+
disabled:opacity-50
|
|
313
|
+
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs ${themeClasses.textMuted} whitespace-nowrap`, children: alertType.thresholdUnit })] })), !alertType.alwaysSent && ((0, jsx_runtime_1.jsx)("button", { onClick: () => updateAlertConfig(alertType.id, { enabled: !config.enabled }), className: `
|
|
314
|
+
relative w-11 h-6 rounded-full transition-colors
|
|
315
315
|
${config.enabled
|
|
316
316
|
? (isDark ? 'bg-indigo-600' : 'bg-indigo-500')
|
|
317
|
-
: (isDark ? 'bg-slate-600' : 'bg-gray-300')}
|
|
318
|
-
`, children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
319
|
-
absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform
|
|
320
|
-
${config.enabled ? 'translate-x-5' : ''}
|
|
317
|
+
: (isDark ? 'bg-slate-600' : 'bg-gray-300')}
|
|
318
|
+
`, children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
319
|
+
absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform
|
|
320
|
+
${config.enabled ? 'translate-x-5' : ''}
|
|
321
321
|
` }) }))] })] }) }, alertType.id));
|
|
322
|
-
}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Rate Limiting" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Maximum alerts:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "100", value: editedRateLimit, onChange: (e) => setEditedRateLimit(parseInt(e.target.value) || 10), className: `
|
|
323
|
-
w-20 px-3 py-2 rounded-lg text-sm
|
|
324
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
325
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
322
|
+
}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Rate Limiting" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Maximum alerts:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "100", value: editedRateLimit, onChange: (e) => setEditedRateLimit(parseInt(e.target.value) || 10), className: `
|
|
323
|
+
w-20 px-3 py-2 rounded-lg text-sm
|
|
324
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
325
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
326
326
|
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "per hour" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-start gap-2 mt-3 text-xs ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { size: 14, className: "flex-shrink-0 mt-0.5" }), (0, jsx_runtime_1.jsx)("span", { children: "Critical alerts (Storage Critical, Agent Expired) are always sent regardless of this limit." })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Send, { className: isDark ? 'text-blue-400' : 'text-blue-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Test & History" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsxs)("select", { disabled: sendingTest || editedRecipients.length === 0, onChange: (e) => {
|
|
327
327
|
if (e.target.value) {
|
|
328
328
|
sendTestAlert(e.target.value);
|
|
329
329
|
e.target.value = '';
|
|
330
330
|
}
|
|
331
|
-
}, className: `
|
|
332
|
-
px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
|
|
333
|
-
${isDark ? 'bg-blue-900/30 text-blue-300 border-blue-700 hover:bg-blue-900/50' : 'bg-blue-50 text-blue-700 border-blue-300 hover:bg-blue-100'}
|
|
334
|
-
border focus:outline-none focus:ring-2 focus:ring-blue-500
|
|
335
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
331
|
+
}, className: `
|
|
332
|
+
px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
|
|
333
|
+
${isDark ? 'bg-blue-900/30 text-blue-300 border-blue-700 hover:bg-blue-900/50' : 'bg-blue-50 text-blue-700 border-blue-300 hover:bg-blue-100'}
|
|
334
|
+
border focus:outline-none focus:ring-2 focus:ring-blue-500
|
|
335
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
336
336
|
`, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Send Test Alert..." }), TEST_ALERT_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-blue-400' : 'text-blue-500'}` })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => {
|
|
337
337
|
// TODO: Open history modal or navigate to history page
|
|
338
338
|
window.open(`${apiBasePath}/history`, '_blank');
|
|
339
|
-
}, className: `
|
|
340
|
-
px-4 py-2.5 rounded-lg text-sm font-medium flex items-center gap-2
|
|
341
|
-
${isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
|
|
342
|
-
border ${isDark ? 'border-slate-600' : 'border-gray-300'}
|
|
343
|
-
`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.History, { size: 16 }), "View Alert History"] })] }), editedRecipients.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-3 ${themeClasses.textMuted}`, children: "Add at least one recipient to send test alerts." }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
|
|
344
|
-
px-6 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
339
|
+
}, className: `
|
|
340
|
+
px-4 py-2.5 rounded-lg text-sm font-medium flex items-center gap-2
|
|
341
|
+
${isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
|
|
342
|
+
border ${isDark ? 'border-slate-600' : 'border-gray-300'}
|
|
343
|
+
`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.History, { size: 16 }), "View Alert History"] })] }), editedRecipients.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-3 ${themeClasses.textMuted}`, children: "Add at least one recipient to send test alerts." }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
|
|
344
|
+
px-6 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
345
345
|
${hasChanges
|
|
346
346
|
? 'bg-indigo-600 text-white hover:bg-indigo-700'
|
|
347
|
-
: (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
|
|
348
|
-
disabled:cursor-not-allowed
|
|
347
|
+
: (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
|
|
348
|
+
disabled:cursor-not-allowed
|
|
349
349
|
`, children: saving ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Saving..."] })) : ('Save Settings') })] })] }));
|
|
350
350
|
}
|
|
351
351
|
exports.default = AlertSettingsTab;
|
|
@@ -280,18 +280,18 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
|
|
|
280
280
|
}
|
|
281
281
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [error && ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center gap-3 p-4 rounded-lg ${isDark ? 'bg-red-900/30 border-red-700' : 'bg-red-50 border-red-300'} border`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: isDark ? 'text-red-400' : 'text-red-500', size: 20 }), (0, jsx_runtime_1.jsx)("span", { className: isDark ? 'text-red-300' : 'text-red-700', children: error })] })), successMessage && ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center gap-3 p-4 rounded-lg ${isDark ? 'bg-green-900/30 border-green-700' : 'bg-green-50 border-green-300'} border`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: isDark ? 'text-green-400' : 'text-green-500', size: 20 }), (0, jsx_runtime_1.jsx)("span", { className: isDark ? 'text-green-300' : 'text-green-700', children: successMessage })] })), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-amber-400' : 'text-amber-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Quick Setup" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Choose a preset to quickly configure log levels" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-3", children: PRESETS.map((preset) => {
|
|
282
282
|
const isActive = getActivePreset() === preset.id;
|
|
283
|
-
return ((0, jsx_runtime_1.jsx)("button", { onClick: () => applyPreset(preset.id), className: `
|
|
284
|
-
px-4 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
283
|
+
return ((0, jsx_runtime_1.jsx)("button", { onClick: () => applyPreset(preset.id), className: `
|
|
284
|
+
px-4 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
285
285
|
${isActive
|
|
286
286
|
? 'bg-indigo-600 text-white ring-2 ring-indigo-500 ring-offset-2 ' + (isDark ? 'ring-offset-slate-800' : 'ring-offset-white')
|
|
287
|
-
: (isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200')}
|
|
287
|
+
: (isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200')}
|
|
288
288
|
`, children: preset.label }, preset.id));
|
|
289
289
|
}) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Bug, { className: isDark ? 'text-violet-400' : 'text-violet-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Log Levels" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Set verbosity per category" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "space-y-4", children: CATEGORIES.map((category) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${themeClasses.textPrimary}`, children: category.label }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${themeClasses.textMuted}`, children: category.description })] }), (0, jsx_runtime_1.jsx)("select", { value: editedLevels?.[category.id] || 'info', onChange: (e) => {
|
|
290
290
|
setEditedLevels(prev => prev ? { ...prev, [category.id]: e.target.value } : null);
|
|
291
|
-
}, className: `
|
|
292
|
-
px-3 py-2 rounded-lg text-sm font-medium cursor-pointer
|
|
293
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
294
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
291
|
+
}, className: `
|
|
292
|
+
px-3 py-2 rounded-lg text-sm font-medium cursor-pointer
|
|
293
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
294
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
295
295
|
`, children: LOG_LEVELS.map((level) => ((0, jsx_runtime_1.jsx)("option", { value: level.value, children: level.label }, level.value))) })] }, category.id))) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: isDark ? 'text-cyan-400' : 'text-cyan-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Retention Settings" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "How long to keep logs by severity level" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4", children: [
|
|
296
296
|
{ key: 'debug_days', label: 'Debug' },
|
|
297
297
|
{ key: 'info_days', label: 'Info' },
|
|
@@ -300,22 +300,22 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
|
|
|
300
300
|
{ key: 'critical_days', label: 'Critical' },
|
|
301
301
|
].map((item) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm ${themeClasses.textSecondary} mb-1`, children: item.label }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "365", value: editedRetention?.[item.key] || 7, onChange: (e) => {
|
|
302
302
|
setEditedRetention(prev => prev ? { ...prev, [item.key]: parseInt(e.target.value) || 7 } : null);
|
|
303
|
-
}, className: `
|
|
304
|
-
w-20 px-3 py-2 rounded-lg text-sm
|
|
305
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
306
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
303
|
+
}, className: `
|
|
304
|
+
w-20 px-3 py-2 rounded-lg text-sm
|
|
305
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
306
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
307
307
|
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "days" })] })] }, item.key))) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.HardDrive, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Storage Limits" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5 space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Max size:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "1000", value: editedLimits?.max_size_mb || 10, onChange: (e) => {
|
|
308
308
|
setEditedLimits(prev => prev ? { ...prev, max_size_mb: parseInt(e.target.value) || 10 } : null);
|
|
309
|
-
}, className: `
|
|
310
|
-
w-20 px-3 py-2 rounded-lg text-sm
|
|
311
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
312
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
309
|
+
}, className: `
|
|
310
|
+
w-20 px-3 py-2 rounded-lg text-sm
|
|
311
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
312
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
313
313
|
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "MB" })] }), (0, jsx_runtime_1.jsx)("span", { className: themeClasses.textMuted, children: "or" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("input", { type: "number", min: "100", max: "1000000", step: "1000", value: editedLimits?.max_rows || 20000, onChange: (e) => {
|
|
314
314
|
setEditedLimits(prev => prev ? { ...prev, max_rows: parseInt(e.target.value) || 20000 } : null);
|
|
315
|
-
}, className: `
|
|
316
|
-
w-28 px-3 py-2 rounded-lg text-sm
|
|
317
|
-
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
318
|
-
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
315
|
+
}, className: `
|
|
316
|
+
w-28 px-3 py-2 rounded-lg text-sm
|
|
317
|
+
${themeClasses.inputBg} ${themeClasses.textPrimary} border
|
|
318
|
+
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
319
319
|
` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "rows" })] })] }), usage && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textSecondary}`, children: "Current usage" }), (0, jsx_runtime_1.jsxs)("span", { className: `text-sm font-medium ${themeClasses.textPrimary}`, children: [usage.total_size_mb?.toFixed(1) || 0, " MB of ", usage.limits.max_size_mb, " MB (", usage.percent_used || 0, "%)"] })] }), (0, jsx_runtime_1.jsx)("div", { className: `h-3 rounded-full overflow-hidden ${isDark ? 'bg-slate-700' : 'bg-gray-200'}`, children: (0, jsx_runtime_1.jsx)("div", { className: `h-full rounded-full transition-all ${(usage.percent_used || 0) > 90 ? 'bg-red-500' :
|
|
320
320
|
(usage.percent_used || 0) > 70 ? 'bg-amber-500' :
|
|
321
321
|
'bg-emerald-500'}`, style: { width: `${Math.min(usage.percent_used || 0, 100)}%` } }) }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between mt-2 text-xs ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsxs)("span", { children: [(usage.total_rows || 0).toLocaleString(), " rows"] }), usage.oldest_entry && ((0, jsx_runtime_1.jsxs)("span", { children: ["Oldest: ", new Date(usage.oldest_entry).toLocaleDateString()] }))] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsxs)("select", { disabled: pruning, onChange: (e) => {
|
|
@@ -323,17 +323,17 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
|
|
|
323
323
|
handlePrune(e.target.value);
|
|
324
324
|
e.target.value = '';
|
|
325
325
|
}
|
|
326
|
-
}, className: `
|
|
327
|
-
px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
|
|
328
|
-
${isDark ? 'bg-red-900/30 text-red-300 border-red-700 hover:bg-red-900/50' : 'bg-red-50 text-red-700 border-red-300 hover:bg-red-100'}
|
|
329
|
-
border focus:outline-none focus:ring-2 focus:ring-red-500
|
|
330
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
331
|
-
`, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Prune Now..." }), PRUNE_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-red-400' : 'text-red-500'}` })] }), pruning && ((0, jsx_runtime_1.jsxs)("span", { className: `flex items-center gap-2 text-sm ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Pruning..."] }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
|
|
332
|
-
px-6 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
326
|
+
}, className: `
|
|
327
|
+
px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
|
|
328
|
+
${isDark ? 'bg-red-900/30 text-red-300 border-red-700 hover:bg-red-900/50' : 'bg-red-50 text-red-700 border-red-300 hover:bg-red-100'}
|
|
329
|
+
border focus:outline-none focus:ring-2 focus:ring-red-500
|
|
330
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
331
|
+
`, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Prune Now..." }), PRUNE_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-red-400' : 'text-red-500'}` })] }), pruning && ((0, jsx_runtime_1.jsxs)("span", { className: `flex items-center gap-2 text-sm ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Pruning..."] }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
|
|
332
|
+
px-6 py-2.5 rounded-lg font-medium text-sm transition-all
|
|
333
333
|
${hasChanges
|
|
334
334
|
? 'bg-indigo-600 text-white hover:bg-indigo-700'
|
|
335
|
-
: (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
|
|
336
|
-
disabled:cursor-not-allowed
|
|
335
|
+
: (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
|
|
336
|
+
disabled:cursor-not-allowed
|
|
337
337
|
`, children: saving ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Saving..."] })) : ('Save Settings') })] })] }));
|
|
338
338
|
}
|
|
339
339
|
exports.default = LoggingSettingsTab;
|
|
@@ -823,27 +823,27 @@ async function releaseRefreshLock(sessionToken, requestId, lockVersion) {
|
|
|
823
823
|
const lockKey = getRefreshLockKey(sessionToken);
|
|
824
824
|
try {
|
|
825
825
|
// Lua script for atomic lock validation and release
|
|
826
|
-
const luaScript = `
|
|
827
|
-
local lockKey = KEYS[1]
|
|
828
|
-
local expectedRequestId = ARGV[1]
|
|
829
|
-
local expectedVersion = ARGV[2]
|
|
830
|
-
|
|
831
|
-
local lockData = redis.call('GET', lockKey)
|
|
832
|
-
if not lockData then
|
|
833
|
-
return 0 -- Lock doesn't exist
|
|
834
|
-
end
|
|
835
|
-
|
|
836
|
-
local lockInfo = cjson.decode(lockData)
|
|
837
|
-
if lockInfo.acquiredBy == expectedRequestId then
|
|
838
|
-
if not expectedVersion or expectedVersion == '' or tostring(lockInfo.lockVersion) == expectedVersion then
|
|
839
|
-
redis.call('DEL', lockKey)
|
|
840
|
-
return 1 -- Successfully released
|
|
841
|
-
else
|
|
842
|
-
return -2 -- Version mismatch
|
|
843
|
-
end
|
|
844
|
-
else
|
|
845
|
-
return -1 -- Wrong owner
|
|
846
|
-
end
|
|
826
|
+
const luaScript = `
|
|
827
|
+
local lockKey = KEYS[1]
|
|
828
|
+
local expectedRequestId = ARGV[1]
|
|
829
|
+
local expectedVersion = ARGV[2]
|
|
830
|
+
|
|
831
|
+
local lockData = redis.call('GET', lockKey)
|
|
832
|
+
if not lockData then
|
|
833
|
+
return 0 -- Lock doesn't exist
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
local lockInfo = cjson.decode(lockData)
|
|
837
|
+
if lockInfo.acquiredBy == expectedRequestId then
|
|
838
|
+
if not expectedVersion or expectedVersion == '' or tostring(lockInfo.lockVersion) == expectedVersion then
|
|
839
|
+
redis.call('DEL', lockKey)
|
|
840
|
+
return 1 -- Successfully released
|
|
841
|
+
else
|
|
842
|
+
return -2 -- Version mismatch
|
|
843
|
+
end
|
|
844
|
+
else
|
|
845
|
+
return -1 -- Wrong owner
|
|
846
|
+
end
|
|
847
847
|
`;
|
|
848
848
|
const result = await redis_1.default.eval(luaScript, 1, lockKey, requestId, lockVersion ? lockVersion.toString() : '');
|
|
849
849
|
if (result === 1) {
|
package/dist/lib/startup-init.js
CHANGED
|
@@ -231,25 +231,25 @@ async function performInitialization() {
|
|
|
231
231
|
const connectionLine = isConnectionError
|
|
232
232
|
? '║ 🔌 CONNECTION REFUSED - IDP appears to be down ║\n║ ║\n'
|
|
233
233
|
: '';
|
|
234
|
-
console.error(`
|
|
235
|
-
╔══════════════════════════════════════════════════════════════╗
|
|
236
|
-
║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║
|
|
237
|
-
║ ║
|
|
238
|
-
║ The app cannot start without a valid auth signing secret. ║
|
|
239
|
-
║ This should be fetched from IDP at startup. ║
|
|
240
|
-
║ ║
|
|
241
|
-
${connectionLine}║ Possible causes: ║
|
|
242
|
-
║ • IDP is not running or unreachable ║
|
|
243
|
-
║ • CLIENT_ID is not registered in IDP ║
|
|
244
|
-
║ • IDP_URL is incorrect ║
|
|
245
|
-
║ • Network connectivity issue ║
|
|
246
|
-
║ ║
|
|
247
|
-
║ Current config: ║
|
|
248
|
-
║ • IDP_URL: ${idpUrl}║
|
|
249
|
-
║ • CLIENT_ID: ${clientId}║
|
|
250
|
-
╚══════════════════════════════════════════════════════════════╝
|
|
251
|
-
|
|
252
|
-
[STARTUP] Error: ${errorMsg}
|
|
234
|
+
console.error(`
|
|
235
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
236
|
+
║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║
|
|
237
|
+
║ ║
|
|
238
|
+
║ The app cannot start without a valid auth signing secret. ║
|
|
239
|
+
║ This should be fetched from IDP at startup. ║
|
|
240
|
+
║ ║
|
|
241
|
+
${connectionLine}║ Possible causes: ║
|
|
242
|
+
║ • IDP is not running or unreachable ║
|
|
243
|
+
║ • CLIENT_ID is not registered in IDP ║
|
|
244
|
+
║ • IDP_URL is incorrect ║
|
|
245
|
+
║ • Network connectivity issue ║
|
|
246
|
+
║ ║
|
|
247
|
+
║ Current config: ║
|
|
248
|
+
║ • IDP_URL: ${idpUrl}║
|
|
249
|
+
║ • CLIENT_ID: ${clientId}║
|
|
250
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
251
|
+
|
|
252
|
+
[STARTUP] Error: ${errorMsg}
|
|
253
253
|
`);
|
|
254
254
|
// Re-throw so callers know initialization failed
|
|
255
255
|
throw lastInitError;
|
|
@@ -198,16 +198,17 @@ function serviceUnavailableResponse(request, check = 'viability', unavailablePag
|
|
|
198
198
|
// callback, and no Set-Cookie (nothing here touches the session). No redirect, so no 404 is possible.
|
|
199
199
|
// `unavailablePage` is accepted only as a PLAIN path ([A-Za-z0-9_-/]; no query, fragment, %-encoding, dot segment or
|
|
200
200
|
// scheme), so the self-check below compares like with like and a mis-set value falls back to the inline page (Scout 74668).
|
|
201
|
-
//
|
|
201
|
+
// PAY-2345: the viability-FAILURE arm (500 / timeout / breaker) takes the same path, so an app with no page of its own
|
|
202
|
+
// gets the inline 503 instead of a 404 during an outage. The two arms differ only in `check` (JSON body for /api/*). If the
|
|
202
203
|
// request is already FOR the named page (an app that did not register it as public), answer inline: redirecting to
|
|
203
204
|
// it again would loop with a longer callbackUrl each time (QAPert 74651).
|
|
204
|
-
const named =
|
|
205
|
-
if (
|
|
205
|
+
const named = unavailablePage && /^\/[A-Za-z0-9_\-\/]*$/.test(unavailablePage) && !unavailablePage.includes('//') ? unavailablePage : null;
|
|
206
|
+
if (!named || pathname.replace(/\/+$/, '') === named.replace(/\/+$/, '')) {
|
|
206
207
|
const href = callback.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
207
208
|
return new server_1.NextResponse('<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Temporarily unavailable</title>' +
|
|
208
209
|
'<main role="status" style="font-family:system-ui,sans-serif;max-width:28rem;margin:20vh auto;padding:0 1rem;text-align:center">' +
|
|
209
210
|
'<h1 style="font-size:1.25rem">Temporarily unavailable</h1>' +
|
|
210
|
-
'<p>We could not check your session just now. You are still signed in
|
|
211
|
+
'<p>' + (check === 'revocation' ? 'We could not check your session just now. You are still signed in.' : 'We could not reach the sign-in service just now. Please try again.') + '</p>' +
|
|
211
212
|
'<p><a href="' + href + '">Try again</a></p></main>', { status: 503, headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store', 'retry-after': '30' } });
|
|
212
213
|
}
|
|
213
214
|
const target = new URL(named || '/service-unavailable', request.url);
|
|
@@ -766,7 +767,7 @@ async function handleRefresh(request, safeCallback, opts) {
|
|
|
766
767
|
log.warn('Circuit breaker preventing refresh attempt');
|
|
767
768
|
// PAY-2147: a breaker that merely declines to TRY a refresh has learned nothing about the session; wiping the
|
|
768
769
|
// cookies here logged out every user on the first request after a blip (HALF_OPEN refuses all but one probe).
|
|
769
|
-
return serviceUnavailableResponse(request);
|
|
770
|
+
return serviceUnavailableResponse(request, 'viability', opts.unavailablePage);
|
|
770
771
|
}
|
|
771
772
|
try {
|
|
772
773
|
const baseUrl = (0, internal_api_url_1.getInternalApiUrl)(request);
|
package/dist/pages/login/page.js
CHANGED
|
@@ -152,34 +152,34 @@ function LoginForm({ altAuthSlot }) {
|
|
|
152
152
|
if (!forceShowForm && !shownError && (loading || (status === 'loading' && !session))) {
|
|
153
153
|
return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-col items-center justify-center min-h-screen ${colors.background}`, children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin h-6 w-6", style: { color: buttonColor }, 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: `mt-4 text-sm ${colors.muted}`, children: status === 'loading' ? 'Loading...' : 'Authenticating...' })] }));
|
|
154
154
|
}
|
|
155
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: `
|
|
156
|
-
.themed-input:focus {
|
|
157
|
-
outline: none;
|
|
158
|
-
border-color: ${buttonColor} !important;
|
|
159
|
-
box-shadow: 0 0 0 2px ${buttonColor}40 !important;
|
|
160
|
-
}
|
|
161
|
-
.themed-button {
|
|
162
|
-
background-color: ${buttonColor} !important;
|
|
163
|
-
}
|
|
164
|
-
.themed-button:hover:not(:disabled) {
|
|
165
|
-
background-color: ${buttonHoverColor} !important;
|
|
166
|
-
}
|
|
167
|
-
.themed-link {
|
|
168
|
-
color: ${buttonColor} !important;
|
|
169
|
-
}
|
|
170
|
-
.themed-link:hover {
|
|
171
|
-
color: ${buttonHoverColor} !important;
|
|
172
|
-
}
|
|
173
|
-
.themed-status-submitting {
|
|
174
|
-
background-color: ${buttonColor}1A !important;
|
|
175
|
-
border-color: ${buttonColor}33 !important;
|
|
176
|
-
}
|
|
177
|
-
.themed-status-submitting-text {
|
|
178
|
-
color: ${buttonColor} !important;
|
|
179
|
-
}
|
|
180
|
-
.themed-status-submitting svg {
|
|
181
|
-
color: ${buttonColor} !important;
|
|
182
|
-
}
|
|
155
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: `
|
|
156
|
+
.themed-input:focus {
|
|
157
|
+
outline: none;
|
|
158
|
+
border-color: ${buttonColor} !important;
|
|
159
|
+
box-shadow: 0 0 0 2px ${buttonColor}40 !important;
|
|
160
|
+
}
|
|
161
|
+
.themed-button {
|
|
162
|
+
background-color: ${buttonColor} !important;
|
|
163
|
+
}
|
|
164
|
+
.themed-button:hover:not(:disabled) {
|
|
165
|
+
background-color: ${buttonHoverColor} !important;
|
|
166
|
+
}
|
|
167
|
+
.themed-link {
|
|
168
|
+
color: ${buttonColor} !important;
|
|
169
|
+
}
|
|
170
|
+
.themed-link:hover {
|
|
171
|
+
color: ${buttonHoverColor} !important;
|
|
172
|
+
}
|
|
173
|
+
.themed-status-submitting {
|
|
174
|
+
background-color: ${buttonColor}1A !important;
|
|
175
|
+
border-color: ${buttonColor}33 !important;
|
|
176
|
+
}
|
|
177
|
+
.themed-status-submitting-text {
|
|
178
|
+
color: ${buttonColor} !important;
|
|
179
|
+
}
|
|
180
|
+
.themed-status-submitting svg {
|
|
181
|
+
color: ${buttonColor} !important;
|
|
182
|
+
}
|
|
183
183
|
` } }), (0, jsx_runtime_1.jsx)("div", { className: `min-h-screen flex items-center justify-center px-4 ${colors.background}`, children: (0, jsx_runtime_1.jsx)("div", { className: "max-w-md w-full", children: (0, jsx_runtime_1.jsxs)("div", { className: `rounded-2xl shadow-xl p-8 ${colors.card} ${colors.border} text-gray-800`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-3xl font-bold mb-2", style: { color: 'inherit' }, children: branding.tagline || 'Welcome Back' }), (0, jsx_runtime_1.jsx)("p", { className: colors.muted, children: "Sign in to your account" })] }), shownError && ((0, jsx_runtime_1.jsx)("div", { role: "alert", "data-testid": "login-error", className: "mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700", children: shownError })), googleEnabled && ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => void handleGoogleSignIn(), disabled: googleLoading || isSubmitting, "data-testid": "google-sign-in", className: "w-full px-4 py-3 mb-6 rounded-lg border border-gray-300 font-semibold transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50", style: { backgroundColor: 'white', color: '#1f2937' }, children: [googleLoading ? ((0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin w-5 h-5", 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-8v4a4 4 0 00-4 4H4z" })] })) : ((0, jsx_runtime_1.jsxs)("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "currentColor", children: [(0, jsx_runtime_1.jsx)("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }), (0, jsx_runtime_1.jsx)("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })] })), googleLoading ? 'Redirecting to Google…' : 'Continue with Google'] }) })), altAuthSlot && (0, jsx_runtime_1.jsx)("div", { className: "mt-4", children: altAuthSlot }), (0, jsx_runtime_1.jsx)("div", { className: `mt-6 text-center text-sm ${colors.muted}`, children: (0, jsx_runtime_1.jsx)("a", { href: "/account-auth/recovery", className: "themed-link hover:underline", children: "Start account recovery" }) })] }) }) })] }));
|
|
184
184
|
}
|
|
185
185
|
function LoginPageFallback() {
|