Initial commit: PodcastYes — AI podcast platform
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// Quick DB verification. Run: npx tsx scripts/check-db.ts
|
||||
import "dotenv/config";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
async function main() {
|
||||
const tables = await prisma.$queryRawUnsafe<{ table_name: string }[]>(
|
||||
"select table_name from information_schema.tables where table_schema = 'public' order by table_name"
|
||||
);
|
||||
const names = tables.map((t) => t.table_name);
|
||||
console.log(`Tables (${names.length}):`, names.join(", "));
|
||||
|
||||
const expected = [
|
||||
"user",
|
||||
"session",
|
||||
"account",
|
||||
"verification",
|
||||
"organization",
|
||||
"member",
|
||||
"invitation",
|
||||
"subscription",
|
||||
"episode",
|
||||
"script",
|
||||
"audio_asset",
|
||||
"cover_art",
|
||||
"generation_job",
|
||||
"plan",
|
||||
"usage_record",
|
||||
"api_key",
|
||||
"audit_log",
|
||||
];
|
||||
const have = new Set(names);
|
||||
const missing = expected.filter((t) => !have.has(t));
|
||||
console.log(missing.length ? `MISSING: ${missing.join(", ")}` : "✓ All expected tables present");
|
||||
|
||||
const [users, plans] = await Promise.all([prisma.user.count(), prisma.plan.count()]);
|
||||
console.log(`Users: ${users} · Plans: ${plans}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error("Check failed:", e.message ?? e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -0,0 +1,23 @@
|
||||
// Promote a user to admin by email. Run: npx tsx scripts/make-admin.ts you@email.com
|
||||
import "dotenv/config";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
async function main() {
|
||||
const email = process.argv[2];
|
||||
if (!email) {
|
||||
console.error("Usage: tsx scripts/make-admin.ts <email>");
|
||||
process.exit(1);
|
||||
}
|
||||
const user = await prisma.user.update({
|
||||
where: { email },
|
||||
data: { role: "admin" },
|
||||
});
|
||||
console.log(`✓ ${user.email} is now an admin.`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((err) => {
|
||||
console.error("Failed:", err.message ?? err);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -0,0 +1,22 @@
|
||||
// After `next build` with output:"standalone", Next does NOT copy static assets or
|
||||
// /public into the standalone folder. PM2 runs .next/standalone/server.js, so copy
|
||||
// them in here. Safe to run anywhere — no-ops if there's no standalone output.
|
||||
import { cp } from "node:fs/promises";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const root = process.cwd();
|
||||
const standalone = path.join(root, ".next", "standalone");
|
||||
|
||||
if (!existsSync(standalone)) {
|
||||
console.log("postbuild: no standalone output, skipping");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
await cp(path.join(root, ".next", "static"), path.join(standalone, ".next", "static"), {
|
||||
recursive: true,
|
||||
});
|
||||
if (existsSync(path.join(root, "public"))) {
|
||||
await cp(path.join(root, "public"), path.join(standalone, "public"), { recursive: true });
|
||||
}
|
||||
console.log("postbuild: ✓ copied static (+ public) into .next/standalone");
|
||||
@@ -0,0 +1,44 @@
|
||||
// Verify the Better Auth ↔ Prisma schema by performing a real signup.
|
||||
// Run: npx tsx scripts/test-auth.ts
|
||||
import "dotenv/config";
|
||||
import { auth } from "@/lib/auth/auth";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
async function main() {
|
||||
const email = `verify_${Date.now()}@podcastyes.test`;
|
||||
try {
|
||||
await auth.api.signUpEmail({
|
||||
body: { email, password: "Password123!", name: "Verify User" },
|
||||
});
|
||||
console.log("signUpEmail: completed");
|
||||
} catch (e) {
|
||||
// The nextCookies plugin may throw when run outside a Next request — the DB
|
||||
// write happens first, so we still verify via the row below.
|
||||
console.log("signUpEmail threw (often just the cookie step):", (e as Error).message);
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
include: { accounts: true, sessions: true },
|
||||
});
|
||||
|
||||
if (user) {
|
||||
console.log(
|
||||
`✓ Better Auth schema works — user=${user.email} role=${user.role} ` +
|
||||
`accounts=${user.accounts.length} (provider=${user.accounts[0]?.providerId}) ` +
|
||||
`hasPassword=${!!user.accounts[0]?.password}`
|
||||
);
|
||||
// Clean up the test user.
|
||||
await prisma.user.delete({ where: { id: user.id } });
|
||||
console.log("✓ test user cleaned up");
|
||||
} else {
|
||||
console.log("✗ No user row created — schema mismatch likely");
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error("Failed:", e.message ?? e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -0,0 +1,32 @@
|
||||
// Dev sanity check for the audio segmenter. Run: npx tsx scripts/test-segment.ts
|
||||
import { segmentTurns, splitLongText, flattenTurns } from "@/lib/ai/pipeline/segment";
|
||||
import type { StructuredScript } from "@/lib/ai/types";
|
||||
|
||||
const turns = [
|
||||
{ text: "Hello and welcome to the show.", voiceId: "A" },
|
||||
{ text: "x".repeat(2500), voiceId: "B" }, // oversized single turn
|
||||
{ text: "Thanks for having me.", voiceId: "A" },
|
||||
{ text: "Let's dive in.", voiceId: "A" },
|
||||
];
|
||||
|
||||
const segs = segmentTurns(turns, 1800);
|
||||
console.log(`segmentTurns -> ${segs.length} segments`);
|
||||
segs.forEach((s, i) =>
|
||||
console.log(` #${i}: chars=${s.characters} voices=${s.uniqueVoices} turns=${s.turns.length}`)
|
||||
);
|
||||
|
||||
const parts = splitLongText("One. Two. Three. " + "w".repeat(40), 15);
|
||||
console.log(`splitLongText -> ${parts.length} parts (max len ${Math.max(...parts.map((p) => p.length))})`);
|
||||
|
||||
const script: StructuredScript = {
|
||||
title: "T",
|
||||
sections: [
|
||||
{ id: "a", title: "A", turns: [{ speakerKey: "host", text: "Hi" }, { speakerKey: "ghost", text: "" }] },
|
||||
{ id: "b", title: "B", turns: [{ speakerKey: "guest", text: "Hello" }] },
|
||||
],
|
||||
};
|
||||
const flat = flattenTurns(script, { host: "V1", guest: "V2" }, "V1");
|
||||
console.log(`flattenTurns -> ${flat.length} turns (empty dropped), voices: ${flat.map((t) => t.voiceId).join(",")}`);
|
||||
|
||||
const allUnder = segs.every((s) => s.characters <= 1800);
|
||||
console.log(allUnder ? "PASS: all segments within limit" : "FAIL: a segment exceeds the limit");
|
||||
Reference in New Issue
Block a user