- Discord-OAuth2-Login (identify-Scope, CSRF-State, signierte Session-Cookies, keine Token-Speicherung) - REST-API: /api/devlogs (öffentlich), /api/commits (nur ADMIN_DISCORD_ID), /api/me - React + Vite Frontend: Devlog-Archiv mit Mini-Markdown-Renderer, Commit-Tabelle, dunkles EcoGame-Theme - Fastify liefert frontend/dist mit SPA-Fallback aus; Vite-Dev-Proxy für lokale Entwicklung - Multi-Stage-Dockerfile (Frontend-Build im Image), neue Env-Vars in Compose + .env.example - README: OAuth2-Setup (Redirect-URLs, Client Secret) und Frontend-Workflow Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
2.9 KiB
JavaScript
83 lines
2.9 KiB
JavaScript
// SQLite-Anbindung (better-sqlite3, synchron & schnell) — Schema wird beim Start angelegt
|
|
import Database from 'better-sqlite3';
|
|
import { mkdirSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { config } from './config.js';
|
|
|
|
const dbFile = resolve(config.dbPath);
|
|
mkdirSync(dirname(dbFile), { recursive: true });
|
|
|
|
export const db = new Database(dbFile);
|
|
db.pragma('journal_mode = WAL');
|
|
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS commits (
|
|
sha TEXT PRIMARY KEY,
|
|
repo TEXT NOT NULL,
|
|
branch TEXT NOT NULL,
|
|
message TEXT NOT NULL,
|
|
author_name TEXT,
|
|
author_user TEXT,
|
|
url TEXT,
|
|
committed_at TEXT NOT NULL,
|
|
received_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_commits_repo_time ON commits (repo, committed_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS devlogs (
|
|
message_id TEXT PRIMARY KEY,
|
|
channel_id TEXT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
author_name TEXT,
|
|
posted_at TEXT NOT NULL,
|
|
archived_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_devlogs_posted ON devlogs (posted_at DESC);
|
|
`);
|
|
|
|
const insertCommit = db.prepare(`
|
|
INSERT OR IGNORE INTO commits (sha, repo, branch, message, author_name, author_user, url, committed_at)
|
|
VALUES (@sha, @repo, @branch, @message, @author_name, @author_user, @url, @committed_at)
|
|
`);
|
|
|
|
/** Commits eines Pushes speichern — bereits bekannte SHAs werden ignoriert. Gibt Anzahl neuer Zeilen zurück. */
|
|
export const saveCommits = db.transaction((commits) => {
|
|
let inserted = 0;
|
|
for (const commit of commits) {
|
|
inserted += insertCommit.run(commit).changes;
|
|
}
|
|
return inserted;
|
|
});
|
|
|
|
const insertDevlog = db.prepare(`
|
|
INSERT OR IGNORE INTO devlogs (message_id, channel_id, content, author_name, posted_at)
|
|
VALUES (@message_id, @channel_id, @content, @author_name, @posted_at)
|
|
`);
|
|
|
|
/** Devlog speichern — bereits bekannte Message-IDs werden ignoriert. Gibt true zurück, wenn neu. */
|
|
export function saveDevlog(devlog) {
|
|
return insertDevlog.run(devlog).changes > 0;
|
|
}
|
|
|
|
// --- Lese-Queries für die Web-API (Feature 4) ---
|
|
|
|
const selectDevlogs = db.prepare(`
|
|
SELECT message_id, content, author_name, posted_at
|
|
FROM devlogs ORDER BY posted_at DESC LIMIT ? OFFSET ?
|
|
`);
|
|
const countDevlogsStmt = db.prepare('SELECT COUNT(*) AS n FROM devlogs');
|
|
|
|
export function listDevlogs(limit, offset) {
|
|
return { items: selectDevlogs.all(limit, offset), total: countDevlogsStmt.get().n };
|
|
}
|
|
|
|
const selectCommits = db.prepare(`
|
|
SELECT sha, repo, branch, message, author_name, url, committed_at
|
|
FROM commits ORDER BY committed_at DESC LIMIT ? OFFSET ?
|
|
`);
|
|
const countCommitsStmt = db.prepare('SELECT COUNT(*) AS n FROM commits');
|
|
|
|
export function listCommits(limit, offset) {
|
|
return { items: selectCommits.all(limit, offset), total: countCommitsStmt.get().n };
|
|
}
|