sitepaige-mcp-server 1.2.0 → 1.2.1
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/slideshow.tsx +1 -9
- package/defaultapp/api/Auth/resend-verification/route.ts +2 -2
- package/defaultapp/api/Auth/route.ts +2 -2
- package/defaultapp/api/Auth/signup/route.ts +2 -2
- package/defaultapp/api/Auth/verify-email/route.ts +3 -3
- package/defaultapp/api/admin/users/route.ts +3 -3
- package/dist/components/slideshow.tsx +1 -9
- package/dist/defaultapp/api/Auth/resend-verification/route.ts +2 -2
- package/dist/defaultapp/api/Auth/route.ts +2 -2
- package/dist/defaultapp/api/Auth/signup/route.ts +2 -2
- package/dist/defaultapp/api/Auth/verify-email/route.ts +3 -3
- package/dist/defaultapp/api/admin/users/route.ts +3 -3
- package/dist/defaultapp/api/csrf.ts +111 -0
- package/dist/defaultapp/api/db-mysql.ts +183 -0
- package/dist/defaultapp/api/db-password-auth.ts +362 -0
- package/dist/defaultapp/api/db-postgres.ts +189 -0
- package/dist/defaultapp/api/db-sqlite.ts +335 -0
- package/dist/defaultapp/api/db-users.ts +520 -0
- package/dist/defaultapp/api/db.ts +149 -0
- package/dist/defaultapp/api/storage/email.ts +162 -0
- package/dist/defaultapp/api/storage/files.ts +160 -0
- package/dist/generators/skeleton.js +3 -5
- package/dist/generators/skeleton.js.map +1 -1
- package/dist/generators/sql.js +60 -0
- package/dist/generators/sql.js.map +1 -1
- package/package.json +1 -1
- package/defaultapp/api/example-secure/route.ts +0 -100
- package/defaultapp/migrate.ts +0 -142
- /package/defaultapp/{csrf.ts → api/csrf.ts} +0 -0
- /package/defaultapp/{db-mysql.ts → api/db-mysql.ts} +0 -0
- /package/defaultapp/{db-password-auth.ts → api/db-password-auth.ts} +0 -0
- /package/defaultapp/{db-postgres.ts → api/db-postgres.ts} +0 -0
- /package/defaultapp/{db-sqlite.ts → api/db-sqlite.ts} +0 -0
- /package/defaultapp/{db-users.ts → api/db-users.ts} +0 -0
- /package/defaultapp/{db.ts → api/db.ts} +0 -0
- /package/defaultapp/{storage → api/storage}/email.ts +0 -0
- /package/defaultapp/{storage → api/storage}/files.ts +0 -0
package/components/slideshow.tsx
CHANGED
|
@@ -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="
|
|
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 '
|
|
8
|
-
import { send_email } from '
|
|
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 '
|
|
12
|
-
import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '
|
|
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 '
|
|
8
|
-
import { send_email } from '
|
|
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 '
|
|
8
|
-
import { upsertUser } from '
|
|
9
|
-
import { db_init, db_query } from '
|
|
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 '
|
|
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 '
|
|
16
|
-
import { validateCsrfToken } from '
|
|
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="
|
|
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 '
|
|
8
|
-
import { send_email } from '
|
|
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 '
|
|
12
|
-
import { upsertUser, storeOAuthToken, validateSession, rotateSession } from '
|
|
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 '
|
|
8
|
-
import { send_email } from '
|
|
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 '
|
|
8
|
-
import { upsertUser } from '
|
|
9
|
-
import { db_init, db_query } from '
|
|
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 '
|
|
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 '
|
|
16
|
-
import { validateCsrfToken } from '
|
|
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
|
+
}
|