sitepaige-mcp-server 1.2.0 → 1.2.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.
Files changed (41) hide show
  1. package/components/slideshow.tsx +1 -9
  2. package/defaultapp/api/Auth/resend-verification/route.ts +2 -2
  3. package/defaultapp/api/Auth/route.ts +2 -2
  4. package/defaultapp/api/Auth/signup/route.ts +2 -2
  5. package/defaultapp/api/Auth/verify-email/route.ts +3 -3
  6. package/defaultapp/api/admin/users/route.ts +3 -3
  7. package/dist/components/slideshow.tsx +1 -9
  8. package/dist/defaultapp/api/Auth/auth.ts +32 -0
  9. package/dist/defaultapp/api/Auth/resend-verification/route.ts +2 -2
  10. package/dist/defaultapp/api/Auth/route.ts +2 -2
  11. package/dist/defaultapp/api/Auth/signup/route.ts +2 -2
  12. package/dist/defaultapp/api/Auth/verify-email/route.ts +3 -3
  13. package/dist/defaultapp/api/admin/users/route.ts +3 -3
  14. package/dist/defaultapp/api/csrf.ts +111 -0
  15. package/dist/defaultapp/api/db-mysql.ts +183 -0
  16. package/dist/defaultapp/api/db-password-auth.ts +362 -0
  17. package/dist/defaultapp/api/db-postgres.ts +189 -0
  18. package/dist/defaultapp/api/db-sqlite.ts +335 -0
  19. package/dist/defaultapp/api/db-users.ts +520 -0
  20. package/dist/defaultapp/api/db.ts +149 -0
  21. package/dist/defaultapp/api/storage/email.ts +162 -0
  22. package/dist/defaultapp/api/storage/files.ts +160 -0
  23. package/dist/generators/apis.js +2 -2
  24. package/dist/generators/apis.js.map +1 -1
  25. package/dist/generators/skeleton.js +3 -5
  26. package/dist/generators/skeleton.js.map +1 -1
  27. package/dist/generators/sql.js +60 -0
  28. package/dist/generators/sql.js.map +1 -1
  29. package/package.json +1 -1
  30. package/defaultapp/api/example-secure/route.ts +0 -100
  31. package/defaultapp/migrate.ts +0 -142
  32. /package/defaultapp/{auth → api/Auth}/auth.ts +0 -0
  33. /package/defaultapp/{csrf.ts → api/csrf.ts} +0 -0
  34. /package/defaultapp/{db-mysql.ts → api/db-mysql.ts} +0 -0
  35. /package/defaultapp/{db-password-auth.ts → api/db-password-auth.ts} +0 -0
  36. /package/defaultapp/{db-postgres.ts → api/db-postgres.ts} +0 -0
  37. /package/defaultapp/{db-sqlite.ts → api/db-sqlite.ts} +0 -0
  38. /package/defaultapp/{db-users.ts → api/db-users.ts} +0 -0
  39. /package/defaultapp/{db.ts → api/db.ts} +0 -0
  40. /package/defaultapp/{storage → api/storage}/email.ts +0 -0
  41. /package/defaultapp/{storage → api/storage}/files.ts +0 -0
