sitepaige-mcp-server 1.1.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/headerlogin.tsx +10 -28
- package/components/profile.tsx +302 -46
- package/components/slideshow.tsx +1 -9
- package/defaultapp/api/Auth/resend-verification/route.ts +3 -3
- package/defaultapp/api/Auth/route.ts +2 -2
- package/defaultapp/api/Auth/signup/route.ts +3 -3
- package/defaultapp/api/Auth/verify-email/route.ts +5 -5
- package/defaultapp/api/admin/users/route.ts +3 -3
- package/defaultapp/{db-users.ts → api/db-users.ts} +1 -1
- package/defaultapp/api/profile/route.ts +154 -0
- package/defaultapp/profile/page.tsx +6 -0
- package/dist/blueprintWriter.js.map +1 -1
- package/dist/components/headerlogin.tsx +10 -28
- package/dist/components/profile.tsx +302 -46
- package/dist/components/slideshow.tsx +1 -9
- package/dist/defaultapp/api/Auth/resend-verification/route.ts +3 -3
- package/dist/defaultapp/api/Auth/route.ts +2 -2
- package/dist/defaultapp/api/Auth/signup/route.ts +3 -3
- package/dist/defaultapp/api/Auth/verify-email/route.ts +5 -5
- 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/profile/route.ts +154 -0
- package/dist/defaultapp/api/storage/email.ts +162 -0
- package/dist/defaultapp/api/storage/files.ts +160 -0
- package/dist/defaultapp/db-users.ts +1 -1
- package/dist/defaultapp/profile/page.tsx +6 -0
- package/dist/generators/env-example-template.txt +4 -3
- 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/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/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.ts → api/db.ts} +0 -0
- /package/defaultapp/{storage → api/storage}/email.ts +0 -0
- /package/defaultapp/{storage → api/storage}/files.ts +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
3
|
+
import { validateSession } from '../../db-users';
|
|
4
|
+
import { updatePassword, getPasswordAuthByEmail } from '../../db-password-auth';
|
|
5
|
+
import { deleteUser } from '../../db-users';
|
|
6
|
+
|
|
7
|
+
// Handle password change
|
|
8
|
+
export async function PUT(request: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
// Get session token from cookies
|
|
11
|
+
const sessionCookie = await cookies();
|
|
12
|
+
const sessionToken = sessionCookie.get('session_id')?.value;
|
|
13
|
+
|
|
14
|
+
if (!sessionToken) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: 'Not authenticated' },
|
|
17
|
+
{ status: 401 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Validate session and get user details
|
|
22
|
+
const sessionData = await validateSession(sessionToken);
|
|
23
|
+
|
|
24
|
+
if (!sessionData.valid || !sessionData.user) {
|
|
25
|
+
return NextResponse.json(
|
|
26
|
+
{ error: 'Invalid session' },
|
|
27
|
+
{ status: 401 }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { currentPassword, newPassword } = await request.json();
|
|
32
|
+
|
|
33
|
+
if (!currentPassword || !newPassword) {
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ error: 'Current password and new password are required' },
|
|
36
|
+
{ status: 400 }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if user is using password authentication
|
|
41
|
+
if (sessionData.user.source !== 'userpass') {
|
|
42
|
+
return NextResponse.json(
|
|
43
|
+
{ error: 'Password changes are only available for email/password accounts' },
|
|
44
|
+
{ status: 400 }
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Validate password requirements
|
|
49
|
+
if (newPassword.length < 8) {
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ error: 'Password must be at least 8 characters long' },
|
|
52
|
+
{ status: 400 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if user has an email (required for password auth)
|
|
57
|
+
if (!sessionData.user.email) {
|
|
58
|
+
return NextResponse.json(
|
|
59
|
+
{ error: 'No email associated with this account' },
|
|
60
|
+
{ status: 400 }
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Update the password
|
|
65
|
+
const success = await updatePassword(
|
|
66
|
+
sessionData.user.email,
|
|
67
|
+
currentPassword,
|
|
68
|
+
newPassword
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!success) {
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: 'Current password is incorrect' },
|
|
74
|
+
{ status: 401 }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return NextResponse.json({
|
|
79
|
+
success: true,
|
|
80
|
+
message: 'Password updated successfully'
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('Password update error:', error);
|
|
85
|
+
return NextResponse.json(
|
|
86
|
+
{ error: error instanceof Error ? error.message : 'Failed to update password' },
|
|
87
|
+
{ status: 500 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Handle account deletion
|
|
93
|
+
export async function DELETE(request: NextRequest) {
|
|
94
|
+
try {
|
|
95
|
+
// Get session token from cookies
|
|
96
|
+
const sessionCookie = await cookies();
|
|
97
|
+
const sessionToken = sessionCookie.get('session_id')?.value;
|
|
98
|
+
|
|
99
|
+
if (!sessionToken) {
|
|
100
|
+
return NextResponse.json(
|
|
101
|
+
{ error: 'Not authenticated' },
|
|
102
|
+
{ status: 401 }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Validate session and get user details
|
|
107
|
+
const sessionData = await validateSession(sessionToken);
|
|
108
|
+
|
|
109
|
+
if (!sessionData.valid || !sessionData.user) {
|
|
110
|
+
return NextResponse.json(
|
|
111
|
+
{ error: 'Invalid session' },
|
|
112
|
+
{ status: 401 }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { confirmDelete } = await request.json();
|
|
117
|
+
|
|
118
|
+
if (confirmDelete !== true) {
|
|
119
|
+
return NextResponse.json(
|
|
120
|
+
{ error: 'Please confirm account deletion' },
|
|
121
|
+
{ status: 400 }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Delete the user account
|
|
126
|
+
try {
|
|
127
|
+
await deleteUser(sessionData.user.userid);
|
|
128
|
+
} catch (error: any) {
|
|
129
|
+
if (error.message && error.message.includes('last admin')) {
|
|
130
|
+
return NextResponse.json(
|
|
131
|
+
{ error: 'Cannot delete the last admin account' },
|
|
132
|
+
{ status: 400 }
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Clear the session cookie
|
|
139
|
+
const response = NextResponse.json({
|
|
140
|
+
success: true,
|
|
141
|
+
message: 'Account deleted successfully'
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
response.cookies.delete('session_id');
|
|
145
|
+
return response;
|
|
146
|
+
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Account deletion error:', error);
|
|
149
|
+
return NextResponse.json(
|
|
150
|
+
{ error: error instanceof Error ? error.message : 'Failed to delete account' },
|
|
151
|
+
{ status: 500 }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// Simple in-memory storage for email tracking (in production, use a database)
|
|
4
|
+
const emailHistory: Map<string, any> = new Map();
|
|
5
|
+
|
|
6
|
+
export interface EmailOptions {
|
|
7
|
+
to: string | string[];
|
|
8
|
+
from: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
html?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
cc?: string | string[];
|
|
13
|
+
bcc?: string | string[];
|
|
14
|
+
replyTo?: string | string[];
|
|
15
|
+
attachments?: Array<{
|
|
16
|
+
filename: string;
|
|
17
|
+
content: string; // base64 encoded content
|
|
18
|
+
contentType?: string;
|
|
19
|
+
}>;
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
tags?: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
value: string;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Send email function that sends emails via the Resend API
|
|
29
|
+
* @param options Email options including to, from, subject, message content, etc.
|
|
30
|
+
* @returns Promise that resolves to the email ID from Resend
|
|
31
|
+
*/
|
|
32
|
+
export async function send_email(options: EmailOptions): Promise<string> {
|
|
33
|
+
console.log(`📧 Sending email to: ${Array.isArray(options.to) ? options.to.join(', ') : options.to}`);
|
|
34
|
+
console.log(`📧 Subject: ${options.subject}`);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Get API key from environment
|
|
38
|
+
const apiKey = process.env.RESEND_API_KEY;
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
throw new Error('RESEND_API_KEY environment variable is not set');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Prepare request body for Resend API
|
|
44
|
+
const requestBody: any = {
|
|
45
|
+
to: options.to,
|
|
46
|
+
from: options.from,
|
|
47
|
+
subject: options.subject,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Add optional fields
|
|
51
|
+
if (options.html) requestBody.html = options.html;
|
|
52
|
+
if (options.text) requestBody.text = options.text;
|
|
53
|
+
if (options.cc) requestBody.cc = options.cc;
|
|
54
|
+
if (options.bcc) requestBody.bcc = options.bcc;
|
|
55
|
+
if (options.replyTo) requestBody.reply_to = options.replyTo;
|
|
56
|
+
if (options.headers) requestBody.headers = options.headers;
|
|
57
|
+
if (options.tags) requestBody.tags = options.tags;
|
|
58
|
+
|
|
59
|
+
// Handle attachments
|
|
60
|
+
if (options.attachments && options.attachments.length > 0) {
|
|
61
|
+
requestBody.attachments = options.attachments.map(att => ({
|
|
62
|
+
filename: att.filename,
|
|
63
|
+
content: att.content,
|
|
64
|
+
content_type: att.contentType,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Make API request to Resend
|
|
69
|
+
const response = await fetch('https://api.resend.com/emails', {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify(requestBody),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
80
|
+
throw new Error(`Resend API error: ${response.status} - ${errorData.message || response.statusText}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = await response.json();
|
|
84
|
+
const emailId = result.id;
|
|
85
|
+
|
|
86
|
+
// Store email metadata
|
|
87
|
+
const emailKey = `${Date.now()}_${randomBytes(6).toString('hex')}`;
|
|
88
|
+
emailHistory.set(emailKey, {
|
|
89
|
+
id: emailId,
|
|
90
|
+
to: options.to,
|
|
91
|
+
from: options.from,
|
|
92
|
+
subject: options.subject,
|
|
93
|
+
sentAt: new Date().toISOString(),
|
|
94
|
+
status: 'sent',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(`✅ Email sent successfully. ID: ${emailId}`);
|
|
98
|
+
|
|
99
|
+
return emailId;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('❌ Error sending email:', error);
|
|
102
|
+
throw new Error(`Failed to send email: ${error}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get email status by ID
|
|
108
|
+
* @param emailId The email ID returned from send_email
|
|
109
|
+
* @returns Email metadata or undefined if not found
|
|
110
|
+
*/
|
|
111
|
+
export async function getEmailStatus(emailId: string): Promise<any> {
|
|
112
|
+
// Find email in history by Resend ID
|
|
113
|
+
for (const [key, data] of emailHistory.entries()) {
|
|
114
|
+
if (data.id === emailId) {
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If not found locally, could query Resend API for status
|
|
120
|
+
const apiKey = process.env.RESEND_API_KEY;
|
|
121
|
+
if (!apiKey) {
|
|
122
|
+
throw new Error('RESEND_API_KEY environment variable is not set');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(`https://api.resend.com/emails/${emailId}`, {
|
|
127
|
+
headers: {
|
|
128
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return await response.json();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error fetching email status:', error);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* List sent emails from local history
|
|
145
|
+
* @returns Array of email metadata
|
|
146
|
+
*/
|
|
147
|
+
export function listSentEmails(): Array<any> {
|
|
148
|
+
return Array.from(emailHistory.values());
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Initialize global send_email function for generated API code
|
|
153
|
+
* This makes send_email available in the same way as the preview environment
|
|
154
|
+
*/
|
|
155
|
+
export function initializeGlobalEmailAPI(): void {
|
|
156
|
+
// Make send_email available globally for API code execution
|
|
157
|
+
(global as any).send_email = send_email;
|
|
158
|
+
(global as any).getEmailStatus = getEmailStatus;
|
|
159
|
+
(global as any).listSentEmails = listSentEmails;
|
|
160
|
+
|
|
161
|
+
console.log('✅ Global email API initialized');
|
|
162
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Sitepaige v1.0.0
|
|
3
|
+
WARNING: This file is automatically generated and should not be modified.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { randomBytes } from 'crypto';
|
|
9
|
+
|
|
10
|
+
// File storage for the application
|
|
11
|
+
const fileStorage: { [key: string]: string } = {};
|
|
12
|
+
const fileNames: { [key: string]: string } = {};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the files storage path
|
|
16
|
+
* Uses EFS_MOUNT_PATH for production (in container with EFS) or SQLITE_DIR for local development
|
|
17
|
+
*/
|
|
18
|
+
function getFilesStoragePath(): string {
|
|
19
|
+
const efsMountPath = process.env.EFS_MOUNT_PATH;
|
|
20
|
+
const sqliteDir = process.env.SQLITE_DIR || '.';
|
|
21
|
+
|
|
22
|
+
if (efsMountPath) {
|
|
23
|
+
// In production with EFS
|
|
24
|
+
return path.join(efsMountPath, 'files');
|
|
25
|
+
} else {
|
|
26
|
+
// In local development - use a separate files directory
|
|
27
|
+
return path.join(sqliteDir, 'files');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Store file function that writes files to the filesystem and returns a reference
|
|
33
|
+
* @param filename The original filename
|
|
34
|
+
* @param filedata Base64 encoded file data
|
|
35
|
+
* @param isImage Whether this is an image file
|
|
36
|
+
* @returns Promise that resolves to a file reference string (path starting with /files/)
|
|
37
|
+
*/
|
|
38
|
+
export async function store_file(filename: string, filedata: string, isImage: boolean): Promise<string> {
|
|
39
|
+
console.log(`📁 Storing ${isImage ? 'image' : 'file'}: ${filename} (${filedata.length} bytes)`);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Generate a unique key for this file
|
|
43
|
+
const fileKey = `${Date.now()}_${randomBytes(6).toString('hex')}`;
|
|
44
|
+
|
|
45
|
+
// Determine file extension
|
|
46
|
+
const extension = path.extname(filename) || (isImage ? '.png' : '.bin');
|
|
47
|
+
const safeFilename = `${fileKey}${extension}`;
|
|
48
|
+
|
|
49
|
+
// Get storage directory and create subdirectories if needed
|
|
50
|
+
const storageBasePath = getFilesStoragePath();
|
|
51
|
+
const storageDir = path.join(storageBasePath, isImage ? 'images' : 'documents');
|
|
52
|
+
await fs.mkdir(storageDir, { recursive: true });
|
|
53
|
+
|
|
54
|
+
// Write file to filesystem
|
|
55
|
+
const filePath = path.join(storageDir, safeFilename);
|
|
56
|
+
const buffer = Buffer.from(filedata, 'base64');
|
|
57
|
+
await fs.writeFile(filePath, buffer);
|
|
58
|
+
|
|
59
|
+
// Store metadata
|
|
60
|
+
fileStorage[fileKey] = filePath;
|
|
61
|
+
fileNames[fileKey] = filename;
|
|
62
|
+
|
|
63
|
+
console.log(`✅ File stored successfully: ${filePath}`);
|
|
64
|
+
|
|
65
|
+
// Return the public path that will be used to access the file
|
|
66
|
+
// This path starts with /files/ and matches what the API route will serve
|
|
67
|
+
const publicPath = `/files/${isImage ? 'images' : 'documents'}/${safeFilename}`;
|
|
68
|
+
return publicPath;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('❌ Error storing file:', error);
|
|
71
|
+
throw new Error(`Failed to store file: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the filesystem path for a stored file
|
|
77
|
+
* @param fileKey The file key returned from store_file
|
|
78
|
+
* @returns The filesystem path or undefined if not found
|
|
79
|
+
*/
|
|
80
|
+
export function getStoredFilePath(fileKey: string): string | undefined {
|
|
81
|
+
return fileStorage[fileKey];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the original filename for a stored file
|
|
86
|
+
* @param fileKey The file key returned from store_file
|
|
87
|
+
* @returns The original filename or undefined if not found
|
|
88
|
+
*/
|
|
89
|
+
export function getStoredFileName(fileKey: string): string | undefined {
|
|
90
|
+
return fileNames[fileKey];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the file data as base64 for a stored file
|
|
95
|
+
* @param fileKey The file key returned from store_file
|
|
96
|
+
* @returns Promise that resolves to base64 file data or undefined if not found
|
|
97
|
+
*/
|
|
98
|
+
export async function getStoredFileData(fileKey: string): Promise<string | undefined> {
|
|
99
|
+
const filePath = fileStorage[fileKey];
|
|
100
|
+
if (!filePath) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const buffer = await fs.readFile(filePath);
|
|
106
|
+
return buffer.toString('base64');
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('Error reading stored file:', error);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Delete a stored file from the filesystem
|
|
115
|
+
* @param fileKey The file key returned from store_file
|
|
116
|
+
* @returns Promise that resolves to true if deleted, false if not found
|
|
117
|
+
*/
|
|
118
|
+
export async function deleteStoredFile(fileKey: string): Promise<boolean> {
|
|
119
|
+
const filePath = fileStorage[fileKey];
|
|
120
|
+
if (!filePath) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await fs.unlink(filePath);
|
|
126
|
+
delete fileStorage[fileKey];
|
|
127
|
+
delete fileNames[fileKey];
|
|
128
|
+
console.log(`🗑️ File deleted: ${filePath}`);
|
|
129
|
+
return true;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('Error deleting stored file:', error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* List all stored files
|
|
138
|
+
* @returns Array of objects containing file information
|
|
139
|
+
*/
|
|
140
|
+
export function listStoredFiles(): Array<{fileKey: string, filename: string, path: string}> {
|
|
141
|
+
return Object.keys(fileStorage).map(fileKey => ({
|
|
142
|
+
fileKey,
|
|
143
|
+
filename: fileNames[fileKey],
|
|
144
|
+
path: fileStorage[fileKey]
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Initialize global store_file function for generated API code
|
|
150
|
+
* This makes store_file available in the same way as the preview environment
|
|
151
|
+
*/
|
|
152
|
+
export function initializeGlobalStorageAPI(): void {
|
|
153
|
+
// Make store_file available globally for API code execution
|
|
154
|
+
(global as any).store_file = store_file;
|
|
155
|
+
(global as any).getStoredFilePath = getStoredFilePath;
|
|
156
|
+
(global as any).getStoredFileName = getStoredFileName;
|
|
157
|
+
(global as any).getStoredFileData = getStoredFileData;
|
|
158
|
+
|
|
159
|
+
console.log('✅ Global storage API initialized');
|
|
160
|
+
}
|
|
@@ -111,7 +111,7 @@ export async function upsertUser(
|
|
|
111
111
|
} else {
|
|
112
112
|
// Check if this is the first user (should be admin)
|
|
113
113
|
const allUsers = await db_query(client, "SELECT COUNT(*) as count FROM users");
|
|
114
|
-
const isFirstUser = allUsers[0].count === 0;
|
|
114
|
+
const isFirstUser = Number(allUsers[0].count) === 0;
|
|
115
115
|
|
|
116
116
|
// Create new user
|
|
117
117
|
const userId = crypto.randomUUID();
|
|
@@ -58,9 +58,10 @@ DATABASE_TYPE=sqlite
|
|
|
58
58
|
# EMAIL_FROM=noreply@yourdomain.com
|
|
59
59
|
|
|
60
60
|
# Site Configuration
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
61
|
+
# DOMAIN is used for generating absolute URLs in production (e.g., in emails)
|
|
62
|
+
# LOCAL_DOMAIN is used for development environment
|
|
63
|
+
# DOMAIN=https://yourdomain.com
|
|
64
|
+
# LOCAL_DOMAIN=http://localhost:3000
|
|
64
65
|
|
|
65
66
|
# OAuth Configuration
|
|
66
67
|
# Google OAuth
|
|
@@ -96,11 +96,9 @@ export async function writePackageJson(targetDir, projectName, databaseType = "p
|
|
|
96
96
|
pkg.private = true;
|
|
97
97
|
pkg.scripts = {
|
|
98
98
|
...(pkg.scripts ?? {}),
|
|
99
|
-
dev: pkg.scripts?.dev ?? "
|
|
100
|
-
build: pkg.scripts?.build ?? "
|
|
101
|
-
start: pkg.scripts?.start ?? "next start"
|
|
102
|
-
migrate: pkg.scripts?.migrate ?? "npx tsx src/app/migrate.ts",
|
|
103
|
-
postinstall: pkg.scripts?.postinstall ?? "npx tsx src/app/migrate.ts"
|
|
99
|
+
dev: pkg.scripts?.dev ?? "next dev",
|
|
100
|
+
build: pkg.scripts?.build ?? "next build",
|
|
101
|
+
start: pkg.scripts?.start ?? "next start"
|
|
104
102
|
};
|
|
105
103
|
pkg.dependencies = {
|
|
106
104
|
...(pkg.dependencies ?? {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeleton.js","sourceRoot":"","sources":["../../src/generators/skeleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB;IACrD,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BjB,CAAC;IACA,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjD,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,SAAS,CAAC,SAAS,CAAC,CAAC;IAErB,6BAA6B;IAC7B,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACvC,MAAM,cAAc,GAAG;;;;;;;;;;;;;CAa1B,CAAC;QACE,MAAM,GAAG,CAAC,SAAS,CAAC,kBAAkB,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG;;;;;;CAMzB,CAAC;QACE,MAAM,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,6EAA6E;IAC7E,uCAAuC;AACzC,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB,EAAE,WAAoB,EAAE,eAAgD,UAAU;IACxI,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACrD,IAAI,GAAG,GAAQ,EAAE,CAAC;IAElB,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACrD,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW,IAAI,eAAe,CAAC;SAC1D,QAAQ,EAAE;SACV,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhB,GAAG,CAAC,IAAI,GAAG,QAAQ,IAAI,eAAe,CAAC;IACvC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IAEnB,GAAG,CAAC,OAAO,GAAG;QACZ,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;QACtB,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"skeleton.js","sourceRoot":"","sources":["../../src/generators/skeleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB;IACrD,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BjB,CAAC;IACA,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjD,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,SAAS,CAAC,SAAS,CAAC,CAAC;IAErB,6BAA6B;IAC7B,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACvC,MAAM,cAAc,GAAG;;;;;;;;;;;;;CAa1B,CAAC;QACE,MAAM,GAAG,CAAC,SAAS,CAAC,kBAAkB,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG;;;;;;CAMzB,CAAC;QACE,MAAM,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,6EAA6E;IAC7E,uCAAuC;AACzC,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB,EAAE,WAAoB,EAAE,eAAgD,UAAU;IACxI,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACrD,IAAI,GAAG,GAAQ,EAAE,CAAC;IAElB,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACrD,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW,IAAI,eAAe,CAAC;SAC1D,QAAQ,EAAE;SACV,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhB,GAAG,CAAC,IAAI,GAAG,QAAQ,IAAI,eAAe,CAAC;IACvC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IAEnB,GAAG,CAAC,OAAO,GAAG;QACZ,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;QACtB,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,UAAU;QACnC,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,YAAY;QACzC,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,YAAY;KAC1C,CAAC;IAEF,GAAG,CAAC,YAAY,GAAG;QACjB,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC3B,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,IAAI,IAAI,QAAQ;QACxC,KAAK,EAAE,GAAG,CAAC,YAAY,EAAE,KAAK,IAAI,QAAQ;QAC1C,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,WAAW,CAAC,IAAI,QAAQ;QACxD,cAAc,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,cAAc,CAAC,IAAI,QAAQ;QAC9D,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,YAAY,CAAC,IAAI,QAAQ;QAC1D,GAAG,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,IAAI,QAAQ;QACtC,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,WAAW,IAAI,QAAQ;QACtD,OAAO,EAAE,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,QAAQ;QAC9C,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,YAAY,IAAI,QAAQ;QACxD,6DAA6D;QAC7D,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE,UAAU,IAAI,QAAQ;QACpD,aAAa,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,aAAa,CAAC,IAAI,QAAQ;QAC5D,cAAc,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,cAAc,CAAC,IAAI,QAAQ;QAC9D,kBAAkB,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,kBAAkB,CAAC,IAAI,QAAQ;QACtE,mBAAmB,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,mBAAmB,CAAC,IAAI,QAAQ;KACzE,CAAC;IAEF,qCAAqC;IACrC,QAAQ,YAAY,EAAE,CAAC;QACrB,KAAK,UAAU;YACb,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,EAAE,IAAI,SAAS,CAAC;YACxD,MAAM;QACR,KAAK,OAAO;YACV,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,MAAM,IAAI,QAAQ,CAAC;YAC/D,MAAM;QACR,KAAK,QAAQ,CAAC;QACd;YACE,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,gBAAgB,CAAC,IAAI,QAAQ,CAAC;YACtF,MAAM;IACV,CAAC;IAED,iEAAiE;IACjE,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;IAEhD,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5E,CAAC"}
|
package/dist/generators/sql.js
CHANGED
|
@@ -85,6 +85,66 @@ export async function writeModelsSql(targetDir, blueprint, databaseType = "postg
|
|
|
85
85
|
};
|
|
86
86
|
const dbTypeMap = typeMap[databaseType] || typeMap.sqlite;
|
|
87
87
|
const quoteChar = databaseType === 'mysql' ? '`' : '"';
|
|
88
|
+
// Add auth tables at the beginning
|
|
89
|
+
lines.push("\n-- Authentication and session management tables");
|
|
90
|
+
// Migrations table
|
|
91
|
+
lines.push(`\n-- Migrations tracking table`);
|
|
92
|
+
if (databaseType === 'sqlite') {
|
|
93
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}migrations${quoteChar} (`, ` ${quoteChar}id${quoteChar} INTEGER PRIMARY KEY AUTOINCREMENT,`, ` ${quoteChar}filename${quoteChar} TEXT UNIQUE NOT NULL,`, ` ${quoteChar}executed_at${quoteChar} TEXT DEFAULT CURRENT_TIMESTAMP`, `);`);
|
|
94
|
+
}
|
|
95
|
+
else if (databaseType === 'postgres') {
|
|
96
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}migrations${quoteChar} (`, ` ${quoteChar}id${quoteChar} SERIAL PRIMARY KEY,`, ` ${quoteChar}filename${quoteChar} VARCHAR(255) UNIQUE NOT NULL,`, ` ${quoteChar}executed_at${quoteChar} TIMESTAMP DEFAULT NOW()`, `);`);
|
|
97
|
+
}
|
|
98
|
+
else if (databaseType === 'mysql') {
|
|
99
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}migrations${quoteChar} (`, ` ${quoteChar}id${quoteChar} INT AUTO_INCREMENT PRIMARY KEY,`, ` ${quoteChar}filename${quoteChar} VARCHAR(255) UNIQUE NOT NULL,`, ` ${quoteChar}executed_at${quoteChar} TIMESTAMP DEFAULT CURRENT_TIMESTAMP`, `);`);
|
|
100
|
+
}
|
|
101
|
+
// Users table
|
|
102
|
+
lines.push(`\n-- Users table (required for authentication)`);
|
|
103
|
+
if (databaseType === 'sqlite') {
|
|
104
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}users${quoteChar} (`, ` ${quoteChar}userid${quoteChar} TEXT PRIMARY KEY,`, ` ${quoteChar}oauthid${quoteChar} TEXT NOT NULL UNIQUE,`, ` ${quoteChar}source${quoteChar} TEXT NOT NULL CHECK(${quoteChar}source${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}username${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}email${quoteChar} TEXT,`, ` ${quoteChar}avatarurl${quoteChar} TEXT,`, ` ${quoteChar}userlevel${quoteChar} INTEGER NOT NULL DEFAULT 1 CHECK(${quoteChar}userlevel${quoteChar} IN (0, 1, 2)),`, ` ${quoteChar}usertier${quoteChar} INTEGER NOT NULL DEFAULT 0,`, ` ${quoteChar}lastlogindate${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}createddate${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}isactive${quoteChar} INTEGER NOT NULL DEFAULT 1`, `);`);
|
|
105
|
+
}
|
|
106
|
+
else if (databaseType === 'postgres') {
|
|
107
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}users${quoteChar} (`, ` ${quoteChar}userid${quoteChar} UUID PRIMARY KEY,`, ` ${quoteChar}oauthid${quoteChar} TEXT NOT NULL UNIQUE,`, ` ${quoteChar}source${quoteChar} TEXT NOT NULL CHECK(${quoteChar}source${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}username${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}email${quoteChar} TEXT,`, ` ${quoteChar}avatarurl${quoteChar} TEXT,`, ` ${quoteChar}userlevel${quoteChar} INTEGER NOT NULL DEFAULT 1 CHECK(${quoteChar}userlevel${quoteChar} IN (0, 1, 2)),`, ` ${quoteChar}usertier${quoteChar} INTEGER NOT NULL DEFAULT 0,`, ` ${quoteChar}lastlogindate${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}createddate${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}isactive${quoteChar} INTEGER NOT NULL DEFAULT 1`, `);`);
|
|
108
|
+
}
|
|
109
|
+
else if (databaseType === 'mysql') {
|
|
110
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}users${quoteChar} (`, ` ${quoteChar}userid${quoteChar} VARCHAR(36) PRIMARY KEY,`, ` ${quoteChar}oauthid${quoteChar} VARCHAR(255) NOT NULL UNIQUE,`, ` ${quoteChar}source${quoteChar} VARCHAR(20) NOT NULL CHECK(${quoteChar}source${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}username${quoteChar} VARCHAR(255) NOT NULL,`, ` ${quoteChar}email${quoteChar} VARCHAR(255),`, ` ${quoteChar}avatarurl${quoteChar} TEXT,`, ` ${quoteChar}userlevel${quoteChar} INT NOT NULL DEFAULT 1 CHECK(${quoteChar}userlevel${quoteChar} IN (0, 1, 2)),`, ` ${quoteChar}usertier${quoteChar} INT NOT NULL DEFAULT 0,`, ` ${quoteChar}lastlogindate${quoteChar} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}createddate${quoteChar} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}isactive${quoteChar} TINYINT NOT NULL DEFAULT 1`, `);`);
|
|
111
|
+
}
|
|
112
|
+
// Indexes for Users table
|
|
113
|
+
lines.push(`\n-- Indexes for Users table`);
|
|
114
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_users_oauthid ON ${quoteChar}users${quoteChar}(${quoteChar}oauthid${quoteChar});`);
|
|
115
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_users_email ON ${quoteChar}users${quoteChar}(${quoteChar}email${quoteChar});`);
|
|
116
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_users_usertier ON ${quoteChar}users${quoteChar}(${quoteChar}usertier${quoteChar});`);
|
|
117
|
+
// UserSession table
|
|
118
|
+
lines.push(`\n-- UserSession table`);
|
|
119
|
+
if (databaseType === 'sqlite') {
|
|
120
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}usersession${quoteChar} (`, ` ${quoteChar}id${quoteChar} TEXT PRIMARY KEY,`, ` ${quoteChar}sessiontoken${quoteChar} TEXT NOT NULL UNIQUE,`, ` ${quoteChar}userid${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}expirationdate${quoteChar} TEXT NOT NULL,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
121
|
+
}
|
|
122
|
+
else if (databaseType === 'postgres') {
|
|
123
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}usersession${quoteChar} (`, ` ${quoteChar}id${quoteChar} UUID PRIMARY KEY,`, ` ${quoteChar}sessiontoken${quoteChar} TEXT NOT NULL UNIQUE,`, ` ${quoteChar}userid${quoteChar} UUID NOT NULL,`, ` ${quoteChar}expirationdate${quoteChar} TEXT NOT NULL,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
124
|
+
}
|
|
125
|
+
else if (databaseType === 'mysql') {
|
|
126
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}usersession${quoteChar} (`, ` ${quoteChar}id${quoteChar} VARCHAR(36) PRIMARY KEY,`, ` ${quoteChar}sessiontoken${quoteChar} VARCHAR(255) NOT NULL UNIQUE,`, ` ${quoteChar}userid${quoteChar} VARCHAR(36) NOT NULL,`, ` ${quoteChar}expirationdate${quoteChar} VARCHAR(255) NOT NULL,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
127
|
+
}
|
|
128
|
+
// Indexes for UserSession table
|
|
129
|
+
lines.push(`\n-- Indexes for UserSession table`);
|
|
130
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_session_token ON ${quoteChar}usersession${quoteChar}(${quoteChar}sessiontoken${quoteChar});`);
|
|
131
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_session_user ON ${quoteChar}usersession${quoteChar}(${quoteChar}userid${quoteChar});`);
|
|
132
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_session_expiry ON ${quoteChar}usersession${quoteChar}(${quoteChar}expirationdate${quoteChar});`);
|
|
133
|
+
// OAuthTokens table
|
|
134
|
+
lines.push(`\n-- OAuthTokens table`);
|
|
135
|
+
if (databaseType === 'sqlite') {
|
|
136
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}oauthtokens${quoteChar} (`, ` ${quoteChar}id${quoteChar} TEXT PRIMARY KEY,`, ` ${quoteChar}userid${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}provider${quoteChar} TEXT NOT NULL CHECK(${quoteChar}provider${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}accesstoken${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}refreshtoken${quoteChar} TEXT,`, ` ${quoteChar}expiresat${quoteChar} TEXT,`, ` ${quoteChar}createdat${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}updatedat${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
137
|
+
}
|
|
138
|
+
else if (databaseType === 'postgres') {
|
|
139
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}oauthtokens${quoteChar} (`, ` ${quoteChar}id${quoteChar} UUID PRIMARY KEY,`, ` ${quoteChar}userid${quoteChar} UUID NOT NULL,`, ` ${quoteChar}provider${quoteChar} TEXT NOT NULL CHECK(${quoteChar}provider${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}accesstoken${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}refreshtoken${quoteChar} TEXT,`, ` ${quoteChar}expiresat${quoteChar} TEXT,`, ` ${quoteChar}createdat${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}updatedat${quoteChar} TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
140
|
+
}
|
|
141
|
+
else if (databaseType === 'mysql') {
|
|
142
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${quoteChar}oauthtokens${quoteChar} (`, ` ${quoteChar}id${quoteChar} VARCHAR(36) PRIMARY KEY,`, ` ${quoteChar}userid${quoteChar} VARCHAR(36) NOT NULL,`, ` ${quoteChar}provider${quoteChar} VARCHAR(20) NOT NULL CHECK(${quoteChar}provider${quoteChar} IN ('google', 'facebook', 'apple', 'github', 'userpass')),`, ` ${quoteChar}accesstoken${quoteChar} TEXT NOT NULL,`, ` ${quoteChar}refreshtoken${quoteChar} TEXT,`, ` ${quoteChar}expiresat${quoteChar} VARCHAR(255),`, ` ${quoteChar}createdat${quoteChar} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` ${quoteChar}updatedat${quoteChar} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`, ` FOREIGN KEY (${quoteChar}userid${quoteChar}) REFERENCES ${quoteChar}users${quoteChar}(${quoteChar}userid${quoteChar}) ON DELETE CASCADE`, `);`);
|
|
143
|
+
}
|
|
144
|
+
// Indexes for OAuthTokens table
|
|
145
|
+
lines.push(`\n-- Indexes for OAuthTokens table`);
|
|
146
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_oauth_user ON ${quoteChar}oauthtokens${quoteChar}(${quoteChar}userid${quoteChar});`);
|
|
147
|
+
lines.push(`CREATE INDEX IF NOT EXISTS idx_oauth_provider ON ${quoteChar}oauthtokens${quoteChar}(${quoteChar}userid${quoteChar}, ${quoteChar}provider${quoteChar});`);
|
|
88
148
|
for (const model of models) {
|
|
89
149
|
const tableName = sanitizeTableName(model.name || model.id || "table");
|
|
90
150
|
lines.push(`\n-- Model: ${tableName}`);
|