Brings the working codebase (Next.js app, auth system, Stripe billing, Docker/deploy config, tests, docs) into version control on top of the placeholder initial commit, and adds account self-deletion (Danger Zone in Settings, password + typed-email confirmation, cascading DB cleanup, Stripe cancellation) per GDPR right-to-erasure. Excludes local build caches, node_modules, and internal agent scratch files; .gitignore hardened to keep those out going forward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import fs from "fs";
|
|
|
|
// Load .env.local into process.env (same pattern as other scripts) BEFORE the
|
|
// db module initialises, so it picks up the real DATABASE_URL (port 5436).
|
|
const dotenvContent = fs.readFileSync(".env.local", "utf8");
|
|
for (const l of dotenvContent.split("\n")) {
|
|
const trimmed = l.trim();
|
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
const idx = trimmed.indexOf("=");
|
|
if (idx !== -1) {
|
|
process.env[trimmed.slice(0, idx).trim()] = trimmed.slice(idx + 1).trim();
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const { db } = await import("../src/lib/db");
|
|
const { users } = await import("../src/lib/schema/db");
|
|
const { hashPassword } = await import("../src/lib/auth/password");
|
|
const { canonicaliseEmail, normaliseEmail } = await import("../src/lib/auth/email");
|
|
const { newId } = await import("../src/lib/auth/tokens");
|
|
const { eq } = await import("drizzle-orm");
|
|
|
|
const rawEmail = process.env.ADMIN_EMAILS?.split(",")[0]?.trim() ?? "";
|
|
const password = process.env.ADMIN_PASSWORD ?? "fiesta";
|
|
|
|
if (!rawEmail) throw new Error("ADMIN_EMAILS not set");
|
|
|
|
const email = canonicaliseEmail(rawEmail);
|
|
const emailKey = normaliseEmail(rawEmail);
|
|
if (!emailKey) throw new Error(`Invalid admin email: ${rawEmail}`);
|
|
|
|
const passwordHash = await hashPassword(password);
|
|
|
|
const existing = await db.select().from(users).where(eq(users.emailKey, emailKey)).limit(1);
|
|
|
|
if (existing.length > 0) {
|
|
await db
|
|
.update(users)
|
|
.set({
|
|
passwordHash,
|
|
emailVerifiedAt: new Date(),
|
|
isGuest: false,
|
|
updatedAt: new Date(),
|
|
})
|
|
.where(eq(users.id, existing[0].id));
|
|
console.log(`Updated admin ${email} (${existing[0].id}) — password reset, email verified`);
|
|
} else {
|
|
const row = (
|
|
await db
|
|
.insert(users)
|
|
.values({
|
|
id: newId("usr"),
|
|
email,
|
|
emailKey,
|
|
name: "Timo Knuth",
|
|
passwordHash,
|
|
emailVerifiedAt: new Date(),
|
|
isGuest: false,
|
|
plan: "free",
|
|
})
|
|
.returning()
|
|
)[0];
|
|
console.log(`Created admin ${email} (${row.id}) — email verified`);
|
|
}
|
|
|
|
process.exit(0);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
}); |