Skip to content

Commit b6eb86a

Browse files
committed
Add Better Auth account and credential migration crons with verify script.
1 parent 2ecf48c commit b6eb86a

4 files changed

Lines changed: 197 additions & 5 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { qstash } from "@/lib/cron";
2+
import { withCron } from "@/lib/cron/with-cron";
3+
import { prisma } from "@/lib/prisma";
4+
import { APP_DOMAIN_WITH_NGROK } from "@dub/utils";
5+
import { logAndRespond } from "../../utils";
6+
7+
export const dynamic = "force-dynamic";
8+
9+
const BATCH_SIZE = 1000;
10+
const ITERATIONS = 10;
11+
12+
// POST /api/cron/better-auth/migrate-accounts
13+
export const POST = withCron(async () => {
14+
for (let i = 0; i < ITERATIONS; i++) {
15+
const count = await prisma.$executeRaw`
16+
UPDATE Account
17+
SET
18+
accountId = providerAccountId,
19+
providerId = provider
20+
WHERE
21+
accountId IS NULL
22+
AND providerAccountId IS NOT NULL
23+
LIMIT ${BATCH_SIZE}
24+
`;
25+
26+
console.log(`Migrated ${count} accounts.`);
27+
28+
if (count === 0) {
29+
return logAndRespond("Finished migrating accounts.");
30+
}
31+
}
32+
33+
const qstashResponse = await qstash.publishJSON({
34+
method: "POST",
35+
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/better-auth/migrate-accounts`,
36+
delay: "10s",
37+
retries: 0,
38+
flowControl: {
39+
key: "better-auth-migrate-accounts",
40+
parallelism: 1,
41+
},
42+
});
43+
44+
return logAndRespond(
45+
`Scheduled next batch of accounts to migrate ${qstashResponse.messageId}`,
46+
);
47+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { qstash } from "@/lib/cron";
2+
import { withCron } from "@/lib/cron/with-cron";
3+
import { prisma } from "@/lib/prisma";
4+
import { APP_DOMAIN_WITH_NGROK } from "@dub/utils";
5+
import { logAndRespond } from "../../utils";
6+
7+
export const dynamic = "force-dynamic";
8+
9+
const BATCH_SIZE = 1000;
10+
const ITERATIONS = 10;
11+
12+
// POST /api/cron/better-auth/migrate-credentials
13+
export const POST = withCron(async () => {
14+
for (let i = 0; i < ITERATIONS; i++) {
15+
const users = await prisma.user.findMany({
16+
where: {
17+
passwordHash: {
18+
not: null,
19+
},
20+
accounts: {
21+
none: {
22+
providerId: "credential",
23+
},
24+
},
25+
},
26+
select: {
27+
id: true,
28+
passwordHash: true,
29+
},
30+
take: BATCH_SIZE,
31+
});
32+
33+
if (users.length === 0) {
34+
return logAndRespond("Finished migrating credentials.");
35+
}
36+
37+
const { count } = await prisma.account.createMany({
38+
skipDuplicates: true,
39+
data: users.map((user) => ({
40+
userId: user.id,
41+
accountId: user.id,
42+
providerId: "credential",
43+
password: user.passwordHash,
44+
})),
45+
});
46+
47+
console.log(`Migrated ${count} credentials.`);
48+
}
49+
50+
const qstashResponse = await qstash.publishJSON({
51+
method: "POST",
52+
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/better-auth/migrate-credentials`,
53+
delay: "10s",
54+
retries: 0,
55+
flowControl: {
56+
key: "better-auth-migrate-credentials",
57+
parallelism: 1,
58+
},
59+
});
60+
61+
return logAndRespond(
62+
`Scheduled next batch of credentials to migrate ${qstashResponse.messageId}`,
63+
);
64+
});

apps/web/app/(ee)/api/cron/better-auth/migrate-users/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const ITERATIONS = 10;
1111

1212
// POST /api/cron/better-auth/migrate-users
1313
export const POST = withCron(async () => {
14-
let totalUpdated = 0;
15-
1614
for (let i = 0; i < ITERATIONS; i++) {
1715
const { count } = await prisma.user.updateMany({
1816
where: {
@@ -27,7 +25,7 @@ export const POST = withCron(async () => {
2725
limit: BATCH_SIZE,
2826
});
2927

30-
totalUpdated += count;
28+
console.log(`Migrated ${count} users.`);
3129

3230
if (count === 0) {
3331
return logAndRespond("Finished migrating users.");

apps/web/scripts/better-auth/migrate.ts

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22
import "dotenv-flow/config";
33

44
import { qstash } from "@/lib/cron";
5+
import { prisma } from "@/lib/prisma";
56
import { APP_DOMAIN_WITH_NGROK } from "@dub/utils";
67

78
async function main() {
8-
await migrateUsers();
9+
// await migrateUsers();
10+
// await migrateAccounts();
11+
// await migrateCredentials();
12+
await verifyMigration();
913
}
1014

1115
async function migrateUsers() {
1216
const qstashResponse = await qstash.publishJSON({
1317
method: "POST",
1418
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/better-auth/migrate-users`,
15-
delay: "10s",
1619
retries: 0,
1720
flowControl: {
1821
key: "better-auth-migrate-users",
@@ -23,4 +26,84 @@ async function migrateUsers() {
2326
console.log(`migrateUsers executed: ${qstashResponse.messageId}`);
2427
}
2528

29+
async function migrateAccounts() {
30+
const qstashResponse = await qstash.publishJSON({
31+
method: "POST",
32+
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/better-auth/migrate-accounts`,
33+
retries: 0,
34+
flowControl: {
35+
key: "better-auth-migrate-accounts",
36+
parallelism: 1,
37+
},
38+
});
39+
40+
console.log(`migrateAccounts executed: ${qstashResponse.messageId}`);
41+
}
42+
43+
async function migrateCredentials() {
44+
const qstashResponse = await qstash.publishJSON({
45+
method: "POST",
46+
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/better-auth/migrate-credentials`,
47+
retries: 0,
48+
flowControl: {
49+
key: "better-auth-migrate-credentials",
50+
parallelism: 1,
51+
},
52+
});
53+
54+
console.log(`migrateCredentials executed: ${qstashResponse.messageId}`);
55+
}
56+
57+
async function verifyMigration() {
58+
const [users, accounts, credentials] = await Promise.all([
59+
prisma.user.count({
60+
where: {
61+
emailVerified: {
62+
not: null,
63+
},
64+
emailVerifiedBa: false,
65+
},
66+
}),
67+
68+
prisma.account.count({
69+
where: {
70+
accountId: null,
71+
providerAccountId: {
72+
not: null,
73+
},
74+
},
75+
}),
76+
77+
prisma.user.count({
78+
where: {
79+
passwordHash: {
80+
not: null,
81+
},
82+
accounts: {
83+
none: {
84+
providerId: "credential",
85+
},
86+
},
87+
},
88+
}),
89+
]);
90+
91+
const ok = users === 0 && accounts === 0 && credentials === 0;
92+
93+
console.log(
94+
ok
95+
? "Better Auth migration complete."
96+
: "Better Auth migration incomplete.",
97+
{
98+
users,
99+
accounts,
100+
credentials,
101+
},
102+
);
103+
104+
if (!ok) {
105+
process.exit(1);
106+
}
107+
}
108+
26109
main();

0 commit comments

Comments
 (0)