fix
This commit is contained in:
@@ -1,32 +1,89 @@
|
|||||||
import { Pool } from 'pg';
|
import pg from 'pg';
|
||||||
import { readFile } from 'node:fs/promises';
|
|
||||||
import { dirname, join } from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import bcrypt from 'bcryptjs';
|
|
||||||
import { config } from './config.js';
|
|
||||||
|
|
||||||
export const pool = new Pool({ connectionString: config.databaseUrl });
|
const { Pool } = pg;
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
export const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
});
|
||||||
|
|
||||||
export async function initDb(): Promise<void> {
|
function sleep(ms: number) {
|
||||||
const migration = await readFile(join(__dirname, '../migrations/001_init.sql'), 'utf8').catch(async () => {
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
return readFile(join(process.cwd(), 'migrations/001_init.sql'), 'utf8');
|
|
||||||
});
|
|
||||||
await pool.query(migration);
|
|
||||||
|
|
||||||
await pool.query(
|
|
||||||
`INSERT INTO nodes(name, hostname, is_current)
|
|
||||||
VALUES($1, $2, true)
|
|
||||||
ON CONFLICT (name) DO UPDATE SET hostname = EXCLUDED.hostname, is_current = true, updated_at = now()`,
|
|
||||||
[config.nodeName, config.nodeHostname],
|
|
||||||
);
|
|
||||||
|
|
||||||
const hash = await bcrypt.hash(config.adminPassword, 12);
|
|
||||||
await pool.query(
|
|
||||||
`INSERT INTO admin_users(email, password_hash, role)
|
|
||||||
VALUES($1, $2, 'super_admin')
|
|
||||||
ON CONFLICT (email) DO NOTHING`,
|
|
||||||
[config.adminEmail.toLowerCase(), hash],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function initDb() {
|
||||||
|
const maxAttempts = 30;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
await pool.query('SELECT 1');
|
||||||
|
console.log(`✓ PostgreSQL connected`);
|
||||||
|
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS nodes (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
hostname TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS domains (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
domain TEXT UNIQUE NOT NULL,
|
||||||
|
node_name TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
|
first_seen_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
last_seen_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS mailboxes (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
email_address TEXT UNIQUE NOT NULL,
|
||||||
|
domain TEXT NOT NULL,
|
||||||
|
node_name TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
|
used_bytes BIGINT DEFAULT 0,
|
||||||
|
last_usage_scan_at TIMESTAMPTZ,
|
||||||
|
first_seen_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
last_seen_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS admin_users (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
email TEXT UNIQUE NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL DEFAULT 'super_admin',
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS audit_log (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
actor_email TEXT,
|
||||||
|
action TEXT NOT NULL,
|
||||||
|
target TEXT,
|
||||||
|
details JSONB,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (err: any) {
|
||||||
|
console.warn(
|
||||||
|
`PostgreSQL not ready yet (${attempt}/${maxAttempts}): ${err.message}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (attempt === maxAttempts) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,19 @@ services:
|
|||||||
mailadmin-db:
|
mailadmin-db:
|
||||||
image: postgres:16
|
image: postgres:16
|
||||||
container_name: mailadmin-db
|
container_name: mailadmin-db
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: mailadmin
|
POSTGRES_DB: mailadmin
|
||||||
POSTGRES_USER: mailadmin
|
POSTGRES_USER: mailadmin
|
||||||
POSTGRES_PASSWORD: ${MAILADMIN_DB_PASSWORD:-change-me}
|
POSTGRES_PASSWORD: ${MAILADMIN_DB_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/postgres:/var/lib/postgresql/data
|
- mailadmin-db-data:/var/lib/postgresql/data
|
||||||
networks:
|
healthcheck:
|
||||||
- mail_network
|
test: ["CMD-SHELL", "pg_isready -U mailadmin -d mailadmin"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 20
|
||||||
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
mailadmin:
|
mailadmin:
|
||||||
build:
|
build:
|
||||||
@@ -19,7 +23,8 @@ services:
|
|||||||
container_name: mailadmin
|
container_name: mailadmin
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- mailadmin-db
|
mailadmin-db:
|
||||||
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
PORT: 3000
|
PORT: 3000
|
||||||
|
|||||||
Reference in New Issue
Block a user