sitepaige-mcp-server 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/IntegrationComponent.tsx +1 -0
- package/components/admin.tsx +30 -27
- package/components/auth.tsx +9 -9
- package/components/cta.tsx +3 -10
- package/components/headerlogin.tsx +9 -9
- package/components/login.tsx +90 -11
- package/components/logincallback.tsx +1 -0
- package/components/menu.tsx +0 -6
- package/components/profile.tsx +12 -11
- package/defaultapp/api/Auth/resend-verification/route.ts +130 -0
- package/defaultapp/api/Auth/route.ts +39 -49
- package/defaultapp/api/Auth/signup/route.ts +5 -15
- package/defaultapp/api/Auth/verify-email/route.ts +12 -5
- package/defaultapp/api/admin/users/route.ts +5 -3
- package/defaultapp/auth/auth.ts +9 -9
- package/defaultapp/db-mysql.ts +1 -1
- package/defaultapp/db-password-auth.ts +37 -0
- package/defaultapp/db-postgres.ts +1 -1
- package/defaultapp/db-sqlite.ts +1 -1
- package/defaultapp/db-users.ts +73 -73
- package/defaultapp/middleware.ts +15 -17
- package/dist/components/IntegrationComponent.tsx +1 -0
- package/dist/components/admin.tsx +30 -27
- package/dist/components/auth.tsx +9 -9
- package/dist/components/cta.tsx +3 -10
- package/dist/components/headerlogin.tsx +9 -9
- package/dist/components/login.tsx +90 -11
- package/dist/components/logincallback.tsx +1 -0
- package/dist/components/menu.tsx +0 -6
- package/dist/components/profile.tsx +12 -11
- package/dist/defaultapp/api/Auth/resend-verification/route.ts +130 -0
- package/dist/defaultapp/api/Auth/route.ts +39 -49
- package/dist/defaultapp/api/Auth/signup/route.ts +5 -15
- package/dist/defaultapp/api/Auth/verify-email/route.ts +12 -5
- package/dist/defaultapp/api/admin/users/route.ts +5 -3
- package/dist/defaultapp/auth/auth.ts +9 -9
- package/dist/defaultapp/db-mysql.ts +1 -1
- package/dist/defaultapp/db-password-auth.ts +37 -0
- package/dist/defaultapp/db-postgres.ts +1 -1
- package/dist/defaultapp/db-sqlite.ts +1 -1
- package/dist/defaultapp/db-users.ts +73 -73
- package/dist/defaultapp/middleware.ts +15 -17
- package/dist/generators/sql.js +11 -3
- package/dist/generators/sql.js.map +1 -1
- package/dist/generators/views.js +14 -14
- package/dist/generators/views.js.map +1 -1
- package/package.json +1 -1
|
@@ -35,25 +35,32 @@ export async function GET(request: Request) {
|
|
|
35
35
|
// Create or update user in the main Users table
|
|
36
36
|
const user = await upsertUser(
|
|
37
37
|
`password_${authRecord.id}`, // Unique OAuth ID for password users
|
|
38
|
-
'
|
|
38
|
+
'userpass' as any, // Source type
|
|
39
39
|
authRecord.email.split('@')[0], // Username from email
|
|
40
40
|
authRecord.email,
|
|
41
41
|
undefined // No avatar for password auth
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
+
if (!user) {
|
|
45
|
+
return NextResponse.json(
|
|
46
|
+
{ error: 'Failed to create user account' },
|
|
47
|
+
{ status: 500 }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
// Auto-login the user after verification
|
|
45
52
|
const db = await db_init();
|
|
46
53
|
|
|
47
54
|
// Delete existing sessions for this user
|
|
48
55
|
const existingSessions = await db_query(db,
|
|
49
|
-
"SELECT
|
|
56
|
+
"SELECT id FROM usersession WHERE userid = ?",
|
|
50
57
|
[user.userid]
|
|
51
58
|
);
|
|
52
59
|
|
|
53
60
|
if (existingSessions && existingSessions.length > 0) {
|
|
54
|
-
const sessionIds = existingSessions.map(session => session.
|
|
61
|
+
const sessionIds = existingSessions.map(session => session.id);
|
|
55
62
|
const placeholders = sessionIds.map(() => '?').join(',');
|
|
56
|
-
await db_query(db, `DELETE FROM usersession WHERE
|
|
63
|
+
await db_query(db, `DELETE FROM usersession WHERE id IN (${placeholders})`, sessionIds);
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
// Generate secure session token and ID
|
|
@@ -62,7 +69,7 @@ export async function GET(request: Request) {
|
|
|
62
69
|
|
|
63
70
|
// Create new session with secure token
|
|
64
71
|
await db_query(db,
|
|
65
|
-
"INSERT INTO usersession (
|
|
72
|
+
"INSERT INTO usersession (id, sessiontoken, userid, expirationdate) VALUES (?, ?, ?, ?)",
|
|
66
73
|
[sessionId, sessionToken, user.userid, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()]
|
|
67
74
|
);
|
|
68
75
|
|
|
@@ -28,9 +28,11 @@ async function checkAdminAuth(): Promise<{ isAdmin: boolean; userId?: string }>
|
|
|
28
28
|
|
|
29
29
|
// Get session
|
|
30
30
|
const sessions = await db_query(db,
|
|
31
|
-
"SELECT userid FROM
|
|
31
|
+
"SELECT userid FROM usersession WHERE sessiontoken = ?",
|
|
32
32
|
[sessionId]
|
|
33
33
|
);
|
|
34
|
+
|
|
35
|
+
console.log(sessions);
|
|
34
36
|
|
|
35
37
|
if (sessions.length === 0) {
|
|
36
38
|
return { isAdmin: false };
|
|
@@ -38,8 +40,8 @@ async function checkAdminAuth(): Promise<{ isAdmin: boolean; userId?: string }>
|
|
|
38
40
|
|
|
39
41
|
// Check if user is admin
|
|
40
42
|
const user = await getUserByID(sessions[0].userid);
|
|
41
|
-
|
|
42
|
-
if (!user || user.
|
|
43
|
+
|
|
44
|
+
if (!user || user.userlevel !== 2) {
|
|
43
45
|
return { isAdmin: false };
|
|
44
46
|
}
|
|
45
47
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { cookies } from 'next/headers';
|
|
2
2
|
|
|
3
|
-
export async function check_auth(db: any, db_query: any): Promise<{ userid: string,
|
|
3
|
+
export async function check_auth(db: any, db_query: any): Promise<{ userid: string, userlevel: number, usertier: number, isadmin: boolean }> {
|
|
4
4
|
const cookieStore = await cookies();
|
|
5
5
|
const sessionId = cookieStore.get('session_id')?.value;
|
|
6
6
|
|
|
7
7
|
const sessionInfo = {
|
|
8
8
|
userid: '',
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
userlevel: 0,
|
|
10
|
+
usertier: 0,
|
|
11
|
+
isadmin: false
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
if (!sessionId) {
|
|
@@ -16,16 +16,16 @@ export async function check_auth(db: any, db_query: any): Promise<{ userid: stri
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// SQLite is case-insensitive for identifiers, but use standard column names (no quotes, correct names)
|
|
19
|
-
const session = await db_query(db, 'SELECT userid FROM
|
|
19
|
+
const session = await db_query(db, 'SELECT userid FROM usersession WHERE sessiontoken = ?', [sessionId]);
|
|
20
20
|
if (session.length > 0) {
|
|
21
21
|
sessionInfo.userid = session[0].userid;
|
|
22
22
|
|
|
23
23
|
// In your Users table, the primary key is userid, and there is no IsAdmin column, but UserLevel (2 = admin)
|
|
24
|
-
const user = await db_query(db, 'SELECT
|
|
24
|
+
const user = await db_query(db, 'SELECT userlevel, usertier FROM users WHERE userid = ?', [session[0].userid]);
|
|
25
25
|
if (user.length > 0) {
|
|
26
|
-
sessionInfo.
|
|
27
|
-
sessionInfo.
|
|
28
|
-
sessionInfo.
|
|
26
|
+
sessionInfo.userlevel = user[0].userlevel;
|
|
27
|
+
sessionInfo.isadmin = user[0].userlevel === 2;
|
|
28
|
+
sessionInfo.usertier = user[0].usertier;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return sessionInfo;
|
|
@@ -95,7 +95,7 @@ export async function db_query(
|
|
|
95
95
|
*/
|
|
96
96
|
export function db_migrate(model: Model, dbType: string): string {
|
|
97
97
|
|
|
98
|
-
const sanitizedTableName = model.name;
|
|
98
|
+
const sanitizedTableName = model.name.toLowerCase().replace(/\s+/g, '_');
|
|
99
99
|
|
|
100
100
|
// Start with the model's fields
|
|
101
101
|
let fields = [...model.fields];
|
|
@@ -295,6 +295,43 @@ export async function updatePassword(email: string, currentPassword: string, new
|
|
|
295
295
|
return true;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Regenerate email verification token for unverified accounts
|
|
300
|
+
*/
|
|
301
|
+
export async function regenerateVerificationToken(email: string): Promise<{ passwordAuth: PasswordAuth; verificationToken: string } | null> {
|
|
302
|
+
const client = await db_init();
|
|
303
|
+
|
|
304
|
+
const authRecord = await getPasswordAuthByEmail(email);
|
|
305
|
+
if (!authRecord) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Only regenerate for unverified accounts
|
|
310
|
+
if (authRecord.emailverified) {
|
|
311
|
+
throw new Error('Email is already verified');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Generate new verification token
|
|
315
|
+
const verificationToken = randomBytes(32).toString('base64url');
|
|
316
|
+
const verificationTokenExpires = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
|
|
317
|
+
|
|
318
|
+
await db_query(client,
|
|
319
|
+
`UPDATE passwordauth
|
|
320
|
+
SET verificationtoken = ?,
|
|
321
|
+
verificationtokenexpires = ?,
|
|
322
|
+
updatedat = CURRENT_TIMESTAMP
|
|
323
|
+
WHERE id = ?`,
|
|
324
|
+
[verificationToken, verificationTokenExpires.toISOString(), authRecord.id]
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const updatedAuth = await getPasswordAuthByEmail(email);
|
|
328
|
+
if (!updatedAuth) {
|
|
329
|
+
throw new Error('Failed to update verification token');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return { passwordAuth: updatedAuth, verificationToken };
|
|
333
|
+
}
|
|
334
|
+
|
|
298
335
|
/**
|
|
299
336
|
* Check if an email is already registered
|
|
300
337
|
*/
|
|
@@ -101,7 +101,7 @@ export async function db_query(
|
|
|
101
101
|
*/
|
|
102
102
|
export function db_migrate(model: Model, dbType: string): string {
|
|
103
103
|
|
|
104
|
-
const sanitizedTableName = model.name;
|
|
104
|
+
const sanitizedTableName = model.name.toLowerCase().replace(/\s+/g, '_');
|
|
105
105
|
|
|
106
106
|
// Start with the model's fields
|
|
107
107
|
let fields = [...model.fields];
|
|
@@ -175,7 +175,7 @@ export async function db_query(
|
|
|
175
175
|
export function db_migrate(model: Model, dbType: string): string {
|
|
176
176
|
// Special handling for auth tables - create them first
|
|
177
177
|
|
|
178
|
-
const sanitizedTableName = model.name;
|
|
178
|
+
const sanitizedTableName = model.name.toLowerCase().replace(/\s+/g, '_');
|
|
179
179
|
|
|
180
180
|
// Start with the model's fields
|
|
181
181
|
let fields = [...model.fields];
|
|
@@ -9,33 +9,33 @@ import * as crypto from 'node:crypto';
|
|
|
9
9
|
|
|
10
10
|
export interface User {
|
|
11
11
|
userid: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
oauthid: string;
|
|
13
|
+
source: 'google' | 'facebook' | 'apple' | 'github' | 'userpass';
|
|
14
|
+
username: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
avatarurl?: string;
|
|
17
|
+
userlevel: number; // 0: everyone, 1: registered user, 2: admin
|
|
18
|
+
lastlogindate: string;
|
|
19
|
+
createddate: string;
|
|
20
|
+
isactive: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface UserSession {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
id: string;
|
|
25
|
+
sessiontoken: string;
|
|
26
26
|
userid: string;
|
|
27
|
-
|
|
27
|
+
expirationdate: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export interface OAuthToken {
|
|
31
|
-
|
|
31
|
+
id: string;
|
|
32
32
|
userid: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
provider: 'google' | 'facebook' | 'apple' | 'github';
|
|
34
|
+
accesstoken: string;
|
|
35
|
+
refreshtoken?: string;
|
|
36
|
+
expiresat?: string;
|
|
37
|
+
createdat: string;
|
|
38
|
+
updatedat: string;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -46,9 +46,9 @@ export async function getAllUsers(): Promise<User[]> {
|
|
|
46
46
|
|
|
47
47
|
const users = await db_query(client,
|
|
48
48
|
`SELECT * FROM users
|
|
49
|
-
WHERE
|
|
50
|
-
ORDER BY
|
|
51
|
-
[true
|
|
49
|
+
WHERE isactive = ?
|
|
50
|
+
ORDER BY userlevel DESC, username ASC`,
|
|
51
|
+
[1] // Use 1 instead of true for PostgreSQL compatibility
|
|
52
52
|
);
|
|
53
53
|
|
|
54
54
|
return users as User[];
|
|
@@ -61,8 +61,8 @@ export async function getUserByOAuthID(oauthId: string): Promise<User | null> {
|
|
|
61
61
|
const client = await db_init();
|
|
62
62
|
|
|
63
63
|
const users = await db_query(client,
|
|
64
|
-
"SELECT * FROM users WHERE
|
|
65
|
-
[oauthId, true
|
|
64
|
+
"SELECT * FROM users WHERE oauthid = ? AND isactive = ?",
|
|
65
|
+
[oauthId, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
66
66
|
);
|
|
67
67
|
|
|
68
68
|
return users.length > 0 ? users[0] as User : null;
|
|
@@ -75,8 +75,8 @@ export async function getUserByID(userId: string): Promise<User | null> {
|
|
|
75
75
|
const client = await db_init();
|
|
76
76
|
|
|
77
77
|
const users = await db_query(client,
|
|
78
|
-
"SELECT * FROM users WHERE userid = ? AND
|
|
79
|
-
[userId, true
|
|
78
|
+
"SELECT * FROM users WHERE userid = ? AND isactive = ?",
|
|
79
|
+
[userId, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
return users.length > 0 ? users[0] as User : null;
|
|
@@ -87,7 +87,7 @@ export async function getUserByID(userId: string): Promise<User | null> {
|
|
|
87
87
|
*/
|
|
88
88
|
export async function upsertUser(
|
|
89
89
|
oauthId: string,
|
|
90
|
-
source: 'google' | 'facebook' | 'apple' | 'github',
|
|
90
|
+
source: 'google' | 'facebook' | 'apple' | 'github' | 'userpass',
|
|
91
91
|
userName: string,
|
|
92
92
|
email?: string,
|
|
93
93
|
avatarUrl?: string
|
|
@@ -101,9 +101,9 @@ export async function upsertUser(
|
|
|
101
101
|
// Update existing user
|
|
102
102
|
await db_query(client,
|
|
103
103
|
`UPDATE users
|
|
104
|
-
SET
|
|
105
|
-
|
|
106
|
-
WHERE
|
|
104
|
+
SET username = ?, email = COALESCE(?, email), avatarurl = ?,
|
|
105
|
+
lastlogindate = CURRENT_TIMESTAMP, source = ?
|
|
106
|
+
WHERE oauthid = ?`,
|
|
107
107
|
[userName, email, avatarUrl || '', source, oauthId]
|
|
108
108
|
);
|
|
109
109
|
|
|
@@ -119,10 +119,10 @@ export async function upsertUser(
|
|
|
119
119
|
|
|
120
120
|
await db_query(client,
|
|
121
121
|
`INSERT INTO users
|
|
122
|
-
(userid,
|
|
123
|
-
|
|
122
|
+
(userid, oauthid, source, username, email, avatarurl, userlevel, usertier,
|
|
123
|
+
lastlogindate, createddate, isactive)
|
|
124
124
|
VALUES (?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?)`,
|
|
125
|
-
[userId, oauthId, source, userName, email || null, avatarUrl || '', permissionLevel, true
|
|
125
|
+
[userId, oauthId, source, userName, email || null, avatarUrl || '', permissionLevel, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
126
126
|
);
|
|
127
127
|
|
|
128
128
|
return (await getUserByOAuthID(oauthId))!;
|
|
@@ -141,8 +141,8 @@ export async function updateUserPermission(
|
|
|
141
141
|
// Ensure there's always at least one admin
|
|
142
142
|
if (permissionLevel < 2) {
|
|
143
143
|
const admins = await db_query(client,
|
|
144
|
-
"SELECT COUNT(*) as count FROM users WHERE
|
|
145
|
-
[2, userId, true
|
|
144
|
+
"SELECT COUNT(*) as count FROM users WHERE userlevel = ? AND userid != ? AND isactive = ?",
|
|
145
|
+
[2, userId, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
146
146
|
);
|
|
147
147
|
|
|
148
148
|
if (admins[0].count === 0) {
|
|
@@ -151,7 +151,7 @@ export async function updateUserPermission(
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
const result = await db_query(client,
|
|
154
|
-
"UPDATE users SET
|
|
154
|
+
"UPDATE users SET userlevel = ? WHERE userid = ?",
|
|
155
155
|
[permissionLevel, userId]
|
|
156
156
|
);
|
|
157
157
|
|
|
@@ -166,10 +166,10 @@ export async function deleteUser(userId: string): Promise<boolean> {
|
|
|
166
166
|
|
|
167
167
|
// Ensure there's always at least one admin
|
|
168
168
|
const user = await getUserByID(userId);
|
|
169
|
-
if (user && user.
|
|
169
|
+
if (user && user.userlevel === 2) {
|
|
170
170
|
const admins = await db_query(client,
|
|
171
|
-
"SELECT COUNT(*) as count FROM users WHERE
|
|
172
|
-
[2, userId, true
|
|
171
|
+
"SELECT COUNT(*) as count FROM users WHERE userlevel = ? AND userid != ? AND isactive = ?",
|
|
172
|
+
[2, userId, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
173
173
|
);
|
|
174
174
|
|
|
175
175
|
if (admins[0].count === 0) {
|
|
@@ -179,8 +179,8 @@ export async function deleteUser(userId: string): Promise<boolean> {
|
|
|
179
179
|
|
|
180
180
|
// Soft delete the user
|
|
181
181
|
const result = await db_query(client,
|
|
182
|
-
"UPDATE users SET
|
|
183
|
-
[
|
|
182
|
+
"UPDATE users SET isactive = ? WHERE userid = ?",
|
|
183
|
+
[0, userId] // Use 0 instead of false for PostgreSQL compatibility
|
|
184
184
|
);
|
|
185
185
|
|
|
186
186
|
return result[0].changes > 0;
|
|
@@ -200,12 +200,12 @@ export async function getUserStats(): Promise<{
|
|
|
200
200
|
const stats = await db_query(client, `
|
|
201
201
|
SELECT
|
|
202
202
|
COUNT(*) as totalUsers,
|
|
203
|
-
SUM(CASE WHEN
|
|
204
|
-
SUM(CASE WHEN
|
|
205
|
-
SUM(CASE WHEN
|
|
203
|
+
SUM(CASE WHEN userlevel = 2 THEN 1 ELSE 0 END) as admins,
|
|
204
|
+
SUM(CASE WHEN userlevel = 1 THEN 1 ELSE 0 END) as registeredUsers,
|
|
205
|
+
SUM(CASE WHEN userlevel = 0 THEN 1 ELSE 0 END) as guestUsers
|
|
206
206
|
FROM users
|
|
207
|
-
WHERE
|
|
208
|
-
`, [
|
|
207
|
+
WHERE isactive = ?
|
|
208
|
+
`, [1]); // Use 1 instead of true for PostgreSQL compatibility
|
|
209
209
|
|
|
210
210
|
return stats[0];
|
|
211
211
|
}
|
|
@@ -217,7 +217,7 @@ export async function cleanupExpiredSessions(): Promise<number> {
|
|
|
217
217
|
const client = await db_init();
|
|
218
218
|
|
|
219
219
|
const result = await db_query(client,
|
|
220
|
-
"DELETE FROM usersession WHERE
|
|
220
|
+
"DELETE FROM usersession WHERE expirationdate::TIMESTAMP < CURRENT_TIMESTAMP"
|
|
221
221
|
);
|
|
222
222
|
|
|
223
223
|
return result[0].changes;
|
|
@@ -241,14 +241,14 @@ export async function storeOAuthToken(
|
|
|
241
241
|
|
|
242
242
|
// Delete existing tokens for this user/provider combo
|
|
243
243
|
await db_query(client,
|
|
244
|
-
"DELETE FROM oauthtokens WHERE userid = ? AND
|
|
244
|
+
"DELETE FROM oauthtokens WHERE userid = ? AND provider = ?",
|
|
245
245
|
[userId, provider]
|
|
246
246
|
);
|
|
247
247
|
|
|
248
248
|
// Insert new token
|
|
249
249
|
await db_query(client,
|
|
250
250
|
`INSERT INTO oauthtokens
|
|
251
|
-
(
|
|
251
|
+
(id, userid, provider, accesstoken, refreshtoken, expiresat, createdat, updatedat)
|
|
252
252
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
253
253
|
[tokenId, userId, provider, accessToken, refreshToken || null, expiresAt, now, now]
|
|
254
254
|
);
|
|
@@ -264,7 +264,7 @@ export async function getOAuthToken(
|
|
|
264
264
|
const client = await db_init();
|
|
265
265
|
|
|
266
266
|
const tokens = await db_query(client,
|
|
267
|
-
"SELECT * FROM oauthtokens WHERE userid = ? AND
|
|
267
|
+
"SELECT * FROM oauthtokens WHERE userid = ? AND provider = ?",
|
|
268
268
|
[userId, provider]
|
|
269
269
|
);
|
|
270
270
|
|
|
@@ -285,8 +285,8 @@ export async function validateSession(sessionToken: string): Promise<{
|
|
|
285
285
|
const sessions = await db_query(client,
|
|
286
286
|
`SELECT s.*, u.* FROM usersession s
|
|
287
287
|
JOIN users u ON s.userid = u.userid
|
|
288
|
-
WHERE s.
|
|
289
|
-
[sessionToken, true
|
|
288
|
+
WHERE s.sessiontoken = ? AND s.expirationdate::TIMESTAMP > CURRENT_TIMESTAMP AND u.isactive = ?`,
|
|
289
|
+
[sessionToken, 1] // Use 1 instead of true for PostgreSQL compatibility
|
|
290
290
|
);
|
|
291
291
|
|
|
292
292
|
if (!sessions || sessions.length === 0) {
|
|
@@ -296,22 +296,22 @@ export async function validateSession(sessionToken: string): Promise<{
|
|
|
296
296
|
const session = sessions[0];
|
|
297
297
|
|
|
298
298
|
// Check if session needs rotation (older than 24 hours)
|
|
299
|
-
const sessionAge = Date.now() - new Date(session.
|
|
299
|
+
const sessionAge = Date.now() - new Date(session.id).getTime();
|
|
300
300
|
const needsRotation = sessionAge > 24 * 60 * 60 * 1000; // 24 hours
|
|
301
301
|
|
|
302
302
|
return {
|
|
303
303
|
valid: true,
|
|
304
304
|
user: {
|
|
305
305
|
userid: session.userid,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
306
|
+
oauthid: session.oauthid,
|
|
307
|
+
source: session.source,
|
|
308
|
+
username: session.username,
|
|
309
|
+
email: session.email,
|
|
310
|
+
avatarurl: session.avatarurl,
|
|
311
|
+
userlevel: session.userlevel,
|
|
312
|
+
lastlogindate: session.lastlogindate,
|
|
313
|
+
createddate: session.createddate,
|
|
314
|
+
isactive: session.isactive
|
|
315
315
|
} as User,
|
|
316
316
|
needsRotation
|
|
317
317
|
};
|
|
@@ -325,7 +325,7 @@ export async function rotateSession(oldSessionToken: string): Promise<string | n
|
|
|
325
325
|
|
|
326
326
|
// Get existing session
|
|
327
327
|
const sessions = await db_query(client,
|
|
328
|
-
"SELECT * FROM usersession WHERE
|
|
328
|
+
"SELECT * FROM usersession WHERE sessiontoken = ? AND expirationdate::TIMESTAMP > CURRENT_TIMESTAMP",
|
|
329
329
|
[oldSessionToken]
|
|
330
330
|
);
|
|
331
331
|
|
|
@@ -338,8 +338,8 @@ export async function rotateSession(oldSessionToken: string): Promise<string | n
|
|
|
338
338
|
|
|
339
339
|
// Update session with new token
|
|
340
340
|
await db_query(client,
|
|
341
|
-
"UPDATE usersession SET
|
|
342
|
-
[newSessionToken, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), session.
|
|
341
|
+
"UPDATE usersession SET sessiontoken = ?, expirationdate = ? WHERE id = ?",
|
|
342
|
+
[newSessionToken, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), session.id]
|
|
343
343
|
);
|
|
344
344
|
|
|
345
345
|
return newSessionToken;
|
|
@@ -371,9 +371,9 @@ export async function validateOAuthToken(
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// Check if token is expired based on stored expiry
|
|
374
|
-
if (token.
|
|
374
|
+
if (token.expiresat && new Date(token.expiresat) < new Date()) {
|
|
375
375
|
// Try to refresh the token
|
|
376
|
-
if (token.
|
|
376
|
+
if (token.refreshtoken) {
|
|
377
377
|
return await refreshOAuthToken(userId, provider);
|
|
378
378
|
}
|
|
379
379
|
return false;
|
|
@@ -384,10 +384,10 @@ export async function validateOAuthToken(
|
|
|
384
384
|
let validationUrl: string;
|
|
385
385
|
switch (provider) {
|
|
386
386
|
case 'google':
|
|
387
|
-
validationUrl = `https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${token.
|
|
387
|
+
validationUrl = `https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${token.accesstoken}`;
|
|
388
388
|
break;
|
|
389
389
|
case 'facebook':
|
|
390
|
-
validationUrl = `https://graph.facebook.com/me?access_token=${token.
|
|
390
|
+
validationUrl = `https://graph.facebook.com/me?access_token=${token.accesstoken}`;
|
|
391
391
|
break;
|
|
392
392
|
case 'github':
|
|
393
393
|
validationUrl = 'https://api.github.com/user';
|
|
@@ -398,7 +398,7 @@ export async function validateOAuthToken(
|
|
|
398
398
|
|
|
399
399
|
const response = await fetch(validationUrl, {
|
|
400
400
|
headers: provider === 'github' ? {
|
|
401
|
-
Authorization: `Bearer ${token.
|
|
401
|
+
Authorization: `Bearer ${token.accesstoken}`
|
|
402
402
|
} : {}
|
|
403
403
|
});
|
|
404
404
|
|
|
@@ -419,7 +419,7 @@ export async function refreshOAuthToken(
|
|
|
419
419
|
|
|
420
420
|
// Get stored OAuth token with refresh token
|
|
421
421
|
const token = await getOAuthToken(userId, provider);
|
|
422
|
-
if (!token || !token.
|
|
422
|
+
if (!token || !token.refreshtoken) {
|
|
423
423
|
return false;
|
|
424
424
|
}
|
|
425
425
|
|
|
@@ -428,7 +428,7 @@ export async function refreshOAuthToken(
|
|
|
428
428
|
|
|
429
429
|
const params: Record<string, string> = {
|
|
430
430
|
grant_type: 'refresh_token',
|
|
431
|
-
refresh_token: token.
|
|
431
|
+
refresh_token: token.refreshtoken,
|
|
432
432
|
client_id: process.env[`NEXT_PUBLIC_${provider.toUpperCase()}_CLIENT_ID`]!,
|
|
433
433
|
client_secret: process.env[`${provider.toUpperCase()}_CLIENT_SECRET`]!
|
|
434
434
|
};
|
|
@@ -453,7 +453,7 @@ export async function refreshOAuthToken(
|
|
|
453
453
|
userId,
|
|
454
454
|
provider,
|
|
455
455
|
data.access_token,
|
|
456
|
-
data.refresh_token || token.
|
|
456
|
+
data.refresh_token || token.refreshtoken, // Some providers don't return new refresh token
|
|
457
457
|
data.expires_in
|
|
458
458
|
);
|
|
459
459
|
|
|
@@ -89,23 +89,21 @@ export function middleware(request: NextRequest) {
|
|
|
89
89
|
// Using Report-Only mode to monitor CSP violations without breaking functionality
|
|
90
90
|
response.headers.set('Content-Security-Policy-Report-Only', csp)
|
|
91
91
|
|
|
92
|
-
// Add CSRF token generation for
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
108
|
-
}
|
|
92
|
+
// Add CSRF token generation for all requests
|
|
93
|
+
const csrfToken = request.cookies.get('csrf-token')?.value;
|
|
94
|
+
|
|
95
|
+
// If no CSRF token exists, generate one
|
|
96
|
+
if (!csrfToken) {
|
|
97
|
+
const newCsrfToken = crypto.randomUUID();
|
|
98
|
+
response.cookies.set({
|
|
99
|
+
name: 'csrf-token',
|
|
100
|
+
value: newCsrfToken,
|
|
101
|
+
httpOnly: true,
|
|
102
|
+
secure: process.env.NODE_ENV === 'production',
|
|
103
|
+
sameSite: 'strict',
|
|
104
|
+
maxAge: 60 * 60 * 24, // 24 hours
|
|
105
|
+
path: '/'
|
|
106
|
+
});
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
|
package/dist/generators/sql.js
CHANGED
|
@@ -5,6 +5,14 @@ import { ensureDir } from "./utils.js";
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
|
+
/**
|
|
9
|
+
* Sanitizes a table name by converting to lowercase and replacing spaces with underscores
|
|
10
|
+
* @param name The raw table name
|
|
11
|
+
* @returns The sanitized table name
|
|
12
|
+
*/
|
|
13
|
+
function sanitizeTableName(name) {
|
|
14
|
+
return name.toLowerCase().replace(/\s+/g, '_');
|
|
15
|
+
}
|
|
8
16
|
export async function writeModelsSql(targetDir, blueprint, databaseType = "postgres") {
|
|
9
17
|
const migrationsDir = path.join(targetDir, "migrations");
|
|
10
18
|
ensureDir(migrationsDir);
|
|
@@ -78,7 +86,7 @@ export async function writeModelsSql(targetDir, blueprint, databaseType = "postg
|
|
|
78
86
|
const dbTypeMap = typeMap[databaseType] || typeMap.sqlite;
|
|
79
87
|
const quoteChar = databaseType === 'mysql' ? '`' : '"';
|
|
80
88
|
for (const model of models) {
|
|
81
|
-
const tableName = (model.name || model.id || "table")
|
|
89
|
+
const tableName = sanitizeTableName(model.name || model.id || "table");
|
|
82
90
|
lines.push(`\n-- Model: ${tableName}`);
|
|
83
91
|
const fieldDefs = [];
|
|
84
92
|
const fields = model.fields || [];
|
|
@@ -182,7 +190,7 @@ export function generateSQLFromMigrations(migrations, databaseType = "postgres")
|
|
|
182
190
|
const sql = [];
|
|
183
191
|
for (const m of migrations) {
|
|
184
192
|
const action = m.action;
|
|
185
|
-
const modelName = (m.modelName || m.modelId || "")
|
|
193
|
+
const modelName = sanitizeTableName(m.modelName || m.modelId || "");
|
|
186
194
|
if (action === "create") {
|
|
187
195
|
const modelChange = m.changes.find((c) => c.type === "model" && c.operation === "add");
|
|
188
196
|
const model = (modelChange?.newValue ?? { name: modelName });
|
|
@@ -200,7 +208,7 @@ export function generateSQLFromMigrations(migrations, databaseType = "postgres")
|
|
|
200
208
|
const pk = (f.key === "primary") ? " PRIMARY KEY" : "";
|
|
201
209
|
return ` ${quoteChar}${f.name.toLowerCase()}${quoteChar} ${mappedType}${required}${pk}`;
|
|
202
210
|
});
|
|
203
|
-
sql.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}${(model.name || modelName)
|
|
211
|
+
sql.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}${sanitizeTableName(model.name || modelName)}${quoteChar} (\n${fieldDefs.join(",\n")}\n);`);
|
|
204
212
|
continue;
|
|
205
213
|
}
|
|
206
214
|
if (action === "delete") {
|