diff --git a/apps/api/src/pkg/key_migration/message.ts b/apps/api/src/pkg/key_migration/message.ts index 91a9e39ccc..032a619a76 100644 --- a/apps/api/src/pkg/key_migration/message.ts +++ b/apps/api/src/pkg/key_migration/message.ts @@ -4,7 +4,6 @@ export type MessageBody = { keyAuthId: string; rootKeyId: string; prefix?: string; - name?: string; hash: string; start?: string; diff --git a/apps/api/src/routes/schema.ts b/apps/api/src/routes/schema.ts index a9d4d4b14b..1c86a004cb 100644 --- a/apps/api/src/routes/schema.ts +++ b/apps/api/src/routes/schema.ts @@ -58,7 +58,7 @@ export const keySchema = z }), refill: z .object({ - interval: z.enum(["daily", "monthly"]).openapi({ + interval: z.enum(["daily", "monthly"]).optional().openapi({ description: "Determines the rate at which verifications will be refilled. When 'daily' is set for 'interval' 'refillDay' will be set to null.", example: "daily", @@ -69,7 +69,7 @@ export const keySchema = z }), refillDay: z.number().min(1).max(31).default(1).nullable().openapi({ description: - "The day verifications will refill each month, when interval is set to 'monthly'. Value is not zero-indexed making 1 the first day of the month. If left blank it will default to the first day of the month. When 'daily' is set for 'interval' 'refillDay' will be set to null.", + "The amount will refill on the day of the month specified on `refillDay`. If `refillDay` beyond the last day in the month, it will refill on the last day of the month. If left empty, it will refill daily.", example: 15, }), lastRefillAt: z.number().int().optional().openapi({ diff --git a/apps/api/src/routes/v1_apis_listKeys.ts b/apps/api/src/routes/v1_apis_listKeys.ts index 3bb90dd0bd..c10ceb8e58 100644 --- a/apps/api/src/routes/v1_apis_listKeys.ts +++ b/apps/api/src/routes/v1_apis_listKeys.ts @@ -318,11 +318,11 @@ export const registerV1ApisListKeys = (app: App) => : undefined, remaining: k.remaining ?? undefined, refill: - k.refillInterval && k.refillAmount && k.lastRefillAt + k.refillAmount && k.lastRefillAt ? { - interval: k.refillInterval, + interval: k.refillInterval ?? undefined, amount: k.refillAmount, - refillDay: k.refillInterval === "monthly" && k.refillDay ? k.refillDay : null, + refillDay: k.refillDay ?? null, lastRefillAt: k.lastRefillAt?.getTime(), } : undefined, diff --git a/apps/api/src/routes/v1_keys_createKey.error.test.ts b/apps/api/src/routes/v1_keys_createKey.error.test.ts index f9a197f956..99e7b27341 100644 --- a/apps/api/src/routes/v1_keys_createKey.error.test.ts +++ b/apps/api/src/routes/v1_keys_createKey.error.test.ts @@ -146,7 +146,7 @@ test("reject invalid refill config when daily interval has non-null refillDay", error: { code: "BAD_REQUEST", docs: "https://unkey.dev/docs/api-reference/errors/code/BAD_REQUEST", - message: "when interval is set to 'daily', 'refillDay' must be null.", + message: "When interval is set to 'daily', 'refillDay' must be null.", }, }); }); diff --git a/apps/api/src/routes/v1_keys_createKey.ts b/apps/api/src/routes/v1_keys_createKey.ts index cb0ed1c5f0..2bd06ce7d0 100644 --- a/apps/api/src/routes/v1_keys_createKey.ts +++ b/apps/api/src/routes/v1_keys_createKey.ts @@ -110,7 +110,7 @@ When validating a key, we will return this back to you, so you can clearly ident }), refill: z .object({ - interval: z.enum(["daily", "monthly"]).openapi({ + interval: z.enum(["daily", "monthly"]).optional().openapi({ description: "Unkey will automatically refill verifications at the set interval.", }), amount: z.number().int().min(1).positive().openapi({ @@ -313,18 +313,27 @@ export const registerV1KeysCreateKey = (app: App) => message: "remaining must be greater than 0.", }); } - if ((req.remaining === null || req.remaining === undefined) && req.refill?.interval) { - throw new UnkeyApiError({ - code: "BAD_REQUEST", - message: "remaining must be set if you are using refill.", - }); - } - if (req.refill?.refillDay && req.refill.interval === "daily") { - throw new UnkeyApiError({ - code: "BAD_REQUEST", - message: "when interval is set to 'daily', 'refillDay' must be null.", - }); + if (req.refill) { + if (req.remaining === null || req.remaining === undefined) { + throw new UnkeyApiError({ + code: "BAD_REQUEST", + message: "remaining must be set if you are using refill.", + }); + } + if (!req.refill.amount) { + throw new UnkeyApiError({ + code: "BAD_REQUEST", + message: "refill.amount must be set if you are using refill.", + }); + } + if (req.refill.interval === "daily" && req.refill.refillDay) { + throw new UnkeyApiError({ + code: "BAD_REQUEST", + message: "When interval is set to 'daily', 'refillDay' must be null.", + }); + } } + /** * Set up an api for production */ @@ -373,10 +382,10 @@ export const registerV1KeysCreateKey = (app: App) => ratelimitLimit: req.ratelimit?.limit ?? req.ratelimit?.refillRate, ratelimitDuration: req.ratelimit?.duration ?? req.ratelimit?.refillInterval, remaining: req.remaining, - refillInterval: req.refill?.interval, + refillInterval: req.refill?.interval ?? null, refillDay: req.refill?.interval === "daily" ? null : req?.refill?.refillDay ?? 1, refillAmount: req.refill?.amount, - lastRefillAt: req.refill?.interval ? new Date() : null, + lastRefillAt: null, deletedAt: null, enabled: req.enabled, environment: req.environment ?? null, diff --git a/apps/api/src/routes/v1_keys_getKey.ts b/apps/api/src/routes/v1_keys_getKey.ts index 524b5b215c..77c98cc88e 100644 --- a/apps/api/src/routes/v1_keys_getKey.ts +++ b/apps/api/src/routes/v1_keys_getKey.ts @@ -150,20 +150,18 @@ export const registerV1KeysGetKey = (app: App) => updatedAt: key.updatedAtM ?? undefined, expires: key.expires?.getTime() ?? undefined, remaining: key.remaining ?? undefined, - refill: - key.refillInterval && key.refillAmount - ? { - interval: key.refillInterval, - amount: key.refillAmount, - refillDay: key.refillInterval === "monthly" ? key.refillDay : null, - lastRefillAt: key.lastRefillAt?.getTime(), - } - : undefined, + refill: key.refillAmount + ? { + interval: key.refillInterval ?? undefined, + amount: key.refillAmount, + refillDay: key.refillDay ?? null, + lastRefillAt: key.lastRefillAt?.getTime(), + } + : undefined, ratelimit: key.ratelimitAsync !== null && key.ratelimitLimit !== null && key.ratelimitDuration !== null ? { async: key.ratelimitAsync, - type: key.ratelimitAsync ? "fast" : ("consistent" as any), limit: key.ratelimitLimit, duration: key.ratelimitDuration, diff --git a/apps/api/src/routes/v1_migrations_createKey.ts b/apps/api/src/routes/v1_migrations_createKey.ts index a9fd772504..f5e691bad1 100644 --- a/apps/api/src/routes/v1_migrations_createKey.ts +++ b/apps/api/src/routes/v1_migrations_createKey.ts @@ -123,25 +123,31 @@ When validating a key, we will return this back to you, so you can clearly ident }), refill: z .object({ - interval: z.enum(["daily", "monthly"]).openapi({ + interval: z.enum(["monthly", "daily"]).optional().openapi({ description: - "Unkey will automatically refill verifications at the set interval.", + "The interval at which we will refill the remaining verifications.", }), amount: z.number().int().min(1).positive().openapi({ description: "The number of verifications to refill for each occurrence is determined individually for each key.", }), - refillDay: z.number().min(1).max(31).optional().openapi({ - description: - "The day verifications will refill each month, when interval is set to 'monthly'", - }), + refillDay: z + .number() + .min(1) + .max(31) + .optional() + .openapi({ + description: `The day of the month, when we will refill the remaining verifications. To refill on the 15th of each month, set 'refillDay': 15. + If the day does not exist, for example you specified the 30th and it's february, we will refill them on the last day of the month instead.`, + }), }) .optional() .openapi({ description: "Unkey enables you to refill verifications for each key at regular intervals.", example: { - interval: "daily", + interval: "monthly", + refillDay: 15, amount: 100, }, }), @@ -403,6 +409,12 @@ export const registerV1MigrationsCreateKeys = (app: App) => const hash = key.plaintext ? await sha256(key.plaintext) : key.hash!.value; + if (key.refill?.interval === "monthly" && key.refill?.refillDay === undefined) { + key.refill.refillDay = 1; + } + if (key.refill?.interval === "daily" && key.refill?.refillDay !== undefined) { + key.refill.refillDay = undefined; + } keys.push({ id: key.keyId, keyAuthId: api.keyAuthId!, diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx index 57b235ce45..f6906b6967 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx @@ -19,13 +19,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; @@ -42,27 +35,14 @@ const formSchema = z.object({ remaining: z.coerce.number().positive({ message: "Please enter a positive number" }).optional(), refill: z .object({ - interval: z.enum(["none", "daily", "monthly"]), - amount: z.coerce - .number() - .int() - .min(1, { - message: "Please enter the number of uses per interval", - }) - .positive() - .optional(), - refillDay: z.coerce - .number({ - errorMap: (issue, { defaultError }) => ({ - message: - issue.code === "invalid_type" - ? "Refill day must be an integer between 1 and 31" - : defaultError, - }), - }) - .int() - .min(1) - .max(31) + amount: z + .literal("") + .transform(() => undefined) + .or(z.coerce.number().int().positive()), + refillDay: z + .literal("") + .transform(() => undefined) + .or(z.coerce.number().int().max(31).positive()) .optional(), }) .optional(), @@ -72,9 +52,8 @@ type Props = { id: string; workspaceId: string; remaining: number | null; - refillInterval: "daily" | "monthly" | null; refillAmount: number | null; - refillDay: number | null; + refillDay: number | null | undefined; }; }; @@ -90,10 +69,8 @@ export const UpdateKeyRemaining: React.FC = ({ apiKey }) => { limitEnabled: apiKey.remaining ? true : false, remaining: apiKey.remaining ? apiKey.remaining : undefined, refill: { - interval: apiKey.refillInterval === null ? "none" : apiKey.refillInterval, amount: apiKey.refillAmount ? apiKey.refillAmount : undefined, - refillDay: - apiKey.refillInterval === "monthly" && apiKey.refillDay ? apiKey.refillDay : undefined, + refillDay: apiKey.refillDay ?? undefined, }, }, }); @@ -101,7 +78,7 @@ export const UpdateKeyRemaining: React.FC = ({ apiKey }) => { // set them to undefined so the form resets properly. form.resetField("remaining", undefined); form.resetField("refill.amount", undefined); - form.resetField("refill.interval", { defaultValue: "none" }); + form.resetField("refill.refillDay", undefined); form.resetField("refill", undefined); }; const updateRemaining = trpc.key.update.remaining.useMutation({ @@ -116,13 +93,13 @@ export const UpdateKeyRemaining: React.FC = ({ apiKey }) => { }); async function onSubmit(values: z.infer) { - if (values.refill?.interval !== "none" && !values.refill?.amount) { + if (!values.refill?.amount && values.refill?.refillDay) { form.setError("refill.amount", { - message: "Please enter the number of uses per interval", + message: "Please enter the number of uses per interval or remove the refill day", }); return; } - if (values.refill.interval !== "none" && values.remaining === undefined) { + if (values.remaining === undefined) { form.setError("remaining", { message: "Please enter a value" }); return; } @@ -130,9 +107,6 @@ export const UpdateKeyRemaining: React.FC = ({ apiKey }) => { delete values.refill; delete values.remaining; } - if (values.refill?.interval === "none") { - delete values.refill; - } await updateRemaining.mutateAsync(values); } @@ -176,37 +150,11 @@ export const UpdateKeyRemaining: React.FC = ({ apiKey }) => { )} /> - - ( - - Refill Rate - - - - )} - /> = ({ apiKey }) => { className="w-full" type="number" {...field} - value={form.watch("refill.interval") === "none" ? undefined : field.value} + value={form.getValues("limitEnabled") ? field.value : undefined} /> Enter the number of uses to refill per interval. - + )} /> ( - Day of the month to refill uses + Day of the month to refill uses. - Enter the day to refill monthly. - + + Enter the day to refill monthly or leave blank for daily refill + + )} /> diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx index ebcecf9150..4ad9495363 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx @@ -17,13 +17,6 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import { Button } from "@unkey/ui"; import { Separator } from "@/components/ui/separator"; @@ -98,12 +91,8 @@ export const CreateKey = ({ apiId, keyAuthId, defaultBytes, defaultPrefix }: Pro if (!values.ratelimitEnabled) { delete values.ratelimit; } - const refill = values.limit?.refill; - if (refill?.interval === "daily") { - refill?.refillDay === undefined; - } - if (refill?.interval === "monthly" && !refill.refillDay) { - refill.refillDay = 1; + if (!values.limit?.refill?.amount) { + delete values.limit?.refill; } await key.mutateAsync({ @@ -113,7 +102,10 @@ export const CreateKey = ({ apiId, keyAuthId, defaultBytes, defaultPrefix }: Pro expires: values.expires?.getTime() ?? undefined, ownerId: values.ownerId ?? undefined, remaining: values.limit?.remaining ?? undefined, - refill: refill, + refill: + values.limit?.refill?.amount !== undefined + ? { amount: values.limit.refill.amount, refillDay: values.limit.refill.refillDay } + : undefined, enabled: true, }); @@ -143,7 +135,6 @@ export const CreateKey = ({ apiId, keyAuthId, defaultBytes, defaultPrefix }: Pro const resetLimited = () => { form.resetField("limit.refill.amount", undefined); - form.resetField("limit.refill.interval", undefined); form.resetField("limit.refill", undefined); form.resetField("limit.remaining", undefined); form.resetField("limit", undefined); @@ -493,31 +484,6 @@ export const CreateKey = ({ apiId, keyAuthId, defaultBytes, defaultPrefix }: Pro )} /> - ( - - Refill Rate - - - Interval key will be refilled. - - - )} - /> ( @@ -595,7 +558,6 @@ export const CreateKey = ({ apiId, keyAuthId, defaultBytes, defaultPrefix }: Pro ( diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts index 69d47f70ca..d3465a981b 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts @@ -51,31 +51,39 @@ export const formSchema = z.object({ .positive({ message: "Please enter a positive number" }), refill: z .object({ - interval: z.enum(["daily", "monthly"]).default("monthly"), - amount: z.coerce - .number({ - errorMap: (issue, { defaultError }) => ({ - message: - issue.code === "invalid_type" - ? "Refill amount must be greater than 0 and a integer" - : defaultError, - }), - }) - .int() - .min(1) - .positive(), - refillDay: z.coerce - .number({ - errorMap: (issue, { defaultError }) => ({ - message: - issue.code === "invalid_type" - ? "Refill day must be an integer between 1 and 31" - : defaultError, - }), - }) - .int() - .min(1) - .max(31) + amount: z + .literal("") + .transform(() => undefined) + .or( + z.coerce + .number({ + errorMap: (issue, { defaultError }) => ({ + message: + issue.code === "invalid_type" + ? "Refill amount must be greater than 0 and a integer" + : defaultError, + }), + }) + .int() + .positive(), + ), + refillDay: z + .literal("") + .transform(() => undefined) + .or( + z.coerce + .number({ + errorMap: (issue, { defaultError }) => ({ + message: + issue.code === "invalid_type" + ? "Refill day must be an integer between 1 and 31" + : defaultError, + }), + }) + .int() + .max(31) + .positive(), + ) .optional(), }) .optional(), diff --git a/apps/dashboard/components/dashboard/api-key-table/index.tsx b/apps/dashboard/components/dashboard/api-key-table/index.tsx index bf85aab70c..3e07dd8817 100644 --- a/apps/dashboard/components/dashboard/api-key-table/index.tsx +++ b/apps/dashboard/components/dashboard/api-key-table/index.tsx @@ -51,7 +51,7 @@ type Column = { ratelimitRefillRate: number | null; ratelimitRefillInterval: number | null; remaining: number | null; - refillInterval: string | null; + refillDay: string | null; refillAmount: number | null; }; @@ -181,12 +181,12 @@ export const ApiKeyTable: React.FC = ({ data }) => { ), }, { - accessorKey: "refillInterval", - header: "Refill Rate", + accessorKey: "refillDay", + header: "Refill Day", cell: ({ row }) => - row.original.refillInterval && row.original.refillAmount && row.original.remaining ? ( + row.original.refillDay && row.original.refillAmount && row.original.remaining ? (
- {row.original.refillInterval} + {row.original.refillDay ?? "Daily"}
) : ( diff --git a/apps/dashboard/lib/trpc/routers/key/create.ts b/apps/dashboard/lib/trpc/routers/key/create.ts index 93fb4c659f..2af2ec4dca 100644 --- a/apps/dashboard/lib/trpc/routers/key/create.ts +++ b/apps/dashboard/lib/trpc/routers/key/create.ts @@ -18,7 +18,6 @@ export const createKey = t.procedure remaining: z.number().int().positive().optional(), refill: z .object({ - interval: z.enum(["daily", "monthly"]), amount: z.coerce.number().int().min(1), refillDay: z.number().int().min(1).max(31).optional(), }) @@ -102,10 +101,10 @@ export const createKey = t.procedure ratelimitLimit: input.ratelimit?.limit, ratelimitDuration: input.ratelimit?.duration, remaining: input.remaining, - refillInterval: input.refill?.interval ?? null, + refillInterval: input.refill?.refillDay ? "monthly" : "daily", refillDay: input.refill?.refillDay ?? null, refillAmount: input.refill?.amount ?? null, - lastRefillAt: input.refill?.interval ? new Date() : null, + lastRefillAt: input.refill?.amount ? new Date() : null, deletedAt: null, enabled: input.enabled, environment: input.environment, diff --git a/apps/dashboard/lib/trpc/routers/key/updateRemaining.ts b/apps/dashboard/lib/trpc/routers/key/updateRemaining.ts index 835ece954c..e438c109b0 100644 --- a/apps/dashboard/lib/trpc/routers/key/updateRemaining.ts +++ b/apps/dashboard/lib/trpc/routers/key/updateRemaining.ts @@ -12,7 +12,6 @@ export const updateKeyRemaining = t.procedure remaining: z.number().int().positive().optional(), refill: z .object({ - interval: z.enum(["daily", "monthly", "none"]), amount: z.number().int().min(1).optional(), refillDay: z.number().int().min(1).max(31).optional(), }) @@ -24,10 +23,6 @@ export const updateKeyRemaining = t.procedure input.remaining = undefined; input.refill = undefined; } - if (input.refill?.interval === "none") { - input.refill = undefined; - } - await db .transaction(async (tx) => { const key = await tx.query.keys.findFirst({ @@ -37,7 +32,7 @@ export const updateKeyRemaining = t.procedure workspace: true, }, }); - const isMonthlyInterval = input.refill?.interval === "monthly"; + if (!key || key.workspace.tenantId !== ctx.tenant.id) { throw new TRPCError({ message: @@ -48,14 +43,11 @@ export const updateKeyRemaining = t.procedure await tx .update(schema.keys) .set({ + refillInterval: input.refill?.refillDay ? "monthly" : "daily", remaining: input.remaining ?? null, - refillInterval: - input.refill?.interval === "none" || input.refill?.interval === undefined - ? null - : input.refill?.interval, - refillDay: isMonthlyInterval ? input.refill?.refillDay : null, + refillDay: input.refill?.refillDay ?? null, refillAmount: input.refill?.amount ?? null, - lastRefillAt: input.refill?.interval ? new Date() : null, + lastRefillAt: input.refill?.amount ? new Date() : null, }) .where(eq(schema.keys.id, key.id)) .catch((_err) => { @@ -74,7 +66,7 @@ export const updateKeyRemaining = t.procedure event: "key.update", description: input.limitEnabled ? `Changed remaining for ${key.id} to remaining=${input.remaining}, refill=${ - input.refill ? `${input.refill.amount}@${input.refill.interval}` : "none" + input.refill ? `${input.refill.amount}@${input.refill.refillDay}` : "none" }` : `Disabled limit for ${key.id}`, resources: [ diff --git a/apps/dashboard/lib/zod-helper.ts b/apps/dashboard/lib/zod-helper.ts index 7970b612ec..71d327220d 100644 --- a/apps/dashboard/lib/zod-helper.ts +++ b/apps/dashboard/lib/zod-helper.ts @@ -31,7 +31,6 @@ export const formSchema = z.object({ .positive({ message: "Please enter a positive number" }), refill: z .object({ - interval: z.enum(["none", "daily", "monthly"]), amount: z.coerce .number({ errorMap: (issue, { defaultError }) => ({ diff --git a/internal/db/src/schema/key_migrations.ts b/internal/db/src/schema/key_migrations.ts index 0c3470435b..1198fb998e 100644 --- a/internal/db/src/schema/key_migrations.ts +++ b/internal/db/src/schema/key_migrations.ts @@ -20,7 +20,6 @@ export const keyMigrationErrors = mysqlTable("key_migration_errors", { keyAuthId: string; rootKeyId: string; prefix?: string; - name?: string; hash: string; start?: string; diff --git a/tools/migrate/refill-migrate.ts b/tools/migrate/refill-migrate.ts new file mode 100644 index 0000000000..c01541d5ca --- /dev/null +++ b/tools/migrate/refill-migrate.ts @@ -0,0 +1,57 @@ +import { eq, isNull, mysqlDrizzle, schema } 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={}`, + ); + + await conn.ping(); + const db = mysqlDrizzle(conn, { schema, mode: "default" }); + + let cursor = ""; + let keyChanges = 0; + do { + const keys = await db.query.keys.findMany({ + where: (table, { isNotNull, gt, and, or }) => + and( + gt(table.id, cursor), + isNotNull(table.refillAmount), + isNotNull(table.remaining), + or( + and(eq(table.refillInterval, "monthly"), isNull(table.refillDay)), + and(eq(table.refillInterval, "daily"), isNotNull(table.refillDay)), + ), + ), + limit: 1000, + orderBy: (table, { asc }) => asc(table.id), + }); + + cursor = keys.at(-1)?.id ?? ""; + console.info({ cursor, keys: keys.length }); + + for (const key of keys) { + if (key.refillInterval === "monthly" && key.refillDay === null) { + const changed = await db + .update(schema.keys) + .set({ refillDay: 1 }) + .where(eq(schema.keys.id, key.id)); + if (changed) { + keyChanges++; + } + } else if (key.refillInterval === "daily" && key.refillDay !== null) { + const changed = await db + .update(schema.keys) + .set({ refillDay: null }) + .where(eq(schema.keys.id, key.id)); + if (changed) { + keyChanges++; + } + } + } + } while (cursor); + await conn.end(); + console.info("Migration completed. Keys Changed", keyChanges); +} + +main();