sitepaige-mcp-server 1.0.3 → 1.2.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 +19 -37
- package/components/login.tsx +7 -4
- package/components/logincallback.tsx +1 -0
- package/components/menu.tsx +0 -6
- package/components/profile.tsx +304 -47
- package/defaultapp/api/Auth/resend-verification/route.ts +1 -1
- package/defaultapp/api/Auth/route.ts +39 -39
- package/defaultapp/api/Auth/signup/route.ts +1 -1
- package/defaultapp/api/Auth/verify-email/route.ts +6 -6
- package/defaultapp/api/admin/users/route.ts +5 -3
- package/defaultapp/api/profile/route.ts +154 -0
- 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 +64 -64
- package/defaultapp/profile/page.tsx +6 -0
- package/dist/blueprintWriter.js.map +1 -1
- 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 +19 -37
- 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 +304 -47
- package/dist/defaultapp/api/Auth/resend-verification/route.ts +1 -1
- package/dist/defaultapp/api/Auth/route.ts +39 -39
- package/dist/defaultapp/api/Auth/signup/route.ts +1 -1
- package/dist/defaultapp/api/Auth/verify-email/route.ts +6 -6
- package/dist/defaultapp/api/admin/users/route.ts +5 -3
- package/dist/defaultapp/api/profile/route.ts +154 -0
- 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 +64 -64
- package/dist/defaultapp/profile/page.tsx +6 -0
- package/dist/generators/env-example-template.txt +4 -3
- package/dist/generators/sql.js +11 -3
- package/dist/generators/sql.js.map +1 -1
- package/dist/generators/views.js +2 -2
- package/dist/sitepaige.js +2 -1
- package/dist/sitepaige.js.map +1 -1
- package/package.json +1 -1
- package/defaultapp/admin/page.tsx +0 -6
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 => {
|
|
@@ -45,7 +45,6 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
45
45
|
const [userData, setUserData] = useState<UserData | null>(null);
|
|
46
46
|
const [isLoading, setIsLoading] = useState(true);
|
|
47
47
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
48
|
-
const [profileMenuItems, setProfileMenuItems] = useState<MenuItem[]>([]);
|
|
49
48
|
const userMenuRef = useRef<HTMLDivElement>(null);
|
|
50
49
|
|
|
51
50
|
// Hardcoded values
|
|
@@ -53,33 +52,16 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
53
52
|
const align = 'Right' as "Left" | "Center" | "Right";
|
|
54
53
|
const mobileViewMode = 'desktop' as 'desktop' | 'phone' | 'sm-tablet' | 'lg-tablet';
|
|
55
54
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
setProfileMenuItems([
|
|
67
|
-
{ label: 'Profile', link: '/profile' },
|
|
68
|
-
{ label: 'Settings', link: '/settings' }
|
|
69
|
-
]);
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
console.error('Failed to load profile menu from localStorage:', error);
|
|
73
|
-
// Default fallback menu items
|
|
74
|
-
setProfileMenuItems([
|
|
75
|
-
{ label: 'Profile', link: '/profile' },
|
|
76
|
-
{ label: 'Settings', link: '/settings' }
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
loadProfileMenuItems();
|
|
82
|
-
}, []);
|
|
55
|
+
// Build menu items based on user level
|
|
56
|
+
const profileMenuItems: MenuItem[] = [
|
|
57
|
+
{ label: 'Dashboard', link: '/dashboard' },
|
|
58
|
+
{ label: 'Profile', link: '/profile' }
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Add Admin Dashboard only if user is an admin (userlevel == 2)
|
|
62
|
+
if (userData?.userlevel === 2) {
|
|
63
|
+
profileMenuItems.push({ label: 'Admin Dashboard', link: '/admin_dashboard' });
|
|
64
|
+
}
|
|
83
65
|
|
|
84
66
|
// Fetch user authentication status and data
|
|
85
67
|
useEffect(() => {
|
|
@@ -210,11 +192,11 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
210
192
|
className="flex items-center focus:outline-none relative group cursor-pointer"
|
|
211
193
|
style={{ color: textColor }}
|
|
212
194
|
>
|
|
213
|
-
<span className="mr-2" style={{ color: textColor }}>{userData.
|
|
195
|
+
<span className="mr-2" style={{ color: textColor }}>{userData.username || getLocalizedText('Sample User', websiteLanguage)}</span>
|
|
214
196
|
<div className="relative">
|
|
215
|
-
{userData.
|
|
197
|
+
{userData.avatarurl ? (
|
|
216
198
|
<img
|
|
217
|
-
src={userData.
|
|
199
|
+
src={userData.avatarurl}
|
|
218
200
|
alt="User avatar"
|
|
219
201
|
className="w-10 h-10 rounded-full"
|
|
220
202
|
referrerPolicy="no-referrer"
|
|
@@ -222,7 +204,7 @@ const LoginSection: React.FC<LoginSectionProps> = ({ websiteLanguage = 'English'
|
|
|
222
204
|
/>
|
|
223
205
|
) : (
|
|
224
206
|
<div className="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold">
|
|
225
|
-
{userData.
|
|
207
|
+
{userData.username ? userData.username.charAt(0).toUpperCase() : 'U'}
|
|
226
208
|
</div>
|
|
227
209
|
)}
|
|
228
210
|
</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
|
}}
|