@payez/next-mvp 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-handlers/account/change-password.js +110 -110
- package/dist/api-handlers/admin/analytics.d.ts +19 -19
- package/dist/api-handlers/admin/analytics.js +378 -378
- package/dist/api-handlers/admin/audit.d.ts +19 -19
- package/dist/api-handlers/admin/audit.js +213 -213
- package/dist/api-handlers/admin/index.d.ts +21 -21
- package/dist/api-handlers/admin/index.js +42 -42
- package/dist/api-handlers/admin/redis-sessions.d.ts +35 -35
- package/dist/api-handlers/admin/redis-sessions.js +203 -203
- package/dist/api-handlers/admin/sessions.d.ts +20 -20
- package/dist/api-handlers/admin/sessions.js +283 -283
- package/dist/api-handlers/admin/site-logs.d.ts +45 -45
- package/dist/api-handlers/admin/site-logs.js +317 -317
- package/dist/api-handlers/admin/stats.d.ts +20 -20
- package/dist/api-handlers/admin/stats.js +239 -239
- package/dist/api-handlers/admin/users.d.ts +19 -19
- package/dist/api-handlers/admin/users.js +221 -221
- package/dist/api-handlers/admin/vibe-data.d.ts +79 -79
- package/dist/api-handlers/admin/vibe-data.js +267 -267
- package/dist/api-handlers/auth/refresh.js +633 -633
- package/dist/api-handlers/auth/signout.js +186 -186
- package/dist/api-handlers/auth/verify-code.d.ts +43 -43
- package/dist/api-handlers/auth/verify-code.js +90 -90
- package/dist/api-handlers/session/viability.js +114 -114
- package/dist/api-handlers/test/force-expire.js +59 -59
- package/dist/auth/auth-decision.js +182 -182
- package/dist/auth/utils/token-utils.d.ts +83 -83
- package/dist/auth/utils/token-utils.js +218 -218
- package/dist/client/AuthContext.js +115 -115
- package/dist/client/better-auth-client.d.ts +1020 -1020
- package/dist/components/SessionSync.js +121 -121
- package/dist/components/account/MobileNavDrawer.js +64 -64
- package/dist/components/account/UserAvatarMenu.js +91 -91
- package/dist/components/admin/VibeAdminLayout.js +71 -71
- package/dist/hooks/useAuthSettings.js +93 -93
- package/dist/hooks/useAvailableProviders.d.ts +43 -43
- package/dist/hooks/useAvailableProviders.js +112 -112
- package/dist/lib/app-slug.d.ts +95 -95
- package/dist/lib/app-slug.js +172 -172
- package/dist/lib/test-aware-get-token.js +86 -86
- package/dist/lib/token-lifecycle.d.ts +78 -78
- package/dist/lib/token-lifecycle.js +360 -360
- package/dist/pages/admin-login/page.js +73 -73
- package/dist/pages/client-admin/ClientSiteAdminPage.js +179 -179
- package/dist/pages/login/page.js +202 -202
- package/dist/pages/showcase/ShowcasePage.js +142 -142
- package/dist/pages/test-env/EmergencyLogoutPage.js +99 -99
- package/dist/pages/test-env/JwtInspectPage.js +116 -116
- package/dist/pages/test-env/TestEnvPage.js +51 -51
- package/dist/pages/verify-code/page.js +412 -412
- package/dist/routes/auth/logout.d.ts +31 -31
- package/dist/routes/auth/logout.js +98 -98
- package/dist/routes/auth/session.js +157 -157
- package/dist/routes/auth/viability.js +190 -190
- package/package.json +6 -16
- package/dist/auth/auth-options.d.ts +0 -57
- package/dist/auth/auth-options.js +0 -213
- package/dist/auth/callbacks/index.d.ts +0 -6
- package/dist/auth/callbacks/index.js +0 -12
- package/dist/auth/callbacks/jwt.d.ts +0 -45
- package/dist/auth/callbacks/jwt.js +0 -305
- package/dist/auth/callbacks/session.d.ts +0 -60
- package/dist/auth/callbacks/session.js +0 -170
- package/dist/auth/callbacks/signin.d.ts +0 -23
- package/dist/auth/callbacks/signin.js +0 -44
- package/dist/auth/events/index.d.ts +0 -4
- package/dist/auth/events/index.js +0 -8
- package/dist/auth/events/signout.d.ts +0 -17
- package/dist/auth/events/signout.js +0 -32
- package/dist/auth/providers/credentials.d.ts +0 -32
- package/dist/auth/providers/credentials.js +0 -223
- package/dist/auth/providers/index.d.ts +0 -5
- package/dist/auth/providers/index.js +0 -21
- package/dist/auth/providers/oauth.d.ts +0 -26
- package/dist/auth/providers/oauth.js +0 -105
- package/dist/lib/nextauth-secret.d.ts +0 -10
- package/dist/lib/nextauth-secret.js +0 -100
- package/dist/pages/profile/profile-patch.d.ts +0 -1
- package/dist/pages/profile/profile-patch.js +0 -281
- package/dist/pages/security/security-patch.d.ts +0 -1
- package/dist/pages/security/security-patch.js +0 -302
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin Redis Sessions API Handler
|
|
3
|
-
*
|
|
4
|
-
* Provides admin-level access to active sessions stored in Redis.
|
|
5
|
-
* Reads directly from Redis {APP_SLUG}:sess:* pattern.
|
|
6
|
-
*
|
|
7
|
-
* Note: This is different from sessions.ts which queries Vibe login_sessions table.
|
|
8
|
-
* This handler shows real-time active sessions in Redis.
|
|
9
|
-
*
|
|
10
|
-
* @version 1.0
|
|
11
|
-
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
12
|
-
*/
|
|
13
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
14
|
-
export interface RedisSessionsHandlerConfig {
|
|
15
|
-
appSlug?: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Create Redis sessions handler
|
|
19
|
-
* GET /api/admin/redis-sessions - List all active sessions from Redis
|
|
20
|
-
*/
|
|
21
|
-
export declare function createRedisSessionsHandler(config: RedisSessionsHandlerConfig): {
|
|
22
|
-
GET(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
23
|
-
DELETE(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Create Redis session revoke handler for specific session
|
|
27
|
-
* POST /api/admin/redis-sessions/[sessionId]/revoke
|
|
28
|
-
*/
|
|
29
|
-
export declare function createRedisSessionRevokeHandler(config: RedisSessionsHandlerConfig): {
|
|
30
|
-
POST(request: NextRequest, { params }: {
|
|
31
|
-
params: {
|
|
32
|
-
sessionId: string;
|
|
33
|
-
};
|
|
34
|
-
}): Promise<NextResponse<unknown>>;
|
|
35
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Admin Redis Sessions API Handler
|
|
3
|
+
*
|
|
4
|
+
* Provides admin-level access to active sessions stored in Redis.
|
|
5
|
+
* Reads directly from Redis {APP_SLUG}:sess:* pattern.
|
|
6
|
+
*
|
|
7
|
+
* Note: This is different from sessions.ts which queries Vibe login_sessions table.
|
|
8
|
+
* This handler shows real-time active sessions in Redis.
|
|
9
|
+
*
|
|
10
|
+
* @version 1.0
|
|
11
|
+
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
12
|
+
*/
|
|
13
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
14
|
+
export interface RedisSessionsHandlerConfig {
|
|
15
|
+
appSlug?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create Redis sessions handler
|
|
19
|
+
* GET /api/admin/redis-sessions - List all active sessions from Redis
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRedisSessionsHandler(config: RedisSessionsHandlerConfig): {
|
|
22
|
+
GET(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
23
|
+
DELETE(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Create Redis session revoke handler for specific session
|
|
27
|
+
* POST /api/admin/redis-sessions/[sessionId]/revoke
|
|
28
|
+
*/
|
|
29
|
+
export declare function createRedisSessionRevokeHandler(config: RedisSessionsHandlerConfig): {
|
|
30
|
+
POST(request: NextRequest, { params }: {
|
|
31
|
+
params: {
|
|
32
|
+
sessionId: string;
|
|
33
|
+
};
|
|
34
|
+
}): Promise<NextResponse<unknown>>;
|
|
35
|
+
};
|
|
@@ -1,203 +1,203 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Admin Redis Sessions API Handler
|
|
4
|
-
*
|
|
5
|
-
* Provides admin-level access to active sessions stored in Redis.
|
|
6
|
-
* Reads directly from Redis {APP_SLUG}:sess:* pattern.
|
|
7
|
-
*
|
|
8
|
-
* Note: This is different from sessions.ts which queries Vibe login_sessions table.
|
|
9
|
-
* This handler shows real-time active sessions in Redis.
|
|
10
|
-
*
|
|
11
|
-
* @version 1.0
|
|
12
|
-
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
13
|
-
*/
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.createRedisSessionsHandler = createRedisSessionsHandler;
|
|
16
|
-
exports.createRedisSessionRevokeHandler = createRedisSessionRevokeHandler;
|
|
17
|
-
const server_1 = require("next/server");
|
|
18
|
-
const auth_1 = require("../../server/auth");
|
|
19
|
-
const redis_1 = require("../../lib/redis");
|
|
20
|
-
const roles_1 = require("../../lib/roles");
|
|
21
|
-
/**
|
|
22
|
-
* Check if the current user has admin role
|
|
23
|
-
*/
|
|
24
|
-
async function checkAdminRole(request) {
|
|
25
|
-
const session = await (0, auth_1.getSession)(request);
|
|
26
|
-
if (!session?.user) {
|
|
27
|
-
return {
|
|
28
|
-
isAdmin: false,
|
|
29
|
-
error: server_1.NextResponse.json({ success: false, error: 'Please sign in' }, { status: 401 }),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
const userRoles = session.user?.roles || [];
|
|
33
|
-
const hasAdminRole = roles_1.ADMIN_ROLES.some(role => userRoles.includes(role));
|
|
34
|
-
if (!hasAdminRole) {
|
|
35
|
-
return {
|
|
36
|
-
isAdmin: false,
|
|
37
|
-
error: server_1.NextResponse.json({ success: false, error: 'Admin access required' }, { status: 403 }),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return { isAdmin: true, userId: session.user?.id };
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Create Redis sessions handler
|
|
44
|
-
* GET /api/admin/redis-sessions - List all active sessions from Redis
|
|
45
|
-
*/
|
|
46
|
-
function createRedisSessionsHandler(config) {
|
|
47
|
-
const getSessionPrefix = () => {
|
|
48
|
-
const appSlug = config.appSlug || process.env.APP_SLUG || process.env.CLIENT_ID || 'app';
|
|
49
|
-
return `${appSlug}:sess:`;
|
|
50
|
-
};
|
|
51
|
-
return {
|
|
52
|
-
async GET(request) {
|
|
53
|
-
const adminCheck = await checkAdminRole(request);
|
|
54
|
-
if (adminCheck.error)
|
|
55
|
-
return adminCheck.error;
|
|
56
|
-
try {
|
|
57
|
-
const redis = (0, redis_1.getRedis)();
|
|
58
|
-
const sessionPrefix = getSessionPrefix();
|
|
59
|
-
// Scan Redis for all session keys (exclude version keys)
|
|
60
|
-
const sessionKeys = [];
|
|
61
|
-
let cursor = '0';
|
|
62
|
-
do {
|
|
63
|
-
const [newCursor, keys] = await redis.scan(cursor, 'MATCH', `${sessionPrefix}*`, 'COUNT', 100);
|
|
64
|
-
cursor = newCursor;
|
|
65
|
-
// Filter out version keys (ver:) - only want actual session keys
|
|
66
|
-
sessionKeys.push(...keys.filter((k) => !k.includes(':ver:')));
|
|
67
|
-
} while (cursor !== '0');
|
|
68
|
-
// Fetch all session data
|
|
69
|
-
const sessions = [];
|
|
70
|
-
for (const key of sessionKeys) {
|
|
71
|
-
const sessionJson = await redis.get(key);
|
|
72
|
-
if (!sessionJson)
|
|
73
|
-
continue;
|
|
74
|
-
try {
|
|
75
|
-
const sessionData = JSON.parse(sessionJson);
|
|
76
|
-
const sessionToken = key.replace(sessionPrefix, '');
|
|
77
|
-
sessions.push({
|
|
78
|
-
id: sessionToken.substring(0, 16), // Truncated for display
|
|
79
|
-
session_token: sessionToken.substring(0, 8) + '...', // Masked
|
|
80
|
-
user_id: sessionData.userId,
|
|
81
|
-
user_name: sessionData.name,
|
|
82
|
-
user_email: sessionData.email,
|
|
83
|
-
status: 'active', // Sessions in Redis are active
|
|
84
|
-
created_at: sessionData.createdAt,
|
|
85
|
-
mfa_verified: sessionData.mfaVerified || sessionData.twoFactorComplete || false,
|
|
86
|
-
access_token_expires: sessionData.idpAccessTokenExpires
|
|
87
|
-
? new Date(sessionData.idpAccessTokenExpires).toISOString()
|
|
88
|
-
: null,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
catch (parseError) {
|
|
92
|
-
console.error('[admin/redis-sessions] Failed to parse session:', key, parseError);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Sort by most recent first (if created_at available)
|
|
96
|
-
sessions.sort((a, b) => {
|
|
97
|
-
if (!a.created_at || !b.created_at)
|
|
98
|
-
return 0;
|
|
99
|
-
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
100
|
-
});
|
|
101
|
-
// Calculate stats
|
|
102
|
-
const uniqueUsers = new Set(sessions.map(s => s.user_email).filter(Boolean)).size;
|
|
103
|
-
return server_1.NextResponse.json({
|
|
104
|
-
sessions,
|
|
105
|
-
total: sessions.length,
|
|
106
|
-
stats: {
|
|
107
|
-
total_active: sessions.length,
|
|
108
|
-
total_revoked: 0, // Revoked sessions are deleted from Redis
|
|
109
|
-
unique_users: uniqueUsers,
|
|
110
|
-
},
|
|
111
|
-
redis_prefix: sessionPrefix,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
console.error('[admin/redis-sessions] Error:', error);
|
|
116
|
-
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
async DELETE(request) {
|
|
120
|
-
const adminCheck = await checkAdminRole(request);
|
|
121
|
-
if (adminCheck.error)
|
|
122
|
-
return adminCheck.error;
|
|
123
|
-
try {
|
|
124
|
-
const { searchParams } = new URL(request.url);
|
|
125
|
-
const sessionToken = searchParams.get('session_token');
|
|
126
|
-
if (!sessionToken) {
|
|
127
|
-
return server_1.NextResponse.json({ error: 'session_token parameter required' }, { status: 400 });
|
|
128
|
-
}
|
|
129
|
-
const redis = (0, redis_1.getRedis)();
|
|
130
|
-
const sessionPrefix = getSessionPrefix();
|
|
131
|
-
const sessionKey = `${sessionPrefix}${sessionToken}`;
|
|
132
|
-
// Delete the session
|
|
133
|
-
const deleted = await redis.del(sessionKey);
|
|
134
|
-
if (deleted === 0) {
|
|
135
|
-
return server_1.NextResponse.json({ error: 'Session not found or already deleted' }, { status: 404 });
|
|
136
|
-
}
|
|
137
|
-
return server_1.NextResponse.json({
|
|
138
|
-
success: true,
|
|
139
|
-
message: 'Session revoked',
|
|
140
|
-
session_token: sessionToken.substring(0, 8) + '...',
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
console.error('[admin/redis-sessions] DELETE Error:', error);
|
|
145
|
-
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Create Redis session revoke handler for specific session
|
|
152
|
-
* POST /api/admin/redis-sessions/[sessionId]/revoke
|
|
153
|
-
*/
|
|
154
|
-
function createRedisSessionRevokeHandler(config) {
|
|
155
|
-
const getSessionPrefix = () => {
|
|
156
|
-
const appSlug = config.appSlug || process.env.APP_SLUG || process.env.CLIENT_ID || 'app';
|
|
157
|
-
return `${appSlug}:sess:`;
|
|
158
|
-
};
|
|
159
|
-
return {
|
|
160
|
-
async POST(request, { params }) {
|
|
161
|
-
const adminCheck = await checkAdminRole(request);
|
|
162
|
-
if (adminCheck.error)
|
|
163
|
-
return adminCheck.error;
|
|
164
|
-
try {
|
|
165
|
-
const sessionId = params.sessionId;
|
|
166
|
-
if (!sessionId) {
|
|
167
|
-
return server_1.NextResponse.json({ error: 'Session ID required' }, { status: 400 });
|
|
168
|
-
}
|
|
169
|
-
const redis = (0, redis_1.getRedis)();
|
|
170
|
-
const sessionPrefix = getSessionPrefix();
|
|
171
|
-
// Find session key that starts with the sessionId
|
|
172
|
-
let targetKey = null;
|
|
173
|
-
let cursor = '0';
|
|
174
|
-
do {
|
|
175
|
-
const [newCursor, keys] = await redis.scan(cursor, 'MATCH', `${sessionPrefix}*`, 'COUNT', 100);
|
|
176
|
-
cursor = newCursor;
|
|
177
|
-
for (const key of keys) {
|
|
178
|
-
const token = key.replace(sessionPrefix, '');
|
|
179
|
-
if (token.startsWith(sessionId) || token.substring(0, 16) === sessionId) {
|
|
180
|
-
targetKey = key;
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (targetKey)
|
|
185
|
-
break;
|
|
186
|
-
} while (cursor !== '0');
|
|
187
|
-
if (!targetKey) {
|
|
188
|
-
return server_1.NextResponse.json({ error: 'Session not found' }, { status: 404 });
|
|
189
|
-
}
|
|
190
|
-
// Delete the session
|
|
191
|
-
await redis.del(targetKey);
|
|
192
|
-
return server_1.NextResponse.json({
|
|
193
|
-
success: true,
|
|
194
|
-
message: 'Session revoked',
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
console.error('[admin/redis-sessions/revoke] Error:', error);
|
|
199
|
-
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Admin Redis Sessions API Handler
|
|
4
|
+
*
|
|
5
|
+
* Provides admin-level access to active sessions stored in Redis.
|
|
6
|
+
* Reads directly from Redis {APP_SLUG}:sess:* pattern.
|
|
7
|
+
*
|
|
8
|
+
* Note: This is different from sessions.ts which queries Vibe login_sessions table.
|
|
9
|
+
* This handler shows real-time active sessions in Redis.
|
|
10
|
+
*
|
|
11
|
+
* @version 1.0
|
|
12
|
+
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createRedisSessionsHandler = createRedisSessionsHandler;
|
|
16
|
+
exports.createRedisSessionRevokeHandler = createRedisSessionRevokeHandler;
|
|
17
|
+
const server_1 = require("next/server");
|
|
18
|
+
const auth_1 = require("../../server/auth");
|
|
19
|
+
const redis_1 = require("../../lib/redis");
|
|
20
|
+
const roles_1 = require("../../lib/roles");
|
|
21
|
+
/**
|
|
22
|
+
* Check if the current user has admin role
|
|
23
|
+
*/
|
|
24
|
+
async function checkAdminRole(request) {
|
|
25
|
+
const session = await (0, auth_1.getSession)(request);
|
|
26
|
+
if (!session?.user) {
|
|
27
|
+
return {
|
|
28
|
+
isAdmin: false,
|
|
29
|
+
error: server_1.NextResponse.json({ success: false, error: 'Please sign in' }, { status: 401 }),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const userRoles = session.user?.roles || [];
|
|
33
|
+
const hasAdminRole = roles_1.ADMIN_ROLES.some(role => userRoles.includes(role));
|
|
34
|
+
if (!hasAdminRole) {
|
|
35
|
+
return {
|
|
36
|
+
isAdmin: false,
|
|
37
|
+
error: server_1.NextResponse.json({ success: false, error: 'Admin access required' }, { status: 403 }),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { isAdmin: true, userId: session.user?.id };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create Redis sessions handler
|
|
44
|
+
* GET /api/admin/redis-sessions - List all active sessions from Redis
|
|
45
|
+
*/
|
|
46
|
+
function createRedisSessionsHandler(config) {
|
|
47
|
+
const getSessionPrefix = () => {
|
|
48
|
+
const appSlug = config.appSlug || process.env.APP_SLUG || process.env.CLIENT_ID || 'app';
|
|
49
|
+
return `${appSlug}:sess:`;
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
async GET(request) {
|
|
53
|
+
const adminCheck = await checkAdminRole(request);
|
|
54
|
+
if (adminCheck.error)
|
|
55
|
+
return adminCheck.error;
|
|
56
|
+
try {
|
|
57
|
+
const redis = (0, redis_1.getRedis)();
|
|
58
|
+
const sessionPrefix = getSessionPrefix();
|
|
59
|
+
// Scan Redis for all session keys (exclude version keys)
|
|
60
|
+
const sessionKeys = [];
|
|
61
|
+
let cursor = '0';
|
|
62
|
+
do {
|
|
63
|
+
const [newCursor, keys] = await redis.scan(cursor, 'MATCH', `${sessionPrefix}*`, 'COUNT', 100);
|
|
64
|
+
cursor = newCursor;
|
|
65
|
+
// Filter out version keys (ver:) - only want actual session keys
|
|
66
|
+
sessionKeys.push(...keys.filter((k) => !k.includes(':ver:')));
|
|
67
|
+
} while (cursor !== '0');
|
|
68
|
+
// Fetch all session data
|
|
69
|
+
const sessions = [];
|
|
70
|
+
for (const key of sessionKeys) {
|
|
71
|
+
const sessionJson = await redis.get(key);
|
|
72
|
+
if (!sessionJson)
|
|
73
|
+
continue;
|
|
74
|
+
try {
|
|
75
|
+
const sessionData = JSON.parse(sessionJson);
|
|
76
|
+
const sessionToken = key.replace(sessionPrefix, '');
|
|
77
|
+
sessions.push({
|
|
78
|
+
id: sessionToken.substring(0, 16), // Truncated for display
|
|
79
|
+
session_token: sessionToken.substring(0, 8) + '...', // Masked
|
|
80
|
+
user_id: sessionData.userId,
|
|
81
|
+
user_name: sessionData.name,
|
|
82
|
+
user_email: sessionData.email,
|
|
83
|
+
status: 'active', // Sessions in Redis are active
|
|
84
|
+
created_at: sessionData.createdAt,
|
|
85
|
+
mfa_verified: sessionData.mfaVerified || sessionData.twoFactorComplete || false,
|
|
86
|
+
access_token_expires: sessionData.idpAccessTokenExpires
|
|
87
|
+
? new Date(sessionData.idpAccessTokenExpires).toISOString()
|
|
88
|
+
: null,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (parseError) {
|
|
92
|
+
console.error('[admin/redis-sessions] Failed to parse session:', key, parseError);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Sort by most recent first (if created_at available)
|
|
96
|
+
sessions.sort((a, b) => {
|
|
97
|
+
if (!a.created_at || !b.created_at)
|
|
98
|
+
return 0;
|
|
99
|
+
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
100
|
+
});
|
|
101
|
+
// Calculate stats
|
|
102
|
+
const uniqueUsers = new Set(sessions.map(s => s.user_email).filter(Boolean)).size;
|
|
103
|
+
return server_1.NextResponse.json({
|
|
104
|
+
sessions,
|
|
105
|
+
total: sessions.length,
|
|
106
|
+
stats: {
|
|
107
|
+
total_active: sessions.length,
|
|
108
|
+
total_revoked: 0, // Revoked sessions are deleted from Redis
|
|
109
|
+
unique_users: uniqueUsers,
|
|
110
|
+
},
|
|
111
|
+
redis_prefix: sessionPrefix,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error('[admin/redis-sessions] Error:', error);
|
|
116
|
+
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
async DELETE(request) {
|
|
120
|
+
const adminCheck = await checkAdminRole(request);
|
|
121
|
+
if (adminCheck.error)
|
|
122
|
+
return adminCheck.error;
|
|
123
|
+
try {
|
|
124
|
+
const { searchParams } = new URL(request.url);
|
|
125
|
+
const sessionToken = searchParams.get('session_token');
|
|
126
|
+
if (!sessionToken) {
|
|
127
|
+
return server_1.NextResponse.json({ error: 'session_token parameter required' }, { status: 400 });
|
|
128
|
+
}
|
|
129
|
+
const redis = (0, redis_1.getRedis)();
|
|
130
|
+
const sessionPrefix = getSessionPrefix();
|
|
131
|
+
const sessionKey = `${sessionPrefix}${sessionToken}`;
|
|
132
|
+
// Delete the session
|
|
133
|
+
const deleted = await redis.del(sessionKey);
|
|
134
|
+
if (deleted === 0) {
|
|
135
|
+
return server_1.NextResponse.json({ error: 'Session not found or already deleted' }, { status: 404 });
|
|
136
|
+
}
|
|
137
|
+
return server_1.NextResponse.json({
|
|
138
|
+
success: true,
|
|
139
|
+
message: 'Session revoked',
|
|
140
|
+
session_token: sessionToken.substring(0, 8) + '...',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error('[admin/redis-sessions] DELETE Error:', error);
|
|
145
|
+
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create Redis session revoke handler for specific session
|
|
152
|
+
* POST /api/admin/redis-sessions/[sessionId]/revoke
|
|
153
|
+
*/
|
|
154
|
+
function createRedisSessionRevokeHandler(config) {
|
|
155
|
+
const getSessionPrefix = () => {
|
|
156
|
+
const appSlug = config.appSlug || process.env.APP_SLUG || process.env.CLIENT_ID || 'app';
|
|
157
|
+
return `${appSlug}:sess:`;
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
async POST(request, { params }) {
|
|
161
|
+
const adminCheck = await checkAdminRole(request);
|
|
162
|
+
if (adminCheck.error)
|
|
163
|
+
return adminCheck.error;
|
|
164
|
+
try {
|
|
165
|
+
const sessionId = params.sessionId;
|
|
166
|
+
if (!sessionId) {
|
|
167
|
+
return server_1.NextResponse.json({ error: 'Session ID required' }, { status: 400 });
|
|
168
|
+
}
|
|
169
|
+
const redis = (0, redis_1.getRedis)();
|
|
170
|
+
const sessionPrefix = getSessionPrefix();
|
|
171
|
+
// Find session key that starts with the sessionId
|
|
172
|
+
let targetKey = null;
|
|
173
|
+
let cursor = '0';
|
|
174
|
+
do {
|
|
175
|
+
const [newCursor, keys] = await redis.scan(cursor, 'MATCH', `${sessionPrefix}*`, 'COUNT', 100);
|
|
176
|
+
cursor = newCursor;
|
|
177
|
+
for (const key of keys) {
|
|
178
|
+
const token = key.replace(sessionPrefix, '');
|
|
179
|
+
if (token.startsWith(sessionId) || token.substring(0, 16) === sessionId) {
|
|
180
|
+
targetKey = key;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (targetKey)
|
|
185
|
+
break;
|
|
186
|
+
} while (cursor !== '0');
|
|
187
|
+
if (!targetKey) {
|
|
188
|
+
return server_1.NextResponse.json({ error: 'Session not found' }, { status: 404 });
|
|
189
|
+
}
|
|
190
|
+
// Delete the session
|
|
191
|
+
await redis.del(targetKey);
|
|
192
|
+
return server_1.NextResponse.json({
|
|
193
|
+
success: true,
|
|
194
|
+
message: 'Session revoked',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
console.error('[admin/redis-sessions/revoke] Error:', error);
|
|
199
|
+
return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin Sessions API Handler
|
|
3
|
-
*
|
|
4
|
-
* Provides admin-level access to login sessions using service account credentials.
|
|
5
|
-
* Used by SessionsTab component.
|
|
6
|
-
*
|
|
7
|
-
* @version 1.0
|
|
8
|
-
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
9
|
-
*/
|
|
10
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
11
|
-
export interface AdminSessionsHandlerConfig {
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* GET /api/admin/sessions - List sessions
|
|
15
|
-
* POST /api/admin/sessions - Stats, revoke actions
|
|
16
|
-
*/
|
|
17
|
-
export declare function createSessionsHandler(config: AdminSessionsHandlerConfig): {
|
|
18
|
-
GET(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
19
|
-
POST(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
20
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Admin Sessions API Handler
|
|
3
|
+
*
|
|
4
|
+
* Provides admin-level access to login sessions using service account credentials.
|
|
5
|
+
* Used by SessionsTab component.
|
|
6
|
+
*
|
|
7
|
+
* @version 1.0
|
|
8
|
+
* @requires Admin role (vibe_app_admin or payez_admin)
|
|
9
|
+
*/
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
11
|
+
export interface AdminSessionsHandlerConfig {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* GET /api/admin/sessions - List sessions
|
|
15
|
+
* POST /api/admin/sessions - Stats, revoke actions
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSessionsHandler(config: AdminSessionsHandlerConfig): {
|
|
18
|
+
GET(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
19
|
+
POST(request: NextRequest): Promise<NextResponse<unknown>>;
|
|
20
|
+
};
|