sitepaige-mcp-server 1.0.3 → 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 +7 -4
- package/components/logincallback.tsx +1 -0
- package/components/menu.tsx +0 -6
- package/components/profile.tsx +12 -11
- package/defaultapp/api/Auth/route.ts +39 -39
- package/defaultapp/api/Auth/verify-email/route.ts +4 -4
- 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-postgres.ts +1 -1
- package/defaultapp/db-sqlite.ts +1 -1
- package/defaultapp/db-users.ts +63 -63
- 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 +7 -4
- 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/route.ts +39 -39
- package/dist/defaultapp/api/Auth/verify-email/route.ts +4 -4
- 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-postgres.ts +1 -1
- package/dist/defaultapp/db-sqlite.ts +1 -1
- package/dist/defaultapp/db-users.ts +63 -63
- package/dist/generators/sql.js +11 -3
- package/dist/generators/sql.js.map +1 -1
- package/dist/generators/views.js +2 -2
- package/package.json +1 -1
package/components/admin.tsx
CHANGED
|
@@ -5,15 +5,15 @@ import { useRouter } from 'next/navigation';
|
|
|
5
5
|
|
|
6
6
|
interface User {
|
|
7
7
|
userid: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
oauthid: string;
|
|
9
|
+
source: string;
|
|
10
|
+
username: string;
|
|
11
|
+
email?: string;
|
|
12
|
+
avatarurl?: string;
|
|
13
|
+
userlevel: number;
|
|
14
|
+
lastlogindate: string;
|
|
15
|
+
createddate: string;
|
|
16
|
+
isactive: boolean;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface UserStats {
|
|
@@ -46,7 +46,9 @@ export default function AdminPanel() {
|
|
|
46
46
|
setError(null);
|
|
47
47
|
|
|
48
48
|
// Fetch users - authorization is handled server-side via session cookie
|
|
49
|
-
const response = await fetch('/api/admin/users'
|
|
49
|
+
const response = await fetch('/api/admin/users', {
|
|
50
|
+
credentials: 'include'
|
|
51
|
+
});
|
|
50
52
|
if (!response.ok) {
|
|
51
53
|
if (response.status === 401) {
|
|
52
54
|
setError('Access denied. Admin privileges required.');
|
|
@@ -80,6 +82,7 @@ export default function AdminPanel() {
|
|
|
80
82
|
headers: {
|
|
81
83
|
'Content-Type': 'application/json',
|
|
82
84
|
},
|
|
85
|
+
credentials: 'include',
|
|
83
86
|
body: JSON.stringify({
|
|
84
87
|
userId,
|
|
85
88
|
permissionLevel: newLevel,
|
|
@@ -169,13 +172,13 @@ export default function AdminPanel() {
|
|
|
169
172
|
|
|
170
173
|
// Filter users based on search and permission level
|
|
171
174
|
const filteredUsers = users.filter(user => {
|
|
172
|
-
const matchesSearch = user.
|
|
173
|
-
(user.
|
|
175
|
+
const matchesSearch = user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
176
|
+
(user.email && user.email.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
174
177
|
|
|
175
178
|
const matchesFilter = filterLevel === 'all' ||
|
|
176
|
-
(filterLevel === 'admin' && user.
|
|
177
|
-
(filterLevel === 'registered' && user.
|
|
178
|
-
(filterLevel === 'guest' && user.
|
|
179
|
+
(filterLevel === 'admin' && user.userlevel === 2) ||
|
|
180
|
+
(filterLevel === 'registered' && user.userlevel === 1) ||
|
|
181
|
+
(filterLevel === 'guest' && user.userlevel === 0);
|
|
179
182
|
|
|
180
183
|
return matchesSearch && matchesFilter;
|
|
181
184
|
});
|
|
@@ -298,26 +301,26 @@ export default function AdminPanel() {
|
|
|
298
301
|
<tr key={user.userid} className="hover:bg-gray-50">
|
|
299
302
|
<td className="px-6 py-4 whitespace-nowrap">
|
|
300
303
|
<div className="flex items-center">
|
|
301
|
-
{user.
|
|
304
|
+
{user.avatarurl ? (
|
|
302
305
|
<img
|
|
303
306
|
className="h-10 w-10 rounded-full"
|
|
304
|
-
src={user.
|
|
305
|
-
alt={user.
|
|
307
|
+
src={user.avatarurl}
|
|
308
|
+
alt={user.username}
|
|
306
309
|
/>
|
|
307
310
|
) : (
|
|
308
311
|
<div className="h-10 w-10 rounded-full bg-gray-300 flex items-center justify-center">
|
|
309
312
|
<span className="text-gray-600 font-medium">
|
|
310
|
-
{user.
|
|
313
|
+
{user.username.charAt(0).toUpperCase()}
|
|
311
314
|
</span>
|
|
312
315
|
</div>
|
|
313
316
|
)}
|
|
314
317
|
<div className="ml-4">
|
|
315
318
|
<div className="text-sm font-medium text-gray-900">
|
|
316
|
-
{user.
|
|
319
|
+
{user.username}
|
|
317
320
|
</div>
|
|
318
|
-
{user.
|
|
321
|
+
{user.email && (
|
|
319
322
|
<div className="text-sm text-gray-500">
|
|
320
|
-
{user.
|
|
323
|
+
{user.email}
|
|
321
324
|
</div>
|
|
322
325
|
)}
|
|
323
326
|
</div>
|
|
@@ -325,15 +328,15 @@ export default function AdminPanel() {
|
|
|
325
328
|
</td>
|
|
326
329
|
<td className="px-6 py-4 whitespace-nowrap">
|
|
327
330
|
<span className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">
|
|
328
|
-
{user.
|
|
331
|
+
{user.source}
|
|
329
332
|
</span>
|
|
330
333
|
</td>
|
|
331
334
|
<td className="px-6 py-4 whitespace-nowrap">
|
|
332
335
|
<select
|
|
333
|
-
value={user.
|
|
336
|
+
value={user.userlevel}
|
|
334
337
|
onChange={(e) => handlePermissionChange(user.userid, parseInt(e.target.value))}
|
|
335
338
|
disabled={isUpdating === user.userid}
|
|
336
|
-
className={`px-2 py-1 text-xs leading-5 font-semibold rounded-full ${getPermissionLevelColor(user.
|
|
339
|
+
className={`px-2 py-1 text-xs leading-5 font-semibold rounded-full ${getPermissionLevelColor(user.userlevel)} cursor-pointer hover:opacity-80 disabled:opacity-50 disabled:cursor-not-allowed`}
|
|
337
340
|
>
|
|
338
341
|
<option value={0}>Guest</option>
|
|
339
342
|
<option value={1}>Registered User</option>
|
|
@@ -341,11 +344,11 @@ export default function AdminPanel() {
|
|
|
341
344
|
</select>
|
|
342
345
|
</td>
|
|
343
346
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
344
|
-
{formatDate(user.
|
|
347
|
+
{formatDate(user.lastlogindate)}
|
|
345
348
|
</td>
|
|
346
349
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
|
347
350
|
<button
|
|
348
|
-
onClick={() => handleDeleteUser(user.userid, user.
|
|
351
|
+
onClick={() => handleDeleteUser(user.userid, user.username)}
|
|
349
352
|
disabled={isDeleting === user.userid}
|
|
350
353
|
className="text-red-600 hover:text-red-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
351
354
|
>
|
package/components/auth.tsx
CHANGED
|
@@ -11,11 +11,11 @@ import { useUserStore } from '@/store/user';
|
|
|
11
11
|
|
|
12
12
|
interface AuthProps {
|
|
13
13
|
auth: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
id: string;
|
|
15
|
+
username: string;
|
|
16
|
+
avatarurl: string;
|
|
17
|
+
userlevel: string;
|
|
18
|
+
isadmin: boolean;
|
|
19
19
|
} | null;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -25,10 +25,10 @@ export default function Auth({ auth }: AuthProps) {
|
|
|
25
25
|
useEffect(() => {
|
|
26
26
|
if (auth) {
|
|
27
27
|
setIsAuthenticated(true);
|
|
28
|
-
setUserLevel(auth.
|
|
29
|
-
setIsAdmin(auth.
|
|
30
|
-
setUserName(auth.
|
|
31
|
-
setAvatarURL(auth.
|
|
28
|
+
setUserLevel(auth.userlevel);
|
|
29
|
+
setIsAdmin(auth.isadmin);
|
|
30
|
+
setUserName(auth.username);
|
|
31
|
+
setAvatarURL(auth.avatarurl);
|
|
32
32
|
} else {
|
|
33
33
|
setIsAuthenticated(false);
|
|
34
34
|
setUserLevel('0');
|
package/components/cta.tsx
CHANGED
|
@@ -28,10 +28,9 @@ interface CTAData {
|
|
|
28
28
|
interface RCTAProps {
|
|
29
29
|
custom_view_description: string;
|
|
30
30
|
onNavigate: (pageId: string) => void;
|
|
31
|
-
isPaigeLoading?: boolean;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
const RCTA: React.FC<RCTAProps> = ({ custom_view_description, onNavigate
|
|
33
|
+
const RCTA: React.FC<RCTAProps> = ({ custom_view_description, onNavigate }) => {
|
|
35
34
|
let ctaData: CTAData;
|
|
36
35
|
|
|
37
36
|
try {
|
|
@@ -101,10 +100,6 @@ const RCTA: React.FC<RCTAProps> = ({ custom_view_description, onNavigate, isPaig
|
|
|
101
100
|
};
|
|
102
101
|
|
|
103
102
|
const handleButtonClick = (pageId: string) => {
|
|
104
|
-
if (isPaigeLoading) {
|
|
105
|
-
console.log('Navigation blocked: Paige is currently processing a request');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
103
|
if (pageId) {
|
|
109
104
|
onNavigate(pageId);
|
|
110
105
|
}
|
|
@@ -175,11 +170,9 @@ const RCTA: React.FC<RCTAProps> = ({ custom_view_description, onNavigate, isPaig
|
|
|
175
170
|
<button
|
|
176
171
|
key={index}
|
|
177
172
|
onClick={() => handleButtonClick(button.page)}
|
|
178
|
-
className={`px-6 py-3 rounded-lg transition-all ${hoverClass} ${
|
|
179
|
-
isPaigeLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
180
|
-
} ${!button.page ? 'opacity-75 cursor-not-allowed' : ''}`}
|
|
173
|
+
className={`px-6 py-3 rounded-lg transition-all ${hoverClass} ${!button.page ? 'opacity-75 cursor-not-allowed' : ''}`}
|
|
181
174
|
style={buttonStyles}
|
|
182
|
-
disabled={
|
|
175
|
+
disabled={!button.page}
|
|
183
176
|
>
|
|
184
177
|
{button.buttonTitle || 'Button'}
|
|
185
178
|
</button>
|
|
@@ -17,11 +17,11 @@ interface MenuItem {
|
|
|
17
17
|
|
|
18
18
|
interface UserData {
|
|
19
19
|
userid: string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
username: string;
|
|
21
|
+
avatarurl: string;
|
|
22
|
+
email: string;
|
|
23
|
+
userlevel: number;
|
|
24
|
+
isadmin: boolean;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const getLocalizedText = (text: string, language: string = 'English'): string => {
|
|
@@ -210,11 +210,11 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
210
210
|
className="flex items-center focus:outline-none relative group cursor-pointer"
|
|
211
211
|
style={{ color: textColor }}
|
|
212
212
|
>
|
|
213
|
-
<span className="mr-2" style={{ color: textColor }}>{userData.
|
|
213
|
+
<span className="mr-2" style={{ color: textColor }}>{userData.username || getLocalizedText('Sample User', websiteLanguage)}</span>
|
|
214
214
|
<div className="relative">
|
|
215
|
-
{userData.
|
|
215
|
+
{userData.avatarurl ? (
|
|
216
216
|
<img
|
|
217
|
-
src={userData.
|
|
217
|
+
src={userData.avatarurl}
|
|
218
218
|
alt="User avatar"
|
|
219
219
|
className="w-10 h-10 rounded-full"
|
|
220
220
|
referrerPolicy="no-referrer"
|
|
@@ -222,7 +222,7 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
222
222
|
/>
|
|
223
223
|
) : (
|
|
224
224
|
<div className="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold">
|
|
225
|
-
{userData.
|
|
225
|
+
{userData.username ? userData.username.charAt(0).toUpperCase() : 'U'}
|
|
226
226
|
</div>
|
|
227
227
|
)}
|
|
228
228
|
</div>
|
package/components/login.tsx
CHANGED
|
@@ -9,7 +9,7 @@ checked in the system build settings. It is safe to modify this file without it
|
|
|
9
9
|
import React, { useState } from 'react';
|
|
10
10
|
|
|
11
11
|
interface LoginProps {
|
|
12
|
-
providers: ('apple' | 'facebook' | 'github' | 'google' | '
|
|
12
|
+
providers: ('apple' | 'facebook' | 'github' | 'google' | 'userpass')[];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export default function Login({ providers }: LoginProps) {
|
|
@@ -62,6 +62,7 @@ export default function Login({ providers }: LoginProps) {
|
|
|
62
62
|
headers: {
|
|
63
63
|
'Content-Type': 'application/json'
|
|
64
64
|
},
|
|
65
|
+
credentials: 'include', // Include cookies in request
|
|
65
66
|
body: JSON.stringify({ email: resendEmail || email })
|
|
66
67
|
});
|
|
67
68
|
|
|
@@ -101,6 +102,7 @@ export default function Login({ providers }: LoginProps) {
|
|
|
101
102
|
headers: {
|
|
102
103
|
'Content-Type': 'application/json'
|
|
103
104
|
},
|
|
105
|
+
credentials: 'include', // Include cookies in request
|
|
104
106
|
body: JSON.stringify({ email, password })
|
|
105
107
|
});
|
|
106
108
|
|
|
@@ -121,7 +123,8 @@ export default function Login({ providers }: LoginProps) {
|
|
|
121
123
|
headers: {
|
|
122
124
|
'Content-Type': 'application/json'
|
|
123
125
|
},
|
|
124
|
-
|
|
126
|
+
credentials: 'include', // Include cookies in request
|
|
127
|
+
body: JSON.stringify({ email, password, provider: 'userpass' })
|
|
125
128
|
});
|
|
126
129
|
|
|
127
130
|
const data = await response.json();
|
|
@@ -145,8 +148,8 @@ export default function Login({ providers }: LoginProps) {
|
|
|
145
148
|
}
|
|
146
149
|
};
|
|
147
150
|
|
|
148
|
-
const showUsernamePasswordForm = providers?.includes('
|
|
149
|
-
const oauthProviders = providers?.filter(p => p !== '
|
|
151
|
+
const showUsernamePasswordForm = providers?.includes('userpass');
|
|
152
|
+
const oauthProviders = providers?.filter(p => p !== 'userpass') || [];
|
|
150
153
|
|
|
151
154
|
return (
|
|
152
155
|
<div className="flex flex-col items-center justify-center min-h-[500px] p-4">
|
package/components/menu.tsx
CHANGED
|
@@ -60,7 +60,6 @@ export default function Menu({ menu, onClick, pages = [] }: MenuProps) {
|
|
|
60
60
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
61
61
|
const [isMobile, setIsMobile] = useState(false);
|
|
62
62
|
const [selectedPage, setSelectedPage] = useState<string | null>(null);
|
|
63
|
-
const [isPaigeLoading, setIsPaigeLoading] = useState(false);
|
|
64
63
|
|
|
65
64
|
// Handle case where menu is undefined/null
|
|
66
65
|
if (!menu) {
|
|
@@ -230,7 +229,6 @@ export default function Menu({ menu, onClick, pages = [] }: MenuProps) {
|
|
|
230
229
|
items-center
|
|
231
230
|
justify-center
|
|
232
231
|
${isSelected ? 'border-blue-600 bg-blue-50' : ''}
|
|
233
|
-
${isPaigeLoading ? 'opacity-50 cursor-not-allowed' : ''}
|
|
234
232
|
`}>
|
|
235
233
|
<h3
|
|
236
234
|
className={`${isSelected ? 'font-bold' : 'font-medium'} text-gray-800`}
|
|
@@ -284,10 +282,6 @@ export default function Menu({ menu, onClick, pages = [] }: MenuProps) {
|
|
|
284
282
|
href={linkUrl}
|
|
285
283
|
onClick={(e) => {
|
|
286
284
|
e.preventDefault();
|
|
287
|
-
if (isPaigeLoading) {
|
|
288
|
-
console.log('Navigation blocked: Paige is currently processing a request');
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
285
|
setSelectedPage(item.page);
|
|
292
286
|
onClick?.();
|
|
293
287
|
}}
|
package/components/profile.tsx
CHANGED
|
@@ -9,9 +9,9 @@ checked in the system build settings. It is safe to modify this file without it
|
|
|
9
9
|
import { useState, useEffect } from 'react';
|
|
10
10
|
|
|
11
11
|
interface UserProfile {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
id: string;
|
|
13
|
+
username: string;
|
|
14
|
+
avatarurl: string | null;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export default function Profile() {
|
|
@@ -23,7 +23,8 @@ export default function Profile() {
|
|
|
23
23
|
const fetchProfile = async () => {
|
|
24
24
|
try {
|
|
25
25
|
const response = await fetch('/api/Auth', {
|
|
26
|
-
method: 'GET'
|
|
26
|
+
method: 'GET',
|
|
27
|
+
credentials: 'include'
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
if (!response.ok) {
|
|
@@ -39,9 +40,9 @@ export default function Profile() {
|
|
|
39
40
|
// Use the user object from the response
|
|
40
41
|
const userData = data.user;
|
|
41
42
|
setProfile({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
id: userData.userid,
|
|
44
|
+
username: userData.username,
|
|
45
|
+
avatarurl: userData.avatarurl
|
|
45
46
|
});
|
|
46
47
|
} catch (err) {
|
|
47
48
|
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
@@ -68,17 +69,17 @@ export default function Profile() {
|
|
|
68
69
|
return (
|
|
69
70
|
<div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
|
|
70
71
|
<div className="flex flex-col items-center">
|
|
71
|
-
{profile.
|
|
72
|
+
{profile.avatarurl && (
|
|
72
73
|
<img
|
|
73
|
-
src={profile.
|
|
74
|
+
src={profile.avatarurl}
|
|
74
75
|
alt="Profile avatar"
|
|
75
76
|
className="w-32 h-32 rounded-full mb-4"
|
|
76
77
|
referrerPolicy="no-referrer"
|
|
77
78
|
crossOrigin="anonymous"
|
|
78
79
|
/>
|
|
79
80
|
)}
|
|
80
|
-
<h2 className="text-2xl font-semibold text-gray-800">{profile.
|
|
81
|
-
<p className="text-gray-500 mt-2">User ID: {profile.
|
|
81
|
+
<h2 className="text-2xl font-semibold text-gray-800">{profile.username}</h2>
|
|
82
|
+
<p className="text-gray-500 mt-2">User ID: {profile.id}</p>
|
|
82
83
|
</div>
|
|
83
84
|
</div>
|
|
84
85
|
);
|
|
@@ -41,7 +41,7 @@ export async function POST(request: Request) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// Handle username/password authentication
|
|
44
|
-
if (provider === '
|
|
44
|
+
if (provider === 'userpass') {
|
|
45
45
|
if (!email || !password) {
|
|
46
46
|
return NextResponse.json(
|
|
47
47
|
{ error: 'Email and password are required' },
|
|
@@ -69,7 +69,7 @@ export async function POST(request: Request) {
|
|
|
69
69
|
// Create or update user in the main Users table
|
|
70
70
|
const user = await upsertUser(
|
|
71
71
|
`password_${authRecord.id}`, // Unique OAuth ID for password users
|
|
72
|
-
'
|
|
72
|
+
'userpass' as any, // Source type
|
|
73
73
|
email.split('@')[0], // Username from email
|
|
74
74
|
email,
|
|
75
75
|
undefined // No avatar for password auth
|
|
@@ -77,14 +77,14 @@ export async function POST(request: Request) {
|
|
|
77
77
|
|
|
78
78
|
// Delete existing sessions for this user
|
|
79
79
|
const existingSessions = await db_query(db,
|
|
80
|
-
"SELECT
|
|
80
|
+
"SELECT id FROM usersession WHERE userid = ?",
|
|
81
81
|
[user.userid]
|
|
82
82
|
);
|
|
83
83
|
|
|
84
84
|
if (existingSessions && existingSessions.length > 0) {
|
|
85
|
-
const sessionIds = existingSessions.map(session => session.
|
|
85
|
+
const sessionIds = existingSessions.map(session => session.id);
|
|
86
86
|
const placeholders = sessionIds.map(() => '?').join(',');
|
|
87
|
-
await db_query(db, `DELETE FROM usersession WHERE
|
|
87
|
+
await db_query(db, `DELETE FROM usersession WHERE id IN (${placeholders})`, sessionIds);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// Generate secure session token and ID
|
|
@@ -93,7 +93,7 @@ export async function POST(request: Request) {
|
|
|
93
93
|
|
|
94
94
|
// Create new session with secure token
|
|
95
95
|
await db_query(db,
|
|
96
|
-
"INSERT INTO usersession (
|
|
96
|
+
"INSERT INTO usersession (id, sessiontoken, userid, expirationdate) VALUES (?, ?, ?, ?)",
|
|
97
97
|
[sessionId, sessionToken, user.userid, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()]
|
|
98
98
|
);
|
|
99
99
|
|
|
@@ -112,10 +112,10 @@ export async function POST(request: Request) {
|
|
|
112
112
|
// Create a completely clean object to avoid any database result object issues
|
|
113
113
|
const cleanUserData = {
|
|
114
114
|
userid: String(user.userid),
|
|
115
|
-
userName: String(user.
|
|
116
|
-
avatarURL: String(user.
|
|
117
|
-
userLevel: Number(user.
|
|
118
|
-
isAdmin: Number(user.
|
|
115
|
+
userName: String(user.username),
|
|
116
|
+
avatarURL: String(user.avatarurl || ''),
|
|
117
|
+
userLevel: Number(user.userlevel),
|
|
118
|
+
isAdmin: Number(user.userlevel) === 2
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
return NextResponse.json({
|
|
@@ -152,7 +152,7 @@ export async function POST(request: Request) {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
let userData = {
|
|
155
|
-
|
|
155
|
+
id: '',
|
|
156
156
|
name: '',
|
|
157
157
|
email: '',
|
|
158
158
|
avatar_url: '',
|
|
@@ -203,28 +203,28 @@ export async function POST(request: Request) {
|
|
|
203
203
|
switch (validProvider) {
|
|
204
204
|
|
|
205
205
|
case 'google':
|
|
206
|
-
userData.
|
|
206
|
+
userData.id = fetchedUserData.id;
|
|
207
207
|
userData.name = fetchedUserData.name;
|
|
208
208
|
userData.email = fetchedUserData.email;
|
|
209
209
|
userData.avatar_url = fetchedUserData.picture;
|
|
210
210
|
break;
|
|
211
211
|
|
|
212
212
|
case 'facebook':
|
|
213
|
-
userData.
|
|
213
|
+
userData.id = fetchedUserData.id;
|
|
214
214
|
userData.name = fetchedUserData.name;
|
|
215
215
|
userData.email = fetchedUserData.email;
|
|
216
216
|
userData.avatar_url = fetchedUserData.picture?.data?.url;
|
|
217
217
|
break;
|
|
218
218
|
|
|
219
219
|
case 'apple':
|
|
220
|
-
userData.
|
|
220
|
+
userData.id = fetchedUserData.sub;
|
|
221
221
|
userData.name = `${fetchedUserData.given_name || ''} ${fetchedUserData.family_name || ''}`.trim();
|
|
222
222
|
userData.email = fetchedUserData.email;
|
|
223
223
|
// Apple doesn't provide avatar URL
|
|
224
224
|
break;
|
|
225
225
|
|
|
226
226
|
case 'github':
|
|
227
|
-
userData.
|
|
227
|
+
userData.id = fetchedUserData.id?.toString();
|
|
228
228
|
userData.name = fetchedUserData.name || fetchedUserData.login;
|
|
229
229
|
userData.email = fetchedUserData.email;
|
|
230
230
|
userData.avatar_url = fetchedUserData.avatar_url;
|
|
@@ -239,7 +239,7 @@ export async function POST(request: Request) {
|
|
|
239
239
|
|
|
240
240
|
// Create or update user using the new user management system
|
|
241
241
|
const user = await upsertUser(
|
|
242
|
-
userData.
|
|
242
|
+
userData.id,
|
|
243
243
|
validProvider,
|
|
244
244
|
userData.name,
|
|
245
245
|
userData.email,
|
|
@@ -257,14 +257,14 @@ export async function POST(request: Request) {
|
|
|
257
257
|
|
|
258
258
|
// Delete existing sessions for this user
|
|
259
259
|
const existingSessions = await db_query(db,
|
|
260
|
-
"SELECT
|
|
260
|
+
"SELECT id FROM usersession WHERE userid = ?",
|
|
261
261
|
[user.userid]
|
|
262
262
|
);
|
|
263
263
|
|
|
264
264
|
if (existingSessions && existingSessions.length > 0) {
|
|
265
|
-
const sessionIds = existingSessions.map(session => session.
|
|
265
|
+
const sessionIds = existingSessions.map(session => session.id);
|
|
266
266
|
const placeholders = sessionIds.map(() => '?').join(',');
|
|
267
|
-
await db_query(db, `DELETE FROM usersession WHERE
|
|
267
|
+
await db_query(db, `DELETE FROM usersession WHERE id IN (${placeholders})`, sessionIds);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
// Generate secure session token and ID
|
|
@@ -273,7 +273,7 @@ export async function POST(request: Request) {
|
|
|
273
273
|
|
|
274
274
|
// Create new session with secure token
|
|
275
275
|
await db_query(db,
|
|
276
|
-
"INSERT INTO usersession (
|
|
276
|
+
"INSERT INTO usersession (id, sessiontoken, userid, expirationdate) VALUES (?, ?, ?, ?)",
|
|
277
277
|
[sessionId, sessionToken, user.userid, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()]
|
|
278
278
|
);
|
|
279
279
|
|
|
@@ -292,10 +292,10 @@ export async function POST(request: Request) {
|
|
|
292
292
|
// Create a completely clean object to avoid any database result object issues
|
|
293
293
|
const cleanUserData = {
|
|
294
294
|
userid: String(user.userid),
|
|
295
|
-
userName: String(user.
|
|
296
|
-
avatarURL: String(user.
|
|
297
|
-
userLevel: Number(user.
|
|
298
|
-
isAdmin: Number(user.
|
|
295
|
+
userName: String(user.username),
|
|
296
|
+
avatarURL: String(user.avatarurl || ''),
|
|
297
|
+
userLevel: Number(user.userlevel),
|
|
298
|
+
isAdmin: Number(user.userlevel) === 2
|
|
299
299
|
};
|
|
300
300
|
|
|
301
301
|
return NextResponse.json({
|
|
@@ -346,13 +346,13 @@ export async function GET() {
|
|
|
346
346
|
const response = NextResponse.json({
|
|
347
347
|
user: {
|
|
348
348
|
userid: sessionData.user.userid,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
349
|
+
username: sessionData.user.username,
|
|
350
|
+
avatarurl: sessionData.user.avatarurl,
|
|
351
|
+
email: sessionData.user.email,
|
|
352
|
+
userlevel: sessionData.user.userlevel,
|
|
353
|
+
isadmin: sessionData.user.userlevel === 2,
|
|
354
|
+
source: sessionData.user.source,
|
|
355
|
+
lastlogindate: sessionData.user.lastlogindate
|
|
356
356
|
}
|
|
357
357
|
});
|
|
358
358
|
|
|
@@ -375,13 +375,13 @@ export async function GET() {
|
|
|
375
375
|
return NextResponse.json({
|
|
376
376
|
user: {
|
|
377
377
|
userid: sessionData.user.userid,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
378
|
+
username: sessionData.user.username,
|
|
379
|
+
avatarurl: sessionData.user.avatarurl,
|
|
380
|
+
email: sessionData.user.email,
|
|
381
|
+
userlevel: sessionData.user.userlevel,
|
|
382
|
+
isadmin: sessionData.user.userlevel === 2,
|
|
383
|
+
source: sessionData.user.source,
|
|
384
|
+
lastlogindate: sessionData.user.lastlogindate
|
|
385
385
|
}
|
|
386
386
|
});
|
|
387
387
|
|
|
@@ -410,7 +410,7 @@ export async function DELETE(request: Request) {
|
|
|
410
410
|
|
|
411
411
|
// Delete session from database using the actual session token
|
|
412
412
|
await db_query(db,
|
|
413
|
-
"DELETE FROM usersession WHERE
|
|
413
|
+
"DELETE FROM usersession WHERE sessiontoken = ?",
|
|
414
414
|
[sessionToken]
|
|
415
415
|
);
|
|
416
416
|
|
|
@@ -53,14 +53,14 @@ export async function GET(request: Request) {
|
|
|
53
53
|
|
|
54
54
|
// Delete existing sessions for this user
|
|
55
55
|
const existingSessions = await db_query(db,
|
|
56
|
-
"SELECT
|
|
56
|
+
"SELECT id FROM usersession WHERE userid = ?",
|
|
57
57
|
[user.userid]
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
if (existingSessions && existingSessions.length > 0) {
|
|
61
|
-
const sessionIds = existingSessions.map(session => session.
|
|
61
|
+
const sessionIds = existingSessions.map(session => session.id);
|
|
62
62
|
const placeholders = sessionIds.map(() => '?').join(',');
|
|
63
|
-
await db_query(db, `DELETE FROM usersession WHERE
|
|
63
|
+
await db_query(db, `DELETE FROM usersession WHERE id IN (${placeholders})`, sessionIds);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Generate secure session token and ID
|
|
@@ -69,7 +69,7 @@ export async function GET(request: Request) {
|
|
|
69
69
|
|
|
70
70
|
// Create new session with secure token
|
|
71
71
|
await db_query(db,
|
|
72
|
-
"INSERT INTO usersession (
|
|
72
|
+
"INSERT INTO usersession (id, sessiontoken, userid, expirationdate) VALUES (?, ?, ?, ?)",
|
|
73
73
|
[sessionId, sessionToken, user.userid, new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()]
|
|
74
74
|
);
|
|
75
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
|
|