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.
Files changed (51) hide show
  1. package/components/headerlogin.tsx +10 -28
  2. package/components/profile.tsx +302 -46
  3. package/components/slideshow.tsx +1 -9
  4. package/defaultapp/api/Auth/resend-verification/route.ts +3 -3
  5. package/defaultapp/api/Auth/route.ts +2 -2
  6. package/defaultapp/api/Auth/signup/route.ts +3 -3
  7. package/defaultapp/api/Auth/verify-email/route.ts +5 -5
  8. package/defaultapp/api/admin/users/route.ts +3 -3
  9. package/defaultapp/{db-users.ts → api/db-users.ts} +1 -1
  10. package/defaultapp/api/profile/route.ts +154 -0
  11. package/defaultapp/profile/page.tsx +6 -0
  12. package/dist/blueprintWriter.js.map +1 -1
  13. package/dist/components/headerlogin.tsx +10 -28
  14. package/dist/components/profile.tsx +302 -46
  15. package/dist/components/slideshow.tsx +1 -9
  16. package/dist/defaultapp/api/Auth/resend-verification/route.ts +3 -3
  17. package/dist/defaultapp/api/Auth/route.ts +2 -2
  18. package/dist/defaultapp/api/Auth/signup/route.ts +3 -3
  19. package/dist/defaultapp/api/Auth/verify-email/route.ts +5 -5
  20. package/dist/defaultapp/api/admin/users/route.ts +3 -3
  21. package/dist/defaultapp/api/csrf.ts +111 -0
  22. package/dist/defaultapp/api/db-mysql.ts +183 -0
  23. package/dist/defaultapp/api/db-password-auth.ts +362 -0
  24. package/dist/defaultapp/api/db-postgres.ts +189 -0
  25. package/dist/defaultapp/api/db-sqlite.ts +335 -0
  26. package/dist/defaultapp/api/db-users.ts +520 -0
  27. package/dist/defaultapp/api/db.ts +149 -0
  28. package/dist/defaultapp/api/profile/route.ts +154 -0
  29. package/dist/defaultapp/api/storage/email.ts +162 -0
  30. package/dist/defaultapp/api/storage/files.ts +160 -0
  31. package/dist/defaultapp/db-users.ts +1 -1
  32. package/dist/defaultapp/profile/page.tsx +6 -0
  33. package/dist/generators/env-example-template.txt +4 -3
  34. package/dist/generators/skeleton.js +3 -5
  35. package/dist/generators/skeleton.js.map +1 -1
  36. package/dist/generators/sql.js +60 -0
  37. package/dist/generators/sql.js.map +1 -1
  38. package/dist/sitepaige.js +2 -1
  39. package/dist/sitepaige.js.map +1 -1
  40. package/package.json +1 -1
  41. package/defaultapp/admin/page.tsx +0 -6
  42. package/defaultapp/api/example-secure/route.ts +0 -100
  43. package/defaultapp/migrate.ts +0 -142
  44. /package/defaultapp/{csrf.ts → api/csrf.ts} +0 -0
  45. /package/defaultapp/{db-mysql.ts → api/db-mysql.ts} +0 -0
  46. /package/defaultapp/{db-password-auth.ts → api/db-password-auth.ts} +0 -0
  47. /package/defaultapp/{db-postgres.ts → api/db-postgres.ts} +0 -0
  48. /package/defaultapp/{db-sqlite.ts → api/db-sqlite.ts} +0 -0
  49. /package/defaultapp/{db.ts → api/db.ts} +0 -0
  50. /package/defaultapp/{storage → api/storage}/email.ts +0 -0
  51. /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();
@@ -0,0 +1,6 @@
1
+ import Profile from '../../components/profile';
2
+ import React from 'react';
3
+
4
+ export default function ProfilePage() {
5
+ return <Profile />;
6
+ }
@@ -58,9 +58,10 @@ DATABASE_TYPE=sqlite
58
58
  # EMAIL_FROM=noreply@yourdomain.com
59
59
 
60
60
  # Site Configuration
61
- # SITE_DOMAIN is used for generating absolute URLs (e.g., in emails)
62
- # Defaults to https://sitepaige.com if not set
63
- # SITE_DOMAIN=https://yourdomain.com
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 ?? "npx tsx src/app/migrate.ts && next dev",
100
- build: pkg.scripts?.build ?? "npx tsx src/app/migrate.ts && next 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":"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"}
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"}
@@ -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}`);