nextjs-cms-kit 0.5.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/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/commands/db-config.d.ts +3 -0
- package/dist/commands/db-config.d.ts.map +1 -0
- package/dist/commands/db-config.js +20 -0
- package/dist/commands/fix-master-admin.d.ts +3 -0
- package/dist/commands/fix-master-admin.d.ts.map +1 -0
- package/dist/commands/fix-master-admin.js +19 -0
- package/dist/commands/set-master-admin.d.ts +3 -0
- package/dist/commands/set-master-admin.d.ts.map +1 -0
- package/dist/commands/set-master-admin.js +29 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +61 -0
- package/dist/commands/update-sections.d.ts +3 -0
- package/dist/commands/update-sections.d.ts.map +1 -0
- package/dist/commands/update-sections.js +33 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/lib/actions.d.ts +7 -0
- package/dist/lib/actions.d.ts.map +1 -0
- package/dist/lib/actions.js +62 -0
- package/dist/lib/add-table-keys.d.ts +32 -0
- package/dist/lib/add-table-keys.d.ts.map +1 -0
- package/dist/lib/add-table-keys.js +168 -0
- package/dist/lib/db-config-setup.d.ts +4 -0
- package/dist/lib/db-config-setup.d.ts.map +1 -0
- package/dist/lib/db-config-setup.js +216 -0
- package/dist/lib/fix-master-admin.d.ts +2 -0
- package/dist/lib/fix-master-admin.d.ts.map +1 -0
- package/dist/lib/fix-master-admin.js +7 -0
- package/dist/lib/reload-env.d.ts +16 -0
- package/dist/lib/reload-env.d.ts.map +1 -0
- package/dist/lib/reload-env.js +42 -0
- package/dist/lib/schema-generator.d.ts +10 -0
- package/dist/lib/schema-generator.d.ts.map +1 -0
- package/dist/lib/schema-generator.js +168 -0
- package/dist/lib/set-master-admin.d.ts +2 -0
- package/dist/lib/set-master-admin.d.ts.map +1 -0
- package/dist/lib/set-master-admin.js +53 -0
- package/dist/lib/update-sections.d.ts +2 -0
- package/dist/lib/update-sections.d.ts.map +1 -0
- package/dist/lib/update-sections.js +898 -0
- package/package.json +55 -0
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
// import '@/envConfig'
|
|
2
|
+
import { db } from 'nextjs-cms/db/client';
|
|
3
|
+
import { LZTablesTable } from 'nextjs-cms/db/schema';
|
|
4
|
+
import { eq, sql } from 'drizzle-orm';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { SectionFactory } from 'nextjs-cms/core/factories';
|
|
7
|
+
import { CheckboxField, ColorField, DateField, MapField, NumberField, PasswordField, RichTextField, SelectField, SelectMultipleField, TagsField, TextAreaField, TextField, textAreaField, textField, } from 'nextjs-cms/core/fields';
|
|
8
|
+
import { is } from 'nextjs-cms/core/helpers';
|
|
9
|
+
import { FileField } from 'nextjs-cms/core/fields';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { intro, select, spinner, log } from '@clack/prompts';
|
|
12
|
+
import { MysqlTableChecker } from 'nextjs-cms/core/db';
|
|
13
|
+
import { generateDrizzleSchema } from './schema-generator';
|
|
14
|
+
import { addTableKeys } from './add-table-keys';
|
|
15
|
+
function generateFieldSQL(input) {
|
|
16
|
+
let fieldSQL = `\`${input.name}\` `;
|
|
17
|
+
/**
|
|
18
|
+
* Check the field type and generate the SQL accordingly
|
|
19
|
+
*/
|
|
20
|
+
switch (true) {
|
|
21
|
+
case is(input, TextField):
|
|
22
|
+
fieldSQL += `VARCHAR(${input.maxLength ?? 255}) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`;
|
|
23
|
+
break;
|
|
24
|
+
case is(input, TextAreaField) || is(input, RichTextField):
|
|
25
|
+
fieldSQL += 'LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
26
|
+
break;
|
|
27
|
+
case is(input, SelectMultipleField):
|
|
28
|
+
fieldSQL += 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
29
|
+
break;
|
|
30
|
+
case is(input, SelectField):
|
|
31
|
+
if (input.optionsType === 'static' && input.options) {
|
|
32
|
+
fieldSQL += `ENUM(${input.options.map((option) => `'${option.value}'`).join(', ')}) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
fieldSQL += 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case is(input, CheckboxField):
|
|
39
|
+
fieldSQL += 'BOOLEAN';
|
|
40
|
+
break;
|
|
41
|
+
case is(input, ColorField):
|
|
42
|
+
fieldSQL += 'CHAR(7)'; // #RRGGBB
|
|
43
|
+
break;
|
|
44
|
+
case is(input, DateField):
|
|
45
|
+
switch (input.format) {
|
|
46
|
+
case 'date':
|
|
47
|
+
fieldSQL += 'DATE';
|
|
48
|
+
break;
|
|
49
|
+
case 'datetime':
|
|
50
|
+
fieldSQL += 'DATETIME';
|
|
51
|
+
break;
|
|
52
|
+
case 'timestamp':
|
|
53
|
+
fieldSQL += 'TIMESTAMP';
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
fieldSQL += 'DATETIME';
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case is(input, NumberField): {
|
|
61
|
+
// Default format: 'int' if not specified
|
|
62
|
+
const format = input.format;
|
|
63
|
+
// FLOAT / DOUBLE
|
|
64
|
+
if (format === 'float' || format === 'double') {
|
|
65
|
+
fieldSQL += format === 'float' ? 'FLOAT' : 'DOUBLE';
|
|
66
|
+
if (input.minValue !== undefined && input.minValue >= 0) {
|
|
67
|
+
fieldSQL += ' UNSIGNED';
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
// ---------- INTEGER TYPES ----------
|
|
72
|
+
const unsigned = input.minValue !== undefined && input.minValue >= 0;
|
|
73
|
+
// Decide if we have any bounds we can use to shrink the integer type
|
|
74
|
+
const hasBounds = input.minValue !== undefined || input.maxValue !== undefined || input.maxLength !== undefined;
|
|
75
|
+
// From maxLength, approximate numeric bounds if explicit min/max not provided
|
|
76
|
+
const pow10 = (d) => Math.pow(10, d);
|
|
77
|
+
const approxFromDigits = (digits) => {
|
|
78
|
+
if (digits === undefined)
|
|
79
|
+
return {};
|
|
80
|
+
const max = pow10(digits) - 1;
|
|
81
|
+
// if unsigned, min will be clamped to 0 below
|
|
82
|
+
const min = -max;
|
|
83
|
+
return { min, max };
|
|
84
|
+
};
|
|
85
|
+
const approx = approxFromDigits(input.maxLength);
|
|
86
|
+
let neededMin = input.minValue ?? approx.min;
|
|
87
|
+
const neededMax = input.maxValue ?? approx.max;
|
|
88
|
+
if (unsigned)
|
|
89
|
+
neededMin = neededMin === undefined ? 0 : Math.max(0, neededMin);
|
|
90
|
+
const ranges = unsigned
|
|
91
|
+
? [
|
|
92
|
+
{ type: 'TINYINT', min: 0, max: 255 },
|
|
93
|
+
{ type: 'SMALLINT', min: 0, max: 65_535 },
|
|
94
|
+
{ type: 'MEDIUMINT', min: 0, max: 16_777_215 },
|
|
95
|
+
{ type: 'INT', min: 0, max: 4_294_967_295 },
|
|
96
|
+
// BIGINT max clipped to safe JS number for comparison purposes
|
|
97
|
+
{ type: 'BIGINT', min: 0, max: Number.MAX_SAFE_INTEGER },
|
|
98
|
+
]
|
|
99
|
+
: [
|
|
100
|
+
{ type: 'TINYINT', min: -128, max: 127 },
|
|
101
|
+
{ type: 'SMALLINT', min: -32_768, max: 32_767 },
|
|
102
|
+
{ type: 'MEDIUMINT', min: -8_388_608, max: 8_388_607 },
|
|
103
|
+
{ type: 'INT', min: -2_147_483_648, max: 2_147_483_647 },
|
|
104
|
+
{ type: 'BIGINT', min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER },
|
|
105
|
+
];
|
|
106
|
+
const fits = (r) => {
|
|
107
|
+
const okMin = neededMin === undefined || neededMin >= r.min;
|
|
108
|
+
const okMax = neededMax === undefined || neededMax <= r.max;
|
|
109
|
+
return okMin && okMax;
|
|
110
|
+
};
|
|
111
|
+
// Choose type:
|
|
112
|
+
// - If no bounds at all, default to INT (common + avoids accidental TINYINT)
|
|
113
|
+
// - If auto-increment and still no bounds, keep INT
|
|
114
|
+
// - Otherwise, pick the smallest that fits
|
|
115
|
+
let chosen = 'INT';
|
|
116
|
+
if (hasBounds) {
|
|
117
|
+
chosen = ranges.find(fits)?.type ?? 'INT';
|
|
118
|
+
// If auto-increment, don't go smaller than INT by default (optional safety)
|
|
119
|
+
if (input.hasAutoIncrement &&
|
|
120
|
+
(chosen === 'TINYINT' || chosen === 'SMALLINT' || chosen === 'MEDIUMINT')) {
|
|
121
|
+
chosen = 'INT';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
fieldSQL += chosen;
|
|
125
|
+
if (unsigned)
|
|
126
|
+
fieldSQL += ' UNSIGNED';
|
|
127
|
+
// Display width:
|
|
128
|
+
// - If user specified maxLength, honor it for any integer type
|
|
129
|
+
// - Else, if chosen is INT, default to (11) as requested
|
|
130
|
+
const width = input.maxLength ?? (chosen === 'INT' ? 11 : undefined);
|
|
131
|
+
if (width !== undefined) {
|
|
132
|
+
fieldSQL += `(${width})`;
|
|
133
|
+
}
|
|
134
|
+
// AUTO_INCREMENT only for integer types
|
|
135
|
+
if (input.hasAutoIncrement) {
|
|
136
|
+
fieldSQL += ' AUTO_INCREMENT';
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case is(input, MapField):
|
|
141
|
+
fieldSQL += 'VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
142
|
+
break;
|
|
143
|
+
case is(input, FileField):
|
|
144
|
+
fieldSQL += 'VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
145
|
+
break;
|
|
146
|
+
case is(input, PasswordField):
|
|
147
|
+
fieldSQL += 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
148
|
+
break;
|
|
149
|
+
case is(input, TagsField):
|
|
150
|
+
fieldSQL += 'VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
fieldSQL += 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (input.required && !input.isConditional()) {
|
|
157
|
+
fieldSQL += ' NOT NULL';
|
|
158
|
+
}
|
|
159
|
+
else if (!input.defaultValue) {
|
|
160
|
+
fieldSQL += ' DEFAULT NULL';
|
|
161
|
+
}
|
|
162
|
+
if (input.defaultValue !== null && input.defaultValue !== undefined) {
|
|
163
|
+
fieldSQL += ` DEFAULT '${input.defaultValue}'`;
|
|
164
|
+
}
|
|
165
|
+
return fieldSQL;
|
|
166
|
+
}
|
|
167
|
+
async function createTable(table, options) {
|
|
168
|
+
/**
|
|
169
|
+
* Generate the CREATE TABLE SQL
|
|
170
|
+
*/
|
|
171
|
+
let createTableSQL = `CREATE TABLE \`${table.name}\` (`;
|
|
172
|
+
/**
|
|
173
|
+
* Loop through the fields and generate the SQL for each field
|
|
174
|
+
*/
|
|
175
|
+
for (const input of table.fields) {
|
|
176
|
+
if (input.destinationDb)
|
|
177
|
+
continue;
|
|
178
|
+
let fieldSQL = generateFieldSQL(input);
|
|
179
|
+
/**
|
|
180
|
+
* Check if the field is a primary key
|
|
181
|
+
*/
|
|
182
|
+
if (input.name === table.identifier?.name) {
|
|
183
|
+
fieldSQL += ' UNIQUE';
|
|
184
|
+
}
|
|
185
|
+
createTableSQL += `${fieldSQL}, `;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Now, let's add the `created_at`, `updated_at`, `created_by`, and `updated_by` fields
|
|
189
|
+
*/
|
|
190
|
+
if (options) {
|
|
191
|
+
if (options.createdAt !== false) {
|
|
192
|
+
createTableSQL += '`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ';
|
|
193
|
+
}
|
|
194
|
+
if (options.updatedAt !== false) {
|
|
195
|
+
createTableSQL += '`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ';
|
|
196
|
+
}
|
|
197
|
+
if (options.createdBy !== false) {
|
|
198
|
+
createTableSQL += '`created_by` VARCHAR(50) NOT NULL, '; // Should reference the admin ID?
|
|
199
|
+
}
|
|
200
|
+
if (options.updatedBy !== false) {
|
|
201
|
+
createTableSQL += '`updated_by` VARCHAR(50), '; // Should reference the admin ID?
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Add the primary key
|
|
206
|
+
*/
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
208
|
+
if (!(table.primaryKey && table.primaryKey.length > 0)) {
|
|
209
|
+
/**
|
|
210
|
+
* Log a warning if there's no primary key
|
|
211
|
+
*/
|
|
212
|
+
console.warn(chalk.yellow(` - Warning: Table \`${table.name}\` has no primary key!`));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
// addTableKeys function will handle the primary key
|
|
216
|
+
/*createTableSQL += 'PRIMARY KEY ('
|
|
217
|
+
for (const key of table.primaryKey) {
|
|
218
|
+
createTableSQL += `\`${key.name}\`, `
|
|
219
|
+
}
|
|
220
|
+
createTableSQL = createTableSQL.slice(0, -2) + '), '*/
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Remove the last comma and space, and then close the CREATE TABLE statement
|
|
224
|
+
*/
|
|
225
|
+
createTableSQL = createTableSQL.slice(0, -2) + ') ENGINE=InnoDB;';
|
|
226
|
+
/**
|
|
227
|
+
* Try to create the table
|
|
228
|
+
*/
|
|
229
|
+
try {
|
|
230
|
+
/**
|
|
231
|
+
* Execute the CREATE TABLE SQL
|
|
232
|
+
*/
|
|
233
|
+
await db.execute(sql `${sql.raw(createTableSQL)}`);
|
|
234
|
+
/**
|
|
235
|
+
* Insert the table name into the `__lz_tables` table
|
|
236
|
+
*/
|
|
237
|
+
await db
|
|
238
|
+
.insert(LZTablesTable)
|
|
239
|
+
.values({
|
|
240
|
+
tableName: table.name,
|
|
241
|
+
sectionName: table.sectionName,
|
|
242
|
+
})
|
|
243
|
+
.catch((error) => {
|
|
244
|
+
console.error('Error inserting into __lz_tables table:', error);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
console.log(chalk.red(` - Error creating table \`${table.name}\`:`, error));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the existing keys for the table
|
|
252
|
+
*/
|
|
253
|
+
const existingKeys = await MysqlTableChecker.getExistingKeys(table.name);
|
|
254
|
+
/**
|
|
255
|
+
* Add the keys to the table
|
|
256
|
+
*/
|
|
257
|
+
const keyErrors = await addTableKeys(table, existingKeys);
|
|
258
|
+
console.log(chalk.italic.hex(`${keyErrors > 0 ? `#FFA500` : `#fafafa`}`)(` - Table \`${table.name}\` created successfully ${keyErrors > 0 ? `with ${keyErrors} error(s).` : ''}`));
|
|
259
|
+
}
|
|
260
|
+
async function renameTable(oldName, newName) {
|
|
261
|
+
/**
|
|
262
|
+
* Execute ALTER TABLE statement to rename the table
|
|
263
|
+
*/
|
|
264
|
+
try {
|
|
265
|
+
await db.execute(sql `ALTER TABLE \`${sql.raw(oldName)}\` RENAME \`${sql.raw(newName)}\``);
|
|
266
|
+
/**
|
|
267
|
+
* Update the table name in the `__lz_tables` table
|
|
268
|
+
*/
|
|
269
|
+
await db
|
|
270
|
+
.update(LZTablesTable)
|
|
271
|
+
.set({
|
|
272
|
+
tableName: newName,
|
|
273
|
+
})
|
|
274
|
+
.where(eq(LZTablesTable.tableName, oldName))
|
|
275
|
+
.catch((error) => {
|
|
276
|
+
console.error('Error updating __lz_tables table:', error);
|
|
277
|
+
});
|
|
278
|
+
console.log(`Table \`${oldName}\` renamed to \`${newName}\` successfully.`);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error(`Error renaming table \`${oldName}\` to \`${newName}\`:`, error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async function updateTable(table, s) {
|
|
285
|
+
console.log();
|
|
286
|
+
console.log(chalk.blueBright(`Updating table '${table.name}' for section '${table.sectionName}'`));
|
|
287
|
+
s.start();
|
|
288
|
+
/**
|
|
289
|
+
* Get the existing keys for the table
|
|
290
|
+
*/
|
|
291
|
+
const existingKeys = await MysqlTableChecker.getExistingKeys(table.name);
|
|
292
|
+
/**
|
|
293
|
+
* Get the existing table structure (columns)
|
|
294
|
+
*/
|
|
295
|
+
const existingFieldsData = await MysqlTableChecker.getExistingTableStructure(table.name);
|
|
296
|
+
let sqlErrors = 0;
|
|
297
|
+
const alterTableSQLs = [];
|
|
298
|
+
/**
|
|
299
|
+
* Filter out the fields that already exist in the table
|
|
300
|
+
*/
|
|
301
|
+
const existingFields = existingFieldsData ? Object.keys(existingFieldsData) : [];
|
|
302
|
+
const fieldsToAdd = table.fields.filter((field) => !existingFields.some((existingField) => existingField === field.name));
|
|
303
|
+
/**
|
|
304
|
+
* Let's find out fields that need to be updated.
|
|
305
|
+
* If a field exists in both the desired fields and the existing fields, we should mark it for update.
|
|
306
|
+
* TODO: Actually, we should check if the field needs to be updated, and only update if necessary. (INFORMATION_SCHEMA will help)
|
|
307
|
+
*/
|
|
308
|
+
const fieldsToUpdate = table.fields.filter((field) => existingFields.some((existingField) => existingField === field.name));
|
|
309
|
+
/**
|
|
310
|
+
* Let's find out fields to remove as well.
|
|
311
|
+
* If a field exists in the table but not in the desired fields,
|
|
312
|
+
* or if it has a destinationDb, TODO: Add the values to the destination table? (CRITICAL! Loss of data if not done)
|
|
313
|
+
* we should mark it for removal.
|
|
314
|
+
*/
|
|
315
|
+
let fieldsToRemove = existingFields.filter((existingField) => !table.fields.some((field) => field.name === existingField) ||
|
|
316
|
+
table.fields.some((field) => field.destinationDb && field.name === existingField));
|
|
317
|
+
/**
|
|
318
|
+
* Check if there are fields to update
|
|
319
|
+
*/
|
|
320
|
+
if (fieldsToUpdate.length > 0) {
|
|
321
|
+
/**
|
|
322
|
+
* Loop through the tables to update
|
|
323
|
+
*/
|
|
324
|
+
for (const field of fieldsToUpdate) {
|
|
325
|
+
// TODO: We should also check if the field type has changed,
|
|
326
|
+
// and if so, notify the user that the field type will be updated,
|
|
327
|
+
// and data may be lost! (e.g. changing a field from VARCHAR to INT)
|
|
328
|
+
if (field.destinationDb)
|
|
329
|
+
continue;
|
|
330
|
+
let fieldSQL = generateFieldSQL(field);
|
|
331
|
+
/**
|
|
332
|
+
* Check if the field is a primary key
|
|
333
|
+
*/
|
|
334
|
+
if (field.name === table.identifier?.name) {
|
|
335
|
+
fieldSQL += ' FIRST';
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Add the SQL to modify the field
|
|
339
|
+
*/
|
|
340
|
+
alterTableSQLs.push({
|
|
341
|
+
field: field.name,
|
|
342
|
+
table: table.name,
|
|
343
|
+
action: 'modify',
|
|
344
|
+
sql: `MODIFY COLUMN ${fieldSQL}`,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Check if there are fields to add
|
|
350
|
+
*/
|
|
351
|
+
if (fieldsToAdd.length > 0) {
|
|
352
|
+
/**
|
|
353
|
+
* Loop through the fields to add
|
|
354
|
+
*/
|
|
355
|
+
for (const field of fieldsToAdd) {
|
|
356
|
+
if (field.destinationDb)
|
|
357
|
+
continue;
|
|
358
|
+
let fieldSQL = generateFieldSQL(field);
|
|
359
|
+
/**
|
|
360
|
+
* Check if the field is an identifier
|
|
361
|
+
*/
|
|
362
|
+
if (field.name === table.identifier?.name) {
|
|
363
|
+
fieldSQL += ' UNIQUE FIRST';
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Check if there are fields to remove
|
|
367
|
+
* If there are, ask the user if they want to add the new field or rename a to-be-removed field
|
|
368
|
+
*/
|
|
369
|
+
if (fieldsToRemove.length > 0) {
|
|
370
|
+
s.stop();
|
|
371
|
+
const opType = await select({
|
|
372
|
+
message: `Is field '${field.name}' of table '${table.name}' a new field?`,
|
|
373
|
+
options: [
|
|
374
|
+
{ value: 'new', label: 'Create new field' },
|
|
375
|
+
{ value: 'rename', label: 'Rename existing field' },
|
|
376
|
+
],
|
|
377
|
+
});
|
|
378
|
+
s.start();
|
|
379
|
+
switch (opType) {
|
|
380
|
+
case 'new':
|
|
381
|
+
/**
|
|
382
|
+
* Field doesn't exist.
|
|
383
|
+
* Add the field to the table
|
|
384
|
+
*/
|
|
385
|
+
alterTableSQLs.push({
|
|
386
|
+
field: field.name,
|
|
387
|
+
table: table.name,
|
|
388
|
+
action: 'add',
|
|
389
|
+
sql: `ADD COLUMN ${fieldSQL}`,
|
|
390
|
+
});
|
|
391
|
+
break;
|
|
392
|
+
case 'rename': {
|
|
393
|
+
s.stop();
|
|
394
|
+
const oldField = await select({
|
|
395
|
+
message: `Select the field to rename to '${field.name}'`,
|
|
396
|
+
options: fieldsToRemove.map((field) => {
|
|
397
|
+
return { value: field, label: field };
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
s.start();
|
|
401
|
+
if (oldField && typeof oldField === 'string') {
|
|
402
|
+
console.log(`Renaming field '${oldField}' to '${field.name}'`);
|
|
403
|
+
/**
|
|
404
|
+
* Rename the field
|
|
405
|
+
*/
|
|
406
|
+
alterTableSQLs.push({
|
|
407
|
+
field: field.name,
|
|
408
|
+
table: table.name,
|
|
409
|
+
action: 'rename',
|
|
410
|
+
sql: `RENAME COLUMN \`${oldField}\` TO ${field.name}`,
|
|
411
|
+
});
|
|
412
|
+
/**
|
|
413
|
+
* Remove the field from the fieldsToRemove array
|
|
414
|
+
*/
|
|
415
|
+
fieldsToRemove = fieldsToRemove.filter((field) => {
|
|
416
|
+
return field !== oldField;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
/**
|
|
425
|
+
* Field doesn't exist.
|
|
426
|
+
* Add the field to the table
|
|
427
|
+
*/
|
|
428
|
+
alterTableSQLs.push({
|
|
429
|
+
field: field.name,
|
|
430
|
+
table: table.name,
|
|
431
|
+
action: 'add',
|
|
432
|
+
sql: `ADD COLUMN ${fieldSQL}`,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Finally, check if there are fields to remove
|
|
439
|
+
*/
|
|
440
|
+
if (fieldsToRemove.length > 0) {
|
|
441
|
+
/**
|
|
442
|
+
* Loop through the fields to remove
|
|
443
|
+
*/
|
|
444
|
+
s.stop('Found ' + fieldsToRemove.length + ' field(s) to remove:');
|
|
445
|
+
for (const field of fieldsToRemove) {
|
|
446
|
+
const shouldRemove = await select({
|
|
447
|
+
message: `You are about to remove field '${field}' of table '${table.name}'. Remove it?`,
|
|
448
|
+
options: [
|
|
449
|
+
{ value: 'no', label: 'No' },
|
|
450
|
+
{ value: 'yes', label: 'Yes' },
|
|
451
|
+
],
|
|
452
|
+
initialValue: 'no',
|
|
453
|
+
});
|
|
454
|
+
if (shouldRemove === 'yes') {
|
|
455
|
+
log.info(chalk.red(`Removing field '${field}'`));
|
|
456
|
+
alterTableSQLs.push({
|
|
457
|
+
field: field,
|
|
458
|
+
table: table.name,
|
|
459
|
+
action: 'remove',
|
|
460
|
+
sql: `DROP COLUMN \`${field}\``,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
log.info(chalk.yellow(`- Field ${chalk.underline.italic(field)} not removed.`));
|
|
465
|
+
}
|
|
466
|
+
s.start();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Execute ALTER TABLE statements
|
|
471
|
+
*/
|
|
472
|
+
for (const alterSQL of alterTableSQLs) {
|
|
473
|
+
try {
|
|
474
|
+
await db.execute(sql `ALTER TABLE \`${sql.raw(table.name)}\` ${sql.raw(alterSQL.sql)}`);
|
|
475
|
+
switch (alterSQL.action) {
|
|
476
|
+
case 'add':
|
|
477
|
+
console.log(chalk.gray(` - Field ${chalk.underline.italic(alterSQL.field)} Added to \`${table.name}\`.`));
|
|
478
|
+
break;
|
|
479
|
+
case 'modify':
|
|
480
|
+
// Activate this when we only update the fields that need to be updated
|
|
481
|
+
// console.log(chalk.gray(` - ${alterSQL.field}: Modified in \`${table.name}\`.`))
|
|
482
|
+
break;
|
|
483
|
+
case 'remove':
|
|
484
|
+
console.log(chalk.gray(` - Field ${chalk.underline.italic(alterSQL.field)} Removed from \`${table.name}\`.`));
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
sqlErrors++;
|
|
490
|
+
console.error(chalk.red(` - ${alterSQL.field}: Error modifying \`${table.name}\`.\`${alterSQL.field}\`:`, error));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Add the keys to the table
|
|
495
|
+
*/
|
|
496
|
+
const keyErrors = await addTableKeys(table, existingKeys);
|
|
497
|
+
sqlErrors += keyErrors;
|
|
498
|
+
s.stop(chalk.italic.hex(`${sqlErrors > 0 ? `#FFA500` : `#fafafa`}`)(`- Table \`${table.name}\` modified successfully ${sqlErrors > 0 ? `with ${sqlErrors} error(s).` : ''}`));
|
|
499
|
+
}
|
|
500
|
+
const main = async (s) => {
|
|
501
|
+
/**
|
|
502
|
+
* Remove the existing schema file
|
|
503
|
+
*/
|
|
504
|
+
console.log(chalk.white(`Removing existing schema file...`));
|
|
505
|
+
s.start();
|
|
506
|
+
const schemaFilePath = './dynamic-schemas/schema.ts';
|
|
507
|
+
try {
|
|
508
|
+
if (fs.existsSync(schemaFilePath)) {
|
|
509
|
+
fs.unlinkSync(schemaFilePath);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
console.error('Error removing schema file:', error);
|
|
514
|
+
}
|
|
515
|
+
s.stop();
|
|
516
|
+
console.log(chalk.white(`Generating Drizzle schema...`));
|
|
517
|
+
s.start();
|
|
518
|
+
const drizzleTableSchemas = [];
|
|
519
|
+
const drizzleColumnTypes = ['mysqlTable'];
|
|
520
|
+
let sections = [];
|
|
521
|
+
const desiredTables = [];
|
|
522
|
+
let existingTables = [];
|
|
523
|
+
sections = await SectionFactory.getSections();
|
|
524
|
+
console.log(`Found ${sections.length} section(s) to insert: `);
|
|
525
|
+
console.log(chalk.gray(sections.map((s) => s.name).join(', ')));
|
|
526
|
+
/**
|
|
527
|
+
* Let's see if the table `__lz_tables` exists in the database.
|
|
528
|
+
* If it doesn't, we'll create it.
|
|
529
|
+
* It has two fields: `name` and `created_at`.
|
|
530
|
+
*/
|
|
531
|
+
await db.execute(sql ` CREATE TABLE IF NOT EXISTS __lz_tables ( name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `);
|
|
532
|
+
/**
|
|
533
|
+
* Get the existing tables from the database
|
|
534
|
+
*/
|
|
535
|
+
existingTables = await db
|
|
536
|
+
.select({
|
|
537
|
+
name: LZTablesTable.tableName,
|
|
538
|
+
})
|
|
539
|
+
.from(LZTablesTable);
|
|
540
|
+
/**
|
|
541
|
+
* Insert the sections into the database
|
|
542
|
+
*/
|
|
543
|
+
for (const _s of sections) {
|
|
544
|
+
const s = _s.build();
|
|
545
|
+
s.buildFields();
|
|
546
|
+
/**
|
|
547
|
+
* Generate the Drizzle schema for the table
|
|
548
|
+
*/
|
|
549
|
+
const drizzleSchema = generateDrizzleSchema({
|
|
550
|
+
name: s.db.table,
|
|
551
|
+
fields: s.fieldConfigs,
|
|
552
|
+
identifier: s.db.identifier,
|
|
553
|
+
});
|
|
554
|
+
drizzleTableSchemas.push(drizzleSchema.schema);
|
|
555
|
+
drizzleSchema.columnTypes.forEach((type) => {
|
|
556
|
+
if (!drizzleColumnTypes.includes(type)) {
|
|
557
|
+
drizzleColumnTypes.push(type);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
desiredTables.push({
|
|
561
|
+
name: s.db.table,
|
|
562
|
+
fields: s.fields,
|
|
563
|
+
sectionName: s.name,
|
|
564
|
+
sectionType: s.type,
|
|
565
|
+
identifier: s.db.identifier,
|
|
566
|
+
primaryKey: s.db.primaryKey,
|
|
567
|
+
fulltext: s.db.fulltext,
|
|
568
|
+
unique: s.db.unique,
|
|
569
|
+
index: s.db.index,
|
|
570
|
+
});
|
|
571
|
+
/**
|
|
572
|
+
* Check for `destinationDb`
|
|
573
|
+
*/
|
|
574
|
+
for (const field of s.fields) {
|
|
575
|
+
/**
|
|
576
|
+
* TODO: We should also check for input.db in select fields, and add the table to the desiredTables array!
|
|
577
|
+
*/
|
|
578
|
+
if (field.destinationDb) {
|
|
579
|
+
console.log('Destination DB found for input:', field.name, 'with table:', field.destinationDb.table);
|
|
580
|
+
/**
|
|
581
|
+
* TODO: We should get the type of the identifier and the Select fields to match the types
|
|
582
|
+
*/
|
|
583
|
+
const referenceIdFieldConfig = textField({
|
|
584
|
+
name: field.destinationDb.itemIdentifier,
|
|
585
|
+
label: 'Reference Id',
|
|
586
|
+
required: true,
|
|
587
|
+
order: 0,
|
|
588
|
+
});
|
|
589
|
+
const selectIdFieldConfig = textField({
|
|
590
|
+
name: field.destinationDb.selectIdentifier,
|
|
591
|
+
label: 'Select Id',
|
|
592
|
+
required: true,
|
|
593
|
+
order: 0,
|
|
594
|
+
});
|
|
595
|
+
desiredTables.push({
|
|
596
|
+
name: field.destinationDb.table,
|
|
597
|
+
fields: [referenceIdFieldConfig.build(), selectIdFieldConfig.build()],
|
|
598
|
+
sectionName: s.name,
|
|
599
|
+
sectionType: 'destinationDb',
|
|
600
|
+
identifier: undefined,
|
|
601
|
+
/**
|
|
602
|
+
* We can use both the referenceIdField and the selectIdField as a composite primary key
|
|
603
|
+
* since they are unique together for each row
|
|
604
|
+
*/
|
|
605
|
+
primaryKey: [referenceIdFieldConfig, selectIdFieldConfig],
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Check for gallery
|
|
611
|
+
*/
|
|
612
|
+
if (s.gallery?.db.tableName) {
|
|
613
|
+
console.log('Gallery found for section:', s.name, 'with table:', s.gallery.db.tableName);
|
|
614
|
+
const photoField = textField({
|
|
615
|
+
name: s.gallery.db.photoNameField || 'photo',
|
|
616
|
+
label: 'Photo Name',
|
|
617
|
+
required: true,
|
|
618
|
+
order: 0,
|
|
619
|
+
});
|
|
620
|
+
desiredTables.push({
|
|
621
|
+
name: s.gallery.db.tableName,
|
|
622
|
+
fields: [
|
|
623
|
+
textField({
|
|
624
|
+
name: s.gallery.db.referenceIdentifierField || 'reference_id',
|
|
625
|
+
label: 'Reference Id',
|
|
626
|
+
required: true,
|
|
627
|
+
order: 0,
|
|
628
|
+
}).build(),
|
|
629
|
+
photoField.build(),
|
|
630
|
+
textAreaField({
|
|
631
|
+
name: s.gallery.db.metaField || 'meta',
|
|
632
|
+
label: 'Photo Information',
|
|
633
|
+
required: false,
|
|
634
|
+
order: 0,
|
|
635
|
+
}).build(),
|
|
636
|
+
],
|
|
637
|
+
sectionName: s.name,
|
|
638
|
+
sectionType: 'gallery',
|
|
639
|
+
/**
|
|
640
|
+
* We can use the photoField as the identifier for the gallery table
|
|
641
|
+
* since it's unique, we can also use it as the primary key
|
|
642
|
+
*/
|
|
643
|
+
identifier: photoField,
|
|
644
|
+
primaryKey: [photoField],
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Make sure the dynamic-schemas directory exists
|
|
650
|
+
*/
|
|
651
|
+
if (!fs.existsSync('./dynamic-schemas')) {
|
|
652
|
+
fs.mkdirSync('./dynamic-schemas');
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Append the Drizzle column types to the schema file
|
|
656
|
+
*/
|
|
657
|
+
fs.appendFileSync(schemaFilePath, 'import {' + drizzleColumnTypes.join(',') + "} from 'drizzle-orm/mysql-core'\n\n");
|
|
658
|
+
/**
|
|
659
|
+
* Append the Drizzle table schemas to the schema file
|
|
660
|
+
*/
|
|
661
|
+
fs.appendFileSync(schemaFilePath, drizzleTableSchemas.join('\n'));
|
|
662
|
+
s.stop();
|
|
663
|
+
console.log(chalk.white('Finding tables to create, update or remove...'));
|
|
664
|
+
s.start();
|
|
665
|
+
/**
|
|
666
|
+
* Filter out the tables that already exist in the database
|
|
667
|
+
*/
|
|
668
|
+
const tablesToCreate = desiredTables.filter((table) => !existingTables.some((existingTable) => existingTable.name === table.name));
|
|
669
|
+
/**
|
|
670
|
+
* Let's find out tables that need to be updated.
|
|
671
|
+
* If a table exists in both the desired tables and the existing tables, we should mark it for update.
|
|
672
|
+
*/
|
|
673
|
+
const tablesToUpdate = desiredTables.filter((table) => existingTables.some((existingTable) => existingTable.name === table.name));
|
|
674
|
+
/**
|
|
675
|
+
* Let's find out tables to remove as well.
|
|
676
|
+
* If a table exists in the database but not in the desired tables, we should mark it for removal.
|
|
677
|
+
*/
|
|
678
|
+
let tablesToRemove = existingTables.filter((existingTable) => !desiredTables.some((table) => table.name === existingTable.name));
|
|
679
|
+
s.stop();
|
|
680
|
+
console.log('Desired tables:');
|
|
681
|
+
console.log(chalk.gray(desiredTables.map((table) => table.name).join(', ')));
|
|
682
|
+
console.log('Existing tables:');
|
|
683
|
+
console.log(chalk.gray(existingTables.map((table) => table.name).join(', ')));
|
|
684
|
+
console.log('Tables to update:');
|
|
685
|
+
console.log(chalk.gray(tablesToUpdate.map((table) => table.name).join(', ')));
|
|
686
|
+
console.log('Tables to create:');
|
|
687
|
+
console.log(chalk.gray(tablesToCreate.map((table) => table.name).join(', ')));
|
|
688
|
+
console.log('Tables to remove:');
|
|
689
|
+
console.log(chalk.gray(tablesToRemove.map((table) => table.name).join(', ')));
|
|
690
|
+
console.log(`\n`);
|
|
691
|
+
intro(chalk.inverse(' update-sections '));
|
|
692
|
+
/**
|
|
693
|
+
* Check if there are tables to update
|
|
694
|
+
*/
|
|
695
|
+
if (tablesToUpdate.length > 0) {
|
|
696
|
+
/**
|
|
697
|
+
* Loop through the tables to update
|
|
698
|
+
*/
|
|
699
|
+
for (const table of tablesToUpdate) {
|
|
700
|
+
await updateTable(table, s);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Check if there are tables to create
|
|
705
|
+
*/
|
|
706
|
+
if (tablesToCreate.length > 0) {
|
|
707
|
+
console.log(chalk.bold.bgGreenBright(`There are ${tablesToCreate.length} table(s) to create:`));
|
|
708
|
+
/**
|
|
709
|
+
* Loop through the tables to create
|
|
710
|
+
*/
|
|
711
|
+
for (const table of tablesToCreate) {
|
|
712
|
+
/**
|
|
713
|
+
* Check if there are tables to remove
|
|
714
|
+
* If there are, ask the user if they want to create the new table or rename a removed table
|
|
715
|
+
*/
|
|
716
|
+
if (tablesToRemove.length > 0) {
|
|
717
|
+
s.stop();
|
|
718
|
+
const opType = await select({
|
|
719
|
+
message: `Is table '${table.name}' for section '${table.sectionName}' a new table?`,
|
|
720
|
+
options: [
|
|
721
|
+
{ value: 'new', label: 'Create new table' },
|
|
722
|
+
{ value: 'rename', label: 'Rename existing table' },
|
|
723
|
+
],
|
|
724
|
+
});
|
|
725
|
+
s.start();
|
|
726
|
+
switch (opType) {
|
|
727
|
+
case 'new': {
|
|
728
|
+
console.log(chalk.blueBright(`Creating table '${table.name}' for section '${table.sectionName}'`));
|
|
729
|
+
let options = {
|
|
730
|
+
createdAt: true,
|
|
731
|
+
updatedAt: true,
|
|
732
|
+
createdBy: true,
|
|
733
|
+
updatedBy: true,
|
|
734
|
+
};
|
|
735
|
+
if (table.sectionType === 'simple') {
|
|
736
|
+
options = {
|
|
737
|
+
createdAt: false,
|
|
738
|
+
updatedAt: true,
|
|
739
|
+
createdBy: false,
|
|
740
|
+
updatedBy: true,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
if (table.sectionType === 'gallery') {
|
|
744
|
+
options = {
|
|
745
|
+
updatedBy: false,
|
|
746
|
+
updatedAt: false,
|
|
747
|
+
createdBy: true,
|
|
748
|
+
createdAt: true,
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
if (table.sectionType === 'destinationDb, selectDb') {
|
|
752
|
+
options = {
|
|
753
|
+
createdAt: false,
|
|
754
|
+
updatedAt: false,
|
|
755
|
+
createdBy: false,
|
|
756
|
+
updatedBy: false,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
await createTable(table, options);
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
case 'rename': {
|
|
763
|
+
s.stop();
|
|
764
|
+
const tableToRename = await select({
|
|
765
|
+
message: `Select the table to rename to '${table.name}'`,
|
|
766
|
+
options: tablesToRemove.map((table) => {
|
|
767
|
+
return { value: table.name, label: table.name };
|
|
768
|
+
}),
|
|
769
|
+
});
|
|
770
|
+
s.start();
|
|
771
|
+
if (tableToRename && typeof tableToRename === 'string') {
|
|
772
|
+
console.log(`Renaming table '${tableToRename}' to '${table.name}'`);
|
|
773
|
+
await renameTable(tableToRename, table.name);
|
|
774
|
+
await updateTable(table, s);
|
|
775
|
+
/**
|
|
776
|
+
* Remove the table from the tablesToRemove array
|
|
777
|
+
*/
|
|
778
|
+
tablesToRemove = tablesToRemove.filter((table) => {
|
|
779
|
+
return table.name !== tableToRename;
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
/*if (
|
|
788
|
+
await confirm({
|
|
789
|
+
message: `You are about to create a new table '${table.name}' for section '${table.sectionName}'. Proceed?`,
|
|
790
|
+
})
|
|
791
|
+
) {*/
|
|
792
|
+
console.log(chalk.blueBright(`Creating table '${table.name}' for section '${table.sectionName}'`));
|
|
793
|
+
let options = {
|
|
794
|
+
createdAt: true,
|
|
795
|
+
updatedAt: true,
|
|
796
|
+
createdBy: true,
|
|
797
|
+
updatedBy: true,
|
|
798
|
+
};
|
|
799
|
+
if (table.sectionType === 'simple') {
|
|
800
|
+
options = {
|
|
801
|
+
createdAt: false,
|
|
802
|
+
updatedAt: true,
|
|
803
|
+
createdBy: false,
|
|
804
|
+
updatedBy: true,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (table.sectionType === 'gallery') {
|
|
808
|
+
options = {
|
|
809
|
+
updatedBy: false,
|
|
810
|
+
updatedAt: false,
|
|
811
|
+
createdBy: true,
|
|
812
|
+
createdAt: true,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
if (table.sectionType === 'destinationDb') {
|
|
816
|
+
options = {
|
|
817
|
+
createdAt: false,
|
|
818
|
+
updatedAt: false,
|
|
819
|
+
createdBy: false,
|
|
820
|
+
updatedBy: false,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
await createTable(table, options);
|
|
824
|
+
/*} else {
|
|
825
|
+
console.log('Aborting...')
|
|
826
|
+
return null
|
|
827
|
+
}*/
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Finally, check if there are tables to remove
|
|
833
|
+
*/
|
|
834
|
+
if (tablesToRemove.length > 0) {
|
|
835
|
+
console.log(chalk.red(`There are ${tablesToRemove.length} table(s) to remove:`));
|
|
836
|
+
console.log(chalk.red(` - Warning: Removing a table will result in a permanent loss of its data!`));
|
|
837
|
+
/**
|
|
838
|
+
* Loop through the tables to remove
|
|
839
|
+
*/
|
|
840
|
+
for (const table of tablesToRemove) {
|
|
841
|
+
s.stop();
|
|
842
|
+
const opType = await select({
|
|
843
|
+
message: `You are about to remove table '${table.name}'. Proceed?`,
|
|
844
|
+
options: [
|
|
845
|
+
{ value: 'yes', label: 'Yes, drop it' },
|
|
846
|
+
{ value: 'no', label: 'No, keep it' },
|
|
847
|
+
{ value: 'later', label: 'Ask me later' },
|
|
848
|
+
],
|
|
849
|
+
initialValue: 'later',
|
|
850
|
+
});
|
|
851
|
+
s.start();
|
|
852
|
+
switch (opType) {
|
|
853
|
+
case 'yes':
|
|
854
|
+
case 'no':
|
|
855
|
+
if (opType === 'yes') {
|
|
856
|
+
console.log(chalk.gray(` - Dropping table '${table.name}'`));
|
|
857
|
+
await db.execute(sql `DROP TABLE \`${sql.raw(table.name)}\``);
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
console.log(chalk.gray(` - Keeping table '${table.name}'`));
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Remove the table from the `__lz_tables` table
|
|
864
|
+
*/
|
|
865
|
+
await db
|
|
866
|
+
.delete(LZTablesTable)
|
|
867
|
+
.where(eq(LZTablesTable.tableName, table.name))
|
|
868
|
+
.catch((error) => {
|
|
869
|
+
console.error('Error deleting from __lz_tables table:', error);
|
|
870
|
+
});
|
|
871
|
+
break;
|
|
872
|
+
case 'later':
|
|
873
|
+
console.log(chalk.gray(` - You will be asked next time for dropping table '${table.name}'`));
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
export async function updateSections(useDevEnv = false) {
|
|
880
|
+
const s = spinner();
|
|
881
|
+
try {
|
|
882
|
+
// s.start()
|
|
883
|
+
// Set environment file if dev mode
|
|
884
|
+
if (useDevEnv) {
|
|
885
|
+
process.env.NODE_ENV = 'development';
|
|
886
|
+
}
|
|
887
|
+
// Run the main update logic
|
|
888
|
+
await main(s);
|
|
889
|
+
s.stop('Sections updated successfully');
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
s.stop('Failed to update sections');
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
// TODO: Two things to do:
|
|
897
|
+
// 1. Merge the createTable and updateTable functions into one function to also add the constraints while creating tables
|
|
898
|
+
// 2. Add the foreign key constraints logic
|