@payez/next-mvp 4.8.1 → 4.8.3
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/api-handlers/admin/audit.js +13 -21
- package/dist/api-handlers/admin/sessions.js +59 -45
- package/dist/api-handlers/admin/stats.js +1 -1
- package/dist/api-handlers/admin/users.js +15 -23
- package/dist/api-handlers/admin/vibe-data.d.ts +5 -0
- package/dist/api-handlers/admin/vibe-data.js +5 -0
- 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/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/dist/vibe/client.d.ts +1 -1
- package/dist/vibe/client.js +2 -1
- package/dist/vibe/filters.d.ts +36 -0
- package/dist/vibe/filters.js +47 -0
- package/dist/vibe/generic.js +2 -1
- 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 +223 -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 +341 -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 +240 -251
- package/src/api-handlers/admin/vibe-data.ts +338 -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/revocation-fields.ts +16 -16
- 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 +1141 -1141
- 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/service-unavailable/page.tsx +63 -63
- 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 +878 -877
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +209 -209
- package/src/vibe/filters.ts +52 -0
- package/src/vibe/generic.ts +340 -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
|
@@ -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;
|
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() {
|
|
@@ -47,13 +47,13 @@ const DATE_FORMATS = [
|
|
|
47
47
|
];
|
|
48
48
|
// Toggle component
|
|
49
49
|
function Toggle({ enabled, onChange, disabled = false, isDark = true, }) {
|
|
50
|
-
return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => !disabled && onChange(!enabled), disabled: disabled, className: `
|
|
51
|
-
relative inline-flex h-6 w-11 items-center rounded-full transition-colors
|
|
52
|
-
${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
|
|
53
|
-
${enabled ? 'bg-blue-600' : isDark ? 'bg-slate-600' : 'bg-gray-300'}
|
|
54
|
-
`, children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
55
|
-
inline-block h-4 w-4 transform rounded-full bg-white transition-transform
|
|
56
|
-
${enabled ? 'translate-x-6' : 'translate-x-1'}
|
|
50
|
+
return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => !disabled && onChange(!enabled), disabled: disabled, className: `
|
|
51
|
+
relative inline-flex h-6 w-11 items-center rounded-full transition-colors
|
|
52
|
+
${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
|
|
53
|
+
${enabled ? 'bg-blue-600' : isDark ? 'bg-slate-600' : 'bg-gray-300'}
|
|
54
|
+
`, children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
55
|
+
inline-block h-4 w-4 transform rounded-full bg-white transition-transform
|
|
56
|
+
${enabled ? 'translate-x-6' : 'translate-x-1'}
|
|
57
57
|
` }) }));
|
|
58
58
|
}
|
|
59
59
|
// Section wrapper component
|
|
@@ -73,9 +73,9 @@ function Select({ value, onChange, options, isDark, }) {
|
|
|
73
73
|
const bgColor = isDark ? 'bg-slate-700' : 'bg-white';
|
|
74
74
|
const borderColor = isDark ? 'border-slate-600' : 'border-gray-300';
|
|
75
75
|
const textColor = isDark ? 'text-white' : 'text-gray-900';
|
|
76
|
-
return ((0, jsx_runtime_1.jsx)("select", { value: value, onChange: (e) => onChange(e.target.value), className: `
|
|
77
|
-
px-3 py-2 rounded-md border ${borderColor} ${bgColor} ${textColor}
|
|
78
|
-
focus:outline-none focus:ring-2 focus:ring-blue-500 min-w-[200px]
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)("select", { value: value, onChange: (e) => onChange(e.target.value), className: `
|
|
77
|
+
px-3 py-2 rounded-md border ${borderColor} ${bgColor} ${textColor}
|
|
78
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 min-w-[200px]
|
|
79
79
|
`, children: options.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value))) }));
|
|
80
80
|
}
|
|
81
81
|
function EnhancedSettingsPage({ initialPreferences, preferencesEndpoint = '/api/v1/user/preferences', languagesEndpoint = '/api/v1/lookup/languages', timezonesEndpoint = '/api/v1/lookup/timezones', onSave, onThemeChange, }) {
|
|
@@ -206,17 +206,17 @@ function EnhancedSettingsPage({ initialPreferences, preferencesEndpoint = '/api/
|
|
|
206
206
|
const smsEnabled = preferences.notifications?.sms_enabled ?? false;
|
|
207
207
|
const profileVisibility = preferences.privacy?.profile_visibility || 'team';
|
|
208
208
|
const showActivityStatus = preferences.privacy?.show_activity_status ?? true;
|
|
209
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgColor}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `max-w-2xl 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.jsx)("h1", { className: `text-3xl font-bold ${textPrimary}`, children: "Settings" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleSave, disabled: saving, className: `
|
|
210
|
-
px-4 py-2 rounded-md font-medium transition-colors
|
|
211
|
-
${saving ? 'opacity-50 cursor-not-allowed' : ''}
|
|
212
|
-
bg-blue-600 text-white hover:bg-blue-700
|
|
213
|
-
`, children: saving ? 'Saving...' : 'Save Changes' })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 p-3 rounded-md bg-red-500/10 border border-red-500/50 text-red-500", children: error })), successMessage && ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 p-3 rounded-md bg-green-500/10 border border-green-500/50 text-green-500", children: successMessage })), (0, jsx_runtime_1.jsxs)("div", { className: `${cardBg} rounded-lg shadow-lg border ${borderColor} p-6`, children: [(0, jsx_runtime_1.jsxs)(SettingsSection, { title: "Appearance", isDark: isDark, children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Theme" }), (0, jsx_runtime_1.jsx)("div", { className: "flex gap-2", children: ['light', 'dark', 'system'].map((theme) => ((0, jsx_runtime_1.jsx)("button", { onClick: () => handleThemeChange(theme), className: `
|
|
214
|
-
px-4 py-2 rounded-md capitalize transition-colors
|
|
209
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgColor}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `max-w-2xl 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.jsx)("h1", { className: `text-3xl font-bold ${textPrimary}`, children: "Settings" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleSave, disabled: saving, className: `
|
|
210
|
+
px-4 py-2 rounded-md font-medium transition-colors
|
|
211
|
+
${saving ? 'opacity-50 cursor-not-allowed' : ''}
|
|
212
|
+
bg-blue-600 text-white hover:bg-blue-700
|
|
213
|
+
`, children: saving ? 'Saving...' : 'Save Changes' })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 p-3 rounded-md bg-red-500/10 border border-red-500/50 text-red-500", children: error })), successMessage && ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 p-3 rounded-md bg-green-500/10 border border-green-500/50 text-green-500", children: successMessage })), (0, jsx_runtime_1.jsxs)("div", { className: `${cardBg} rounded-lg shadow-lg border ${borderColor} p-6`, children: [(0, jsx_runtime_1.jsxs)(SettingsSection, { title: "Appearance", isDark: isDark, children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Theme" }), (0, jsx_runtime_1.jsx)("div", { className: "flex gap-2", children: ['light', 'dark', 'system'].map((theme) => ((0, jsx_runtime_1.jsx)("button", { onClick: () => handleThemeChange(theme), className: `
|
|
214
|
+
px-4 py-2 rounded-md capitalize transition-colors
|
|
215
215
|
${currentTheme === theme
|
|
216
216
|
? 'bg-blue-600 text-white'
|
|
217
217
|
: isDark
|
|
218
218
|
? 'bg-slate-700 text-gray-300 hover:bg-slate-600'
|
|
219
|
-
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
|
|
219
|
+
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
|
|
220
220
|
`, children: theme }, theme))) })] }), (0, jsx_runtime_1.jsx)(SettingRow, { label: "Compact Mode", description: "Reduce spacing and padding for denser layouts", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: compactMode, onChange: (value) => updatePreference('appearance', 'compact_mode', value), isDark: isDark }) })] }), (0, jsx_runtime_1.jsx)(SettingsSection, { title: "Localization", isDark: isDark, children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Language" }), (0, jsx_runtime_1.jsx)(Select, { value: currentLanguage, onChange: (value) => updatePreference('localization', 'language', value), options: languages.map((l) => ({ value: l.code, label: l.name })), isDark: isDark })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Timezone" }), (0, jsx_runtime_1.jsx)(Select, { value: currentTimezone, onChange: (value) => updatePreference('localization', 'timezone', value), options: timezones.map((tz) => ({ value: tz.id, label: `${tz.display} (${tz.offset})` })), isDark: isDark }), preferences.localization?.timezone_display && ((0, jsx_runtime_1.jsxs)("p", { className: `mt-1 text-sm ${textMuted}`, children: ["Currently: ", preferences.localization.timezone_display] }))] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Date Format" }), (0, jsx_runtime_1.jsx)(Select, { value: currentDateFormat, onChange: (value) => updatePreference('localization', 'date_format', value), options: DATE_FORMATS.map((df) => ({ value: df.value, label: df.label })), isDark: isDark }), (0, jsx_runtime_1.jsxs)("p", { className: `mt-1 text-sm ${textMuted}`, children: ["Example: ", DATE_FORMATS.find((df) => df.value === currentDateFormat)?.example] })] })] }) }), (0, jsx_runtime_1.jsxs)(SettingsSection, { title: "Notifications", isDark: isDark, children: [(0, jsx_runtime_1.jsx)(SettingRow, { label: "Email Notifications", description: "Receive updates and alerts via email", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: emailEnabled, onChange: (value) => updatePreference('notifications', 'email_enabled', value), isDark: isDark }) }), emailEnabled && ((0, jsx_runtime_1.jsxs)("div", { className: `ml-6 border-l-2 ${borderColor} pl-4 space-y-2`, children: [(0, jsx_runtime_1.jsx)(SettingRow, { label: "Security alerts", description: "Login from new device, etc.", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: preferences.notifications?.email_security_alerts ?? true, onChange: (value) => updatePreference('notifications', 'email_security_alerts', value), isDark: isDark }) }), (0, jsx_runtime_1.jsx)(SettingRow, { label: "Account updates", description: "Password expiry, etc.", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: preferences.notifications?.email_account_updates ?? true, onChange: (value) => updatePreference('notifications', 'email_account_updates', value), isDark: isDark }) }), (0, jsx_runtime_1.jsx)(SettingRow, { label: "Product news and tips", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: preferences.notifications?.email_product_news ?? false, onChange: (value) => updatePreference('notifications', 'email_product_news', value), isDark: isDark }) }), (0, jsx_runtime_1.jsx)(SettingRow, { label: "Marketing and promotions", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: preferences.notifications?.email_marketing ?? false, onChange: (value) => updatePreference('notifications', 'email_marketing', value), isDark: isDark }) })] })), (0, jsx_runtime_1.jsx)(SettingRow, { label: "SMS Notifications", description: "Receive urgent alerts via text message", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: smsEnabled, onChange: (value) => updatePreference('notifications', 'sms_enabled', value), isDark: isDark }) }), smsEnabled && ((0, jsx_runtime_1.jsx)("div", { className: `ml-6 border-l-2 ${borderColor} pl-4`, children: (0, jsx_runtime_1.jsx)(SettingRow, { label: "Security alerts only", isDark: isDark, children: (0, jsx_runtime_1.jsx)(Toggle, { enabled: preferences.notifications?.sms_security_alerts ?? true, onChange: (value) => updatePreference('notifications', 'sms_security_alerts', value), isDark: isDark }) }) }))] }), (0, jsx_runtime_1.jsxs)(SettingsSection, { title: "Privacy", isDark: isDark, children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium ${textMuted} mb-2`, children: "Profile Visibility" }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-2", children: [
|
|
221
221
|
{ value: 'private', label: 'Private', description: 'Only you can see your profile' },
|
|
222
222
|
{ value: 'team', label: 'Team', description: 'Team members can see your profile' },
|
package/dist/vibe/client.d.ts
CHANGED
|
@@ -345,7 +345,7 @@ export declare class VibeClient {
|
|
|
345
345
|
* - 401/403 errors: No retry (auth errors)
|
|
346
346
|
* - Network errors: Retry with backoff
|
|
347
347
|
*/
|
|
348
|
-
request<T>(urlOrPath: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE', body?: unknown, authToken?: string): Promise<VibeResponse<T>>;
|
|
348
|
+
request<T>(urlOrPath: string, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', body?: unknown, authToken?: string): Promise<VibeResponse<T>>;
|
|
349
349
|
/**
|
|
350
350
|
* Parse error response body, handling non-JSON responses gracefully.
|
|
351
351
|
*/
|
package/dist/vibe/client.js
CHANGED
|
@@ -331,7 +331,8 @@ class VibeTableDelegate {
|
|
|
331
331
|
*/
|
|
332
332
|
async update(options) {
|
|
333
333
|
const endpoint = this.tableEndpoint(options.where.id);
|
|
334
|
-
|
|
334
|
+
// PAY-2041: PATCH, not PUT. Core PUT REPLACES the document (UpdateAsync SET data = body), and `data` here is Partial, so PUT erased every field not named. PATCH merges (DocumentsController.Patch).
|
|
335
|
+
const response = await this.client.request(endpoint, 'PATCH', options.data);
|
|
335
336
|
return this.requireRow(response.data, 'update');
|
|
336
337
|
}
|
|
337
338
|
/**
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PAY-1810: the ONE place the package builds Vibe /query filters and orderBy, in the shapes Core's VibeQueryBuilder accepts.
|
|
3
|
+
*
|
|
4
|
+
* Core accepts exactly three filter forms (PayEz.Vibe.Public.Api VibeQueryModels / VibeQueryBuilder):
|
|
5
|
+
* standard { field: { op: value } } with AND / OR arrays
|
|
6
|
+
* flat { field, operator, value }
|
|
7
|
+
* compound { operator: 'and' | 'or', filters: [ ...nodes ] } <- the key is `filters`, NOT `conditions`
|
|
8
|
+
* What goes wrong with the shapes this module replaces:
|
|
9
|
+
* - { operator, conditions } has no `filters` key, so Core reads `operator` / `conditions` as COLUMN names and returns ZERO rows
|
|
10
|
+
* (a broken search; for revoke-all, "revoked 0 sessions" reported as success).
|
|
11
|
+
* - operator 'like' is not a Core operator; in a flat filter it is DROPPED and the query returns ALL rows. Core's substring operator is
|
|
12
|
+
* `contains` (ILIKE, case-insensitive); `ilike` is case-insensitive EQUALS.
|
|
13
|
+
* - orderBy must be a { field: 'asc' | 'desc' } map (Core's model is Dictionary<string,string>); a bare string is rejected or ignored, never honoured.
|
|
14
|
+
*/
|
|
15
|
+
export type VibeOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'startsWith' | 'endsWith' | 'ilike' | 'in' | 'notIn';
|
|
16
|
+
export interface VibeFlatFilter {
|
|
17
|
+
field: string;
|
|
18
|
+
operator: VibeOperator;
|
|
19
|
+
value: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface VibeCompoundFilter {
|
|
22
|
+
operator: 'and' | 'or';
|
|
23
|
+
filters: VibeFilter[];
|
|
24
|
+
}
|
|
25
|
+
export type VibeFilter = VibeFlatFilter | VibeCompoundFilter;
|
|
26
|
+
export declare const eq: (field: string, value: unknown) => VibeFlatFilter;
|
|
27
|
+
export declare const gte: (field: string, value: unknown) => VibeFlatFilter;
|
|
28
|
+
export declare const lte: (field: string, value: unknown) => VibeFlatFilter;
|
|
29
|
+
/** Case-insensitive substring (Core `contains` = ILIKE). The value is the bare text: no % wildcards. */
|
|
30
|
+
export declare const contains: (field: string, value: string) => VibeFlatFilter;
|
|
31
|
+
/** AND of the given nodes: none -> undefined (no filter), one -> that node, several -> a compound with `filters`. */
|
|
32
|
+
export declare function allOf(nodes: Array<VibeFilter | undefined | null>): VibeFilter | undefined;
|
|
33
|
+
/** OR of the given nodes: none -> undefined, one -> that node, several -> a compound with `filters`. */
|
|
34
|
+
export declare function anyOf(nodes: Array<VibeFilter | undefined | null>): VibeFilter | undefined;
|
|
35
|
+
/** orderBy as Core binds it. */
|
|
36
|
+
export declare function orderByMap(field: string, direction?: 'asc' | 'desc'): Record<string, 'asc' | 'desc'>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PAY-1810: the ONE place the package builds Vibe /query filters and orderBy, in the shapes Core's VibeQueryBuilder accepts.
|
|
4
|
+
*
|
|
5
|
+
* Core accepts exactly three filter forms (PayEz.Vibe.Public.Api VibeQueryModels / VibeQueryBuilder):
|
|
6
|
+
* standard { field: { op: value } } with AND / OR arrays
|
|
7
|
+
* flat { field, operator, value }
|
|
8
|
+
* compound { operator: 'and' | 'or', filters: [ ...nodes ] } <- the key is `filters`, NOT `conditions`
|
|
9
|
+
* What goes wrong with the shapes this module replaces:
|
|
10
|
+
* - { operator, conditions } has no `filters` key, so Core reads `operator` / `conditions` as COLUMN names and returns ZERO rows
|
|
11
|
+
* (a broken search; for revoke-all, "revoked 0 sessions" reported as success).
|
|
12
|
+
* - operator 'like' is not a Core operator; in a flat filter it is DROPPED and the query returns ALL rows. Core's substring operator is
|
|
13
|
+
* `contains` (ILIKE, case-insensitive); `ilike` is case-insensitive EQUALS.
|
|
14
|
+
* - orderBy must be a { field: 'asc' | 'desc' } map (Core's model is Dictionary<string,string>); a bare string is rejected or ignored, never honoured.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.contains = exports.lte = exports.gte = exports.eq = void 0;
|
|
18
|
+
exports.allOf = allOf;
|
|
19
|
+
exports.anyOf = anyOf;
|
|
20
|
+
exports.orderByMap = orderByMap;
|
|
21
|
+
const eq = (field, value) => ({ field, operator: 'eq', value });
|
|
22
|
+
exports.eq = eq;
|
|
23
|
+
const gte = (field, value) => ({ field, operator: 'gte', value });
|
|
24
|
+
exports.gte = gte;
|
|
25
|
+
const lte = (field, value) => ({ field, operator: 'lte', value });
|
|
26
|
+
exports.lte = lte;
|
|
27
|
+
/** Case-insensitive substring (Core `contains` = ILIKE). The value is the bare text: no % wildcards. */
|
|
28
|
+
const contains = (field, value) => ({ field, operator: 'contains', value });
|
|
29
|
+
exports.contains = contains;
|
|
30
|
+
/** AND of the given nodes: none -> undefined (no filter), one -> that node, several -> a compound with `filters`. */
|
|
31
|
+
function allOf(nodes) {
|
|
32
|
+
const f = nodes.filter((n) => !!n);
|
|
33
|
+
if (f.length === 0)
|
|
34
|
+
return undefined;
|
|
35
|
+
return f.length === 1 ? f[0] : { operator: 'and', filters: f };
|
|
36
|
+
}
|
|
37
|
+
/** OR of the given nodes: none -> undefined, one -> that node, several -> a compound with `filters`. */
|
|
38
|
+
function anyOf(nodes) {
|
|
39
|
+
const f = nodes.filter((n) => !!n);
|
|
40
|
+
if (f.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
return f.length === 1 ? f[0] : { operator: 'or', filters: f };
|
|
43
|
+
}
|
|
44
|
+
/** orderBy as Core binds it. */
|
|
45
|
+
function orderByMap(field, direction = 'desc') {
|
|
46
|
+
return { [field]: direction };
|
|
47
|
+
}
|
package/dist/vibe/generic.js
CHANGED
|
@@ -208,7 +208,8 @@ class GenericTableDelegate {
|
|
|
208
208
|
async update(options) {
|
|
209
209
|
const path = vibeTablePath(this.collection, this.tableName, options.where.id);
|
|
210
210
|
const url = `${this.client.getBaseUrl()}${path}`;
|
|
211
|
-
|
|
211
|
+
// PAY-2041: PATCH merges; PUT would replace the whole document with this Partial.
|
|
212
|
+
const response = await this.client.request(url, 'PATCH', options.data);
|
|
212
213
|
const unwrapped = (0, document_unwrap_2.unwrapVibeDocument)(response.data);
|
|
213
214
|
if (!unwrapped) {
|
|
214
215
|
throw new errors_1.VibeServiceError('Failed to parse updated document');
|
package/dist/vibe/sessions.js
CHANGED
|
@@ -145,6 +145,26 @@ async function createLoginSession(input) {
|
|
|
145
145
|
}
|
|
146
146
|
const now = new Date();
|
|
147
147
|
const expiresAt = new Date(now.getTime() + SESSION_EXPIRY_DAYS * 24 * 60 * 60 * 1000);
|
|
148
|
+
// PAY-2362 (write side): ONE row per session hash. A second record-login for the same session (another tab, a cleared
|
|
149
|
+
// sessionStorage, every open session when an app first runs a version that changes its login-recording key) used to INSERT a second
|
|
150
|
+
// row with the SAME hash, and a revoke PATCHes ONE row by id. If a row for this hash already exists, PATCH last_activity on it (that
|
|
151
|
+
// one field only: it never touches status, so it cannot un-revoke) and return it. A failed lookup must never block a login: fall
|
|
152
|
+
// through to the insert, as before. The read side (findRevokingSession: revoked if ANY row for the hash is) covers duplicates that
|
|
153
|
+
// already exist.
|
|
154
|
+
if (input.sessionTokenHash) {
|
|
155
|
+
try {
|
|
156
|
+
const existingRows = (await getUserSessions(input.idpUserId, input.accessToken))
|
|
157
|
+
.filter(s => s.session_token_hash === input.sessionTokenHash);
|
|
158
|
+
if (existingRows.length > 0) {
|
|
159
|
+
const existing = existingRows.find(s => s.status === 'revoked') ?? existingRows[0];
|
|
160
|
+
await updateSessionActivity(existing.id, input.accessToken);
|
|
161
|
+
return existing;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* lookup failed: insert as before */
|
|
166
|
+
}
|
|
167
|
+
}
|
|
148
168
|
const sessionData = {
|
|
149
169
|
idp_user_id: input.idpUserId,
|
|
150
170
|
email: input.email,
|
|
@@ -561,9 +581,14 @@ function findRevokingSessionWithRule(sessions, sessionTokenHash) {
|
|
|
561
581
|
if (sessions.length === 0)
|
|
562
582
|
return { row: null, rule: 'none' };
|
|
563
583
|
if (sessionTokenHash) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
584
|
+
// PAY-2362: EVERY row carrying this hash, not the first. A second record-login for the same session (another tab, a cleared
|
|
585
|
+
// sessionStorage, every open session at a package roll) writes a second row with the SAME hash, and a revoke PATCHes ONE row by id.
|
|
586
|
+
// `find` took the first row, so an active duplicate listed before the revoked one hid the revoke. Revoked if ANY of them is.
|
|
587
|
+
const matching = sessions.filter(s => s.session_token_hash === sessionTokenHash);
|
|
588
|
+
if (matching.length > 0) {
|
|
589
|
+
const revoked = matching.find(s => s.status === 'revoked');
|
|
590
|
+
return revoked ? { row: revoked, rule: 'own_row_revoked' } : { row: null, rule: 'none' };
|
|
591
|
+
}
|
|
567
592
|
}
|
|
568
593
|
// Hash not found (a legacy row minted before hashing) or no hash given: idealresume's user-wide
|
|
569
594
|
// rules, lib/vibe-sessions.ts:594-651 (BAPert 69189).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payez/next-mvp",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.3",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test": "node --test \"test/*.test.cjs\"",
|
|
17
17
|
"pretest:ci": "npm run build",
|
|
18
18
|
"test:ci": "node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=junit.xml \"test/*.test.cjs\"",
|
|
19
|
-
"prepack": "npm run build",
|
|
19
|
+
"prepack": "npm run build && npm run check:publish-eol",
|
|
20
20
|
"prepublishOnly": "npm run check:publish-eol && npm run build",
|
|
21
21
|
"postpublish": "node scripts/check-publish-health.mjs",
|
|
22
22
|
"postversion": "node scripts/check-publish-health.mjs || echo \"[postversion] see above: local/registry are out of sync after this version bump (171900)\"",
|