Initial top-reservation app: bookings, objects catalog, statistics, PWA

This commit is contained in:
ogrechko committed 2026-08-29 11:30:46 +00:00
commit 14a849bfb9
62 files changed
+12427

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
/**
* Creates the initial admin user from ADMIN_BOOTSTRAP_EMAIL /
* ADMIN_BOOTSTRAP_PASSWORD if no user with that email exists yet.
* Safe to run on every container start — idempotent.
*/
import { db } from "../src/lib/db/client";
import { users } from "../src/lib/db/schema";
import { hashPassword } from "../src/lib/auth/password";
async function main() {
const email = process.env.ADMIN_BOOTSTRAP_EMAIL;
const password = process.env.ADMIN_BOOTSTRAP_PASSWORD;
const name = process.env.ADMIN_BOOTSTRAP_NAME ?? "Admin";
if (!email || !password) {
console.log("ADMIN_BOOTSTRAP_EMAIL/PASSWORD not set — skipping admin bootstrap.");
return;
}
const normalizedEmail = email.toLowerCase().trim();
const existing = await db.query.users.findFirst({
where: (u, { eq }) => eq(u.email, normalizedEmail),
});
if (existing) {
console.log(`Admin user ${normalizedEmail} already exists — skipping.`);
return;
}
const passwordHash = await hashPassword(password);
await db.insert(users).values({
email: normalizedEmail,
passwordHash,
name,
role: "admin",
});
console.log(`Bootstrapped admin user ${normalizedEmail}.`);
}
main()
.then(() => process.exit(0))
.catch((err) => {
console.error("Admin bootstrap failed:", err);
process.exit(1);
});
+41
View File
@@ -0,0 +1,41 @@
/**
* Seeds the bookable-objects catalog with the distinct items visible across
* the old bot's booking history, so the list isn't empty on first use.
* Idempotent — skips names that already exist.
*/
import { db } from "../src/lib/db/client";
import { bookableObjects } from "../src/lib/db/schema";
const SEED_NAMES = [
"Баня",
"Чан",
"Афрейм",
"Шале",
"Банный комплекс",
"Дом с ванной",
"Дом с баней",
"Банкетный зал",
"Беседка открытая",
"Вся база без кафе",
];
async function main() {
const existing = await db.query.bookableObjects.findMany({ columns: { name: true } });
const existingNames = new Set(existing.map((o) => o.name));
const toInsert = SEED_NAMES.filter((n) => !existingNames.has(n));
if (toInsert.length === 0) {
console.log("Bookable objects already seeded — skipping.");
return;
}
await db.insert(bookableObjects).values(toInsert.map((name, i) => ({ name, sortOrder: i })));
console.log(`Seeded ${toInsert.length} bookable object(s): ${toInsert.join(", ")}`);
}
main()
.then(() => process.exit(0))
.catch((err) => {
console.error("Object seeding failed:", err);
process.exit(1);
});