diff --git a/packages/cli/src/databases/migrations/common/1724951148974-AddApiKeysTable.ts b/packages/cli/src/databases/migrations/common/1724951148974-AddApiKeysTable.ts index 547be1a8fb..a9ea5626bb 100644 --- a/packages/cli/src/databases/migrations/common/1724951148974-AddApiKeysTable.ts +++ b/packages/cli/src/databases/migrations/common/1724951148974-AddApiKeysTable.ts @@ -1,8 +1,8 @@ import type { ApiKey } from '@/databases/entities/api-key'; -import type { MigrationContext } from '@/databases/types'; +import type { MigrationContext, ReversibleMigration } from '@/databases/types'; import { generateNanoId } from '@/databases/utils/generators'; -export class AddApiKeysTable1724951148974 { +export class AddApiKeysTable1724951148974 implements ReversibleMigration { async up({ queryRunner, escape, @@ -55,4 +55,55 @@ export class AddApiKeysTable1724951148974 { // Drop apiKey column on user's table await queryRunner.query(`ALTER TABLE ${userTable} DROP COLUMN ${apiKeyColumn};`); } + + async down({ + queryRunner, + runQuery, + schemaBuilder: { dropTable, addColumns, createIndex, column }, + escape, + isMysql, + }: MigrationContext) { + const userTable = escape.tableName('user'); + const userApiKeysTable = escape.tableName('user_api_keys'); + const apiKeyColumn = escape.columnName('apiKey'); + const userIdColumn = escape.columnName('userId'); + const idColumn = escape.columnName('id'); + const createdAtColumn = escape.columnName('createdAt'); + + await addColumns('user', [column('apiKey').varchar()]); + + await createIndex('user', ['apiKey'], true); + + const queryToGetUsersApiKeys = isMysql + ? ` + SELECT ${userIdColumn}, + ${apiKeyColumn}, + ${createdAtColumn} + FROM ${userApiKeysTable} u + WHERE ${createdAtColumn} = (SELECT Min(${createdAtColumn}) + FROM ${userApiKeysTable} + WHERE ${userIdColumn} = u.${userIdColumn});` + : ` + SELECT DISTINCT ON + (${userIdColumn}) ${userIdColumn}, + ${apiKeyColumn}, ${createdAtColumn} + FROM ${userApiKeysTable} + ORDER BY ${userIdColumn}, ${createdAtColumn} ASC;`; + + const oldestApiKeysPerUser = (await queryRunner.query(queryToGetUsersApiKeys)) as Array< + Partial + >; + + await Promise.all( + oldestApiKeysPerUser.map( + async (user: { userId: string; apiKey: string }) => + await runQuery( + `UPDATE ${userTable} SET ${apiKeyColumn} = :apiKey WHERE ${idColumn} = :userId`, + user, + ), + ), + ); + + await dropTable('user_api_keys'); + } } diff --git a/packages/cli/src/databases/migrations/sqlite/1724951148974-AddApiKeysTable.ts b/packages/cli/src/databases/migrations/sqlite/1724951148974-AddApiKeysTable.ts index 19d27aeb85..71dcecdfb3 100644 --- a/packages/cli/src/databases/migrations/sqlite/1724951148974-AddApiKeysTable.ts +++ b/packages/cli/src/databases/migrations/sqlite/1724951148974-AddApiKeysTable.ts @@ -1,8 +1,10 @@ import type { ApiKey } from '@/databases/entities/api-key'; -import type { MigrationContext } from '@/databases/types'; +import type { MigrationContext, ReversibleMigration } from '@/databases/types'; import { generateNanoId } from '@/databases/utils/generators'; -export class AddApiKeysTable1724951148974 { +export class AddApiKeysTable1724951148974 implements ReversibleMigration { + transaction = false as const; + async up({ queryRunner, tablePrefix, runQuery }: MigrationContext) { const tableName = `${tablePrefix}user_api_keys`; @@ -74,4 +76,52 @@ export class AddApiKeysTable1724951148974 { // Rename the temporary table to users await queryRunner.query('ALTER TABLE users_new RENAME TO user;'); } + + async down({ + queryRunner, + runQuery, + tablePrefix, + schemaBuilder: { dropTable, createIndex }, + escape, + }: MigrationContext) { + const userApiKeysTable = escape.tableName('user_api_keys'); + const apiKeyColumn = escape.columnName('apiKey'); + const userIdColumn = escape.columnName('userId'); + const idColumn = escape.columnName('id'); + const createdAtColumn = escape.columnName('createdAt'); + + const queryToGetUsersApiKeys = ` + SELECT + ${userIdColumn}, + ${apiKeyColumn}, + ${createdAtColumn} + FROM + ${userApiKeysTable} + WHERE + ${createdAtColumn} IN( + SELECT + MIN(${createdAtColumn}) + FROM ${userApiKeysTable} + GROUP BY ${userIdColumn});`; + + const oldestApiKeysPerUser = (await queryRunner.query(queryToGetUsersApiKeys)) as Array< + Partial + >; + + await queryRunner.query(`ALTER TABLE ${tablePrefix}user ADD COLUMN "apiKey" varchar;`); + + await createIndex('user', ['apiKey'], true); + + await Promise.all( + oldestApiKeysPerUser.map( + async (user: { userId: string; apiKey: string }) => + await runQuery( + `UPDATE ${tablePrefix}user SET ${apiKeyColumn} = :apiKey WHERE ${idColumn} = :userId`, + user, + ), + ), + ); + + await dropTable('user_api_keys'); + } }