build: setup docker containers for development

This commit is contained in:
2026-03-12 18:37:56 +01:00
parent c16657f996
commit 83cad4b0ae
33 changed files with 170 additions and 138 deletions

15
app/server/db/index.js Normal file
View File

@@ -0,0 +1,15 @@
import { Pool } from 'pg';
const db = new Pool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT,
database: process.env.DB_DATABASE,
max: 20,
idleTimeoutMillis: 30_000,
connectionTimeoutMillis: 2_000,
maxLifetimeSeconds: 60
});
export default db;

68
app/server/db/migrate.js Normal file
View File

@@ -0,0 +1,68 @@
import fs from "node:fs";
import { readdir } from "node:fs/promises";
import path from "node:path";
import dbClient from "./index.js";
const __dirname = import.meta.dirname;
const createMigrationTable = () => {
return dbClient.query(`
CREATE TABLE IF NOT EXISTS migrations (
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255),
executed_at TIMESTAMP
)
`);
}
const getMigrationsFiles = () => {
try {
return readdir(path.resolve(__dirname, "./migrations"));
} catch(err) {
return new Error("[Migration] getMigrations err: ", err);
}
};
const runMigrations = async () => {
try {
// Create migration table if needed
await createMigrationTable();
const migrationFiles = await getMigrationsFiles();
const migrationsDb = await dbClient.query("SELECT name FROM migrations WHERE executed_at IS NOT NULL");
const migrationsToRun = migrationFiles.filter(m => !migrationsDb.rows.map(m => m.name).includes(m));
console.log("[Migration] migrations to run: ", migrationsToRun);
for (const migration of migrationsToRun) {
await dbClient.query("BEGIN");
const sqlQuery = fs.readFileSync(path.resolve(__dirname, `./migrations/${migration}`), "utf-8");
try {
await dbClient.query(sqlQuery);
await dbClient.query({
text: "INSERT INTO migrations(id, name, executed_at) VALUES($1, $2, $3)",
values: [migration.slice(0, -4), migration, new Date().toISOString().slice(0, 19).replace('T', ' ')]
});
await dbClient.query("COMMIT");
} catch(err) {
console.log(err);
await dbClient.query("ROLLBACK");
}
}
console.log("[Migration]: SUCCESS");
} catch (err) {
console.error("Error", err);
process.exit(1);
}
};
// Run this from CLI
if (import.meta.main) {
console.log("Run migrations");
runMigrations()
.then(() => process.exit(0))
.catch(() => process.exit(1));
}

View File

@@ -0,0 +1,9 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title VARCHAR(255) NULL UNIQUE,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -0,0 +1,29 @@
CREATE TABLE users (
id TEXT PRIMARY KEY NOT NULL UNIQUE,
username VARCHAR(255)
);
CREATE TABLE passkeys (
id TEXT PRIMARY KEY NOT NULL UNIQUE,
public_key BYTEA,
webauthn_user__id TEXT UNIQUE,
counter BIGINT,
device_type VARCHAR(32),
transports VARCHAR(255)
);
-- User/passkey junction table
CREATE TABLE user_passkeys (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id TEXT NOT NULL,
passkey_id TEXT NOT NULL,
-- Foreign key constraints
CONSTRAINT fk_user_passkeys_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_user_passkeys_passkeys FOREIGN KEY (passkey_id) REFERENCES passkeys(id) ON DELETE CASCADE,
-- Prevent duplicates
CONSTRAINT unique_user_passkeys UNIQUE (user_id, passkey_id)
);
CREATE INDEX index_passkeys ON passkeys (id, webauthn_user__id);