@stacksjs/database 0.70.258 → 0.70.260
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/auth-tables.js +18 -137
- package/dist/column.js +1 -26
- package/dist/custom/audits.js +20 -54
- package/dist/custom/errors.js +16 -46
- package/dist/custom/index.js +1 -3
- package/dist/custom/jobs.js +13 -137
- package/dist/database.js +1 -181
- package/dist/datetime-columns.js +2 -79
- package/dist/ddl-constraints.js +7 -111
- package/dist/defaults.js +1 -48
- package/dist/dialect.js +1 -79
- package/dist/driver-config.js +1 -172
- package/dist/drivers/defaults/index.js +1 -1
- package/dist/drivers/defaults/traits.js +1 -29
- package/dist/drivers/dynamodb.js +1 -607
- package/dist/drivers/helpers.js +1 -206
- package/dist/drivers/index.js +1 -9
- package/dist/drivers/mysql.js +58 -299
- package/dist/drivers/postgres.js +78 -368
- package/dist/drivers/sqlite.js +61 -379
- package/dist/ensure-database.js +1 -145
- package/dist/fk-audit.js +3 -187
- package/dist/index.js +1 -64
- package/dist/managed-columns.js +1 -59
- package/dist/migration-dialect.js +4 -107
- package/dist/migration-ledger.js +1 -382
- package/dist/migration-lock.js +1 -143
- package/dist/migrations.js +15 -1118
- package/dist/model-sources.js +1 -76
- package/dist/notification-tables.js +4 -49
- package/dist/query-logger.js +2 -241
- package/dist/query-parser.js +1 -93
- package/dist/rbac-tables.js +6 -61
- package/dist/relation-columns.js +1 -66
- package/dist/replicas.js +1 -74
- package/dist/safe-migrations.js +2 -52
- package/dist/schema.js +1 -10
- package/dist/seeder.js +1 -457
- package/dist/sql-helpers.js +1 -50
- package/dist/table.js +1 -26
- package/dist/tools/setup.js +1 -6
- package/dist/trait-tables.js +8 -153
- package/dist/transaction-context.js +1 -62
- package/dist/types.js +1 -98
- package/dist/unique-audit.js +3 -155
- package/dist/utils.js +1 -285
- package/dist/uuid-columns.js +1 -68
- package/dist/validators.js +1 -122
- package/dist/vschema.js +2 -121
- package/package.json +20 -13
package/dist/drivers/postgres.js
CHANGED
|
@@ -1,378 +1,88 @@
|
|
|
1
|
-
import { log } from
|
|
2
|
-
|
|
3
|
-
return `\x1B[3m${str}\x1B[23m`;
|
|
4
|
-
}
|
|
5
|
-
import { db } from "../utils";
|
|
6
|
-
import { ok } from "@stacksjs/error-handling";
|
|
7
|
-
import { fetchOtherModelRelations, getModelName, getPivotTables, getTableName } from "@stacksjs/orm";
|
|
8
|
-
import { path } from "@stacksjs/path";
|
|
9
|
-
import { fs, globSync } from "@stacksjs/storage";
|
|
10
|
-
import { plural, snakeCase } from "@stacksjs/strings";
|
|
11
|
-
import {
|
|
12
|
-
arrangeColumns,
|
|
13
|
-
checkPivotMigration,
|
|
14
|
-
deleteFrameworkModels,
|
|
15
|
-
deleteMigrationFiles,
|
|
16
|
-
findDifferingKeys,
|
|
17
|
-
getLastMigrationFields,
|
|
18
|
-
getLikeableForeignKey,
|
|
19
|
-
getUpvoteTableName,
|
|
20
|
-
hasTableBeenMigrated,
|
|
21
|
-
isArrayEqual,
|
|
22
|
-
mapFieldTypeToColumnType,
|
|
23
|
-
pluckChanges
|
|
24
|
-
} from "./helpers";
|
|
25
|
-
import { dropCommonTables, dropMigrationTables } from "./defaults/traits";
|
|
26
|
-
export async function dropPostgresTables() {
|
|
27
|
-
const tables = await fetchPostgresTables(), userModelFiles = globSync([path.userModelsPath("*.ts"), path.storagePath("framework/defaults/app/Models/**/*.ts")], { absolute: !0 });
|
|
28
|
-
await dropMigrationTables();
|
|
29
|
-
for (const table of tables)
|
|
30
|
-
await db.unsafe(`DROP TABLE IF EXISTS "${table}" CASCADE`).execute();
|
|
31
|
-
await dropCommonTables();
|
|
32
|
-
for (const userModel of userModelFiles) {
|
|
33
|
-
const userModelPath = (await import(userModel)).default, pivotTables = await getPivotTables(userModelPath, userModel);
|
|
34
|
-
for (const pivotTable of pivotTables)
|
|
35
|
-
await db.unsafe(`DROP TABLE IF EXISTS "${pivotTable.table}" CASCADE`).execute();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export async function resetPostgresDatabase() {
|
|
39
|
-
await dropPostgresTables();
|
|
40
|
-
await deleteFrameworkModels();
|
|
41
|
-
await deleteMigrationFiles();
|
|
42
|
-
await db.unsafe('CREATE TABLE IF NOT EXISTS "migrations" (id SERIAL PRIMARY KEY)').execute();
|
|
43
|
-
await db.unsafe('CREATE TABLE IF NOT EXISTS "migration_locks" (id SERIAL PRIMARY KEY)').execute();
|
|
44
|
-
await db.unsafe('CREATE TABLE IF NOT EXISTS "activities" (id SERIAL PRIMARY KEY)').execute();
|
|
45
|
-
return ok("All tables dropped successfully!");
|
|
46
|
-
}
|
|
47
|
-
export async function generatePostgresMigration(modelPath) {
|
|
48
|
-
if ((await fs.promises.readdir(path.userMigrationsPath(""))).length === 0) {
|
|
49
|
-
log.debug("No migrations found in the database folder, clearing the model snapshot cache...");
|
|
50
|
-
const cacheDir = path.frameworkPath("cache/models");
|
|
51
|
-
if (fs.existsSync(cacheDir)) {
|
|
52
|
-
const modelFiles = await fs.promises.readdir(cacheDir);
|
|
53
|
-
if (modelFiles.length) {
|
|
54
|
-
for (const file of modelFiles)
|
|
55
|
-
if (file.endsWith(".ts"))
|
|
56
|
-
await fs.promises.unlink(path.frameworkPath(`cache/models/${file}`));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const model = (await import(modelPath)).default, fileName = path.basename(modelPath), tableName = getTableName(model, modelPath), fieldsString = JSON.stringify(model.attributes, null, 2), copiedModelPath = path.frameworkPath(`cache/models/${fileName}`);
|
|
61
|
-
let haveFieldsChanged = !1;
|
|
62
|
-
if (fs.existsSync(copiedModelPath)) {
|
|
63
|
-
log.info(`Fields have already been generated for ${tableName}`);
|
|
64
|
-
const previousFields = await getLastMigrationFields(fileName);
|
|
65
|
-
if (JSON.stringify(previousFields, null, 2) === fieldsString) {
|
|
66
|
-
log.debug(`Fields have not changed for ${tableName}`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
haveFieldsChanged = !0;
|
|
70
|
-
log.debug(`Fields have changed for ${tableName}`);
|
|
71
|
-
} else
|
|
72
|
-
log.debug(`Fields have not been generated for ${tableName}`);
|
|
73
|
-
await Bun.$`mkdir -p ${path.frameworkPath("cache/models")} && cp ${modelPath} ${copiedModelPath}`;
|
|
74
|
-
const hasBeenMigrated = await hasTableBeenMigrated(tableName);
|
|
75
|
-
log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`);
|
|
76
|
-
if ((model.traits?.billable || !1) && tableName === "users")
|
|
77
|
-
await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));
|
|
78
|
-
if (haveFieldsChanged)
|
|
79
|
-
await createAlterTableMigration(modelPath);
|
|
80
|
-
else
|
|
81
|
-
await createTableMigration(modelPath);
|
|
82
|
-
}
|
|
83
|
-
async function createTableMigration(modelPath) {
|
|
84
|
-
log.debug("createTableMigration modelPath:", modelPath);
|
|
85
|
-
const model = (await import(modelPath)).default, tableName = getTableName(model, modelPath), twoFactorEnabled = model.traits?.useAuth && typeof model.traits.useAuth !== "boolean" ? model.traits.useAuth.useTwoFactor : !1;
|
|
86
|
-
await createPivotTableMigration(model, modelPath);
|
|
87
|
-
const useTimestamps = model.traits?.useTimestamps ?? model.traits?.timestampable ?? !0, useSocials = model?.traits?.useSocials && Array.isArray(model.traits.useSocials) && model.traits.useSocials.length > 0, useLikeable = Array.isArray(model?.traits?.likeable) ? model.traits.likeable.length > 0 : Boolean(model?.traits?.likeable), useSoftDeletes = model.traits?.useSoftDeletes ?? model.traits?.softDeletable ?? !1, usePasskey = (typeof model.traits?.useAuth === "object" && model.traits.useAuth.usePasskey) ?? !1, useBillable = model.traits?.billable || !1, useUuid = model.traits?.useUuid || !1;
|
|
88
|
-
if (useBillable && tableName === "users")
|
|
89
|
-
await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));
|
|
90
|
-
let migrationContent = `import type { Database } from '@stacksjs/database'
|
|
91
|
-
`;
|
|
92
|
-
migrationContent += `import { sql } from '@stacksjs/database'
|
|
1
|
+
import{log}from"@stacksjs/logging";function italic(str){return`\x1B[3m${str}\x1B[23m`}import{db}from"../utils";import{ok}from"@stacksjs/error-handling";import{fetchOtherModelRelations,getModelName,getPivotTables,getTableName}from"@stacksjs/orm";import{path}from"@stacksjs/path";import{fs,globSync}from"@stacksjs/storage";import{plural,snakeCase}from"@stacksjs/strings";import{arrangeColumns,checkPivotMigration,deleteFrameworkModels,deleteMigrationFiles,findDifferingKeys,getLastMigrationFields,getLikeableForeignKey,getUpvoteTableName,hasTableBeenMigrated,isArrayEqual,mapFieldTypeToColumnType,pluckChanges}from"./helpers";import{dropCommonTables,dropMigrationTables}from"./defaults/traits";export async function dropPostgresTables(){const tables=await fetchPostgresTables(),userModelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0});await dropMigrationTables();for(const table of tables)await db.unsafe(`DROP TABLE IF EXISTS "${table}" CASCADE`).execute();await dropCommonTables();for(const userModel of userModelFiles){const userModelPath=(await import(userModel)).default,pivotTables=await getPivotTables(userModelPath,userModel);for(const pivotTable of pivotTables)await db.unsafe(`DROP TABLE IF EXISTS "${pivotTable.table}" CASCADE`).execute()}}export async function resetPostgresDatabase(){await dropPostgresTables();await deleteFrameworkModels();await deleteMigrationFiles();await db.unsafe('CREATE TABLE IF NOT EXISTS "migrations" (id SERIAL PRIMARY KEY)').execute();await db.unsafe('CREATE TABLE IF NOT EXISTS "migration_locks" (id SERIAL PRIMARY KEY)').execute();await db.unsafe('CREATE TABLE IF NOT EXISTS "activities" (id SERIAL PRIMARY KEY)').execute();return ok("All tables dropped successfully!")}export async function generatePostgresMigration(modelPath){if((await fs.promises.readdir(path.userMigrationsPath(""))).length===0){log.debug("No migrations found in the database folder, clearing the model snapshot cache...");const cacheDir=path.frameworkPath("cache/models");if(fs.existsSync(cacheDir)){const modelFiles=await fs.promises.readdir(cacheDir);if(modelFiles.length){for(const file of modelFiles)if(file.endsWith(".ts"))await fs.promises.unlink(path.frameworkPath(`cache/models/${file}`))}}}const model=(await import(modelPath)).default,fileName=path.basename(modelPath),tableName=getTableName(model,modelPath),fieldsString=JSON.stringify(model.attributes,null,2),copiedModelPath=path.frameworkPath(`cache/models/${fileName}`);let haveFieldsChanged=!1;if(fs.existsSync(copiedModelPath)){log.info(`Fields have already been generated for ${tableName}`);const previousFields=await getLastMigrationFields(fileName);if(JSON.stringify(previousFields,null,2)===fieldsString){log.debug(`Fields have not changed for ${tableName}`);return}haveFieldsChanged=!0;log.debug(`Fields have changed for ${tableName}`)}else log.debug(`Fields have not been generated for ${tableName}`);await Bun.$`mkdir -p ${path.frameworkPath("cache/models")} && cp ${modelPath} ${copiedModelPath}`;const hasBeenMigrated=await hasTableBeenMigrated(tableName);log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`);if((model.traits?.billable||!1)&&tableName==="users")await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));if(haveFieldsChanged)await createAlterTableMigration(modelPath);else await createTableMigration(modelPath)}async function createTableMigration(modelPath){log.debug("createTableMigration modelPath:",modelPath);const model=(await import(modelPath)).default,tableName=getTableName(model,modelPath),twoFactorEnabled=model.traits?.useAuth&&typeof model.traits.useAuth!=="boolean"?model.traits.useAuth.useTwoFactor:!1;await createPivotTableMigration(model,modelPath);const useTimestamps=model.traits?.useTimestamps??model.traits?.timestampable??!0,useSocials=model?.traits?.useSocials&&Array.isArray(model.traits.useSocials)&&model.traits.useSocials.length>0,useLikeable=Array.isArray(model?.traits?.likeable)?model.traits.likeable.length>0:Boolean(model?.traits?.likeable),useSoftDeletes=model.traits?.useSoftDeletes??model.traits?.softDeletable??!1,usePasskey=(typeof model.traits?.useAuth==="object"&&model.traits.useAuth.usePasskey)??!1,useBillable=model.traits?.billable||!1,useUuid=model.traits?.useUuid||!1;if(useBillable&&tableName==="users")await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));let migrationContent=`import type { Database } from '@stacksjs/database'
|
|
2
|
+
`;migrationContent+=`import { sql } from '@stacksjs/database'
|
|
93
3
|
|
|
94
|
-
`;
|
|
95
|
-
|
|
96
|
-
`;
|
|
97
|
-
|
|
98
|
-
`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
`;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
migrationContent += ".unique()";
|
|
115
|
-
if (fieldOptions.default !== void 0)
|
|
116
|
-
if (typeof fieldOptions.default === "string")
|
|
117
|
-
migrationContent += `.defaultTo('${fieldOptions.default.replace(/'/g, "\\'")}')`;
|
|
118
|
-
else if (fieldOptions.default === null)
|
|
119
|
-
migrationContent += ".defaultTo(null)";
|
|
120
|
-
else
|
|
121
|
-
migrationContent += `.defaultTo(${fieldOptions.default})`;
|
|
122
|
-
migrationContent += "";
|
|
123
|
-
}
|
|
124
|
-
migrationContent += `)
|
|
125
|
-
`;
|
|
126
|
-
}
|
|
127
|
-
if (useSocials) {
|
|
128
|
-
const socials = model.traits?.useSocials || [];
|
|
129
|
-
if (socials.includes("google"))
|
|
130
|
-
migrationContent += ` .addColumn('google_id', 'text')
|
|
131
|
-
`;
|
|
132
|
-
if (socials.includes("github"))
|
|
133
|
-
migrationContent += ` .addColumn('github_id', 'text')
|
|
134
|
-
`;
|
|
135
|
-
if (socials.includes("apple"))
|
|
136
|
-
migrationContent += ` .addColumn('apple_id', 'text')
|
|
137
|
-
`;
|
|
138
|
-
if (socials.includes("twitter"))
|
|
139
|
-
migrationContent += ` .addColumn('twitter_id', 'text')
|
|
140
|
-
`;
|
|
141
|
-
if (socials.includes("facebook"))
|
|
142
|
-
migrationContent += ` .addColumn('facebook_id', 'text')
|
|
143
|
-
`;
|
|
144
|
-
}
|
|
145
|
-
if (useLikeable) {
|
|
146
|
-
const likeables = Array.isArray(model.traits?.likeable) ? model.traits.likeable : [];
|
|
147
|
-
for (const likeable of likeables)
|
|
148
|
-
migrationContent += ` .addColumn('${likeable}_count', 'integer', (col) => col.defaultTo(0))
|
|
149
|
-
`;
|
|
150
|
-
}
|
|
151
|
-
if (twoFactorEnabled !== !1 && twoFactorEnabled)
|
|
152
|
-
migrationContent += ` .addColumn('two_factor_secret', 'text')
|
|
153
|
-
`;
|
|
154
|
-
if (useBillable)
|
|
155
|
-
migrationContent += ` .addColumn('stripe_id', 'text')
|
|
156
|
-
`;
|
|
157
|
-
if (usePasskey)
|
|
158
|
-
migrationContent += ` .addColumn('public_passkey', 'text')
|
|
159
|
-
`;
|
|
160
|
-
if (useTimestamps) {
|
|
161
|
-
migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
162
|
-
`;
|
|
163
|
-
migrationContent += ` .addColumn('updated_at', 'timestamptz')
|
|
164
|
-
`;
|
|
165
|
-
}
|
|
166
|
-
if (useSoftDeletes)
|
|
167
|
-
migrationContent += ` .addColumn('deleted_at', 'timestamptz')
|
|
168
|
-
`;
|
|
169
|
-
migrationContent += ` .execute()
|
|
170
|
-
`;
|
|
171
|
-
migrationContent += generatePrimaryKeyIndexSQL(tableName);
|
|
172
|
-
if (useLikeable) {
|
|
173
|
-
const upvoteTable = getUpvoteTableName(model, tableName);
|
|
174
|
-
if (upvoteTable) {
|
|
175
|
-
const foreignKey = getLikeableForeignKey(model, tableName);
|
|
176
|
-
migrationContent += `
|
|
4
|
+
`;migrationContent+=`export async function up(db: Database<any>) {
|
|
5
|
+
`;migrationContent+=` await (db as any).schema
|
|
6
|
+
`;migrationContent+=` .createTable('${tableName}')
|
|
7
|
+
`;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
|
|
8
|
+
`;if(useUuid)migrationContent+=` .addColumn('uuid', 'uuid', (col) => col.defaultTo(sql.raw('gen_random_uuid()')))
|
|
9
|
+
`;for(const[fieldName,options]of arrangeColumns(model.attributes)){const fieldOptions=options,fieldNameFormatted=snakeCase(fieldName),columnType=mapFieldTypeToColumnType(fieldOptions.validation?.rule,"postgres"),isRequired="isRequired"in(fieldOptions.validation?.rule??{})?(fieldOptions.validation?.rule).isRequired:!1;migrationContent+=` .addColumn('${fieldNameFormatted}', ${columnType}`;if(isRequired||fieldOptions.unique||fieldOptions.default!==void 0){migrationContent+=", col => col";if(isRequired)migrationContent+=".notNull()";if(fieldOptions.unique)migrationContent+=".unique()";if(fieldOptions.default!==void 0)if(typeof fieldOptions.default==="string")migrationContent+=`.defaultTo('${fieldOptions.default.replace(/'/g,"\\'")}')`;else if(fieldOptions.default===null)migrationContent+=".defaultTo(null)";else migrationContent+=`.defaultTo(${fieldOptions.default})`;migrationContent+=""}migrationContent+=`)
|
|
10
|
+
`}if(useSocials){const socials=model.traits?.useSocials||[];if(socials.includes("google"))migrationContent+=` .addColumn('google_id', 'text')
|
|
11
|
+
`;if(socials.includes("github"))migrationContent+=` .addColumn('github_id', 'text')
|
|
12
|
+
`;if(socials.includes("apple"))migrationContent+=` .addColumn('apple_id', 'text')
|
|
13
|
+
`;if(socials.includes("twitter"))migrationContent+=` .addColumn('twitter_id', 'text')
|
|
14
|
+
`;if(socials.includes("facebook"))migrationContent+=` .addColumn('facebook_id', 'text')
|
|
15
|
+
`}if(useLikeable){const likeables=Array.isArray(model.traits?.likeable)?model.traits.likeable:[];for(const likeable of likeables)migrationContent+=` .addColumn('${likeable}_count', 'integer', (col) => col.defaultTo(0))
|
|
16
|
+
`}if(twoFactorEnabled!==!1&&twoFactorEnabled)migrationContent+=` .addColumn('two_factor_secret', 'text')
|
|
17
|
+
`;if(useBillable)migrationContent+=` .addColumn('stripe_id', 'text')
|
|
18
|
+
`;if(usePasskey)migrationContent+=` .addColumn('public_passkey', 'text')
|
|
19
|
+
`;if(useTimestamps){migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
20
|
+
`;migrationContent+=` .addColumn('updated_at', 'timestamptz')
|
|
21
|
+
`}if(useSoftDeletes)migrationContent+=` .addColumn('deleted_at', 'timestamptz')
|
|
22
|
+
`;migrationContent+=` .execute()
|
|
23
|
+
`;migrationContent+=generatePrimaryKeyIndexSQL(tableName);if(useLikeable){const upvoteTable=getUpvoteTableName(model,tableName);if(upvoteTable){const foreignKey=getLikeableForeignKey(model,tableName);migrationContent+=`
|
|
177
24
|
// Create upvote table
|
|
178
|
-
`;
|
|
179
|
-
|
|
180
|
-
`;
|
|
181
|
-
|
|
182
|
-
`;
|
|
183
|
-
|
|
184
|
-
`;
|
|
185
|
-
|
|
186
|
-
`;
|
|
187
|
-
migrationContent += ` .addColumn('user_id', 'integer', (col) => col.notNull())
|
|
188
|
-
`;
|
|
189
|
-
migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
190
|
-
`;
|
|
191
|
-
migrationContent += ` .addColumn('updated_at', 'timestamptz')
|
|
192
|
-
`;
|
|
193
|
-
migrationContent += ` .execute()
|
|
25
|
+
`;migrationContent+=` await (db as any).schema
|
|
26
|
+
`;migrationContent+=` .createTable('${upvoteTable}')
|
|
27
|
+
`;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
|
|
28
|
+
`;migrationContent+=` .addColumn('${foreignKey}', 'integer', (col) => col.notNull())
|
|
29
|
+
`;migrationContent+=` .addColumn('user_id', 'integer', (col) => col.notNull())
|
|
30
|
+
`;migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
31
|
+
`;migrationContent+=` .addColumn('updated_at', 'timestamptz')
|
|
32
|
+
`;migrationContent+=` .execute()
|
|
194
33
|
|
|
195
|
-
`;
|
|
196
|
-
|
|
197
|
-
`;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`;
|
|
202
|
-
migrationContent += ` await (db as any).schema.createIndex('${upvoteTable}_id_index').on('${upvoteTable}').column('id').execute()
|
|
203
|
-
`;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
migrationContent += `}
|
|
207
|
-
`;
|
|
208
|
-
const migrationFileName = `${new Date().getTime().toString()}-create-${tableName}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
|
|
209
|
-
await Bun.write(migrationFilePath, migrationContent);
|
|
210
|
-
log.success(`Created migration: ${italic(migrationFileName)}`);
|
|
211
|
-
}
|
|
212
|
-
async function createPivotTableMigration(model, modelPath) {
|
|
213
|
-
const pivotTables = await getPivotTables(model, modelPath), processedPivotTables = new Set;
|
|
214
|
-
if (!pivotTables.length)
|
|
215
|
-
return;
|
|
216
|
-
for (const pivotTable of pivotTables) {
|
|
217
|
-
if (processedPivotTables.has(pivotTable.table))
|
|
218
|
-
continue;
|
|
219
|
-
if (await checkPivotMigration(pivotTable.table)) {
|
|
220
|
-
processedPivotTables.add(pivotTable.table);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
let migrationContent = `import type { Database } from '@stacksjs/database'
|
|
224
|
-
`;
|
|
225
|
-
migrationContent += `import { sql } from '@stacksjs/database'
|
|
34
|
+
`;migrationContent+=` // Add indexes for upvote table
|
|
35
|
+
`;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_${foreignKey}_index').on('${upvoteTable}').column('${foreignKey}').execute()
|
|
36
|
+
`;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_user_${foreignKey}_unique').on('${upvoteTable}').columns(['user_id', '${foreignKey}']).unique().execute()
|
|
37
|
+
`;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_id_index').on('${upvoteTable}').column('id').execute()
|
|
38
|
+
`}}migrationContent+=`}
|
|
39
|
+
`;const migrationFileName=`${new Date().getTime().toString()}-create-${tableName}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);log.success(`Created migration: ${italic(migrationFileName)}`)}async function createPivotTableMigration(model,modelPath){const pivotTables=await getPivotTables(model,modelPath),processedPivotTables=new Set;if(!pivotTables.length)return;for(const pivotTable of pivotTables){if(processedPivotTables.has(pivotTable.table))continue;if(await checkPivotMigration(pivotTable.table)){processedPivotTables.add(pivotTable.table);continue}let migrationContent=`import type { Database } from '@stacksjs/database'
|
|
40
|
+
`;migrationContent+=`import { sql } from '@stacksjs/database'
|
|
226
41
|
|
|
227
|
-
`;
|
|
228
|
-
|
|
229
|
-
`;
|
|
230
|
-
|
|
231
|
-
`;
|
|
232
|
-
|
|
233
|
-
`;
|
|
234
|
-
|
|
235
|
-
`;
|
|
236
|
-
migrationContent += ` .addColumn('${pivotTable.firstForeignKey}', 'integer', (col) => col.notNull())
|
|
237
|
-
`;
|
|
238
|
-
migrationContent += ` .addColumn('${pivotTable.secondForeignKey}', 'integer', (col) => col.notNull())
|
|
239
|
-
`;
|
|
240
|
-
migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
241
|
-
`;
|
|
242
|
-
migrationContent += ` .execute()
|
|
42
|
+
`;migrationContent+=`export async function up(db: Database<any>) {
|
|
43
|
+
`;migrationContent+=` await (db as any).schema
|
|
44
|
+
`;migrationContent+=` .createTable('${pivotTable.table}')
|
|
45
|
+
`;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
|
|
46
|
+
`;migrationContent+=` .addColumn('${pivotTable.firstForeignKey}', 'integer', (col) => col.notNull())
|
|
47
|
+
`;migrationContent+=` .addColumn('${pivotTable.secondForeignKey}', 'integer', (col) => col.notNull())
|
|
48
|
+
`;migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
49
|
+
`;migrationContent+=` .execute()
|
|
243
50
|
|
|
244
|
-
`;
|
|
245
|
-
|
|
246
|
-
`;
|
|
247
|
-
|
|
248
|
-
`;
|
|
249
|
-
migrationContent += ` .addForeignKeyConstraint('${pivotTable.table}_${pivotTable.firstForeignKey}_fkey', ['${pivotTable.firstForeignKey}'], '${plural(pivotTable.firstForeignKey?.replace(/_id$/, "") || "")}', ['id'], (cb) => cb.onDelete('cascade'))
|
|
250
|
-
`;
|
|
251
|
-
migrationContent += ` .execute()
|
|
51
|
+
`;migrationContent+=` await (db as any).schema
|
|
52
|
+
`;migrationContent+=` .alterTable('${pivotTable.table}')
|
|
53
|
+
`;migrationContent+=` .addForeignKeyConstraint('${pivotTable.table}_${pivotTable.firstForeignKey}_fkey', ['${pivotTable.firstForeignKey}'], '${plural(pivotTable.firstForeignKey?.replace(/_id$/,"")||"")}', ['id'], (cb) => cb.onDelete('cascade'))
|
|
54
|
+
`;migrationContent+=` .execute()
|
|
252
55
|
|
|
253
|
-
`;
|
|
254
|
-
|
|
255
|
-
`;
|
|
256
|
-
|
|
257
|
-
`;
|
|
258
|
-
migrationContent += ` .addUniqueConstraint('${pivotTable.table}_unique', ['${pivotTable.firstForeignKey}', '${pivotTable.secondForeignKey}'])
|
|
259
|
-
`;
|
|
260
|
-
migrationContent += ` .execute()
|
|
56
|
+
`;migrationContent+=` await (db as any).schema
|
|
57
|
+
`;migrationContent+=` .alterTable('${pivotTable.table}')
|
|
58
|
+
`;migrationContent+=` .addUniqueConstraint('${pivotTable.table}_unique', ['${pivotTable.firstForeignKey}', '${pivotTable.secondForeignKey}'])
|
|
59
|
+
`;migrationContent+=` .execute()
|
|
261
60
|
|
|
262
|
-
`;
|
|
263
|
-
|
|
264
|
-
`;
|
|
265
|
-
|
|
266
|
-
`;
|
|
267
|
-
migrationContent += ` .on('${pivotTable.table}')
|
|
268
|
-
`;
|
|
269
|
-
migrationContent += ` .column('${pivotTable.firstForeignKey}')
|
|
270
|
-
`;
|
|
271
|
-
migrationContent += ` .execute()
|
|
61
|
+
`;migrationContent+=` await (db as any).schema
|
|
62
|
+
`;migrationContent+=` .createIndex('${pivotTable.table}_${pivotTable.firstForeignKey}_idx')
|
|
63
|
+
`;migrationContent+=` .on('${pivotTable.table}')
|
|
64
|
+
`;migrationContent+=` .column('${pivotTable.firstForeignKey}')
|
|
65
|
+
`;migrationContent+=` .execute()
|
|
272
66
|
|
|
273
|
-
`;
|
|
274
|
-
|
|
275
|
-
`;
|
|
276
|
-
|
|
277
|
-
`;
|
|
278
|
-
|
|
279
|
-
`;
|
|
280
|
-
|
|
281
|
-
`;
|
|
282
|
-
migrationContent += ` .execute()
|
|
283
|
-
`;
|
|
284
|
-
migrationContent += `}
|
|
285
|
-
`;
|
|
286
|
-
const migrationFileName = `${new Date().getTime().toString()}-create-${pivotTable.table}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
|
|
287
|
-
await Bun.write(migrationFilePath, migrationContent);
|
|
288
|
-
processedPivotTables.add(pivotTable.table);
|
|
289
|
-
log.success(`Created migration: ${italic(migrationFileName)}`);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async function createAlterTableMigration(modelPath) {
|
|
293
|
-
const model = (await import(modelPath)).default, modelName = getModelName(model, modelPath), tableName = getTableName(model, modelPath);
|
|
294
|
-
let hasChanged = !1;
|
|
295
|
-
const lastFields = await getLastMigrationFields(modelName) ?? {}, currentFields = model.attributes, changes = pluckChanges(Object.keys(lastFields), Object.keys(currentFields)), fieldsToAdd = changes?.added || [], fieldsToRemove = changes?.removed || [];
|
|
296
|
-
let migrationContent = `import type { Database } from '@stacksjs/database'
|
|
297
|
-
`;
|
|
298
|
-
migrationContent += `import { sql } from '@stacksjs/database'
|
|
67
|
+
`;migrationContent+=` await (db as any).schema
|
|
68
|
+
`;migrationContent+=` .createIndex('${pivotTable.table}_${pivotTable.secondForeignKey}_idx')
|
|
69
|
+
`;migrationContent+=` .on('${pivotTable.table}')
|
|
70
|
+
`;migrationContent+=` .column('${pivotTable.secondForeignKey}')
|
|
71
|
+
`;migrationContent+=` .execute()
|
|
72
|
+
`;migrationContent+=`}
|
|
73
|
+
`;const migrationFileName=`${new Date().getTime().toString()}-create-${pivotTable.table}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);processedPivotTables.add(pivotTable.table);log.success(`Created migration: ${italic(migrationFileName)}`)}}async function createAlterTableMigration(modelPath){const model=(await import(modelPath)).default,modelName=getModelName(model,modelPath),tableName=getTableName(model,modelPath);let hasChanged=!1;const lastFields=await getLastMigrationFields(modelName)??{},currentFields=model.attributes,changes=pluckChanges(Object.keys(lastFields),Object.keys(currentFields)),fieldsToAdd=changes?.added||[],fieldsToRemove=changes?.removed||[];let migrationContent=`import type { Database } from '@stacksjs/database'
|
|
74
|
+
`;migrationContent+=`import { sql } from '@stacksjs/database'
|
|
299
75
|
|
|
300
|
-
`;
|
|
301
|
-
|
|
302
|
-
`;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
`;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (isRequired || options.unique || options.default !== void 0) {
|
|
312
|
-
migrationContent += ", col => col";
|
|
313
|
-
if (isRequired)
|
|
314
|
-
migrationContent += ".notNull()";
|
|
315
|
-
if (options.unique)
|
|
316
|
-
migrationContent += ".unique()";
|
|
317
|
-
if (options.default !== void 0)
|
|
318
|
-
if (typeof options.default === "string")
|
|
319
|
-
migrationContent += `.defaultTo('${options.default}')`;
|
|
320
|
-
else if (options.default === null)
|
|
321
|
-
migrationContent += ".defaultTo(null)";
|
|
322
|
-
else
|
|
323
|
-
migrationContent += `.defaultTo(${options.default})`;
|
|
324
|
-
migrationContent += "";
|
|
325
|
-
}
|
|
326
|
-
migrationContent += `)
|
|
327
|
-
`;
|
|
328
|
-
}
|
|
329
|
-
for (const fieldName of fieldsToRemove)
|
|
330
|
-
migrationContent += ` .dropColumn('${fieldName}')
|
|
331
|
-
`;
|
|
332
|
-
const fieldValidations = findDifferingKeys(lastFields, currentFields);
|
|
333
|
-
for (const fieldValidation of fieldValidations) {
|
|
334
|
-
hasChanged = !0;
|
|
335
|
-
const fieldNameFormatted = snakeCase(fieldValidation.key);
|
|
336
|
-
migrationContent += ` .alterColumn('${fieldNameFormatted}', (col) => col.setDataType('text'))
|
|
337
|
-
`;
|
|
338
|
-
}
|
|
339
|
-
const lastFieldOrder = Object.values(lastFields).map((attr) => attr.order), currentFieldOrder = Object.values(currentFields).map((attr) => attr.order);
|
|
340
|
-
if (!isArrayEqual(lastFieldOrder, currentFieldOrder))
|
|
341
|
-
hasChanged = !0;
|
|
342
|
-
if (hasChanged) {
|
|
343
|
-
migrationContent += ` .execute()
|
|
344
|
-
`;
|
|
345
|
-
migrationContent += `}
|
|
346
|
-
`;
|
|
347
|
-
const migrationFileName = `${new Date().getTime().toString()}-alter-${tableName}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
|
|
348
|
-
await Bun.write(migrationFilePath, migrationContent);
|
|
349
|
-
log.success(`Created alter migration: ${italic(migrationFileName)}`);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
export function generateIndexCreationSQL(tableName, index) {
|
|
353
|
-
if (index.unique || index.where) {
|
|
354
|
-
const unique = index.unique ? "UNIQUE " : "", cols = index.columns.map((col) => snakeCase(col)).join(", "), whereClause = index.where ? ` WHERE ${index.where}` : "";
|
|
355
|
-
return ` await db.unsafe(\`CREATE ${unique}INDEX IF NOT EXISTS "${index.name}" ON "${tableName}" (${cols})${whereClause}\`).execute()
|
|
356
|
-
`;
|
|
357
|
-
}
|
|
358
|
-
const columnsStr = index.columns.map((col) => `'${snakeCase(col)}'`).join(", ");
|
|
359
|
-
return ` await (db as any).schema.createIndex('${index.name}').on('${tableName}').columns([${columnsStr}]).execute()
|
|
360
|
-
`;
|
|
361
|
-
}
|
|
362
|
-
function generatePrimaryKeyIndexSQL(tableName) {
|
|
363
|
-
return ` await (db as any).schema.createIndex('${tableName}_id_index').on('${tableName}').column('id').execute()
|
|
364
|
-
`;
|
|
365
|
-
}
|
|
366
|
-
function generateForeignKeyIndexSQL(tableName, foreignKey) {
|
|
367
|
-
return ` await (db as any).schema.createIndex('${tableName}_${foreignKey}_index').on('${tableName}').column('${foreignKey}').execute()
|
|
76
|
+
`;migrationContent+=`export async function up(db: Database<any>) {
|
|
77
|
+
`;if(fieldsToAdd.length||fieldsToRemove.length){hasChanged=!0;migrationContent+=` await (db as any).schema.alterTable('${tableName}')
|
|
78
|
+
`}for(const fieldName of fieldsToAdd){const options=currentFields[fieldName],columnType=mapFieldTypeToColumnType(options.validation?.rule,"postgres"),formattedFieldName=snakeCase(fieldName),isRequired="isRequired"in(options.validation?.rule??{})?(options.validation?.rule).isRequired:!1;migrationContent+=` .addColumn('${formattedFieldName}', '${columnType}'`;if(isRequired||options.unique||options.default!==void 0){migrationContent+=", col => col";if(isRequired)migrationContent+=".notNull()";if(options.unique)migrationContent+=".unique()";if(options.default!==void 0)if(typeof options.default==="string")migrationContent+=`.defaultTo('${options.default}')`;else if(options.default===null)migrationContent+=".defaultTo(null)";else migrationContent+=`.defaultTo(${options.default})`;migrationContent+=""}migrationContent+=`)
|
|
79
|
+
`}for(const fieldName of fieldsToRemove)migrationContent+=` .dropColumn('${fieldName}')
|
|
80
|
+
`;const fieldValidations=findDifferingKeys(lastFields,currentFields);for(const fieldValidation of fieldValidations){hasChanged=!0;const fieldNameFormatted=snakeCase(fieldValidation.key);migrationContent+=` .alterColumn('${fieldNameFormatted}', (col) => col.setDataType('text'))
|
|
81
|
+
`}const lastFieldOrder=Object.values(lastFields).map((attr)=>attr.order),currentFieldOrder=Object.values(currentFields).map((attr)=>attr.order);if(!isArrayEqual(lastFieldOrder,currentFieldOrder))hasChanged=!0;if(hasChanged){migrationContent+=` .execute()
|
|
82
|
+
`;migrationContent+=`}
|
|
83
|
+
`;const migrationFileName=`${new Date().getTime().toString()}-alter-${tableName}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);log.success(`Created alter migration: ${italic(migrationFileName)}`)}}export function generateIndexCreationSQL(tableName,index){if(index.unique||index.where){const unique=index.unique?"UNIQUE ":"",cols=index.columns.map((col)=>snakeCase(col)).join(", "),whereClause=index.where?` WHERE ${index.where}`:"";return` await db.unsafe(\`CREATE ${unique}INDEX IF NOT EXISTS "${index.name}" ON "${tableName}" (${cols})${whereClause}\`).execute()
|
|
84
|
+
`}const columnsStr=index.columns.map((col)=>`'${snakeCase(col)}'`).join(", ");return` await (db as any).schema.createIndex('${index.name}').on('${tableName}').columns([${columnsStr}]).execute()
|
|
85
|
+
`}function generatePrimaryKeyIndexSQL(tableName){return` await (db as any).schema.createIndex('${tableName}_id_index').on('${tableName}').column('id').execute()
|
|
86
|
+
`}function generateForeignKeyIndexSQL(tableName,foreignKey){return` await (db as any).schema.createIndex('${tableName}_${foreignKey}_index').on('${tableName}').column('${foreignKey}').execute()
|
|
368
87
|
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
export async function fetchPostgresTables() {
|
|
372
|
-
const modelFiles = globSync([path.userModelsPath("*.ts"), path.storagePath("framework/defaults/app/Models/**/*.ts")], { absolute: !0 }), tables = [];
|
|
373
|
-
for (const modelPath of modelFiles) {
|
|
374
|
-
const model = (await import(modelPath)).default, tableName = getTableName(model, modelPath);
|
|
375
|
-
tables.push(tableName);
|
|
376
|
-
}
|
|
377
|
-
return tables;
|
|
378
|
-
}
|
|
88
|
+
`}export async function fetchPostgresTables(){const modelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}),tables=[];for(const modelPath of modelFiles){const model=(await import(modelPath)).default,tableName=getTableName(model,modelPath);tables.push(tableName)}return tables}
|