@payez/next-mvp 4.4.5 → 4.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/api/auth-handler.js +5 -3
- package/dist/api-handlers/account/change-password.js +33 -8
- package/dist/api-handlers/account/masked-info.js +2 -1
- package/dist/api-handlers/account/recovery/forward.d.ts +14 -0
- package/dist/api-handlers/account/recovery/forward.js +105 -0
- package/dist/api-handlers/account/recovery/initiate.js +4 -0
- package/dist/api-handlers/account/recovery/send-code.d.ts +12 -2
- package/dist/api-handlers/account/recovery/send-code.js +11 -84
- package/dist/api-handlers/account/recovery/verify-code.d.ts +11 -2
- package/dist/api-handlers/account/recovery/verify-code.js +10 -84
- package/dist/api-handlers/account/reset-password.js +24 -1
- package/dist/api-handlers/account/send-code.d.ts +1 -15
- package/dist/api-handlers/account/send-code.js +3 -6
- package/dist/api-handlers/account/update-phone.js +6 -1
- package/dist/api-handlers/account/validate-password.js +3 -1
- package/dist/api-handlers/account/verify-email.d.ts +1 -17
- package/dist/api-handlers/account/verify-email.js +4 -6
- package/dist/api-handlers/account/verify-sms.d.ts +1 -17
- package/dist/api-handlers/account/verify-sms.js +4 -6
- package/dist/api-handlers/admin/analytics.js +12 -4
- package/dist/api-handlers/admin/audit.js +12 -4
- package/dist/api-handlers/admin/redis-sessions.js +16 -8
- package/dist/api-handlers/admin/sessions.js +14 -6
- package/dist/api-handlers/admin/site-logs.js +39 -16
- package/dist/api-handlers/admin/stats.js +14 -4
- package/dist/api-handlers/admin/users.js +14 -6
- package/dist/api-handlers/admin/vibe-data.js +19 -11
- package/dist/api-handlers/auth/jwks.js +2 -1
- package/dist/api-handlers/auth/login.js +13 -4
- package/dist/api-handlers/auth/passkey-signin.js +6 -0
- package/dist/api-handlers/auth/refresh.js +38 -11
- package/dist/api-handlers/auth/signout.js +2 -1
- package/dist/api-handlers/auth/status.js +4 -1
- package/dist/api-handlers/auth/update-session.d.ts +25 -4
- package/dist/api-handlers/auth/update-session.js +38 -11
- package/dist/api-handlers/auth/validate.js +4 -2
- package/dist/api-handlers/auth/verify-code.js +3 -2
- package/dist/api-handlers/mfa/backup-code-verify.d.ts +1 -18
- package/dist/api-handlers/mfa/backup-code-verify.js +4 -6
- package/dist/api-handlers/mfa/backup-codes.js +2 -1
- package/dist/api-handlers/mfa/methods.js +2 -1
- package/dist/api-handlers/mfa/totp-confirm.js +2 -1
- package/dist/api-handlers/mfa/totp-enroll.js +2 -1
- package/dist/api-handlers/mfa/totp-verify.d.ts +1 -19
- package/dist/api-handlers/mfa/totp-verify.js +4 -6
- package/dist/api-handlers/passkeys/list.js +2 -1
- package/dist/api-handlers/passkeys/manage.js +2 -1
- package/dist/api-handlers/passkeys/register-options.js +2 -1
- package/dist/api-handlers/passkeys/register-verify.js +2 -1
- package/dist/api-handlers/session/refresh-viability.d.ts +4 -2
- package/dist/api-handlers/session/refresh-viability.js +10 -7
- package/dist/api-handlers/session/viability.js +4 -3
- package/dist/api-handlers/test/force-expire.js +5 -3
- package/dist/auth/auth-decision.js +2 -1
- package/dist/auth/better-auth.d.ts +58 -14
- package/dist/auth/better-auth.js +125 -54
- package/dist/auth/utils/idp-client.js +2 -1
- package/dist/client/better-auth-client.d.ts +101 -956
- package/dist/components/admin/DataBrowserTab.js +4 -1
- package/dist/components/admin/SessionsTab.js +9 -3
- package/dist/components/recovery/RequestedStep.d.ts +11 -0
- package/dist/components/recovery/RequestedStep.js +14 -0
- package/dist/components/recovery/SetPasswordStep.js +10 -1
- package/dist/config/env.d.ts +0 -2
- package/dist/config/env.js +0 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/dist/lib/app-slug.d.ts +21 -0
- package/dist/lib/app-slug.js +51 -0
- package/dist/lib/ensure-fresh-access-token.d.ts +26 -0
- package/dist/lib/ensure-fresh-access-token.js +105 -4
- package/dist/lib/idp-client-config.d.ts +20 -0
- package/dist/lib/idp-client-config.js +73 -45
- package/dist/lib/idp-fetch.js +1 -1
- package/dist/lib/never-in-response.d.ts +30 -0
- package/dist/lib/never-in-response.js +71 -0
- package/dist/lib/password-rules.d.ts +22 -0
- package/dist/lib/password-rules.js +43 -0
- package/dist/lib/safe-error-response.d.ts +67 -0
- package/dist/lib/safe-error-response.js +130 -0
- package/dist/lib/session-cookie-shape.d.ts +22 -19
- package/dist/lib/session-cookie-shape.js +14 -18
- package/dist/lib/session-store.d.ts +8 -1
- package/dist/lib/session-store.js +170 -24
- package/dist/lib/startup-init.d.ts +6 -0
- package/dist/lib/startup-init.js +30 -4
- package/dist/lib/token-lifecycle.js +2 -1
- package/dist/lib/token-log-id.d.ts +18 -0
- package/dist/lib/token-log-id.js +84 -0
- package/dist/lib/two-factor-verify-failure.d.ts +78 -0
- package/dist/lib/two-factor-verify-failure.js +220 -0
- package/dist/middleware/create-middleware.d.ts +4 -1
- package/dist/middleware/create-middleware.js +79 -8
- package/dist/pages/login/page.js +5 -0
- package/dist/pages/recovery/page.js +14 -4
- package/dist/pages/test-env/JwtInspectPage.js +7 -149
- package/dist/pages/test-env/RefreshTokenPage.js +4 -1
- package/dist/pages/test-env/redact-credentials.d.ts +6 -0
- package/dist/pages/test-env/redact-credentials.js +101 -0
- package/dist/pages/verify-code/page.js +6 -3
- package/dist/routes/auth/logout.d.ts +2 -1
- package/dist/routes/auth/logout.js +4 -2
- package/dist/routes/auth/session.d.ts +0 -3
- package/dist/routes/auth/session.js +16 -10
- package/dist/routes/auth/settings.js +4 -1
- package/dist/routes/auth/viability.d.ts +3 -2
- package/dist/routes/auth/viability.js +11 -7
- package/dist/server/auth.d.ts +36 -294
- package/dist/server/auth.js +32 -31
- package/dist/server/decode-session.js +15 -26
- package/dist/server/verified-session-token.d.ts +13 -0
- package/dist/server/verified-session-token.js +103 -0
- package/dist/stores/authStore.js +14 -3
- package/dist/types/recovery.d.ts +1 -1
- package/dist/utils/api.d.ts +18 -0
- package/dist/utils/api.js +51 -9
- package/dist/vibe/client.d.ts +132 -16
- package/dist/vibe/client.js +288 -82
- package/dist/vibe/document-unwrap.d.ts +50 -0
- package/dist/vibe/document-unwrap.js +78 -0
- package/dist/vibe/errors.d.ts +17 -0
- package/dist/vibe/errors.js +21 -1
- package/dist/vibe/generic.d.ts +2 -49
- package/dist/vibe/generic.js +17 -78
- package/dist/vibe/index.d.ts +1 -1
- package/dist/vibe/index.js +2 -1
- package/dist/vibe/sessions.js +2 -1
- package/package.json +13 -2
- package/scripts/verify-release-provenance.mjs +410 -0
- package/src/api/auth-handler.ts +5 -3
- package/src/api-handlers/account/change-password.ts +36 -8
- package/src/api-handlers/account/masked-info.ts +2 -1
- package/src/api-handlers/account/recovery/forward.ts +117 -0
- package/src/api-handlers/account/recovery/initiate.ts +3 -0
- package/src/api-handlers/account/recovery/send-code.ts +13 -99
- package/src/api-handlers/account/recovery/verify-code.ts +12 -99
- package/src/api-handlers/account/reset-password.ts +30 -1
- package/src/api-handlers/account/send-code.ts +3 -9
- package/src/api-handlers/account/update-phone.ts +6 -1
- package/src/api-handlers/account/validate-password.ts +3 -1
- package/src/api-handlers/account/verify-email.ts +4 -9
- package/src/api-handlers/account/verify-sms.ts +4 -9
- package/src/api-handlers/admin/analytics.ts +13 -5
- package/src/api-handlers/admin/audit.ts +13 -5
- package/src/api-handlers/admin/redis-sessions.ts +16 -8
- package/src/api-handlers/admin/sessions.ts +15 -7
- package/src/api-handlers/admin/site-logs.ts +39 -16
- package/src/api-handlers/admin/stats.ts +15 -5
- package/src/api-handlers/admin/users.ts +15 -7
- package/src/api-handlers/admin/vibe-data.ts +20 -12
- package/src/api-handlers/auth/jwks.ts +2 -1
- package/src/api-handlers/auth/login.ts +13 -4
- package/src/api-handlers/auth/passkey-signin.ts +4 -0
- package/src/api-handlers/auth/refresh.ts +38 -11
- package/src/api-handlers/auth/signout.ts +2 -1
- package/src/api-handlers/auth/status.ts +4 -1
- package/src/api-handlers/auth/update-session.ts +45 -11
- package/src/api-handlers/auth/validate.ts +4 -2
- package/src/api-handlers/auth/verify-code.ts +3 -2
- package/src/api-handlers/mfa/backup-code-verify.ts +4 -9
- package/src/api-handlers/mfa/backup-codes.ts +2 -1
- package/src/api-handlers/mfa/methods.ts +2 -1
- package/src/api-handlers/mfa/totp-confirm.ts +2 -1
- package/src/api-handlers/mfa/totp-enroll.ts +2 -1
- package/src/api-handlers/mfa/totp-verify.ts +4 -9
- package/src/api-handlers/passkeys/list.ts +2 -1
- package/src/api-handlers/passkeys/manage.ts +2 -1
- package/src/api-handlers/passkeys/register-options.ts +2 -1
- package/src/api-handlers/passkeys/register-verify.ts +2 -1
- package/src/api-handlers/session/refresh-viability.ts +10 -7
- package/src/api-handlers/session/viability.ts +4 -3
- package/src/api-handlers/test/force-expire.ts +5 -3
- package/src/auth/auth-decision.ts +2 -1
- package/src/auth/better-auth.ts +128 -61
- package/src/auth/utils/idp-client.ts +2 -1
- package/src/components/admin/DataBrowserTab.tsx +4 -1
- package/src/components/admin/SessionsTab.tsx +10 -3
- package/src/components/recovery/RequestedStep.tsx +30 -0
- package/src/components/recovery/SetPasswordStep.tsx +24 -3
- package/src/config/env.ts +0 -2
- package/src/hooks/useViabilitySession.ts +2 -1
- package/src/index.ts +4 -0
- package/src/lib/app-slug.ts +50 -0
- package/src/lib/ensure-fresh-access-token.ts +133 -4
- package/src/lib/idp-client-config.ts +98 -45
- package/src/lib/idp-fetch.ts +1 -1
- package/src/lib/never-in-response.ts +64 -0
- package/src/lib/password-rules.ts +50 -0
- package/src/lib/safe-error-response.ts +126 -0
- package/src/lib/session-cookie-shape.ts +23 -22
- package/src/lib/session-store.ts +167 -24
- package/src/lib/startup-init.ts +29 -4
- package/src/lib/token-lifecycle.ts +2 -1
- package/src/lib/token-log-id.ts +69 -0
- package/src/lib/two-factor-verify-failure.ts +248 -0
- package/src/middleware/create-middleware.ts +97 -9
- package/src/pages/login/page.tsx +4 -0
- package/src/pages/recovery/page.tsx +21 -4
- package/src/pages/test-env/JwtInspectPage.tsx +11 -233
- package/src/pages/test-env/RefreshTokenPage.tsx +9 -4
- package/src/pages/test-env/redact-credentials.ts +96 -0
- package/src/pages/verify-code/page.tsx +6 -3
- package/src/routes/auth/logout.ts +4 -2
- package/src/routes/auth/session.ts +16 -10
- package/src/routes/auth/settings.ts +5 -1
- package/src/routes/auth/viability.ts +11 -7
- package/src/server/auth.ts +33 -35
- package/src/server/decode-session.ts +16 -26
- package/src/server/verified-session-token.ts +103 -0
- package/src/stores/authStore.ts +16 -5
- package/src/types/recovery.ts +1 -1
- package/src/utils/api.ts +47 -8
- package/src/vibe/client.ts +378 -91
- package/src/vibe/document-unwrap.ts +103 -0
- package/src/vibe/errors.ts +24 -0
- package/src/vibe/generic.ts +8 -100
- package/src/vibe/index.ts +1 -0
- package/src/vibe/sessions.ts +2 -1
|
@@ -48,9 +48,11 @@ async function POST(req) {
|
|
|
48
48
|
});
|
|
49
49
|
const responseData = await idpResponse.json().catch(() => ({}));
|
|
50
50
|
if (!idpResponse.ok) {
|
|
51
|
+
// PAY-1424: shape only, never the envelope contents (this endpoint carries a password).
|
|
51
52
|
console.error('[VALIDATE_PASSWORD] IDP error:', {
|
|
52
53
|
status: idpResponse.status,
|
|
53
|
-
|
|
54
|
+
bodyKeys: responseData && typeof responseData === 'object' ? Object.keys(responseData) : undefined,
|
|
55
|
+
errorKeys: responseData?.error && typeof responseData?.error === 'object' ? Object.keys(responseData?.error) : undefined,
|
|
54
56
|
});
|
|
55
57
|
return server_1.NextResponse.json({
|
|
56
58
|
is_valid: false,
|
|
@@ -7,20 +7,4 @@
|
|
|
7
7
|
* @package @payez/next-mvp
|
|
8
8
|
*/
|
|
9
9
|
import { NextRequest, NextResponse } from 'next/server';
|
|
10
|
-
export declare function POST(req: NextRequest): Promise<NextResponse<
|
|
11
|
-
success: boolean;
|
|
12
|
-
error: string;
|
|
13
|
-
code: string;
|
|
14
|
-
}> | NextResponse<{
|
|
15
|
-
success: boolean;
|
|
16
|
-
error: any;
|
|
17
|
-
code: any;
|
|
18
|
-
meta: {
|
|
19
|
-
attemptedRefresh: boolean;
|
|
20
|
-
};
|
|
21
|
-
}> | NextResponse<{
|
|
22
|
-
success: boolean;
|
|
23
|
-
verificationSuccessful: boolean;
|
|
24
|
-
twoFactorSessionVerified: boolean;
|
|
25
|
-
message: any;
|
|
26
|
-
}>>;
|
|
10
|
+
export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;
|
|
@@ -15,6 +15,7 @@ const env_1 = require("../../config/env");
|
|
|
15
15
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
16
16
|
const session_store_1 = require("../../lib/session-store");
|
|
17
17
|
const jwt_decode_1 = require("../../lib/jwt-decode");
|
|
18
|
+
const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
|
|
18
19
|
async function POST(req) {
|
|
19
20
|
try {
|
|
20
21
|
// Parse request body
|
|
@@ -34,12 +35,9 @@ async function POST(req) {
|
|
|
34
35
|
body: JSON.stringify({ verification_code: verificationCode }),
|
|
35
36
|
});
|
|
36
37
|
if (!result.ok) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
code: result.json?.code || 'IDP_ERROR',
|
|
41
|
-
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
42
|
-
}, { status: result.status });
|
|
38
|
+
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
39
|
+
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
40
|
+
return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'email');
|
|
43
41
|
}
|
|
44
42
|
// Unwrap IDP envelope
|
|
45
43
|
const unwrappedData = result.json?.data || result.json;
|
|
@@ -7,20 +7,4 @@
|
|
|
7
7
|
* @package @payez/next-mvp
|
|
8
8
|
*/
|
|
9
9
|
import { NextRequest, NextResponse } from 'next/server';
|
|
10
|
-
export declare function POST(req: NextRequest): Promise<NextResponse<
|
|
11
|
-
success: boolean;
|
|
12
|
-
error: string;
|
|
13
|
-
code: string;
|
|
14
|
-
}> | NextResponse<{
|
|
15
|
-
success: boolean;
|
|
16
|
-
error: any;
|
|
17
|
-
code: any;
|
|
18
|
-
meta: {
|
|
19
|
-
attemptedRefresh: boolean;
|
|
20
|
-
};
|
|
21
|
-
}> | NextResponse<{
|
|
22
|
-
success: boolean;
|
|
23
|
-
verificationSuccessful: boolean;
|
|
24
|
-
twoFactorSessionVerified: boolean;
|
|
25
|
-
message: any;
|
|
26
|
-
}>>;
|
|
10
|
+
export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;
|
|
@@ -15,6 +15,7 @@ const env_1 = require("../../config/env");
|
|
|
15
15
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
16
16
|
const session_store_1 = require("../../lib/session-store");
|
|
17
17
|
const jwt_decode_1 = require("../../lib/jwt-decode");
|
|
18
|
+
const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
|
|
18
19
|
async function POST(req) {
|
|
19
20
|
try {
|
|
20
21
|
// Parse request body
|
|
@@ -34,12 +35,9 @@ async function POST(req) {
|
|
|
34
35
|
body: JSON.stringify({ verification_code: verificationCode }),
|
|
35
36
|
});
|
|
36
37
|
if (!result.ok) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
code: result.json?.code || 'IDP_ERROR',
|
|
41
|
-
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
42
|
-
}, { status: result.status });
|
|
38
|
+
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
39
|
+
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
40
|
+
return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'sms');
|
|
43
41
|
}
|
|
44
42
|
// Unwrap IDP envelope
|
|
45
43
|
const unwrappedData = result.json?.data || result.json;
|
|
@@ -48,6 +48,7 @@ const auth_1 = require("../../server/auth");
|
|
|
48
48
|
const startup_init_1 = require("../../lib/startup-init");
|
|
49
49
|
const client_key_1 = require("../../lib/client-key");
|
|
50
50
|
const roles_1 = require("../../lib/roles");
|
|
51
|
+
const crypto_1 = require("crypto");
|
|
51
52
|
async function checkAdminRole(request) {
|
|
52
53
|
const session = await (0, auth_1.getSession)(request);
|
|
53
54
|
if (!session?.user) {
|
|
@@ -67,6 +68,9 @@ async function checkAdminRole(request) {
|
|
|
67
68
|
return { isAdmin: true };
|
|
68
69
|
}
|
|
69
70
|
async function vibeServiceRequest(endpoint, options) {
|
|
71
|
+
// PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
|
|
72
|
+
// exception text. Failures return a fixed message + correlationId; the same id is on the
|
|
73
|
+
// server log line, which carries status + bodyLength only (PAY-1424).
|
|
70
74
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
71
75
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
72
76
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -105,13 +109,17 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
105
109
|
return { ok: true, status: 204, data: null };
|
|
106
110
|
if (!res.ok) {
|
|
107
111
|
const errorText = await res.text();
|
|
108
|
-
|
|
112
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
113
|
+
console.error(`[admin/analytics] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
114
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
109
115
|
}
|
|
110
116
|
const body = await res.json();
|
|
111
117
|
return { ok: true, status: res.status, data: body };
|
|
112
118
|
}
|
|
113
119
|
catch (error) {
|
|
114
|
-
|
|
120
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
121
|
+
console.error(`[admin/analytics] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
122
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
function getCountryFlag(countryCode) {
|
|
@@ -175,7 +183,7 @@ function createAnalyticsHandler(config) {
|
|
|
175
183
|
async function getGeoAnalytics(startDate) {
|
|
176
184
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 5000 } });
|
|
177
185
|
if (!result.ok) {
|
|
178
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
186
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
179
187
|
}
|
|
180
188
|
const sessions = result.data?.data || result.data?.documents || [];
|
|
181
189
|
const recentSessions = sessions.filter((s) => new Date(s.created_at) >= startDate);
|
|
@@ -216,7 +224,7 @@ async function getGeoAnalytics(startDate) {
|
|
|
216
224
|
async function getLoginAnalytics(startDate) {
|
|
217
225
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 5000 } });
|
|
218
226
|
if (!result.ok) {
|
|
219
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
227
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
220
228
|
}
|
|
221
229
|
const sessions = result.data?.data || result.data?.documents || [];
|
|
222
230
|
const recentSessions = sessions.filter((s) => new Date(s.created_at) >= startDate);
|
|
@@ -47,6 +47,7 @@ const auth_1 = require("../../server/auth");
|
|
|
47
47
|
const startup_init_1 = require("../../lib/startup-init");
|
|
48
48
|
const client_key_1 = require("../../lib/client-key");
|
|
49
49
|
const roles_1 = require("../../lib/roles");
|
|
50
|
+
const crypto_1 = require("crypto");
|
|
50
51
|
async function checkAdminRole(request) {
|
|
51
52
|
const session = await (0, auth_1.getSession)(request);
|
|
52
53
|
if (!session?.user) {
|
|
@@ -66,6 +67,9 @@ async function checkAdminRole(request) {
|
|
|
66
67
|
return { isAdmin: true };
|
|
67
68
|
}
|
|
68
69
|
async function vibeServiceRequest(endpoint, options) {
|
|
70
|
+
// PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
|
|
71
|
+
// exception text. Failures return a fixed message + correlationId; the same id is on the
|
|
72
|
+
// server log line, which carries status + bodyLength only (PAY-1424).
|
|
69
73
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
70
74
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
71
75
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -104,13 +108,17 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
104
108
|
return { ok: true, status: 204, data: null };
|
|
105
109
|
if (!res.ok) {
|
|
106
110
|
const errorText = await res.text();
|
|
107
|
-
|
|
111
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
112
|
+
console.error(`[admin/audit] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
113
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
108
114
|
}
|
|
109
115
|
const body = await res.json();
|
|
110
116
|
return { ok: true, status: res.status, data: body };
|
|
111
117
|
}
|
|
112
118
|
catch (error) {
|
|
113
|
-
|
|
119
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
120
|
+
console.error(`[admin/audit] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
121
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
/**
|
|
@@ -157,7 +165,7 @@ function createAuditHandler(config) {
|
|
|
157
165
|
}
|
|
158
166
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/audit_logs/query', { method: 'POST', body: queryBody });
|
|
159
167
|
if (!result.ok) {
|
|
160
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
168
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
161
169
|
}
|
|
162
170
|
const rawLogs = result.data?.data || result.data?.documents || [];
|
|
163
171
|
const logs = rawLogs.map((log) => ({
|
|
@@ -186,7 +194,7 @@ function createAuditHandler(config) {
|
|
|
186
194
|
if (action === 'stats') {
|
|
187
195
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/audit_logs/query', { method: 'POST', body: { page: 1, pageSize: 10000 } });
|
|
188
196
|
if (!result.ok) {
|
|
189
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
197
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
190
198
|
}
|
|
191
199
|
const logs = result.data?.data || result.data?.documents || [];
|
|
192
200
|
const now = new Date();
|
|
@@ -18,6 +18,8 @@ const server_1 = require("next/server");
|
|
|
18
18
|
const auth_1 = require("../../server/auth");
|
|
19
19
|
const redis_1 = require("../../lib/redis");
|
|
20
20
|
const roles_1 = require("../../lib/roles");
|
|
21
|
+
const crypto_1 = require("crypto");
|
|
22
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
21
23
|
/**
|
|
22
24
|
* Check if the current user has admin role
|
|
23
25
|
*/
|
|
@@ -76,7 +78,7 @@ function createRedisSessionsHandler(config) {
|
|
|
76
78
|
const sessionToken = key.replace(sessionPrefix, '');
|
|
77
79
|
sessions.push({
|
|
78
80
|
id: sessionToken.substring(0, 16), // Truncated for display
|
|
79
|
-
session_token:
|
|
81
|
+
session_token: (0, token_log_id_1.tokenLogId)(sessionToken), // Masked
|
|
80
82
|
user_id: sessionData.userId,
|
|
81
83
|
user_name: sessionData.name,
|
|
82
84
|
user_email: sessionData.email,
|
|
@@ -112,8 +114,10 @@ function createRedisSessionsHandler(config) {
|
|
|
112
114
|
});
|
|
113
115
|
}
|
|
114
116
|
catch (error) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
// PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
|
|
118
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
119
|
+
console.error('[admin/redis-sessions] Error:', { correlationId, error });
|
|
120
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
117
121
|
}
|
|
118
122
|
},
|
|
119
123
|
async DELETE(request) {
|
|
@@ -137,12 +141,14 @@ function createRedisSessionsHandler(config) {
|
|
|
137
141
|
return server_1.NextResponse.json({
|
|
138
142
|
success: true,
|
|
139
143
|
message: 'Session revoked',
|
|
140
|
-
session_token:
|
|
144
|
+
session_token: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
141
145
|
});
|
|
142
146
|
}
|
|
143
147
|
catch (error) {
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
// PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
|
|
149
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
150
|
+
console.error('[admin/redis-sessions] DELETE Error:', { correlationId, error });
|
|
151
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
146
152
|
}
|
|
147
153
|
},
|
|
148
154
|
};
|
|
@@ -195,8 +201,10 @@ function createRedisSessionRevokeHandler(config) {
|
|
|
195
201
|
});
|
|
196
202
|
}
|
|
197
203
|
catch (error) {
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
// PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
|
|
205
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
206
|
+
console.error('[admin/redis-sessions/revoke] Error:', { correlationId, error });
|
|
207
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
200
208
|
}
|
|
201
209
|
},
|
|
202
210
|
};
|
|
@@ -48,6 +48,7 @@ const auth_1 = require("../../server/auth");
|
|
|
48
48
|
const startup_init_1 = require("../../lib/startup-init");
|
|
49
49
|
const client_key_1 = require("../../lib/client-key");
|
|
50
50
|
const roles_1 = require("../../lib/roles");
|
|
51
|
+
const crypto_1 = require("crypto");
|
|
51
52
|
/**
|
|
52
53
|
* Check if the current user has admin role
|
|
53
54
|
*/
|
|
@@ -73,6 +74,9 @@ async function checkAdminRole(request) {
|
|
|
73
74
|
* Make a service account request to Vibe (admin mode)
|
|
74
75
|
*/
|
|
75
76
|
async function vibeServiceRequest(endpoint, options) {
|
|
77
|
+
// PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
|
|
78
|
+
// exception text. Failures return a fixed message + correlationId; the same id is on the
|
|
79
|
+
// server log line, which carries status + bodyLength only (PAY-1424).
|
|
76
80
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
77
81
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
78
82
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -112,13 +116,17 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
112
116
|
}
|
|
113
117
|
if (!res.ok) {
|
|
114
118
|
const errorText = await res.text();
|
|
115
|
-
|
|
119
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
120
|
+
console.error(`[admin/sessions] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
121
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
116
122
|
}
|
|
117
123
|
const body = await res.json();
|
|
118
124
|
return { ok: true, status: res.status, data: body };
|
|
119
125
|
}
|
|
120
126
|
catch (error) {
|
|
121
|
-
|
|
127
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
128
|
+
console.error(`[admin/sessions] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
129
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
122
130
|
}
|
|
123
131
|
}
|
|
124
132
|
// Country code to flag emoji mapping
|
|
@@ -167,7 +175,7 @@ function createSessionsHandler(config) {
|
|
|
167
175
|
}
|
|
168
176
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: queryBody });
|
|
169
177
|
if (!result.ok) {
|
|
170
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
178
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
171
179
|
}
|
|
172
180
|
// Extract sessions from response
|
|
173
181
|
const rawSessions = result.data?.data || result.data?.documents || [];
|
|
@@ -202,7 +210,7 @@ function createSessionsHandler(config) {
|
|
|
202
210
|
// Get all sessions for stats calculation
|
|
203
211
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 1000 } });
|
|
204
212
|
if (!result.ok) {
|
|
205
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
213
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
206
214
|
}
|
|
207
215
|
const sessions = result.data?.data || result.data?.documents || [];
|
|
208
216
|
// Calculate stats
|
|
@@ -239,7 +247,7 @@ function createSessionsHandler(config) {
|
|
|
239
247
|
},
|
|
240
248
|
});
|
|
241
249
|
if (!result.ok) {
|
|
242
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
250
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
243
251
|
}
|
|
244
252
|
return server_1.NextResponse.json({ success: true });
|
|
245
253
|
}
|
|
@@ -258,7 +266,7 @@ function createSessionsHandler(config) {
|
|
|
258
266
|
},
|
|
259
267
|
});
|
|
260
268
|
if (!queryResult.ok) {
|
|
261
|
-
return server_1.NextResponse.json({ error: queryResult.error }, { status: queryResult.status || 500 });
|
|
269
|
+
return server_1.NextResponse.json({ error: queryResult.error, code: 'UPSTREAM_ERROR', correlationId: queryResult.correlationId }, { status: queryResult.status || 500 });
|
|
262
270
|
}
|
|
263
271
|
const sessions = queryResult.data?.data || queryResult.data?.documents || [];
|
|
264
272
|
let revokedCount = 0;
|
|
@@ -21,6 +21,7 @@ const server_1 = require("next/server");
|
|
|
21
21
|
const auth_1 = require("../../server/auth");
|
|
22
22
|
const redis_1 = require("../../lib/redis");
|
|
23
23
|
const roles_1 = require("../../lib/roles");
|
|
24
|
+
const crypto_1 = require("crypto");
|
|
24
25
|
const REDIS_SITE_LOG_KEY = 'vibe:site-logs:pending';
|
|
25
26
|
const REDIS_LOG_TTL = 7 * 24 * 60 * 60; // 1 week
|
|
26
27
|
/**
|
|
@@ -99,15 +100,21 @@ function createSiteLogsHandler(config) {
|
|
|
99
100
|
const response = await fetch(`${vibeUrl}/v1/admin/site-logs?${params}`, { headers });
|
|
100
101
|
if (!response.ok) {
|
|
101
102
|
const errorText = await response.text();
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
// PAY-1424: never the raw upstream body in the log sink.
|
|
104
|
+
// PAY-1425: nor in the response - a fixed message + code, and a correlationId that
|
|
105
|
+
// matches this log line.
|
|
106
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
107
|
+
console.error('[admin/site-logs] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
|
|
108
|
+
return server_1.NextResponse.json({ error: 'Failed to fetch site logs', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
|
|
104
109
|
}
|
|
105
110
|
const data = await response.json();
|
|
106
111
|
return server_1.NextResponse.json(data);
|
|
107
112
|
}
|
|
108
113
|
catch (error) {
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
// PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
|
|
115
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
116
|
+
console.error('[admin/site-logs] Error:', { correlationId, error });
|
|
117
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
111
118
|
}
|
|
112
119
|
},
|
|
113
120
|
async POST(request) {
|
|
@@ -172,8 +179,10 @@ function createSiteLogsHandler(config) {
|
|
|
172
179
|
}, { status: 201 });
|
|
173
180
|
}
|
|
174
181
|
catch (error) {
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
// PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
|
|
183
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
184
|
+
console.error('[admin/site-logs] POST Error:', { correlationId, error });
|
|
185
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
177
186
|
}
|
|
178
187
|
},
|
|
179
188
|
};
|
|
@@ -206,16 +215,22 @@ function createSiteLogsStatsHandler(config) {
|
|
|
206
215
|
const response = await fetch(`${vibeUrl}/v1/admin/site-logs/stats?${params}`, { headers });
|
|
207
216
|
if (!response.ok) {
|
|
208
217
|
const errorText = await response.text();
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
// PAY-1424: never the raw upstream body in the log sink.
|
|
219
|
+
// PAY-1425: nor in the response - a fixed message + code, and a correlationId that
|
|
220
|
+
// matches this log line.
|
|
221
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
222
|
+
console.error('[admin/site-logs/stats] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
|
|
223
|
+
return server_1.NextResponse.json({ error: 'Failed to fetch stats', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
|
|
211
224
|
}
|
|
212
225
|
const data = await response.json();
|
|
213
226
|
// Ensure response matches expected format: { total, unique_users, by_level, by_category }
|
|
214
227
|
return server_1.NextResponse.json(data);
|
|
215
228
|
}
|
|
216
229
|
catch (error) {
|
|
217
|
-
|
|
218
|
-
|
|
230
|
+
// PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
|
|
231
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
232
|
+
console.error('[admin/site-logs/stats] Error:', { correlationId, error });
|
|
233
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
219
234
|
}
|
|
220
235
|
},
|
|
221
236
|
};
|
|
@@ -246,15 +261,21 @@ function createSiteLogsDrainHandler(config) {
|
|
|
246
261
|
});
|
|
247
262
|
if (!response.ok) {
|
|
248
263
|
const errorText = await response.text();
|
|
249
|
-
|
|
250
|
-
|
|
264
|
+
// PAY-1424: never the raw upstream body in the log sink.
|
|
265
|
+
// PAY-1425: nor in the response - a fixed message + code, and a correlationId that
|
|
266
|
+
// matches this log line.
|
|
267
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
268
|
+
console.error('[admin/site-logs/drain] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
|
|
269
|
+
return server_1.NextResponse.json({ error: 'Failed to drain site logs', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
|
|
251
270
|
}
|
|
252
271
|
const data = await response.json();
|
|
253
272
|
return server_1.NextResponse.json(data);
|
|
254
273
|
}
|
|
255
274
|
catch (error) {
|
|
256
|
-
|
|
257
|
-
|
|
275
|
+
// PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
|
|
276
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
277
|
+
console.error('[admin/site-logs/drain] Error:', { correlationId, error });
|
|
278
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
258
279
|
}
|
|
259
280
|
},
|
|
260
281
|
};
|
|
@@ -309,8 +330,10 @@ function createSiteLogsQueueHandler(config) {
|
|
|
309
330
|
});
|
|
310
331
|
}
|
|
311
332
|
catch (error) {
|
|
312
|
-
|
|
313
|
-
|
|
333
|
+
// PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
|
|
334
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
335
|
+
console.error('[admin/site-logs/queue] Error:', { correlationId, error });
|
|
336
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
314
337
|
}
|
|
315
338
|
},
|
|
316
339
|
};
|
|
@@ -49,6 +49,7 @@ const startup_init_1 = require("../../lib/startup-init");
|
|
|
49
49
|
const client_key_1 = require("../../lib/client-key");
|
|
50
50
|
const redis_1 = require("../../lib/redis");
|
|
51
51
|
const roles_1 = require("../../lib/roles");
|
|
52
|
+
const crypto_1 = require("crypto");
|
|
52
53
|
async function checkAdminRole(request) {
|
|
53
54
|
const session = await (0, auth_1.getSession)(request);
|
|
54
55
|
if (!session?.user) {
|
|
@@ -68,6 +69,9 @@ async function checkAdminRole(request) {
|
|
|
68
69
|
return { isAdmin: true };
|
|
69
70
|
}
|
|
70
71
|
async function vibeServiceRequest(endpoint, options) {
|
|
72
|
+
// PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
|
|
73
|
+
// exception text. Failures return a fixed message + correlationId; the same id is on the
|
|
74
|
+
// server log line, which carries status + bodyLength only (PAY-1424).
|
|
71
75
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
72
76
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
73
77
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -105,13 +109,17 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
105
109
|
return { ok: true, status: 204, data: null };
|
|
106
110
|
if (!res.ok) {
|
|
107
111
|
const errorText = await res.text();
|
|
108
|
-
|
|
112
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
113
|
+
console.error(`[admin/stats] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
114
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
109
115
|
}
|
|
110
116
|
const body = await res.json();
|
|
111
117
|
return { ok: true, status: res.status, data: body };
|
|
112
118
|
}
|
|
113
119
|
catch (error) {
|
|
114
|
-
|
|
120
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
121
|
+
console.error(`[admin/stats] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
122
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
/**
|
|
@@ -242,8 +250,10 @@ function createStatsHandler(config) {
|
|
|
242
250
|
});
|
|
243
251
|
}
|
|
244
252
|
catch (error) {
|
|
245
|
-
|
|
246
|
-
|
|
253
|
+
// PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
|
|
254
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
255
|
+
console.error('[admin/stats] Error:', { correlationId, error });
|
|
256
|
+
return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
247
257
|
}
|
|
248
258
|
},
|
|
249
259
|
};
|
|
@@ -47,6 +47,7 @@ const auth_1 = require("../../server/auth");
|
|
|
47
47
|
const startup_init_1 = require("../../lib/startup-init");
|
|
48
48
|
const client_key_1 = require("../../lib/client-key");
|
|
49
49
|
const roles_1 = require("../../lib/roles");
|
|
50
|
+
const crypto_1 = require("crypto");
|
|
50
51
|
async function checkAdminRole(request) {
|
|
51
52
|
const session = await (0, auth_1.getSession)(request);
|
|
52
53
|
if (!session?.user) {
|
|
@@ -66,6 +67,9 @@ async function checkAdminRole(request) {
|
|
|
66
67
|
return { isAdmin: true };
|
|
67
68
|
}
|
|
68
69
|
async function vibeServiceRequest(endpoint, options) {
|
|
70
|
+
// PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
|
|
71
|
+
// exception text. Failures return a fixed message + correlationId; the same id is on the
|
|
72
|
+
// server log line, which carries status + bodyLength only (PAY-1424).
|
|
69
73
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
70
74
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
71
75
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -104,13 +108,17 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
104
108
|
return { ok: true, status: 204, data: null };
|
|
105
109
|
if (!res.ok) {
|
|
106
110
|
const errorText = await res.text();
|
|
107
|
-
|
|
111
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
112
|
+
console.error(`[admin/users] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
113
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
108
114
|
}
|
|
109
115
|
const body = await res.json();
|
|
110
116
|
return { ok: true, status: res.status, data: body };
|
|
111
117
|
}
|
|
112
118
|
catch (error) {
|
|
113
|
-
|
|
119
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
120
|
+
console.error(`[admin/users] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
121
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
/**
|
|
@@ -155,7 +163,7 @@ function createUsersHandler(config) {
|
|
|
155
163
|
}
|
|
156
164
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/users/query', { method: 'POST', body: queryBody });
|
|
157
165
|
if (!result.ok) {
|
|
158
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
166
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
159
167
|
}
|
|
160
168
|
const rawUsers = result.data?.data || result.data?.documents || [];
|
|
161
169
|
const users = rawUsers.map((u) => ({
|
|
@@ -183,7 +191,7 @@ function createUsersHandler(config) {
|
|
|
183
191
|
if (action === 'stats') {
|
|
184
192
|
const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/users/query', { method: 'POST', body: { page: 1, pageSize: 10000 } });
|
|
185
193
|
if (!result.ok) {
|
|
186
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
194
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
187
195
|
}
|
|
188
196
|
const users = result.data?.data || result.data?.documents || [];
|
|
189
197
|
const now = new Date();
|
|
@@ -205,14 +213,14 @@ function createUsersHandler(config) {
|
|
|
205
213
|
if (action === 'update_tier' && userId && tier) {
|
|
206
214
|
const result = await vibeServiceRequest(`/v1/collections/vibe_app/tables/users/${userId}`, { method: 'PUT', body: { tier } });
|
|
207
215
|
if (!result.ok) {
|
|
208
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
216
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
209
217
|
}
|
|
210
218
|
return server_1.NextResponse.json({ success: true });
|
|
211
219
|
}
|
|
212
220
|
if (action === 'update_credits' && userId && credits !== undefined) {
|
|
213
221
|
const result = await vibeServiceRequest(`/v1/collections/vibe_app/tables/users/${userId}`, { method: 'PUT', body: { credits } });
|
|
214
222
|
if (!result.ok) {
|
|
215
|
-
return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
|
|
223
|
+
return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
|
|
216
224
|
}
|
|
217
225
|
return server_1.NextResponse.json({ success: true });
|
|
218
226
|
}
|