Ariweb Suite 0.1.1 기반: 공통 라이브러리(db·http·세션·작업 엔진·할당/예약·알림·감사·보관소), 포털 뼈대, Webmaker/AriMail 도입 소스와 manifest
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
9ca4cee341
commit
c7aea8d419
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* SQLite 어댑터. www 사이트(server/db.mjs)와 같은 모양(prepare/bind/first/all/run/batch)을 유지해
|
||||
* 두 저장소의 코드를 서로 옮겨 쓸 수 있게 합니다. node:sqlite 는 Node 22.13+ 에서 플래그 없이 동작합니다.
|
||||
*/
|
||||
import { DatabaseSync } from 'node:sqlite';
|
||||
import { readFileSync, readdirSync, mkdirSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
class Stmt {
|
||||
constructor(db, sql, params = []) { this.db = db; this.sql = sql; this.params = params; }
|
||||
bind(...params) { return new Stmt(this.db, this.sql, this.params.length ? this.params : params); }
|
||||
#prep() { return this.db.prepare(this.sql); }
|
||||
async first() { const row = this.#prep().get(...this.params); return row === undefined ? null : row; }
|
||||
async all() { return { results: this.#prep().all(...this.params) }; }
|
||||
async run() { const r = this.#prep().run(...this.params); return { success: true, meta: { changes: Number(r.changes) } }; }
|
||||
}
|
||||
|
||||
export function openDb(file, { readOnly = false } = {}) {
|
||||
if (file !== ':memory:' && !readOnly) mkdirSync(dirname(file), { recursive: true });
|
||||
const db = new DatabaseSync(file, readOnly ? { readOnly: true } : {});
|
||||
if (!readOnly) {
|
||||
db.exec('PRAGMA foreign_keys = ON;');
|
||||
if (file !== ':memory:') { db.exec('PRAGMA journal_mode = WAL;'); db.exec('PRAGMA busy_timeout = 5000;'); }
|
||||
} else if (file !== ':memory:') db.exec('PRAGMA busy_timeout = 5000;');
|
||||
return db;
|
||||
}
|
||||
|
||||
export function d1(db) {
|
||||
let queue = Promise.resolve();
|
||||
return {
|
||||
prepare: (sql) => new Stmt(db, sql),
|
||||
batch: (stmts) => {
|
||||
const execute = async () => {
|
||||
db.exec('BEGIN');
|
||||
try { const out = []; for (const s of stmts) out.push(await s.run()); db.exec('COMMIT'); return out; }
|
||||
catch (e) { db.exec('ROLLBACK'); throw e; }
|
||||
};
|
||||
const result = queue.then(execute, execute);
|
||||
queue = result.catch(() => undefined);
|
||||
return result;
|
||||
},
|
||||
_raw: db,
|
||||
};
|
||||
}
|
||||
|
||||
export function migrate(db, dir, { quiet = false } = {}) {
|
||||
db.exec('CREATE TABLE IF NOT EXISTS schema_migrations (name TEXT PRIMARY KEY, applied_at TEXT NOT NULL)');
|
||||
const done = new Set(db.prepare('SELECT name FROM schema_migrations').all().map((r) => r.name));
|
||||
let applied = 0;
|
||||
for (const f of readdirSync(dir).filter((x) => x.endsWith('.sql')).sort()) {
|
||||
if (done.has(f)) continue;
|
||||
db.exec('BEGIN');
|
||||
try {
|
||||
db.exec(readFileSync(join(dir, f), 'utf8'));
|
||||
db.prepare('INSERT INTO schema_migrations (name, applied_at) VALUES (?, ?)').run(f, new Date().toISOString());
|
||||
db.exec('COMMIT'); applied++;
|
||||
if (!quiet) console.log(` 적용: ${f}`);
|
||||
} catch (e) { db.exec('ROLLBACK'); throw new Error(`마이그레이션 실패 ${f}: ${e.message}`); }
|
||||
}
|
||||
if (!quiet && !applied) console.log(' 적용할 마이그레이션 없음');
|
||||
return applied;
|
||||
}
|
||||
Reference in New Issue
Block a user