@@ -80,10 +80,6 @@ export default function Slideshow({
80
80
  return (
81
81
  <div
82
82
  className="relative w-full flex items-center justify-center bg-black rounded-lg"
83
- style={{
84
- height: height || 400,
85
- maxHeight: '1000px'
86
- }}
87
83
  >
88
84
  <p className="text-gray-400">No images in slideshow</p>
89
85
  </div>
@@ -102,15 +98,11 @@ export default function Slideshow({
102
98
  return (
103
99
  <div
104
100
  className="relative w-full overflow-hidden rounded-lg bg-black"
105
- style={{
106
- height: height || 600,
107
- maxHeight: '1000px'
108
- }}
109
101
  onMouseEnter={() => setIsHovered(true)}
110
102
  onMouseLeave={() => setIsHovered(false)}
111
103
  >
112
104
  {/* Main image display */}
113
- <div className="absolute inset-0 flex items-center justify-center">
105
+ <div className="flex items-center justify-center">
114
106
  {animationType === 'fade' ? (
115
107
  // Fade animation - render all images with opacity transitions
116
108
  images.map((imageId, index) => (
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { regenerateVerificationToken } from '../../../db-password-auth';
8
- import { send_email } from '../../../storage/email';
7
+ import { regenerateVerificationToken } from '../../db-password-auth';
8
+ import { send_email } from '../../storage/email';
9
9
 
10
10
  // Email validation regex
11
11
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -8,8 +8,8 @@ import { cookies } from 'next/headers';
8
8
  import { NextResponse } from 'next/server';
9
9
  import * as crypto from 'node:crypto';
10
10
 
11
- import { db_init, db_query } from '../../db';
12
- import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '../../db-users';
11
+ import { db_init, db_query } from '../db';
12
+ import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '../db-users';
13
13
 
14
14
  type OAuthProvider = 'google' | 'facebook' | 'apple' | 'github';
15
15
 
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { createPasswordAuth } from '../../../db-password-auth';
8
- import { send_email } from '../../../storage/email';
7
+ import { createPasswordAuth } from '../../db-password-auth';
8
+ import { send_email } from '../../storage/email';
9
9
 
10
10
  // Email validation regex
11
11
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { verifyEmailWithToken } from '../../../db-password-auth';
8
- import { upsertUser } from '../../../db-users';
9
- import { db_init, db_query } from '../../../db';
7
+ import { verifyEmailWithToken } from '../../db-password-auth';
8
+ import { upsertUser } from '../../db-users';
9
+ import { db_init, db_query } from '../../db';
10
10
  import { cookies } from 'next/headers';
11
11
  import * as crypto from 'node:crypto';
12
12
 
@@ -5,15 +5,15 @@
5
5
 
6
6
  import { NextRequest, NextResponse } from 'next/server';
7
7
  import { cookies } from 'next/headers';
8
- import { db_init, db_query } from '../../../db';
8
+ import { db_init, db_query } from '../../db';
9
9
  import {
10
10
  getAllUsers,
11
11
  updateUserPermission,
12
12
  deleteUser,
13
13
  getUserStats,
14
14
  getUserByID
15
- } from '../../../db-users';
16
- import { validateCsrfToken } from '../../../csrf';
15
+ } from '../../db-users';
16
+ import { validateCsrfToken } from '../../csrf';
17
17
 
18
18
  // Helper function to check if the current user is an admin
19
19
  async function checkAdminAuth(): Promise<{ isAdmin: boolean; userId?: string }> {
@@ -80,10 +80,6 @@ export default function Slideshow({
80
80
  return (
81
81
  <div
82
82
  className="relative w-full flex items-center justify-center bg-black rounded-lg"
83
- style={{
84
- height: height || 400,
85
- maxHeight: '1000px'
86
- }}
87
83
  >
88
84
  <p className="text-gray-400">No images in slideshow</p>
89
85
  </div>
@@ -102,15 +98,11 @@ export default function Slideshow({
102
98
  return (
103
99
  <div
104
100
  className="relative w-full overflow-hidden rounded-lg bg-black"
105
- style={{
106
- height: height || 600,
107
- maxHeight: '1000px'
108
- }}
109
101
  onMouseEnter={() => setIsHovered(true)}
110
102
  onMouseLeave={() => setIsHovered(false)}
111
103
  >
112
104
  {/* Main image display */}
113
- <div className="absolute inset-0 flex items-center justify-center">
105
+ <div className="flex items-center justify-center">
114
106
  {animationType === 'fade' ? (
115
107
  // Fade animation - render all images with opacity transitions
116
108
  images.map((imageId, index) => (
@@ -0,0 +1,32 @@
1
+ import { cookies } from 'next/headers';
2
+
3
+ export async function check_auth(db: any, db_query: any): Promise<{ userid: string, userlevel: number, usertier: number, isadmin: boolean }> {
4
+ const cookieStore = await cookies();
5
+ const sessionId = cookieStore.get('session_id')?.value;
6
+
7
+ const sessionInfo = {
8
+ userid: '',
9
+ userlevel: 0,
10
+ usertier: 0,
11
+ isadmin: false
12
+ };
13
+
14
+ if (!sessionId) {
15
+ return sessionInfo;
16
+ }
17
+
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 usersession WHERE sessiontoken = ?', [sessionId]);
20
+ if (session.length > 0) {
21
+ sessionInfo.userid = session[0].userid;
22
+
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 userlevel, usertier FROM users WHERE userid = ?', [session[0].userid]);
25
+ if (user.length > 0) {
26
+ sessionInfo.userlevel = user[0].userlevel;
27
+ sessionInfo.isadmin = user[0].userlevel === 2;
28
+ sessionInfo.usertier = user[0].usertier;
29
+ }
30
+ }
31
+ return sessionInfo;
32
+ }
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { regenerateVerificationToken } from '../../../db-password-auth';
8
- import { send_email } from '../../../storage/email';
7
+ import { regenerateVerificationToken } from '../../db-password-auth';
8
+ import { send_email } from '../../storage/email';
9
9
 
10
10
  // Email validation regex
11
11
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -8,8 +8,8 @@ import { cookies } from 'next/headers';
8
8
  import { NextResponse } from 'next/server';
9
9
  import * as crypto from 'node:crypto';
10
10
 
11
- import { db_init, db_query } from '../../db';
12
- import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '../../db-users';
11
+ import { db_init, db_query } from '../db';
12
+ import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '../db-users';
13
13
 
14
14
  type OAuthProvider = 'google' | 'facebook' | 'apple' | 'github';
15
15
 
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { createPasswordAuth } from '../../../db-password-auth';
8
- import { send_email } from '../../../storage/email';
7
+ import { createPasswordAuth } from '../../db-password-auth';
8
+ import { send_email } from '../../storage/email';
9
9
 
10
10
  // Email validation regex
11
11
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import { NextResponse } from 'next/server';
7
- import { verifyEmailWithToken } from '../../../db-password-auth';
8
- import { upsertUser } from '../../../db-users';
9
- import { db_init, db_query } from '../../../db';
7
+ import { verifyEmailWithToken } from '../../db-password-auth';
8
+ import { upsertUser } from '../../db-users';
9
+ import { db_init, db_query } from '../../db';
10
10
  import { cookies } from 'next/headers';
11
11
  import * as crypto from 'node:crypto';
12
12
 
@@ -5,15 +5,15 @@
5
5
 
6
6
  import { NextRequest, NextResponse } from 'next/server';
7
7
  import { cookies } from 'next/headers';
8
- import { db_init, db_query } from '../../../db';
8
+ import { db_init, db_query } from '../../db';
9
9
  import {
10
10
  getAllUsers,
11
11
  updateUserPermission,
12
12
  deleteUser,
13
13
  getUserStats,
14
14
  getUserByID
15
- } from '../../../db-users';
16
- import { validateCsrfToken } from '../../../csrf';
15
+ } from '../../db-users';
16
+ import { validateCsrfToken } from '../../csrf';
17
17
 
18
18
  // Helper function to check if the current user is an admin
19
19
  async function checkAdminAuth(): Promise<{ isAdmin: boolean; userId?: string }> {
@@ -0,0 +1,111 @@
1
+ /*
2
+ Sitepaige v1.0.0
3
+ CSRF Protection Utility
4
+ WARNING: This file is automatically generated and should not be modified.
5
+ */
6
+
7
+ import { cookies, headers } from 'next/headers';
8
+ import { NextRequest } from 'next/server';
9
+
10
+ export class CsrfError extends Error {
11
+ constructor(message: string) {
12
+ super(message);
13
+ this.name = 'CsrfError';
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Generates a new CSRF token
19
+ */
20
+ export function generateCsrfToken(): string {
21
+ return crypto.randomUUID();
22
+ }
23
+
24
+ /**
25
+ * Gets the CSRF token from cookies
26
+ */
27
+ export async function getCsrfToken(): Promise<string | undefined> {
28
+ const cookieStore = await cookies();
29
+ return cookieStore.get('csrf-token')?.value;
30
+ }
31
+
32
+ /**
33
+ * Validates CSRF token from request
34
+ * Checks both header and form data for the token
35
+ */
36
+ export async function validateCsrfToken(request: NextRequest | Request): Promise<boolean> {
37
+ // Skip CSRF validation for GET and HEAD requests
38
+ if (request.method === 'GET' || request.method === 'HEAD') {
39
+ return true;
40
+ }
41
+
42
+ const cookieStore = await cookies();
43
+ const headerStore = await headers();
44
+ const cookieToken = cookieStore.get('csrf-token')?.value;
45
+
46
+ if (!cookieToken) {
47
+ return false;
48
+ }
49
+
50
+ // Check X-CSRF-Token header first
51
+ const headerToken = headerStore.get('x-csrf-token');
52
+ if (headerToken === cookieToken) {
53
+ return true;
54
+ }
55
+
56
+ // For form submissions, check the request body
57
+ if (request.headers.get('content-type')?.includes('application/x-www-form-urlencoded')) {
58
+ try {
59
+ const formData = await request.formData();
60
+ const formToken = formData.get('csrf_token');
61
+ if (formToken === cookieToken) {
62
+ return true;
63
+ }
64
+ } catch {
65
+ // If form parsing fails, continue to check JSON body
66
+ }
67
+ }
68
+
69
+ // For JSON requests, check the body
70
+ if (request.headers.get('content-type')?.includes('application/json')) {
71
+ try {
72
+ const body = await request.json();
73
+ if (body.csrf_token === cookieToken) {
74
+ return true;
75
+ }
76
+ } catch {
77
+ // If JSON parsing fails, token is invalid
78
+ }
79
+ }
80
+
81
+ return false;
82
+ }
83
+
84
+ /**
85
+ * Middleware helper to validate CSRF token
86
+ * Throws CsrfError if token is invalid
87
+ */
88
+ export async function requireCsrfToken(request: NextRequest | Request): Promise<void> {
89
+ const isValid = await validateCsrfToken(request);
90
+ if (!isValid) {
91
+ throw new CsrfError('Invalid or missing CSRF token');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * React hook helper to get CSRF token for client-side forms
97
+ */
98
+ export function useCsrfToken(): { token: string | undefined; header: Record<string, string> } {
99
+ // This will be used client-side
100
+ const token = typeof document !== 'undefined'
101
+ ? document.cookie
102
+ .split('; ')
103
+ .find(row => row.startsWith('csrf-token='))
104
+ ?.split('=')[1]
105
+ : undefined;
106
+
107
+ return {
108
+ token,
109
+ header: token ? { 'X-CSRF-Token': token } : {}
110
+ };
111
+ }
@@ -0,0 +1,183 @@
1
+ /*
2
+ Sitepaige v1.0.0
3
+ MySQL database implementation
4
+ WARNING: This file is automatically generated and should not be modified.
5
+ */
6
+
7
+ import mysql from 'mysql2/promise';
8
+ import type { Model, ModelField } from './db';
9
+
10
+ // Connection pool for better performance
11
+ let pool: mysql.Pool | null = null;
12
+
13
+ /**
14
+ * Initialize database connection
15
+ * @returns Database client (Pool)
16
+ */
17
+ export async function db_init(): Promise<mysql.Pool> {
18
+
19
+
20
+ // Return existing pool if already initialized
21
+ if (pool) {
22
+ return pool;
23
+ }
24
+
25
+ try {
26
+ // Create connection configuration
27
+ const poolConfig: mysql.PoolOptions = {
28
+ host: process.env.DB_HOST || 'localhost',
29
+ port: parseInt(process.env.DB_PORT || '3306'),
30
+ user: process.env.DB_USER || 'root',
31
+ password: process.env.DB_PASSWORD,
32
+ database: process.env.DB_NAME || 'app',
33
+ waitForConnections: true,
34
+ connectionLimit: 20,
35
+ queueLimit: 0,
36
+ enableKeepAlive: true,
37
+ keepAliveInitialDelay: 0
38
+ };
39
+
40
+ // Create the pool
41
+ pool = mysql.createPool(poolConfig);
42
+
43
+
44
+ return pool;
45
+ } catch (error) {
46
+ throw error;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Execute a SQL query using the provided MySQL client
52
+ * @param client Database client from db_init()
53
+ * @param query SQL query string (MySQL syntax with ? parameter placeholders)
54
+ * @param params Optional array of parameters for the query
55
+ * @returns Array of selected rows or execution results
56
+ */
57
+ export async function db_query(
58
+ client: mysql.Pool,
59
+ query: string,
60
+ params?: any[]
61
+ ): Promise<any[]> {
62
+
63
+
64
+ try {
65
+ // MySQL uses ? placeholders like SQLite, so no conversion needed
66
+ const [results] = await client.execute(query, params);
67
+
68
+ // Handle different result types
69
+ if (Array.isArray(results)) {
70
+ // SELECT query results
71
+ return results;
72
+ } else {
73
+ // INSERT, UPDATE, DELETE results
74
+ const resultInfo = results as mysql.ResultSetHeader;
75
+ return [{
76
+ changes: resultInfo.affectedRows,
77
+ insertId: resultInfo.insertId
78
+ }];
79
+ }
80
+
81
+ } catch (error) {
82
+ // If table doesn't exist for SELECT, return empty array
83
+ if (error instanceof Error && error.message.includes("doesn't exist") && query.trim().toUpperCase().startsWith('SELECT')) {
84
+ return [];
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Generates a CREATE TABLE SQL string for the specified table and fields
92
+ * @param model The model definition
93
+ * @param dbType Database type
94
+ * @returns SQL string for creating the table
95
+ */
96
+ export function db_migrate(model: Model, dbType: string): string {
97
+
98
+ const sanitizedTableName = model.name.toLowerCase().replace(/\s+/g, '_');
99
+
100
+ // Start with the model's fields
101
+ let fields = [...model.fields];
102
+
103
+ // Add userid field if data is user specific
104
+ if (model.data_is_user_specific === "true") {
105
+ fields.push({
106
+ name: 'userid',
107
+ datatype: 'VARCHAR',
108
+ datatypesize: '36', // UUID as string
109
+ key: 'foreign',
110
+ required: 'true',
111
+ default: undefined
112
+ });
113
+ }
114
+
115
+ // Map SQL data types for MySQL
116
+ const sqlTypeMap: { [key: string]: string } = {
117
+ 'UUID': 'VARCHAR(36)',
118
+ 'TINYINT': 'TINYINT',
119
+ 'SMALLINT': 'SMALLINT',
120
+ 'BIGINT': 'BIGINT',
121
+ 'INT128': 'DECIMAL(39,0)',
122
+ 'VARCHAR': 'VARCHAR',
123
+ 'TEXT': 'TEXT',
124
+ 'BINARY': 'BLOB',
125
+ 'DATE': 'DATE',
126
+ 'TIME': 'TIME',
127
+ 'DATETIME': 'DATETIME',
128
+ 'DOUBLE': 'DOUBLE',
129
+ 'FLOAT': 'FLOAT',
130
+ 'BOOLEAN': 'BOOLEAN'
131
+ };
132
+
133
+ // Build column definitions
134
+ const columnDefs = fields.sort((a, b) => (b.key === 'primary' ? 1 : 0) - (a.key === 'primary' ? 1 : 0)).map(field => {
135
+ const dataType = field.datatype.toUpperCase();
136
+ let sqlType = sqlTypeMap[dataType] || dataType;
137
+
138
+ // Add size for VARCHAR
139
+ if (dataType === 'VARCHAR' && field.datatypesize) {
140
+ sqlType = `VARCHAR(${field.datatypesize})`;
141
+ }
142
+
143
+ let def = `\`${field.name}\` ${sqlType}`;
144
+
145
+ // Add NOT NULL if required
146
+ if (field.required === "true") {
147
+ def += ' NOT NULL';
148
+ }
149
+
150
+ // Add default if specified
151
+ if (field.default !== undefined) {
152
+ if (field.datatype === 'VARCHAR' || field.datatype === 'TEXT') {
153
+ def += ` DEFAULT '${field.default}'`;
154
+ } else if (field.datatype === 'BOOLEAN') {
155
+ def += ` DEFAULT ${field.default === 'true' || field.default === true ? 1 : 0}`;
156
+ } else {
157
+ def += ` DEFAULT ${field.default}`;
158
+ }
159
+ }
160
+
161
+ // Add primary key constraint
162
+ if (field.key === 'primary') {
163
+ def += ' PRIMARY KEY';
164
+ }
165
+
166
+ return def;
167
+ }).join(',\n ');
168
+
169
+ // Add foreign key constraints at the end
170
+ const foreignKeys: string[] = [];
171
+ if (model.data_is_user_specific === "true") {
172
+ foreignKeys.push(`FOREIGN KEY (\`userid\`) REFERENCES \`users\` (\`userid\`)`);
173
+ }
174
+
175
+ const allConstraints = foreignKeys.length > 0
176
+ ? columnDefs + ',\n ' + foreignKeys.join(',\n ')
177
+ : columnDefs;
178
+
179
+ // Build the CREATE TABLE statement
180
+ const sql = `CREATE TABLE IF NOT EXISTS \`${sanitizedTableName}\` (\n ${allConstraints}\n);`;
181
+
182
+ return sql;
183
+ }