V1 bingo
Deploy / build-and-deploy (push) Failing after 2m53s

This commit is contained in:
2026-06-14 21:29:43 +03:00
commit 05677924b5
55 changed files with 10816 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
import * as schema from "./schema";
import path from "path";
const DB_PATH = path.join(process.cwd(), "data", "bingo.db");
function getDb() {
const sqlite = new Database(DB_PATH);
sqlite.pragma("journal_mode = WAL");
sqlite.pragma("foreign_keys = ON");
return sqlite;
}
function initDatabase(sqlite: Database.Database) {
sqlite.exec(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
nickname TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
is_admin INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS campaigns (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
grid_size INTEGER NOT NULL DEFAULT 5,
status TEXT NOT NULL DEFAULT 'active',
created_by TEXT NOT NULL REFERENCES users(id),
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS bingo_items (
id TEXT PRIMARY KEY,
campaign_id TEXT NOT NULL REFERENCES campaigns(id) ON DELETE CASCADE,
text TEXT NOT NULL,
emoji TEXT NOT NULL DEFAULT '💀',
sound_category TEXT NOT NULL DEFAULT 'horn',
sound_url TEXT,
grid_index INTEGER NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS marks (
id TEXT PRIMARY KEY,
campaign_id TEXT NOT NULL REFERENCES campaigns(id) ON DELETE CASCADE,
item_id TEXT NOT NULL REFERENCES bingo_items(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_campaign_item_pos ON bingo_items(campaign_id, grid_index);
CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_mark ON marks(campaign_id, item_id);
`);
try { sqlite.exec("ALTER TABLE bingo_items ADD COLUMN sound_url TEXT"); } catch {}
}
const sqlite = getDb();
initDatabase(sqlite);
export const db = drizzle(sqlite, { schema });
+48
View File
@@ -0,0 +1,48 @@
import { sqliteTable, text, integer, uniqueIndex } from "drizzle-orm/sqlite-core";
export const users = sqliteTable("users", {
id: text("id").primaryKey(),
nickname: text("nickname").unique().notNull(),
passwordHash: text("password_hash").notNull(),
isAdmin: integer("is_admin", { mode: "boolean" }).default(false).notNull(),
createdAt: text("created_at").notNull(),
});
export const sessions = sqliteTable("sessions", {
id: text("id").primaryKey(),
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
expiresAt: text("expires_at").notNull(),
createdAt: text("created_at").notNull(),
});
export const campaigns = sqliteTable("campaigns", {
id: text("id").primaryKey(),
name: text("name").notNull(),
gridSize: integer("grid_size").notNull().default(5),
status: text("status").notNull().default("active"),
createdBy: text("created_by").notNull().references(() => users.id),
createdAt: text("created_at").notNull(),
});
export const bingoItems = sqliteTable("bingo_items", {
id: text("id").primaryKey(),
campaignId: text("campaign_id").notNull().references(() => campaigns.id, { onDelete: "cascade" }),
text: text("text").notNull(),
emoji: text("emoji").notNull().default("💀"),
soundCategory: text("sound_category").notNull().default("horn"),
soundUrl: text("sound_url"),
gridIndex: integer("grid_index").notNull(),
createdAt: text("created_at").notNull(),
}, (table) => [
uniqueIndex("idx_campaign_item_pos").on(table.campaignId, table.gridIndex),
]);
export const marks = sqliteTable("marks", {
id: text("id").primaryKey(),
campaignId: text("campaign_id").notNull().references(() => campaigns.id, { onDelete: "cascade" }),
itemId: text("item_id").notNull().references(() => bingoItems.id, { onDelete: "cascade" }),
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
createdAt: text("created_at").notNull(),
}, (table) => [
uniqueIndex("idx_unique_mark").on(table.campaignId, table.itemId),
]);