139 lines
3.5 KiB
JavaScript
139 lines
3.5 KiB
JavaScript
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { passkey } from "@better-auth/passkey";
|
|
import { getSchema } from "better-auth/db";
|
|
import {
|
|
admin,
|
|
emailOTP,
|
|
haveIBeenPwned,
|
|
lastLoginMethod,
|
|
multiSession,
|
|
openAPI,
|
|
twoFactor,
|
|
} from "better-auth/plugins";
|
|
|
|
const outFile = fileURLToPath(
|
|
new URL("../migrations/0001_better_auth_account_center.sql", import.meta.url),
|
|
);
|
|
|
|
const schema = getSchema({
|
|
emailAndPassword: {
|
|
enabled: true,
|
|
},
|
|
plugins: [
|
|
openAPI(),
|
|
haveIBeenPwned(),
|
|
emailOTP({
|
|
sendVerificationOTP: async () => {},
|
|
}),
|
|
twoFactor(),
|
|
multiSession({ maximumSessions: 10 }),
|
|
lastLoginMethod({ storeInDatabase: true }),
|
|
admin(),
|
|
passkey({
|
|
rpID: "localhost",
|
|
rpName: "cfw-auth",
|
|
origin: "http://localhost:8788",
|
|
}),
|
|
],
|
|
});
|
|
|
|
function quoteIdent(value) {
|
|
return `"${value.replaceAll('"', '""')}"`;
|
|
}
|
|
|
|
function sqliteType(field) {
|
|
if (field.type === "boolean" || field.type === "number") {
|
|
return "integer";
|
|
}
|
|
|
|
if (field.type === "date") {
|
|
return "date";
|
|
}
|
|
|
|
return "text";
|
|
}
|
|
|
|
function columnSql(name, field) {
|
|
const parts = [quoteIdent(name), sqliteType(field)];
|
|
|
|
if (field.required !== false) {
|
|
parts.push("NOT NULL");
|
|
}
|
|
|
|
if (field.unique) {
|
|
parts.push("UNIQUE");
|
|
}
|
|
|
|
if (field.references) {
|
|
const onDelete = field.references.onDelete ?? "cascade";
|
|
parts.push(
|
|
`REFERENCES ${quoteIdent(field.references.model)}(${quoteIdent(field.references.field)}) ON DELETE ${onDelete}`,
|
|
);
|
|
}
|
|
|
|
return parts.join(" ");
|
|
}
|
|
|
|
function sortTables(entries) {
|
|
return entries.sort((left, right) => {
|
|
const leftOrder = Number.isFinite(left[1].order) ? left[1].order : Number.MAX_SAFE_INTEGER;
|
|
const rightOrder = Number.isFinite(right[1].order) ? right[1].order : Number.MAX_SAFE_INTEGER;
|
|
|
|
if (leftOrder !== rightOrder) {
|
|
return leftOrder - rightOrder;
|
|
}
|
|
|
|
return left[0].localeCompare(right[0]);
|
|
});
|
|
}
|
|
|
|
function buildSql() {
|
|
const chunks = [
|
|
"-- Generated from Better Auth schema. Do not edit by hand.",
|
|
"-- Run `pnpm db:generate` after changing auth plugins that affect persistence.",
|
|
"PRAGMA foreign_keys = ON;",
|
|
];
|
|
const indexes = [];
|
|
|
|
for (const [tableName, table] of sortTables(Object.entries(schema))) {
|
|
const columns = [`${quoteIdent("id")} text NOT NULL PRIMARY KEY`];
|
|
|
|
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
columns.push(columnSql(fieldName, field));
|
|
|
|
if (field.index) {
|
|
const suffix = field.unique ? "uidx" : "idx";
|
|
const indexName = `${tableName}_${fieldName}_${suffix}`;
|
|
const indexSql = field.unique ? "CREATE UNIQUE INDEX" : "CREATE INDEX";
|
|
indexes.push(
|
|
`${indexSql} IF NOT EXISTS ${quoteIdent(indexName)} ON ${quoteIdent(tableName)} (${quoteIdent(fieldName)});`,
|
|
);
|
|
}
|
|
}
|
|
|
|
chunks.push(
|
|
`CREATE TABLE IF NOT EXISTS ${quoteIdent(tableName)} (\n ${columns.join(",\n ")}\n);`,
|
|
);
|
|
}
|
|
|
|
chunks.push(...indexes);
|
|
|
|
return `${chunks.join("\n\n")}\n`;
|
|
}
|
|
|
|
const sql = buildSql();
|
|
const checkOnly = process.argv.includes("--check");
|
|
|
|
if (checkOnly) {
|
|
const current = await readFile(outFile, "utf8");
|
|
if (current !== sql) {
|
|
throw new Error(`${outFile} is out of date. Run pnpm db:generate.`);
|
|
}
|
|
} else {
|
|
await mkdir(dirname(outFile), { recursive: true });
|
|
await writeFile(outFile, sql, "utf8");
|
|
console.log(`Wrote ${outFile}`);
|
|
}
|