Fix SQLite init: lazy loading, writability check, and host permissions
All checks were successful
Deploy Website / build-and-deploy (push) Successful in 30s
All checks were successful
Deploy Website / build-and-deploy (push) Successful in 30s
DB initialization was eager at module import time, crashing the entire analytics route when /server_storage wasn't writable. Now deferred to first logVisit() call with graceful fallback. Also fix deploy workflow to chown via a bind-mounted container so permissions apply on the host. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,14 +24,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Prepare Host Storage
|
- name: Prepare Host Storage
|
||||||
run: |
|
run: |
|
||||||
# 1. Create the host folder if it doesn't exist
|
# The bind mount creates /server_storage on the host if missing.
|
||||||
sudo mkdir -p /server_storage
|
# Run a throwaway container to fix ownership so UID 1001 (nextjs) can write.
|
||||||
|
docker run --rm -v /server_storage:/data alpine sh -c \
|
||||||
# 2. Fix permissions for the 'nextjs' user (UID 1001)
|
"chown -R 1001:1001 /data && chmod -R 755 /data"
|
||||||
# Since your Dockerfile runs as user 1001, it cannot write to
|
|
||||||
# a root-owned folder without this chown command.
|
|
||||||
sudo chown -R 1001:1001 /server_storage
|
|
||||||
sudo chmod -R 755 /server_storage
|
|
||||||
|
|
||||||
- name: Run New Container
|
- name: Run New Container
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
44
lib/db.ts
44
lib/db.ts
@@ -1,28 +1,52 @@
|
|||||||
import Database from 'better-sqlite3';
|
import Database from 'better-sqlite3';
|
||||||
|
import type BetterSqlite3 from 'better-sqlite3';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
const SERVER_STORAGE = '/server_storage';
|
let db: BetterSqlite3.Database | null = null;
|
||||||
const dbPath = fs.existsSync(SERVER_STORAGE)
|
let insertStmt: BetterSqlite3.Statement | null = null;
|
||||||
? path.join(SERVER_STORAGE, 'visitors.db')
|
|
||||||
: path.join(process.cwd(), 'visitors.db');
|
|
||||||
|
|
||||||
const db = new Database(dbPath);
|
function getDb() {
|
||||||
db.pragma('journal_mode = WAL');
|
if (db) return db;
|
||||||
|
|
||||||
db.exec(`
|
try {
|
||||||
|
const SERVER_STORAGE = '/server_storage';
|
||||||
|
let dbDir: string;
|
||||||
|
try {
|
||||||
|
fs.accessSync(SERVER_STORAGE, fs.constants.W_OK);
|
||||||
|
dbDir = SERVER_STORAGE;
|
||||||
|
} catch {
|
||||||
|
dbDir = process.cwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbPath = path.join(dbDir, 'visitors.db');
|
||||||
|
db = new Database(dbPath);
|
||||||
|
db.pragma('journal_mode = WAL');
|
||||||
|
|
||||||
|
db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS visits (
|
CREATE TABLE IF NOT EXISTS visits (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
ip_address TEXT NOT NULL,
|
ip_address TEXT NOT NULL,
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
visited_at TEXT NOT NULL DEFAULT (datetime('now'))
|
visited_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const insertStmt = db.prepare(
|
insertStmt = db.prepare(
|
||||||
'INSERT INTO visits (ip_address, path) VALUES (?, ?)'
|
'INSERT INTO visits (ip_address, path) VALUES (?, ?)'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return db;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to initialize SQLite database:', e);
|
||||||
|
db = null;
|
||||||
|
insertStmt = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function logVisit(ip: string, visitPath: string) {
|
export function logVisit(ip: string, visitPath: string) {
|
||||||
|
const database = getDb();
|
||||||
|
if (!database || !insertStmt) return;
|
||||||
insertStmt.run(ip, visitPath);
|
insertStmt.run(ip, visitPath);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user