@stacksjs/database 0.64.6 → 0.66.0
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/dist/index.js +55 -55
- package/dist/index.js.map +136 -142
- package/package.json +16 -22
- package/src/drivers/index.ts +65 -171
- package/src/drivers/mysql.ts +95 -54
- package/src/drivers/postgres.ts +34 -32
- package/src/drivers/sqlite.ts +165 -62
- package/src/index.ts +2 -1
- package/src/migrations.ts +39 -62
- package/src/seeder.ts +17 -97
- package/src/table.ts +1 -1
- package/src/types.ts +2 -0
- package/src/utils.ts +19 -11
package/src/drivers/mysql.ts
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
|
+
import type { Attribute, Attributes, Model } from '@stacksjs/types'
|
|
1
2
|
import { italic, log } from '@stacksjs/cli'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { ok } from '@stacksjs/error-handling'
|
|
4
|
-
import { getModelName, getTableName } from '@stacksjs/orm'
|
|
4
|
+
import { type Ok, ok } from '@stacksjs/error-handling'
|
|
5
|
+
import { fetchOtherModelRelations, getModelName, getPivotTables, getTableName } from '@stacksjs/orm'
|
|
5
6
|
import { path } from '@stacksjs/path'
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
import { fs, globSync } from '@stacksjs/storage'
|
|
7
9
|
import { snakeCase } from '@stacksjs/strings'
|
|
8
|
-
import type { Attribute, Attributes, Model } from '@stacksjs/types'
|
|
9
10
|
import {
|
|
10
11
|
arrangeColumns,
|
|
11
12
|
checkPivotMigration,
|
|
12
|
-
|
|
13
|
-
findCharacterLength,
|
|
13
|
+
fetchTables,
|
|
14
14
|
findDifferingKeys,
|
|
15
15
|
getLastMigrationFields,
|
|
16
|
-
getPivotTables,
|
|
17
16
|
hasTableBeenMigrated,
|
|
18
17
|
isArrayEqual,
|
|
19
18
|
mapFieldTypeToColumnType,
|
|
20
19
|
pluckChanges,
|
|
21
20
|
} from '.'
|
|
22
21
|
|
|
23
|
-
export async function resetMysqlDatabase() {
|
|
24
|
-
const tables = await
|
|
22
|
+
export async function resetMysqlDatabase(): Promise<Ok<string, never>> {
|
|
23
|
+
const tables = await fetchTables()
|
|
25
24
|
|
|
26
25
|
for (const table of tables) await db.schema.dropTable(table).ifExists().execute()
|
|
27
26
|
|
|
28
27
|
await db.schema.dropTable('migrations').ifExists().execute()
|
|
29
28
|
await db.schema.dropTable('migration_locks').ifExists().execute()
|
|
29
|
+
await db.schema.dropTable('migrations').ifExists().execute()
|
|
30
30
|
|
|
31
31
|
const files = await fs.readdir(path.userMigrationsPath())
|
|
32
32
|
const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
|
|
33
|
-
|
|
34
|
-
const userModelFiles = glob.sync(path.userModelsPath('*.ts'))
|
|
33
|
+
const userModelFiles = globSync([path.userModelsPath('*.ts')], { absolute: true })
|
|
35
34
|
|
|
36
35
|
for (const userModel of userModelFiles) {
|
|
37
36
|
const model = (await import(userModel)).default as Model
|
|
@@ -45,7 +44,8 @@ export async function resetMysqlDatabase() {
|
|
|
45
44
|
if (modelFile.endsWith('.ts')) {
|
|
46
45
|
const modelPath = path.frameworkPath(`database/models/${modelFile}`)
|
|
47
46
|
|
|
48
|
-
if (fs.existsSync(modelPath))
|
|
47
|
+
if (fs.existsSync(modelPath))
|
|
48
|
+
await Bun.$`rm ${modelPath}`
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -55,7 +55,8 @@ export async function resetMysqlDatabase() {
|
|
|
55
55
|
if (file.endsWith('.ts')) {
|
|
56
56
|
const migrationPath = path.userMigrationsPath(`${file}`)
|
|
57
57
|
|
|
58
|
-
if (fs.existsSync(migrationPath))
|
|
58
|
+
if (fs.existsSync(migrationPath))
|
|
59
|
+
await Bun.$`rm ${migrationPath}`
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -63,9 +64,9 @@ export async function resetMysqlDatabase() {
|
|
|
63
64
|
return ok('All tables dropped successfully!')
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
export async function generateMysqlMigration(modelPath: string) {
|
|
67
|
+
export async function generateMysqlMigration(modelPath: string): Promise<void> {
|
|
67
68
|
// check if any files are in the database folder
|
|
68
|
-
const files = await fs.readdir(path.userMigrationsPath())
|
|
69
|
+
// const files = await fs.readdir(path.userMigrationsPath())
|
|
69
70
|
|
|
70
71
|
// if (files.length === 0) {
|
|
71
72
|
// log.debug('No migrations found in the database folder, deleting all framework/database/*.json files...')
|
|
@@ -84,7 +85,7 @@ export async function generateMysqlMigration(modelPath: string) {
|
|
|
84
85
|
|
|
85
86
|
const model = (await import(modelPath)).default as Model
|
|
86
87
|
const fileName = path.basename(modelPath)
|
|
87
|
-
const tableName =
|
|
88
|
+
const tableName = getTableName(model, modelPath)
|
|
88
89
|
|
|
89
90
|
const fieldsString = JSON.stringify(model.attributes, null, 2) // Pretty print the JSON
|
|
90
91
|
const copiedModelPath = path.frameworkPath(`database/models/${fileName}`)
|
|
@@ -105,7 +106,8 @@ export async function generateMysqlMigration(modelPath: string) {
|
|
|
105
106
|
|
|
106
107
|
haveFieldsChanged = true
|
|
107
108
|
log.debug(`Fields have changed for ${tableName}`)
|
|
108
|
-
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
109
111
|
log.debug(`Fields have not been generated for ${tableName}`)
|
|
110
112
|
}
|
|
111
113
|
|
|
@@ -120,17 +122,19 @@ export async function generateMysqlMigration(modelPath: string) {
|
|
|
120
122
|
|
|
121
123
|
log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`)
|
|
122
124
|
|
|
123
|
-
if (haveFieldsChanged)
|
|
125
|
+
if (haveFieldsChanged)
|
|
126
|
+
await createAlterTableMigration(modelPath)
|
|
124
127
|
else await createTableMigration(modelPath)
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
async function createTableMigration(modelPath: string) {
|
|
130
|
+
async function createTableMigration(modelPath: string): Promise<void> {
|
|
128
131
|
log.debug('createTableMigration modelPath:', modelPath)
|
|
129
132
|
|
|
130
133
|
const model = (await import(modelPath)).default as Model
|
|
131
|
-
const tableName =
|
|
134
|
+
const tableName = getTableName(model, modelPath)
|
|
132
135
|
|
|
133
|
-
const twoFactorEnabled
|
|
136
|
+
const twoFactorEnabled
|
|
137
|
+
= model.traits?.useAuth && typeof model.traits.useAuth !== 'boolean' ? model.traits.useAuth.useTwoFactor : false
|
|
134
138
|
|
|
135
139
|
await createPivotTableMigration(model, modelPath)
|
|
136
140
|
const otherModelRelations = await fetchOtherModelRelations(model, modelPath)
|
|
@@ -138,6 +142,11 @@ async function createTableMigration(modelPath: string) {
|
|
|
138
142
|
const useTimestamps = model?.traits?.useTimestamps ?? model?.traits?.timestampable ?? true
|
|
139
143
|
const useSoftDeletes = model?.traits?.useSoftDeletes ?? model?.traits?.softDeletable ?? false
|
|
140
144
|
|
|
145
|
+
const usePasskey = (typeof model.traits?.useAuth === 'object' && model.traits.useAuth.usePasskey) ?? false
|
|
146
|
+
|
|
147
|
+
if (usePasskey)
|
|
148
|
+
await createPasskeyMigration()
|
|
149
|
+
|
|
141
150
|
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
142
151
|
migrationContent += `import { sql } from '@stacksjs/database'\n\n`
|
|
143
152
|
migrationContent += `export async function up(db: Database<any>) {\n`
|
|
@@ -154,15 +163,17 @@ async function createTableMigration(modelPath: string) {
|
|
|
154
163
|
// Check if there are configurations that require the lambda function
|
|
155
164
|
if (fieldOptions.unique || fieldOptions?.required) {
|
|
156
165
|
migrationContent += `, col => col`
|
|
157
|
-
if (fieldOptions.unique)
|
|
158
|
-
|
|
166
|
+
if (fieldOptions.unique)
|
|
167
|
+
migrationContent += `.unique()`
|
|
168
|
+
if (fieldOptions?.required)
|
|
169
|
+
migrationContent += `.notNull()`
|
|
159
170
|
migrationContent += ``
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
migrationContent += `)\n`
|
|
163
174
|
}
|
|
164
175
|
|
|
165
|
-
if (
|
|
176
|
+
if (twoFactorEnabled !== false && twoFactorEnabled) {
|
|
166
177
|
migrationContent += ` .addColumn('two_factor_secret', 'varchar(255)')\n`
|
|
167
178
|
}
|
|
168
179
|
|
|
@@ -174,14 +185,18 @@ async function createTableMigration(modelPath: string) {
|
|
|
174
185
|
}
|
|
175
186
|
}
|
|
176
187
|
|
|
188
|
+
if (usePasskey)
|
|
189
|
+
migrationContent += ` .addColumn('public_passkey', 'text')\n`
|
|
190
|
+
|
|
177
191
|
// Append created_at and updated_at columns if useTimestamps is true
|
|
178
192
|
if (useTimestamps) {
|
|
179
193
|
migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))\n`
|
|
180
|
-
migrationContent += ` .addColumn('updated_at', 'timestamp'
|
|
194
|
+
migrationContent += ` .addColumn('updated_at', 'timestamp')\n`
|
|
181
195
|
}
|
|
182
196
|
|
|
183
197
|
// Append deleted_at column if useSoftDeletes is true
|
|
184
|
-
if (useSoftDeletes)
|
|
198
|
+
if (useSoftDeletes)
|
|
199
|
+
migrationContent += ` .addColumn('deleted_at', 'timestamp')\n`
|
|
185
200
|
|
|
186
201
|
migrationContent += ` .execute()\n`
|
|
187
202
|
migrationContent += `}\n`
|
|
@@ -190,20 +205,59 @@ async function createTableMigration(modelPath: string) {
|
|
|
190
205
|
const migrationFileName = `${timestamp}-create-${tableName}-table.ts`
|
|
191
206
|
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
192
207
|
|
|
208
|
+
// eslint-disable-next-line no-console
|
|
209
|
+
console.log(migrationFilePath)
|
|
210
|
+
|
|
193
211
|
Bun.write(migrationFilePath, migrationContent)
|
|
194
212
|
|
|
195
213
|
log.success(`Created migration: ${italic(migrationFileName)}`)
|
|
196
214
|
}
|
|
197
215
|
|
|
198
|
-
async function
|
|
216
|
+
async function createPasskeyMigration() {
|
|
217
|
+
const hasBeenMigrated = await hasTableBeenMigrated('users')
|
|
218
|
+
|
|
219
|
+
if (hasBeenMigrated)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
223
|
+
migrationContent += `export async function up(db: Database<any>) {\n`
|
|
224
|
+
migrationContent += ` await db.schema\n`
|
|
225
|
+
migrationContent += ` .createTable('passkeys')\n`
|
|
226
|
+
migrationContent += ` .addColumn('id', 'integer', col => col.primaryKey().autoIncrement())\n`
|
|
227
|
+
migrationContent += ` .addColumn('cred_public_key', 'text')\n`
|
|
228
|
+
migrationContent += ` .addColumn('user_id', 'integer')\n`
|
|
229
|
+
migrationContent += ` .addColumn('webauthn_user_id', 'varchar(255)')\n`
|
|
230
|
+
migrationContent += ` .addColumn('counter', 'integer')\n`
|
|
231
|
+
migrationContent += ` .addColumn('device_type', 'varchar(255)')\n`
|
|
232
|
+
migrationContent += ` .addColumn('backup_eligible', 'boolean')\n`
|
|
233
|
+
migrationContent += ` .addColumn('backup_status', 'boolean')\n`
|
|
234
|
+
migrationContent += ` .addColumn('transports', 'varchar(255)')\n`
|
|
235
|
+
migrationContent += ` .addColumn('last_used_at', 'text')\n`
|
|
236
|
+
migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))\n`
|
|
237
|
+
migrationContent += ` .execute()\n`
|
|
238
|
+
migrationContent += ` }\n`
|
|
239
|
+
|
|
240
|
+
const timestamp = new Date().getTime().toString()
|
|
241
|
+
const migrationFileName = `${timestamp}-create-passkeys-table.ts`
|
|
242
|
+
|
|
243
|
+
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
244
|
+
|
|
245
|
+
Bun.write(migrationFilePath, migrationContent)
|
|
246
|
+
|
|
247
|
+
log.success(`Created pivot migration: ${migrationFileName}`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function createPivotTableMigration(model: Model, modelPath: string): Promise<void> {
|
|
199
251
|
const pivotTables = await getPivotTables(model, modelPath)
|
|
200
252
|
|
|
201
|
-
if (!pivotTables.length)
|
|
253
|
+
if (!pivotTables.length)
|
|
254
|
+
return
|
|
202
255
|
|
|
203
256
|
for (const pivotTable of pivotTables) {
|
|
204
257
|
const hasBeenMigrated = await checkPivotMigration(pivotTable.table)
|
|
205
258
|
|
|
206
|
-
if (hasBeenMigrated)
|
|
259
|
+
if (hasBeenMigrated)
|
|
260
|
+
return
|
|
207
261
|
|
|
208
262
|
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
209
263
|
migrationContent += `export async function up(db: Database<any>) {\n`
|
|
@@ -217,6 +271,7 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
|
|
|
217
271
|
|
|
218
272
|
const timestamp = new Date().getTime().toString()
|
|
219
273
|
const migrationFileName = `${timestamp}-create-${pivotTable.table}-table.ts`
|
|
274
|
+
|
|
220
275
|
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
221
276
|
|
|
222
277
|
Bun.write(migrationFilePath, migrationContent)
|
|
@@ -225,12 +280,10 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
|
|
|
225
280
|
}
|
|
226
281
|
}
|
|
227
282
|
|
|
228
|
-
export async function createAlterTableMigration(modelPath: string) {
|
|
229
|
-
console.log('createAlterTableMigration')
|
|
230
|
-
|
|
283
|
+
export async function createAlterTableMigration(modelPath: string): Promise<void> {
|
|
231
284
|
const model = (await import(modelPath)).default as Model
|
|
232
285
|
const modelName = getModelName(model, modelPath)
|
|
233
|
-
const tableName =
|
|
286
|
+
const tableName = getTableName(model, modelPath)
|
|
234
287
|
let hasChanged = false
|
|
235
288
|
|
|
236
289
|
// Assuming you have a function to get the fields from the last migration
|
|
@@ -257,7 +310,6 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
257
310
|
}
|
|
258
311
|
|
|
259
312
|
const fieldValidations = findDifferingKeys(lastFields, currentFields)
|
|
260
|
-
|
|
261
313
|
for (const fieldValidation of fieldValidations) {
|
|
262
314
|
hasChanged = true
|
|
263
315
|
const fieldNameFormatted = snakeCase(fieldValidation.key)
|
|
@@ -278,8 +330,10 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
278
330
|
// Check if there are configurations that require the lambda function
|
|
279
331
|
if (options.unique || options?.required) {
|
|
280
332
|
migrationContent += `, col => col`
|
|
281
|
-
if (options.unique)
|
|
282
|
-
|
|
333
|
+
if (options.unique)
|
|
334
|
+
migrationContent += `.unique()`
|
|
335
|
+
if (options?.required)
|
|
336
|
+
migrationContent += `.notNull()`
|
|
283
337
|
migrationContent += ``
|
|
284
338
|
}
|
|
285
339
|
|
|
@@ -289,10 +343,11 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
289
343
|
// Remove fields that no longer exist
|
|
290
344
|
for (const fieldName of fieldsToRemove) migrationContent += ` .dropColumn('${fieldName}')\n`
|
|
291
345
|
|
|
292
|
-
if (fieldsToAdd.length || fieldsToRemove.length)
|
|
346
|
+
if (fieldsToAdd.length || fieldsToRemove.length)
|
|
347
|
+
migrationContent += ` .execute();\n`
|
|
293
348
|
|
|
294
|
-
const lastFieldOrder = Object.values(lastFields).map(
|
|
295
|
-
const currentFieldOrder = Object.values(currentFields).map(
|
|
349
|
+
const lastFieldOrder = Object.values(lastFields).map(attr => attr.order)
|
|
350
|
+
const currentFieldOrder = Object.values(currentFields).map(attr => attr.order)
|
|
296
351
|
|
|
297
352
|
if (!isArrayEqual(lastFieldOrder, currentFieldOrder)) {
|
|
298
353
|
hasChanged = true
|
|
@@ -317,7 +372,7 @@ function reArrangeColumns(attributes: Attributes | undefined, tableName: string)
|
|
|
317
372
|
let migrationContent = ''
|
|
318
373
|
|
|
319
374
|
let previousField = ''
|
|
320
|
-
for (const [fieldName
|
|
375
|
+
for (const [fieldName] of fields) {
|
|
321
376
|
const fieldNameFormatted = snakeCase(fieldName)
|
|
322
377
|
|
|
323
378
|
if (previousField) {
|
|
@@ -332,17 +387,3 @@ function reArrangeColumns(attributes: Attributes | undefined, tableName: string)
|
|
|
332
387
|
|
|
333
388
|
return migrationContent
|
|
334
389
|
}
|
|
335
|
-
|
|
336
|
-
export async function fetchMysqlTables(): Promise<string[]> {
|
|
337
|
-
const modelFiles = glob.sync(path.userModelsPath('*.ts'))
|
|
338
|
-
const tables: string[] = []
|
|
339
|
-
|
|
340
|
-
for (const modelPath of modelFiles) {
|
|
341
|
-
const model = (await import(modelPath)).default as Model
|
|
342
|
-
const tableName = await getTableName(model, modelPath)
|
|
343
|
-
|
|
344
|
-
tables.push(tableName)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return tables
|
|
348
|
-
}
|
package/src/drivers/postgres.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
+
import type { Attribute, Attributes, Model } from '@stacksjs/types'
|
|
1
2
|
import { italic, log } from '@stacksjs/cli'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
4
|
import { ok } from '@stacksjs/error-handling'
|
|
4
|
-
import { getTableName } from '@stacksjs/orm'
|
|
5
|
+
import { fetchOtherModelRelations, getPivotTables, getTableName } from '@stacksjs/orm'
|
|
5
6
|
import { path } from '@stacksjs/path'
|
|
6
|
-
import { fs,
|
|
7
|
+
import { fs, globSync } from '@stacksjs/storage'
|
|
7
8
|
import { snakeCase } from '@stacksjs/strings'
|
|
8
|
-
import type { Attribute, Attributes, Model } from '@stacksjs/types'
|
|
9
9
|
import {
|
|
10
10
|
arrangeColumns,
|
|
11
11
|
checkPivotMigration,
|
|
12
|
-
fetchOtherModelRelations,
|
|
13
12
|
getLastMigrationFields,
|
|
14
|
-
getPivotTables,
|
|
15
13
|
hasTableBeenMigrated,
|
|
16
14
|
mapFieldTypeToColumnType,
|
|
17
15
|
pluckChanges,
|
|
18
16
|
} from '.'
|
|
19
17
|
|
|
20
18
|
export async function resetPostgresDatabase() {
|
|
21
|
-
const tables = await
|
|
19
|
+
const tables = await fetchPostgresTables()
|
|
22
20
|
|
|
23
21
|
for (const table of tables) await db.schema.dropTable(table).ifExists().execute()
|
|
24
22
|
|
|
@@ -28,7 +26,7 @@ export async function resetPostgresDatabase() {
|
|
|
28
26
|
const files = await fs.readdir(path.userMigrationsPath())
|
|
29
27
|
const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
|
|
30
28
|
|
|
31
|
-
const userModelFiles =
|
|
29
|
+
const userModelFiles = globSync([path.userModelsPath('*.ts')], { absolute: true })
|
|
32
30
|
|
|
33
31
|
for (const userModel of userModelFiles) {
|
|
34
32
|
const userModelPath = (await import(userModel)).default
|
|
@@ -43,7 +41,8 @@ export async function resetPostgresDatabase() {
|
|
|
43
41
|
if (modelFile.endsWith('.ts')) {
|
|
44
42
|
const modelPath = path.frameworkPath(`database/models/${modelFile}`)
|
|
45
43
|
|
|
46
|
-
if (fs.existsSync(modelPath))
|
|
44
|
+
if (fs.existsSync(modelPath))
|
|
45
|
+
await Bun.$`rm ${modelPath}`
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
}
|
|
@@ -53,7 +52,8 @@ export async function resetPostgresDatabase() {
|
|
|
53
52
|
if (file.endsWith('.ts')) {
|
|
54
53
|
const migrationPath = path.userMigrationsPath(`${file}`)
|
|
55
54
|
|
|
56
|
-
if (fs.existsSync(migrationPath))
|
|
55
|
+
if (fs.existsSync(migrationPath))
|
|
56
|
+
await Bun.$`rm ${migrationPath}`
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -61,7 +61,7 @@ export async function resetPostgresDatabase() {
|
|
|
61
61
|
return ok('All tables dropped successfully!')
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export async function generatePostgresMigration(modelPath: string) {
|
|
64
|
+
export async function generatePostgresMigration(modelPath: string): Promise<void> {
|
|
65
65
|
// check if any files are in the database folder
|
|
66
66
|
const files = await fs.readdir(path.userMigrationsPath())
|
|
67
67
|
|
|
@@ -75,7 +75,8 @@ export async function generatePostgresMigration(modelPath: string) {
|
|
|
75
75
|
log.debug('No existing model files in framework path...')
|
|
76
76
|
|
|
77
77
|
for (const file of modelFiles) {
|
|
78
|
-
if (file.endsWith('.ts'))
|
|
78
|
+
if (file.endsWith('.ts'))
|
|
79
|
+
await fs.unlink(path.frameworkPath(`database/models/${file}`))
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
}
|
|
@@ -103,7 +104,8 @@ export async function generatePostgresMigration(modelPath: string) {
|
|
|
103
104
|
|
|
104
105
|
haveFieldsChanged = true
|
|
105
106
|
log.debug(`Fields have changed for ${tableName}`)
|
|
106
|
-
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
107
109
|
log.debug(`Fields have not been generated for ${tableName}`)
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -118,7 +120,8 @@ export async function generatePostgresMigration(modelPath: string) {
|
|
|
118
120
|
|
|
119
121
|
log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`)
|
|
120
122
|
|
|
121
|
-
if (haveFieldsChanged)
|
|
123
|
+
if (haveFieldsChanged)
|
|
124
|
+
await createAlterTableMigration(modelPath)
|
|
122
125
|
else await createTableMigration(modelPath)
|
|
123
126
|
}
|
|
124
127
|
|
|
@@ -144,14 +147,16 @@ async function createTableMigration(modelPath: string) {
|
|
|
144
147
|
for (const [fieldName, options] of arrangeColumns(model.attributes)) {
|
|
145
148
|
const fieldOptions = options as Attribute
|
|
146
149
|
const fieldNameFormatted = snakeCase(fieldName)
|
|
147
|
-
const columnType = mapFieldTypeToColumnType(fieldOptions.
|
|
150
|
+
const columnType = mapFieldTypeToColumnType(fieldOptions.validation?.rule)
|
|
148
151
|
migrationContent += ` .addColumn('${fieldNameFormatted}', '${columnType}'`
|
|
149
152
|
|
|
150
153
|
// Check if there are configurations that require the lambda function
|
|
151
|
-
if (fieldOptions.unique || fieldOptions.
|
|
154
|
+
if (fieldOptions.unique || fieldOptions.validation?.rule?.required) {
|
|
152
155
|
migrationContent += `, col => col`
|
|
153
|
-
if (fieldOptions.unique)
|
|
154
|
-
|
|
156
|
+
if (fieldOptions.unique)
|
|
157
|
+
migrationContent += `.unique()`
|
|
158
|
+
if (fieldOptions.validation?.rule?.required)
|
|
159
|
+
migrationContent += `.notNull()`
|
|
155
160
|
migrationContent += ``
|
|
156
161
|
}
|
|
157
162
|
|
|
@@ -173,7 +178,8 @@ async function createTableMigration(modelPath: string) {
|
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
// Append deleted_at column if useSoftDeletes is true
|
|
176
|
-
if (useSoftDeletes)
|
|
181
|
+
if (useSoftDeletes)
|
|
182
|
+
migrationContent += ` .addColumn('deleted_at', 'timestamp')\n`
|
|
177
183
|
|
|
178
184
|
migrationContent += ` .execute()\n`
|
|
179
185
|
migrationContent += `}\n`
|
|
@@ -191,11 +197,13 @@ async function createTableMigration(modelPath: string) {
|
|
|
191
197
|
async function createPivotTableMigration(model: Model, modelPath: string) {
|
|
192
198
|
const pivotTables = await getPivotTables(model, modelPath)
|
|
193
199
|
|
|
194
|
-
if (!pivotTables.length)
|
|
200
|
+
if (!pivotTables.length)
|
|
201
|
+
return
|
|
195
202
|
for (const pivotTable of pivotTables) {
|
|
196
203
|
const hasBeenMigrated = await checkPivotMigration(pivotTable.table)
|
|
197
204
|
|
|
198
|
-
if (hasBeenMigrated)
|
|
205
|
+
if (hasBeenMigrated)
|
|
206
|
+
return
|
|
199
207
|
|
|
200
208
|
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
201
209
|
migrationContent += `import { sql } from '@stacksjs/database'\n\n`
|
|
@@ -219,23 +227,18 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
|
|
|
219
227
|
}
|
|
220
228
|
}
|
|
221
229
|
|
|
222
|
-
|
|
223
|
-
console.log('createAlterTableMigration')
|
|
224
|
-
|
|
230
|
+
async function createAlterTableMigration(modelPath: string) {
|
|
225
231
|
const model = (await import(modelPath)).default as Model
|
|
226
232
|
const modelName = path.basename(modelPath)
|
|
227
|
-
const tableName =
|
|
233
|
+
const tableName = getTableName(model, modelPath)
|
|
228
234
|
|
|
229
235
|
// Assuming you have a function to get the fields from the last migration
|
|
230
236
|
// For simplicity, this is not implemented here
|
|
231
237
|
const lastMigrationFields = await getLastMigrationFields(modelName)
|
|
232
238
|
const lastFields = lastMigrationFields ?? {}
|
|
233
239
|
const currentFields = model.attributes as Attributes
|
|
234
|
-
|
|
235
240
|
const changes = pluckChanges(Object.keys(lastFields), Object.keys(currentFields))
|
|
236
|
-
|
|
237
241
|
const fieldsToAdd = changes?.added || []
|
|
238
|
-
|
|
239
242
|
const fieldsToRemove = changes?.removed || []
|
|
240
243
|
|
|
241
244
|
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
@@ -246,7 +249,7 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
246
249
|
// Add new fields
|
|
247
250
|
for (const fieldName of fieldsToAdd) {
|
|
248
251
|
const options = currentFields[fieldName] as Attribute
|
|
249
|
-
const columnType = mapFieldTypeToColumnType(options.
|
|
252
|
+
const columnType = mapFieldTypeToColumnType(options.validation?.rule)
|
|
250
253
|
migrationContent += ` .addColumn('${fieldName}', '${columnType}')\n`
|
|
251
254
|
}
|
|
252
255
|
|
|
@@ -266,14 +269,13 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
266
269
|
log.success(`Created migration: ${italic(migrationFileName)}`)
|
|
267
270
|
}
|
|
268
271
|
|
|
269
|
-
export async function
|
|
270
|
-
const modelFiles =
|
|
272
|
+
export async function fetchPostgresTables(): Promise<string[]> {
|
|
273
|
+
const modelFiles = globSync([path.userModelsPath('*.ts')])
|
|
271
274
|
const tables: string[] = []
|
|
272
275
|
|
|
273
276
|
for (const modelPath of modelFiles) {
|
|
274
277
|
const model = (await import(modelPath)).default
|
|
275
|
-
|
|
276
|
-
const tableName = await getTableName(model, modelPath)
|
|
278
|
+
const tableName = getTableName(model, modelPath)
|
|
277
279
|
|
|
278
280
|
tables.push(tableName)
|
|
279
281
|
}
|