diff --git a/apps/api/src/pkg/testutil/harness.ts b/apps/api/src/pkg/testutil/harness.ts index 35db2d34aa..be20f6dd4c 100644 --- a/apps/api/src/pkg/testutil/harness.ts +++ b/apps/api/src/pkg/testutil/harness.ts @@ -112,6 +112,9 @@ export abstract class Harness { workspaceId: this.resources.unkeyWorkspace.id, createdAt: new Date(), updatedAt: null, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, })); await this.db.primary.insert(schema.permissions).values(create); @@ -206,6 +209,8 @@ export abstract class Harness { createdAt: new Date(), updatedAt: null, description: null, + createdAtM: Date.now(), + updatedAtM: null, }; return this.db.primary.transaction(async (tx) => { @@ -231,6 +236,8 @@ export abstract class Harness { createdAt: new Date(), updatedAt: null, description: null, + createdAtM: Date.now(), + updatedAtM: null, }; return this.db.primary.transaction(async (tx) => { const found = await tx.query.roles.findFirst({ @@ -266,6 +273,9 @@ export abstract class Harness { planDowngradeRequest: null, enabled: true, deleteProtection: true, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; const userWorkspace: Workspace = { id: newId("test"), @@ -285,6 +295,9 @@ export abstract class Harness { planDowngradeRequest: null, enabled: true, deleteProtection: true, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; const unkeyKeyAuth: KeyAuth = { @@ -326,6 +339,9 @@ export abstract class Harness { createdAt: new Date(), deletedAt: null, deleteProtection: true, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; const userApi: Api = { id: newId("test"), @@ -337,6 +353,9 @@ export abstract class Harness { createdAt: new Date(), deletedAt: null, deleteProtection: true, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; return { diff --git a/apps/api/src/routes/v1_apis_deleteApi.ts b/apps/api/src/routes/v1_apis_deleteApi.ts index 32669ac461..2ec9aabe19 100644 --- a/apps/api/src/routes/v1_apis_deleteApi.ts +++ b/apps/api/src/routes/v1_apis_deleteApi.ts @@ -95,7 +95,10 @@ export const registerV1ApisDeleteApi = (app: App) => const rootKeyId = auth.key.id; await db.primary.transaction(async (tx) => { - await tx.update(schema.apis).set({ deletedAt: new Date() }).where(eq(schema.apis.id, apiId)); + await tx + .update(schema.apis) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) + .where(eq(schema.apis.id, apiId)); await insertUnkeyAuditLog(c, tx, { workspaceId: authorizedWorkspaceId, @@ -124,7 +127,7 @@ export const registerV1ApisDeleteApi = (app: App) => }); await tx .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(and(eq(schema.keys.keyAuthId, api.keyAuthId!))); await insertUnkeyAuditLog( diff --git a/apps/api/src/routes/v1_apis_deleteKeys.ts b/apps/api/src/routes/v1_apis_deleteKeys.ts index af49f457a0..ad868ade31 100644 --- a/apps/api/src/routes/v1_apis_deleteKeys.ts +++ b/apps/api/src/routes/v1_apis_deleteKeys.ts @@ -117,7 +117,11 @@ export const registerV1ApisDeleteKeys = (app: App) => .select({ count: sql`count(*)` }) .from(schema.keys) .where(where); - await tx.update(schema.keys).set({ deletedAt: new Date() }).where(where).execute(); + await tx + .update(schema.keys) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) + .where(where) + .execute(); deletedKeys = Number.parseInt(keys.at(0)?.count ?? "0"); }); } diff --git a/apps/api/src/routes/v1_keys_deleteKey.ts b/apps/api/src/routes/v1_keys_deleteKey.ts index 8c054f519c..6bc9d26929 100644 --- a/apps/api/src/routes/v1_keys_deleteKey.ts +++ b/apps/api/src/routes/v1_keys_deleteKey.ts @@ -133,7 +133,7 @@ export const registerV1KeysDeleteKey = (app: App) => } else { await tx .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.keys.id, key.id)); } diff --git a/apps/api/src/routes/v1_keys_verifyKey.test.ts b/apps/api/src/routes/v1_keys_verifyKey.test.ts index 9e94a2d124..79928c20a7 100644 --- a/apps/api/src/routes/v1_keys_verifyKey.test.ts +++ b/apps/api/src/routes/v1_keys_verifyKey.test.ts @@ -827,7 +827,7 @@ describe("key is soft deleted", () => { const key = await h.createKey(); await h.db.primary .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.keys.id, key.keyId)); const res = await h.post({ @@ -853,7 +853,7 @@ describe("key exists but keyspace is soft deleted", () => { await h.db.primary .update(schema.keyAuth) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.keyAuth.id, h.resources.userKeyAuth.id)); const res = await h.post({ @@ -879,7 +879,7 @@ describe("key exists but api is soft deleted", () => { await h.db.primary .update(schema.apis) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.apis.id, h.resources.userApi.id)); const res = await h.post({ diff --git a/apps/api/src/routes/v1_migrations_createKey.ts b/apps/api/src/routes/v1_migrations_createKey.ts index 078858a337..c18eae2ca7 100644 --- a/apps/api/src/routes/v1_migrations_createKey.ts +++ b/apps/api/src/routes/v1_migrations_createKey.ts @@ -492,6 +492,8 @@ export const registerV1MigrationsCreateKeys = (app: App) => createdAt: new Date(), roleId, updatedAt: null, + createdAtM: Date.now(), + updatedAtM: null, workspaceId: authorizedWorkspaceId, }); } @@ -503,6 +505,8 @@ export const registerV1MigrationsCreateKeys = (app: App) => permissionId, tempId: 0, updatedAt: null, + createdAtM: Date.now(), + updatedAtM: null, workspaceId: authorizedWorkspaceId, }); } @@ -526,6 +530,8 @@ export const registerV1MigrationsCreateKeys = (app: App) => keyId: key.keyId, encrypted: encryptionResponse.encrypted, encryptionKeyId: encryptionResponse.keyId, + createdAt: Date.now(), + updatedAt: null, }); } }), diff --git a/apps/api/src/routes/v1_ratelimits_deleteOverride.ts b/apps/api/src/routes/v1_ratelimits_deleteOverride.ts index 47e47c9e54..18a7cc1965 100644 --- a/apps/api/src/routes/v1_ratelimits_deleteOverride.ts +++ b/apps/api/src/routes/v1_ratelimits_deleteOverride.ts @@ -107,7 +107,7 @@ export const registerV1RatelimitDeleteOverride = (app: App) => } await tx .update(schema.ratelimitOverrides) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.ratelimitOverrides.id, override.id)); await insertUnkeyAuditLog(c, tx, { diff --git a/apps/api/src/routes/v1_ratelimits_limit.ts b/apps/api/src/routes/v1_ratelimits_limit.ts index d612d5c01b..7013e36d89 100644 --- a/apps/api/src/routes/v1_ratelimits_limit.ts +++ b/apps/api/src/routes/v1_ratelimits_limit.ts @@ -191,6 +191,9 @@ export const registerV1RatelimitLimit = (app: App) => name: req.namespace, deletedAt: null, updatedAt: null, + createdAtM: Date.now(), + deletedAtM: null, + updatedAtM: null, workspaceId: rootKey.authorizedWorkspaceId, }; try { diff --git a/apps/dashboard/app/(app)/layout.tsx b/apps/dashboard/app/(app)/layout.tsx index 649f74ac72..0196285b08 100644 --- a/apps/dashboard/app/(app)/layout.tsx +++ b/apps/dashboard/app/(app)/layout.tsx @@ -20,9 +20,6 @@ export default async function Layout({ children }: LayoutProps) { apis: { where: (table, { isNull }) => isNull(table.deletedAt), }, - llmGateways: { - columns: { id: true }, - }, }, }); if (!workspace) { diff --git a/apps/dashboard/app/(app)/mobile-sidebar.tsx b/apps/dashboard/app/(app)/mobile-sidebar.tsx index 73a52d2c5c..e1f7f33435 100644 --- a/apps/dashboard/app/(app)/mobile-sidebar.tsx +++ b/apps/dashboard/app/(app)/mobile-sidebar.tsx @@ -19,7 +19,6 @@ type Props = { id: string; name: string; }[]; - llmGateways: { id: string }[]; }; }; diff --git a/apps/dashboard/app/api/v1/vercel/integration/route.ts b/apps/dashboard/app/api/v1/vercel/integration/route.ts index d1b1c5fca2..d4d0c6cc9d 100644 --- a/apps/dashboard/app/api/v1/vercel/integration/route.ts +++ b/apps/dashboard/app/api/v1/vercel/integration/route.ts @@ -34,18 +34,18 @@ export async function POST(request: Request) { case "project.removed": { await db .update(schema.vercelBindings) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.vercelBindings.projectId, p.data.payload.project.id)); break; } case "integration-configuration.removed": { await db .update(schema.vercelBindings) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.vercelBindings.integrationId, p.data.payload.configuration.id)); await db .update(schema.vercelIntegrations) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.vercelIntegrations.id, p.data.payload.configuration.id)); break; } diff --git a/apps/dashboard/app/integrations/vercel/callback/page.tsx b/apps/dashboard/app/integrations/vercel/callback/page.tsx index 875f8fb8ba..52d61c4bd1 100644 --- a/apps/dashboard/app/integrations/vercel/callback/page.tsx +++ b/apps/dashboard/app/integrations/vercel/callback/page.tsx @@ -54,6 +54,9 @@ export default async function Page(props: Props) { accessToken: val.accessToken, createdAt: new Date(), deletedAt: null, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; await db.insert(schema.vercelIntegrations).values(integration).execute(); } diff --git a/apps/dashboard/lib/trpc/routers/api/delete.ts b/apps/dashboard/lib/trpc/routers/api/delete.ts index 23641be8b2..acf3c08124 100644 --- a/apps/dashboard/lib/trpc/routers/api/delete.ts +++ b/apps/dashboard/lib/trpc/routers/api/delete.ts @@ -45,7 +45,7 @@ export const deleteApi = t.procedure await db.transaction(async (tx) => { await tx .update(schema.apis) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.apis.id, input.apiId)); await insertAuditLogs(tx, ctx.workspace.auditLogBucket.id, { workspaceId: api.workspaceId, @@ -75,7 +75,7 @@ export const deleteApi = t.procedure if (keyIds.length > 0) { await tx .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.keys.keyAuthId, api.keyAuthId!)); await insertAuditLogs( tx, diff --git a/apps/dashboard/lib/trpc/routers/key/delete.ts b/apps/dashboard/lib/trpc/routers/key/delete.ts index b089610c34..b0efeadde1 100644 --- a/apps/dashboard/lib/trpc/routers/key/delete.ts +++ b/apps/dashboard/lib/trpc/routers/key/delete.ts @@ -45,7 +45,7 @@ export const deleteKeys = t.procedure .transaction(async (tx) => { await tx .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where( and( eq(schema.keys.workspaceId, workspace.id), diff --git a/apps/dashboard/lib/trpc/routers/key/deleteRootKey.ts b/apps/dashboard/lib/trpc/routers/key/deleteRootKey.ts index 48803e24b5..620cca5957 100644 --- a/apps/dashboard/lib/trpc/routers/key/deleteRootKey.ts +++ b/apps/dashboard/lib/trpc/routers/key/deleteRootKey.ts @@ -28,7 +28,7 @@ export const deleteRootKeys = t.procedure .transaction(async (tx) => { await tx .update(schema.keys) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where( inArray( schema.keys.id, diff --git a/apps/dashboard/lib/trpc/routers/ratelimit/deleteNamespace.ts b/apps/dashboard/lib/trpc/routers/ratelimit/deleteNamespace.ts index 850e1f313b..2404670292 100644 --- a/apps/dashboard/lib/trpc/routers/ratelimit/deleteNamespace.ts +++ b/apps/dashboard/lib/trpc/routers/ratelimit/deleteNamespace.ts @@ -39,7 +39,7 @@ export const deleteNamespace = t.procedure await db.transaction(async (tx) => { await tx .update(schema.ratelimitNamespaces) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.ratelimitNamespaces.id, input.namespaceId)); await insertAuditLogs(tx, ctx.workspace.auditLogBucket.id, { @@ -70,7 +70,7 @@ export const deleteNamespace = t.procedure if (overrides.length > 0) { await tx .update(schema.ratelimitOverrides) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.ratelimitOverrides.namespaceId, namespace.id)) .catch((_err) => { throw new TRPCError({ diff --git a/apps/dashboard/lib/trpc/routers/ratelimit/deleteOverride.ts b/apps/dashboard/lib/trpc/routers/ratelimit/deleteOverride.ts index 298bf5261b..b151d0661c 100644 --- a/apps/dashboard/lib/trpc/routers/ratelimit/deleteOverride.ts +++ b/apps/dashboard/lib/trpc/routers/ratelimit/deleteOverride.ts @@ -47,7 +47,7 @@ export const deleteOverride = t.procedure await db.transaction(async (tx) => { await tx .update(schema.ratelimitOverrides) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.ratelimitOverrides.id, override.id)) .catch((_err) => { throw new TRPCError({ diff --git a/apps/dashboard/lib/trpc/routers/rbac.ts b/apps/dashboard/lib/trpc/routers/rbac.ts index 8a8142f522..cc1f019517 100644 --- a/apps/dashboard/lib/trpc/routers/rbac.ts +++ b/apps/dashboard/lib/trpc/routers/rbac.ts @@ -779,6 +779,9 @@ export async function upsertPermissions( description: null, createdAt: new Date(), updatedAt: null, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; newPermissions.push(permission); diff --git a/apps/dashboard/lib/trpc/routers/rbac/upsertPermission.ts b/apps/dashboard/lib/trpc/routers/rbac/upsertPermission.ts index 7fb1435dde..05bc3d0c3c 100644 --- a/apps/dashboard/lib/trpc/routers/rbac/upsertPermission.ts +++ b/apps/dashboard/lib/trpc/routers/rbac/upsertPermission.ts @@ -29,6 +29,8 @@ export async function upsertPermission(ctx: Context, name: string): Promise { await tx .update(schema.vercelBindings) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.vercelBindings.id, binding.id)); await insertAuditLogs(tx, ctx.workspace.auditLogBucket.id, { workspaceId: binding.vercelIntegrations.workspace.id, @@ -582,7 +582,7 @@ export const vercelRouter = t.router({ await db.transaction(async (tx) => { await tx .update(schema.vercelBindings) - .set({ deletedAt: new Date() }) + .set({ deletedAt: new Date(), deletedAtM: Date.now() }) .where(eq(schema.vercelBindings.id, binding.id)); await insertAuditLogs(tx, ctx.workspace.auditLogBucket.id, { workspaceId: integration.workspace.id, diff --git a/apps/dashboard/lib/trpc/routers/workspace/create.ts b/apps/dashboard/lib/trpc/routers/workspace/create.ts index b38578064f..24d366dd43 100644 --- a/apps/dashboard/lib/trpc/routers/workspace/create.ts +++ b/apps/dashboard/lib/trpc/routers/workspace/create.ts @@ -48,6 +48,9 @@ export const createWorkspace = t.procedure planDowngradeRequest: null, enabled: true, deleteProtection: true, + createdAtM: Date.now(), + updatedAtM: null, + deletedAtM: null, }; await db .transaction(async (tx) => { diff --git a/internal/db/src/schema/apis.ts b/internal/db/src/schema/apis.ts index 923222ff87..12155e189c 100644 --- a/internal/db/src/schema/apis.ts +++ b/internal/db/src/schema/apis.ts @@ -2,6 +2,7 @@ import { relations } from "drizzle-orm"; import { datetime, index, mysqlEnum, mysqlTable, varchar } from "drizzle-orm/mysql-core"; import { keyAuth } from "./keyAuth"; import { deleteProtection } from "./util/delete_protection"; +import { lifecycleDatesMigration } from "./util/lifecycle_dates"; import { workspaces } from "./workspaces"; export const apis = mysqlTable( @@ -21,6 +22,7 @@ export const apis = mysqlTable( createdAt: datetime("created_at", { mode: "date", fsp: 3 }), deletedAt: datetime("deleted_at", { mode: "date", fsp: 3 }), + ...lifecycleDatesMigration, ...deleteProtection, }, (table) => ({ diff --git a/internal/db/src/schema/keys.ts b/internal/db/src/schema/keys.ts index f425e39f8e..5a40a1a431 100644 --- a/internal/db/src/schema/keys.ts +++ b/internal/db/src/schema/keys.ts @@ -15,7 +15,7 @@ import { identities, ratelimits } from "./identity"; import { keyAuth } from "./keyAuth"; import { keysPermissions, keysRoles } from "./rbac"; import { embeddedEncrypted } from "./util/embedded_encrypted"; -import { lifecycleDatesMigration } from "./util/lifecycle_dates"; +import { lifecycleDatesMigration, lifecycleDatesV2 } from "./util/lifecycle_dates"; import { workspaces } from "./workspaces"; export const keys = mysqlTable( @@ -149,7 +149,7 @@ export const encryptedKeys = mysqlTable( keyId: varchar("key_id", { length: 256 }) .notNull() .references(() => keys.id, { onDelete: "cascade" }), - + ...lifecycleDatesV2, ...embeddedEncrypted, }, (table) => ({ diff --git a/internal/db/src/schema/ratelimit.ts b/internal/db/src/schema/ratelimit.ts index 28a001f680..3acb272127 100644 --- a/internal/db/src/schema/ratelimit.ts +++ b/internal/db/src/schema/ratelimit.ts @@ -8,6 +8,7 @@ import { unique, varchar, } from "drizzle-orm/mysql-core"; +import { lifecycleDatesMigration } from "./util/lifecycle_dates"; import { workspaces } from "./workspaces"; export const ratelimitNamespaces = mysqlTable( @@ -23,6 +24,7 @@ export const ratelimitNamespaces = mysqlTable( .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), deletedAt: datetime("deleted_at", { mode: "date" }), + ...lifecycleDatesMigration, }, (table) => { return { @@ -76,6 +78,7 @@ export const ratelimitOverrides = mysqlTable( .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), deletedAt: datetime("deleted_at", { mode: "date", fsp: 3 }), + ...lifecycleDatesMigration, }, (table) => { return { diff --git a/internal/db/src/schema/rbac.ts b/internal/db/src/schema/rbac.ts index 935b2fbbc1..8882d82006 100644 --- a/internal/db/src/schema/rbac.ts +++ b/internal/db/src/schema/rbac.ts @@ -25,6 +25,11 @@ export const permissions = mysqlTable( .notNull() .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), + createdAtM: bigint("created_at_m", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAtM: bigint("updated_at_m", { mode: "number" }).$onUpdateFn(() => Date.now()), }, (table) => { return { @@ -64,6 +69,11 @@ export const keysPermissions = mysqlTable( .notNull() .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), + createdAtM: bigint("created_at_m", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAtM: bigint("updated_at_m", { mode: "number" }).$onUpdateFn(() => Date.now()), }, (table) => { return { @@ -102,6 +112,11 @@ export const roles = mysqlTable( .notNull() .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), + createdAtM: bigint("created_at_m", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAtM: bigint("updated_at_m", { mode: "number" }).$onUpdateFn(() => Date.now()), }, (table) => { return { @@ -143,6 +158,11 @@ export const rolesPermissions = mysqlTable( createdAt: datetime("created_at", { mode: "date", fsp: 3 }) .notNull() .$defaultFn(() => new Date()), + createdAtM: bigint("created_at_m", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAtM: bigint("updated_at_m", { mode: "number" }).$onUpdateFn(() => Date.now()), }, (table) => { return { @@ -187,6 +207,11 @@ export const keysRoles = mysqlTable( .notNull() .$defaultFn(() => new Date()), updatedAt: datetime("updated_at", { mode: "date", fsp: 3 }), + createdAtM: bigint("created_at_m", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAtM: bigint("updated_at_m", { mode: "number" }).$onUpdateFn(() => Date.now()), }, (table) => { return { diff --git a/internal/db/src/schema/util/lifecycle_dates.ts b/internal/db/src/schema/util/lifecycle_dates.ts index ba5df66903..2aedd2e9f1 100644 --- a/internal/db/src/schema/util/lifecycle_dates.ts +++ b/internal/db/src/schema/util/lifecycle_dates.ts @@ -7,6 +7,14 @@ export const lifecycleDates = { updatedAt: bigint("updated_at", { mode: "number" }).$onUpdateFn(() => Date.now()), }; +export const lifecycleDatesV2 = { + createdAt: bigint("created_at", { mode: "number" }) + .notNull() + .default(0) + .$defaultFn(() => Date.now()), + updatedAt: bigint("updated_at", { mode: "number" }).$onUpdateFn(() => Date.now()), +}; + /** * Over time I want to move all of our timestamps to bigints, * diff --git a/internal/db/src/schema/vercel_integration.ts b/internal/db/src/schema/vercel_integration.ts index 448e36fb45..4f955dd3f1 100644 --- a/internal/db/src/schema/vercel_integration.ts +++ b/internal/db/src/schema/vercel_integration.ts @@ -1,6 +1,7 @@ import { relations } from "drizzle-orm"; // db.ts import { datetime, mysqlEnum, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"; +import { lifecycleDatesMigration } from "./util/lifecycle_dates"; import { workspaces } from "./workspaces"; export const vercelIntegrations = mysqlTable("vercel_integrations", { @@ -12,6 +13,7 @@ export const vercelIntegrations = mysqlTable("vercel_integrations", { accessToken: varchar("access_token", { length: 256 }).notNull(), createdAt: datetime("created_at", { fsp: 3 }), deletedAt: datetime("deleted_at", { fsp: 3 }), + ...lifecycleDatesMigration, }); export const vercelBindings = mysqlTable( @@ -34,6 +36,7 @@ export const vercelBindings = mysqlTable( deletedAt: datetime("deleted_at", { fsp: 3 }), // userId lastEditedBy: varchar("last_edited_by", { length: 256 }).notNull(), + ...lifecycleDatesMigration, }, (table) => ({ uniqueProjectEnvironmentResourceIndex: uniqueIndex("project_environment_resource_type_idx").on( diff --git a/internal/db/src/schema/workspaces.ts b/internal/db/src/schema/workspaces.ts index 58ee4816b7..eca991249e 100644 --- a/internal/db/src/schema/workspaces.ts +++ b/internal/db/src/schema/workspaces.ts @@ -11,18 +11,15 @@ import { } from "drizzle-orm/mysql-core"; import { apis } from "./apis"; import { auditLogBucket } from "./audit_logs"; -import { gateways } from "./gateway"; import { identities } from "./identity"; import { keyAuth } from "./keyAuth"; import { keys } from "./keys"; -import { llmGateways } from "./llm-gateway"; -import { verificationMonitors } from "./monitor_verifications"; import { ratelimitNamespaces } from "./ratelimit"; import { permissions, roles } from "./rbac"; import { secrets } from "./secrets"; import { deleteProtection } from "./util/delete_protection"; +import { lifecycleDatesMigration } from "./util/lifecycle_dates"; import { vercelBindings, vercelIntegrations } from "./vercel_integration"; -import { webhooks } from "./webhooks"; export const workspaces = mysqlTable( "workspaces", @@ -110,6 +107,7 @@ export const workspaces = mysqlTable( */ enabled: boolean("enabled").notNull().default(true), ...deleteProtection, + ...lifecycleDatesMigration, }, (table) => ({ tenantIdIdx: uniqueIndex("tenant_id_idx").on(table.tenantId), @@ -131,10 +129,6 @@ export const workspacesRelations = relations(workspaces, ({ many }) => ({ permissions: many(permissions), ratelimitNamespaces: many(ratelimitNamespaces), secrets: many(secrets), - gateways: many(gateways), - llmGateways: many(llmGateways), - webhooks: many(webhooks), - verificationMonitors: many(verificationMonitors), keySpaces: many(keyAuth), identities: many(identities), auditLogBuckets: many(auditLogBucket, { diff --git a/packages/api/src/openapi.d.ts b/packages/api/src/openapi.d.ts index 6637352979..5280f2ceb9 100644 --- a/packages/api/src/openapi.d.ts +++ b/packages/api/src/openapi.d.ts @@ -262,6 +262,28 @@ export interface components { requestId: string; }; }; + ErrPreconditionFailed: { + error: { + /** + * @description A machine readable error code. + * @example PRECONDITION_FAILED + * @enum {string} + */ + code: "PRECONDITION_FAILED"; + /** + * @description A link to our documentation with more details about this error code + * @example https://unkey.dev/docs/api-reference/errors/code/PRECONDITION_FAILED + */ + docs: string; + /** @description A human readable explanation of what went wrong */ + message: string; + /** + * @description Please always include the requestId in your error report + * @example req_1234 + */ + requestId: string; + }; + }; ErrTooManyRequests: { error: { /** @@ -768,6 +790,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -826,6 +854,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -943,6 +977,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -967,7 +1007,7 @@ export interface operations { */ keyId: string; /** - * @description By default Unkey soft deletes keys, so they may be recovered later. If you want to permanently delete it, set permanent=true. This might be necessary if you run into NOT_UNIQUE errors during key migration. + * @description By default Unkey soft deletes keys, so they may be recovered later. If you want to permanently delete it, set permanent=true. This might be necessary if you run into CONFLICT errors during key migration. * @default false */ permanent?: boolean; @@ -1011,6 +1051,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1238,6 +1284,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1295,6 +1347,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1519,6 +1577,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1598,6 +1662,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1682,6 +1752,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1765,6 +1841,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1842,6 +1924,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -1940,6 +2028,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2038,6 +2132,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2115,6 +2215,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2213,6 +2319,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2286,6 +2398,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2355,6 +2473,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2427,6 +2551,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2490,6 +2620,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The api is protected from deletions */ 429: { content: { @@ -2561,6 +2697,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2709,6 +2851,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2803,6 +2951,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2878,6 +3032,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -2951,6 +3111,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3016,6 +3182,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3236,6 +3408,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3446,6 +3624,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3520,6 +3704,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3583,6 +3773,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3656,6 +3852,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3724,6 +3926,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3798,6 +4006,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3861,6 +4075,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -3934,6 +4154,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4002,6 +4228,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4026,7 +4258,7 @@ export interface operations { * This usually comes from your authentication provider and could be a userId, organisationId or even an email. * It does not matter what you use, as long as it uniquely identifies something in your application. * - * `externalId`s are unique across your workspace and therefore a `PRECONDITION_FAILED` error is returned when you try to create duplicates. + * `externalId`s are unique across your workspace and therefore a `CONFLICT` error is returned when you try to create duplicates. * * @example user_123 */ @@ -4107,6 +4339,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4192,6 +4430,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4284,6 +4528,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4429,6 +4679,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4492,6 +4748,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4602,6 +4864,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4743,6 +5011,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4909,6 +5183,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { @@ -4975,6 +5255,12 @@ export interface operations { "application/json": components["schemas"]["ErrConflict"]; }; }; + /** @description The requested operation cannot be completed because certain conditions were not met. This typically occurs when a required resource state or version check fails. */ + 412: { + content: { + "application/json": components["schemas"]["ErrPreconditionFailed"]; + }; + }; /** @description The user has sent too many requests in a given amount of time ("rate limiting") */ 429: { content: { diff --git a/tools/migrate/main.ts b/tools/migrate/main.ts index af2e6fba5a..922f106693 100644 --- a/tools/migrate/main.ts +++ b/tools/migrate/main.ts @@ -1,48 +1,65 @@ -import { eq, mysqlDrizzle, schema } from "@unkey/db"; -import { newId } from "@unkey/id"; +import { and, asc, eq, gt, isNull, mysqlDrizzle, schema, sql } from "@unkey/db"; import mysql from "mysql2/promise"; async function main() { - const conn = await mysql.createConnection( - `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:3306/unkey?ssl={}`, - ); + const conn = await mysql.createConnection(process.env.DRIZZLE_DATABASE_URL!); await conn.ping(); const db = mysqlDrizzle(conn, { schema, mode: "default" }); - let cursor = ""; - do { - const keys = await db.query.keys.findMany({ - where: (table, { isNotNull, gt, and, isNull }) => - and(gt(table.id, cursor), isNotNull(table.ownerId), isNull(table.identityId)), - limit: 1000, - orderBy: (table, { asc }) => asc(table.id), - }); - - cursor = keys.at(-1)?.id ?? ""; - console.info({ cursor, keys: keys.length }); - - for (const key of keys) { - let identity: { id: string } | undefined = await db.query.identities.findFirst({ - where: (table, { eq }) => eq(table.externalId, key.ownerId!), - }); - if (!identity) { - const id = newId("identity"); - await db.insert(schema.identities).values({ - id, - workspaceId: key.workspaceId, - externalId: key.ownerId!, - }); - identity = { - id, - }; - } - await db - .update(schema.keys) - .set({ identityId: identity.id }) - .where(eq(schema.keys.id, key.id)); - } - } while (cursor); + for (const table of [ + schema.apis, + schema.keyAuth, + schema.keys, + schema.permissions, + schema.ratelimitNamespaces, + schema.ratelimitOverrides, + schema.roles, + schema.vercelBindings, + schema.vercelIntegrations, + schema.workspaces, + ]) { + const count = await db + .select({ count: sql`count(*)` }) + .from(table) + .where(isNull(table.createdAtM)) + .then((res) => res.at(0)?.count ?? 0); + console.log({ count }); + + let processed = 0; + let cursor = ""; + do { + const rows = await db + .select() + .from(table) + .where( + and( + isNull(table.createdAtM), + + gt(table.id, cursor), + ), + ) + .orderBy(asc(table.id)) + .limit(100); + + cursor = rows.at(-1)?.id ?? ""; + console.info({ cursor, rows: rows.length, processed, count }); + + await Promise.all( + rows.map(async (row) => { + await db + .update(table) + .set({ + createdAtM: new Date(row.createdAt ?? 0).getTime() ?? 0, + updatedAtM: "updatedAt" in row ? row.updatedAt?.getTime() ?? null : null, + deletedAtM: row.deletedAt?.getTime() ?? null, + }) + .where(eq(table.id, row.id)); + }), + ); + processed += rows.length; + } while (cursor); + } } -main(); +main().then(() => process.exit(0));