From b34a009eb08f8c2bd3e2e5c5555d5f778fc0b4a9 Mon Sep 17 00:00:00 2001 From: SlavaVlad Date: Sun, 14 Jun 2026 21:55:30 +0300 Subject: [PATCH] fix: create data dir with 777 perms + handle in code --- .gitea/workflows/deploy.yml | 3 +- Dockerfile | 2 +- lib/db/index.ts | 110 ++++++++++++++++++------------------ 3 files changed, 59 insertions(+), 56 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 853d5b7..ab77855 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -31,8 +31,9 @@ jobs: - name: Copy files to server run: | + ssh root@barabingo 'mkdir -p /opt/barabingo/data /opt/barabingo/uploads' scp /tmp/barabingo.tar.gz docker-compose.yml root@barabingo:/opt/barabingo/ - name: Deploy on server run: | - ssh root@barabingo 'cd /opt/barabingo && docker load < barabingo.tar.gz && docker compose down --remove-orphans 2>/dev/null; docker compose up -d && rm -f barabingo.tar.gz && docker image prune -f' + ssh root@barabingo 'chmod 777 /opt/barabingo/data /opt/barabingo/uploads && cd /opt/barabingo && docker load < barabingo.tar.gz && docker compose down --remove-orphans 2>/dev/null; docker compose up -d && rm -f barabingo.tar.gz && docker image prune -f' diff --git a/Dockerfile b/Dockerfile index 6a7083f..92a6be8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -RUN mkdir -p /app/data && chown nextjs:nodejs /app/data +RUN mkdir -p /app/data /app/public/uploads && chmod 777 /app/data /app/public/uploads USER nextjs diff --git a/lib/db/index.ts b/lib/db/index.ts index af5bc81..4a94ada 100644 --- a/lib/db/index.ts +++ b/lib/db/index.ts @@ -2,63 +2,65 @@ import Database from "better-sqlite3"; import { drizzle } from "drizzle-orm/better-sqlite3"; import * as schema from "./schema"; import path from "path"; +import fs from "fs"; -const DB_PATH = path.join(process.cwd(), "data", "bingo.db"); +const DB_DIR = path.join(process.cwd(), "data"); +const DB_PATH = path.join(DB_DIR, "bingo.db"); -function getDb() { - const sqlite = new Database(DB_PATH); - sqlite.pragma("journal_mode = WAL"); - sqlite.pragma("foreign_keys = ON"); - return sqlite; -} +try { + fs.mkdirSync(DB_DIR, { recursive: true, mode: 0o777 }); +} catch {} -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); - `); +const sqlite = (() => { + const s = new Database(DB_PATH); + s.pragma("journal_mode = WAL"); + s.pragma("foreign_keys = ON"); + return s; +})(); - try { sqlite.exec("ALTER TABLE bingo_items ADD COLUMN sound_url TEXT"); } catch {} -} +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 });