Scaffold Regress: Fastify+SQLite server and Vue frontend for the Ingress-style BTC Map capture game

- Server: NIP-98 nostr auth (session cookies), BTC Map sync, claim/link/field
  routes, physical-presence checks via geolocation distance. 31 tests passing.
- Frontend: Leaflet map, team pick, claim/link UI, Archipelago identity
  bridge vendored (nostr-provider.js) for dashboard launch support.
- Verified end-to-end against the live BTC Map API (167 real Madeira places)
  and via genuine NIP-98-signed HTTP requests against the built app.
This commit is contained in:
2026-08-05 13:14:03 +00:00
parent cac528ba9a
commit d6ef514c84
47 changed files with 8444 additions and 2 deletions
+30
View File
@@ -0,0 +1,30 @@
import Database from 'better-sqlite3';
import { mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import { migrations } from './migrations.js';
export type DB = Database.Database;
export function openDatabase(path: string): DB {
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
const db = new Database(path);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
migrate(db);
return db;
}
function migrate(db: DB): void {
db.exec('CREATE TABLE IF NOT EXISTS schema_migrations (id INTEGER PRIMARY KEY, applied_at INTEGER NOT NULL)');
const applied = new Set(
db.prepare('SELECT id FROM schema_migrations').all().map((r) => (r as { id: number }).id),
);
const record = db.prepare('INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)');
for (const m of migrations) {
if (applied.has(m.id)) continue;
db.transaction(() => {
db.exec(m.sql);
record.run(m.id, Math.floor(Date.now() / 1000));
})();
}
}