fix(db): bootstrap полной схемы на чистой установке и логи Docker
Docker / build (push) Failing after 18s
Docker / build (push) Failing after 18s
Пустой volume больше не отдаёт 500 на /api/data из-за отсутствующих таблиц. В compose добавлены json-file логи с ротацией и LOG_LEVEL для Fastify. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -43,7 +43,9 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
|||||||
getDb()
|
getDb()
|
||||||
|
|
||||||
const app = Fastify({
|
const app = Fastify({
|
||||||
logger: process.env.NODE_ENV !== 'production',
|
logger: {
|
||||||
|
level: process.env.LOG_LEVEL ?? 'info',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await app.register(cors, { origin: true })
|
await app.register(cors, { origin: true })
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ services:
|
|||||||
options:
|
options:
|
||||||
max-size: "10m"
|
max-size: "10m"
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
tag: "{{.Name}}"
|
||||||
|
|
||||||
app:
|
app:
|
||||||
image: ${VPS_TRACKER_IMAGE:-ghcr.io/denozordec/vps-tracker}:${VPS_TRACKER_IMAGE_TAG:-latest}
|
image: ${VPS_TRACKER_IMAGE:-ghcr.io/denozordec/vps-tracker}:${VPS_TRACKER_IMAGE_TAG:-latest}
|
||||||
@@ -60,6 +61,7 @@ services:
|
|||||||
PORT: "3001"
|
PORT: "3001"
|
||||||
RUNTIME: "fastify"
|
RUNTIME: "fastify"
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
AUTH_REQUIRED: ${AUTH_REQUIRED:-false}
|
AUTH_REQUIRED: ${AUTH_REQUIRED:-false}
|
||||||
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:-}
|
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:-}
|
||||||
AUTH_ISSUER: ${AUTH_ISSUER:-https://auth.shnt.top}
|
AUTH_ISSUER: ${AUTH_ISSUER:-https://auth.shnt.top}
|
||||||
@@ -93,6 +95,7 @@ services:
|
|||||||
options:
|
options:
|
||||||
max-size: "10m"
|
max-size: "10m"
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
tag: "{{.Name}}"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
traefik_letsencrypt:
|
traefik_letsencrypt:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ VPS_DOMAIN=vps.example.com
|
|||||||
# TRAEFIK_HTTP_PORT=80
|
# TRAEFIK_HTTP_PORT=80
|
||||||
# TRAEFIK_HTTPS_PORT=443
|
# TRAEFIK_HTTPS_PORT=443
|
||||||
# TRAEFIK_LOG_LEVEL=INFO
|
# TRAEFIK_LOG_LEVEL=INFO
|
||||||
|
# LOG_LEVEL=info
|
||||||
|
|
||||||
# --- Image ---
|
# --- Image ---
|
||||||
# Default: GHCR. For Gitea registry, e.g.:
|
# Default: GHCR. For Gitea registry, e.g.:
|
||||||
|
|||||||
@@ -8,12 +8,20 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PORT: "3001"
|
PORT: "3001"
|
||||||
RUNTIME: "fastify"
|
RUNTIME: "fastify"
|
||||||
|
NODE_ENV: production
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
AUTH_REQUIRED: ${AUTH_REQUIRED:-false}
|
AUTH_REQUIRED: ${AUTH_REQUIRED:-false}
|
||||||
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:-dev-secret-change-me}
|
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:-dev-secret-change-me}
|
||||||
AUTH_ISSUER: ${AUTH_ISSUER:-https://auth.shnt.top}
|
AUTH_ISSUER: ${AUTH_ISSUER:-https://auth.shnt.top}
|
||||||
AUTH_PORTAL_URL: ${AUTH_PORTAL_URL:-http://localhost:8080}
|
AUTH_PORTAL_URL: ${AUTH_PORTAL_URL:-http://localhost:8080}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
tag: "{{.Name}}"
|
||||||
|
|
||||||
# Раскомментируйте для локальной сборки вместо pull образа:
|
# Раскомментируйте для локальной сборки вместо pull образа:
|
||||||
# app-build:
|
# app-build:
|
||||||
@@ -27,3 +35,8 @@ services:
|
|||||||
# RUNTIME: "express"
|
# RUNTIME: "express"
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./data:/app/data
|
# - ./data:/app/data
|
||||||
|
# logging:
|
||||||
|
# driver: json-file
|
||||||
|
# options:
|
||||||
|
# max-size: "10m"
|
||||||
|
# max-file: "3"
|
||||||
|
|||||||
@@ -196,14 +196,18 @@ docker run --rm -v vps_tracker_traefik_letsencrypt:/data -v "$PWD:/backup" alpin
|
|||||||
| ACME / браузер ругается на сертификат | `CF_DNS_API_TOKEN`, Zone:DNS:Edit, DNS only (не orange cloud), логи Traefik |
|
| ACME / браузер ругается на сертификат | `CF_DNS_API_TOKEN`, Zone:DNS:Edit, DNS only (не orange cloud), логи Traefik |
|
||||||
| `invalid credentials` | Не Global Key; зона в scope токена; нет пробелов/кавычек в `.env` |
|
| `invalid credentials` | Не Global Key; зона в scope токена; нет пробелов/кавычек в `.env` |
|
||||||
| Gateway Timeout / 404 | `docker compose ps`; labels; сеть `vps-tracker`; `VPS_DOMAIN` |
|
| Gateway Timeout / 404 | `docker compose ps`; labels; сеть `vps-tracker`; `VPS_DOMAIN` |
|
||||||
|
| `/health` OK, `/api/data` 500 | Старые образы без bootstrap схемы — `docker compose pull && up -d`; смотрите `docker compose logs app` (`no such table`) |
|
||||||
| `/health` OK, UI пустой | образ / кэш CDN; смотрите `docker compose logs app` |
|
| `/health` OK, UI пустой | образ / кэш CDN; смотрите `docker compose logs app` |
|
||||||
| SSO 401 / issuer mismatch | `AUTH_JWT_SECRET` = `JWT_SECRET` портала; `AUTH_ISSUER` |
|
| SSO 401 / issuer mismatch | `AUTH_JWT_SECRET` = `JWT_SECRET` портала; `AUTH_ISSUER` |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose logs traefik 2>&1 | grep -iE 'acme|certificate|cloudflare|error'
|
docker compose logs traefik 2>&1 | grep -iE 'acme|certificate|cloudflare|error'
|
||||||
docker compose logs app --tail=80
|
docker compose logs app --tail=80
|
||||||
|
docker compose logs -f app # follow
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Логи контейнеров: драйвер `json-file`, ротация `max-size=10m`, `max-file=3`. Уровень приложения — `LOG_LEVEL` (по умолчанию `info`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Простой compose без Traefik
|
## Простой compose без Traefik
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { mkdtempSync, rmSync } from 'node:fs'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { afterEach, describe, expect, it } from 'vitest'
|
||||||
|
import { closeDb, getDb, getSqlite } from './index.js'
|
||||||
|
import { resetRuntimeMigrate } from './runtime-migrate.js'
|
||||||
|
import { getSnapshot } from './repositories/snapshot.js'
|
||||||
|
import { runWithSpace, MAIN_SPACE_ID } from './space-context.js'
|
||||||
|
|
||||||
|
describe('ensureRuntimeSchema (empty DB)', () => {
|
||||||
|
let dir: string
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
closeDb()
|
||||||
|
resetRuntimeMigrate()
|
||||||
|
delete process.env.DB_PATH
|
||||||
|
if (dir) rmSync(dir, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bootstraps core tables and serves empty snapshot', () => {
|
||||||
|
dir = mkdtempSync(join(tmpdir(), 'vps-tracker-db-'))
|
||||||
|
process.env.DB_PATH = join(dir, 'fresh.db')
|
||||||
|
resetRuntimeMigrate()
|
||||||
|
closeDb()
|
||||||
|
|
||||||
|
getDb()
|
||||||
|
const tables = getSqlite()
|
||||||
|
.prepare(
|
||||||
|
`SELECT name FROM sqlite_master WHERE type='table' AND name IN ('vps','settings','providers','spaces')`,
|
||||||
|
)
|
||||||
|
.all() as { name: string }[]
|
||||||
|
expect(tables.map((t) => t.name).sort()).toEqual([
|
||||||
|
'providers',
|
||||||
|
'settings',
|
||||||
|
'spaces',
|
||||||
|
'vps',
|
||||||
|
])
|
||||||
|
|
||||||
|
const snap = runWithSpace(MAIN_SPACE_ID, () => getSnapshot())
|
||||||
|
expect(snap.spaceId).toBe(MAIN_SPACE_ID)
|
||||||
|
expect(snap.vps).toEqual([])
|
||||||
|
expect(snap.settings.length).toBeGreaterThanOrEqual(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -2,41 +2,11 @@ import type Database from 'better-sqlite3'
|
|||||||
|
|
||||||
const MAIN_SPACE_ID = 'space-main'
|
const MAIN_SPACE_ID = 'space-main'
|
||||||
|
|
||||||
const COLUMN_MIGRATIONS: string[] = [
|
/**
|
||||||
`ALTER TABLE vps ADD COLUMN customData TEXT`,
|
* Full bootstrap for empty DB (clean Docker install).
|
||||||
`ALTER TABLE vps ADD COLUMN last_health_status TEXT`,
|
* CREATE IF NOT EXISTS — safe on existing databases; column upgrades stay in COLUMN_MIGRATIONS.
|
||||||
`ALTER TABLE vps ADD COLUMN last_health_checked_at TEXT`,
|
*/
|
||||||
`ALTER TABLE settings ADD COLUMN notifyVpsDownEnabled INTEGER`,
|
const CORE_TABLE_MIGRATIONS: string[] = [
|
||||||
`ALTER TABLE settings ADD COLUMN webhookUrl TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN webhookEnabled INTEGER`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN notifyIntervalMinutes INTEGER`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN uptimeCheckIntervalMinutes INTEGER`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN appSwitcherJson TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN integrationToken TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN integrationEnabled INTEGER`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN integrationLastSyncAt TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN cfdmApiUrl TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN showQuickActions INTEGER`,
|
|
||||||
`ALTER TABLE vps_domains ADD COLUMN targetIps TEXT`,
|
|
||||||
`ALTER TABLE providers ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE provider_accounts ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE server_projects ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE vps ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE payments ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE balance_ledger ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE settings ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE vps_domains ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE notification_log ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE notification_state ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE vps_health_checks ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE audit_log ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE audit_log ADD COLUMN actorUserId TEXT`,
|
|
||||||
`ALTER TABLE sync_log ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE active_tariffs ADD COLUMN spaceId TEXT`,
|
|
||||||
`ALTER TABLE tariff_sync_options ADD COLUMN spaceId TEXT`,
|
|
||||||
]
|
|
||||||
|
|
||||||
const TABLE_MIGRATIONS: string[] = [
|
|
||||||
`CREATE TABLE IF NOT EXISTS spaces (
|
`CREATE TABLE IF NOT EXISTS spaces (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
@@ -52,8 +22,195 @@ const TABLE_MIGRATIONS: string[] = [
|
|||||||
createdAt TEXT NOT NULL,
|
createdAt TEXT NOT NULL,
|
||||||
UNIQUE(spaceId, userId)
|
UNIQUE(spaceId, userId)
|
||||||
)`,
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS providers (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
website TEXT,
|
||||||
|
contact TEXT,
|
||||||
|
baseCurrency TEXT,
|
||||||
|
usdRate TEXT,
|
||||||
|
eurRate TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
apiType TEXT,
|
||||||
|
apiBaseUrl TEXT
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS provider_accounts (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
providerId TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
panelUrl TEXT,
|
||||||
|
currency TEXT,
|
||||||
|
billingMode TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
apiType TEXT,
|
||||||
|
apiBaseUrl TEXT,
|
||||||
|
apiCredentials TEXT,
|
||||||
|
balance_api REAL,
|
||||||
|
balance_currency TEXT,
|
||||||
|
balance_updated_at TEXT,
|
||||||
|
enoughmoneyto TEXT,
|
||||||
|
balance_alert_below REAL,
|
||||||
|
FOREIGN KEY (providerId) REFERENCES providers(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS server_projects (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
color TEXT,
|
||||||
|
sortOrder INTEGER DEFAULT 0,
|
||||||
|
notes TEXT,
|
||||||
|
createdAt TEXT
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS vps (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
ip TEXT,
|
||||||
|
ipv6 TEXT,
|
||||||
|
additionalIps TEXT,
|
||||||
|
dns TEXT,
|
||||||
|
providerId TEXT,
|
||||||
|
providerAccountId TEXT,
|
||||||
|
country TEXT,
|
||||||
|
city TEXT,
|
||||||
|
datacenter TEXT,
|
||||||
|
os TEXT,
|
||||||
|
vcpu INTEGER,
|
||||||
|
ramGb REAL,
|
||||||
|
diskGb INTEGER,
|
||||||
|
diskType TEXT,
|
||||||
|
virtualization TEXT,
|
||||||
|
bandwidthTb INTEGER,
|
||||||
|
sshPort INTEGER,
|
||||||
|
rootUser TEXT,
|
||||||
|
purpose TEXT,
|
||||||
|
environment TEXT,
|
||||||
|
project TEXT,
|
||||||
|
projectId TEXT,
|
||||||
|
monitoringEnabled INTEGER,
|
||||||
|
backupEnabled INTEGER,
|
||||||
|
status TEXT,
|
||||||
|
tariffType TEXT,
|
||||||
|
currency TEXT,
|
||||||
|
dailyRate REAL,
|
||||||
|
monthlyRate REAL,
|
||||||
|
createdAt TEXT,
|
||||||
|
paidUntil TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
userOverrides TEXT,
|
||||||
|
customData TEXT,
|
||||||
|
last_health_status TEXT,
|
||||||
|
last_health_checked_at TEXT,
|
||||||
|
FOREIGN KEY (providerId) REFERENCES providers(id),
|
||||||
|
FOREIGN KEY (providerAccountId) REFERENCES provider_accounts(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS payments (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
amount REAL NOT NULL,
|
||||||
|
currency TEXT,
|
||||||
|
providerAccountId TEXT,
|
||||||
|
vpsId TEXT,
|
||||||
|
note TEXT,
|
||||||
|
FOREIGN KEY (providerAccountId) REFERENCES provider_accounts(id),
|
||||||
|
FOREIGN KEY (vpsId) REFERENCES vps(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS balance_ledger (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
amount REAL NOT NULL,
|
||||||
|
currency TEXT,
|
||||||
|
direction TEXT,
|
||||||
|
providerAccountId TEXT,
|
||||||
|
vpsId TEXT,
|
||||||
|
note TEXT,
|
||||||
|
FOREIGN KEY (providerAccountId) REFERENCES provider_accounts(id),
|
||||||
|
FOREIGN KEY (vpsId) REFERENCES vps(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
baseCurrency TEXT,
|
||||||
|
ratesUrl TEXT,
|
||||||
|
autoConvert INTEGER,
|
||||||
|
ratesUpdatedAt TEXT,
|
||||||
|
syncEnabled INTEGER,
|
||||||
|
syncIntervalMinutes INTEGER,
|
||||||
|
syncTariffsIntervalMinutes INTEGER,
|
||||||
|
customFields TEXT,
|
||||||
|
telegramBotToken TEXT,
|
||||||
|
telegramChatId TEXT,
|
||||||
|
notifyPaymentExpiryEnabled INTEGER,
|
||||||
|
notifyNewTariffsEnabled INTEGER,
|
||||||
|
telegramMessageThreadId TEXT,
|
||||||
|
notifyLowBalanceEnabled INTEGER,
|
||||||
|
notifySyncDigestEnabled INTEGER,
|
||||||
|
notifyVpsDownEnabled INTEGER,
|
||||||
|
webhookUrl TEXT,
|
||||||
|
webhookEnabled INTEGER,
|
||||||
|
notifyIntervalMinutes INTEGER,
|
||||||
|
uptimeCheckIntervalMinutes INTEGER,
|
||||||
|
appSwitcherJson TEXT,
|
||||||
|
integrationToken TEXT,
|
||||||
|
integrationEnabled INTEGER,
|
||||||
|
integrationLastSyncAt TEXT,
|
||||||
|
cfdmApiUrl TEXT,
|
||||||
|
showQuickActions INTEGER
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS sync_log (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
accountId TEXT NOT NULL,
|
||||||
|
startedAt TEXT NOT NULL,
|
||||||
|
finishedAt TEXT,
|
||||||
|
status TEXT,
|
||||||
|
vpsCount INTEGER,
|
||||||
|
paymentsCount INTEGER,
|
||||||
|
error TEXT,
|
||||||
|
summary TEXT,
|
||||||
|
FOREIGN KEY (accountId) REFERENCES provider_accounts(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS active_tariffs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
providerAccountId TEXT NOT NULL,
|
||||||
|
providerId TEXT NOT NULL,
|
||||||
|
externalId TEXT NOT NULL,
|
||||||
|
datacenterKey TEXT,
|
||||||
|
datacenterName TEXT,
|
||||||
|
name TEXT,
|
||||||
|
desc TEXT,
|
||||||
|
vcpu INTEGER,
|
||||||
|
ramGb REAL,
|
||||||
|
diskGb INTEGER,
|
||||||
|
diskType TEXT,
|
||||||
|
virtualization TEXT,
|
||||||
|
channel TEXT,
|
||||||
|
location TEXT,
|
||||||
|
country TEXT,
|
||||||
|
cpuModel TEXT,
|
||||||
|
orderAvailable INTEGER,
|
||||||
|
price TEXT,
|
||||||
|
syncedAt TEXT,
|
||||||
|
FOREIGN KEY (providerAccountId) REFERENCES provider_accounts(id),
|
||||||
|
FOREIGN KEY (providerId) REFERENCES providers(id)
|
||||||
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS tariff_sync_options (
|
||||||
|
providerAccountId TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
|
datacenters TEXT,
|
||||||
|
periods TEXT,
|
||||||
|
syncedAt TEXT,
|
||||||
|
FOREIGN KEY (providerAccountId) REFERENCES provider_accounts(id)
|
||||||
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS vps_health_checks (
|
`CREATE TABLE IF NOT EXISTS vps_health_checks (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
vpsId TEXT NOT NULL REFERENCES vps(id),
|
vpsId TEXT NOT NULL REFERENCES vps(id),
|
||||||
checkedAt TEXT NOT NULL,
|
checkedAt TEXT NOT NULL,
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
@@ -62,14 +219,17 @@ const TABLE_MIGRATIONS: string[] = [
|
|||||||
)`,
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS audit_log (
|
`CREATE TABLE IF NOT EXISTS audit_log (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
entity TEXT NOT NULL,
|
entity TEXT NOT NULL,
|
||||||
entityId TEXT NOT NULL,
|
entityId TEXT NOT NULL,
|
||||||
action TEXT NOT NULL,
|
action TEXT NOT NULL,
|
||||||
diff TEXT,
|
diff TEXT,
|
||||||
|
actorUserId TEXT,
|
||||||
createdAt TEXT NOT NULL
|
createdAt TEXT NOT NULL
|
||||||
)`,
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS notification_log (
|
`CREATE TABLE IF NOT EXISTS notification_log (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
event TEXT NOT NULL,
|
event TEXT NOT NULL,
|
||||||
channel TEXT NOT NULL,
|
channel TEXT NOT NULL,
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
@@ -80,20 +240,14 @@ const TABLE_MIGRATIONS: string[] = [
|
|||||||
)`,
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS notification_state (
|
`CREATE TABLE IF NOT EXISTS notification_state (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
lastFingerprint TEXT,
|
lastFingerprint TEXT,
|
||||||
lastSentAt TEXT,
|
lastSentAt TEXT,
|
||||||
lastStatus TEXT
|
lastStatus TEXT
|
||||||
)`,
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS server_projects (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
color TEXT,
|
|
||||||
sortOrder INTEGER DEFAULT 0,
|
|
||||||
notes TEXT,
|
|
||||||
createdAt TEXT
|
|
||||||
)`,
|
|
||||||
`CREATE TABLE IF NOT EXISTS vps_domains (
|
`CREATE TABLE IF NOT EXISTS vps_domains (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
spaceId TEXT NOT NULL DEFAULT 'space-main',
|
||||||
vpsId TEXT REFERENCES vps(id) ON DELETE SET NULL,
|
vpsId TEXT REFERENCES vps(id) ON DELETE SET NULL,
|
||||||
fqdn TEXT NOT NULL,
|
fqdn TEXT NOT NULL,
|
||||||
zoneName TEXT NOT NULL,
|
zoneName TEXT NOT NULL,
|
||||||
@@ -128,6 +282,54 @@ const TABLE_MIGRATIONS: string[] = [
|
|||||||
)`,
|
)`,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/** Additive columns for DBs created before spaces / notifications / etc. */
|
||||||
|
const COLUMN_MIGRATIONS: string[] = [
|
||||||
|
`ALTER TABLE vps ADD COLUMN customData TEXT`,
|
||||||
|
`ALTER TABLE vps ADD COLUMN last_health_status TEXT`,
|
||||||
|
`ALTER TABLE vps ADD COLUMN last_health_checked_at TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifyVpsDownEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN webhookUrl TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN webhookEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifyIntervalMinutes INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN uptimeCheckIntervalMinutes INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN appSwitcherJson TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN integrationToken TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN integrationEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN integrationLastSyncAt TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN cfdmApiUrl TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN showQuickActions INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN telegramBotToken TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN telegramChatId TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifyPaymentExpiryEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifyNewTariffsEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN telegramMessageThreadId TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifyLowBalanceEnabled INTEGER`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN notifySyncDigestEnabled INTEGER`,
|
||||||
|
`ALTER TABLE vps_domains ADD COLUMN targetIps TEXT`,
|
||||||
|
`ALTER TABLE providers ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN balance_api REAL`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN balance_currency TEXT`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN balance_updated_at TEXT`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN enoughmoneyto TEXT`,
|
||||||
|
`ALTER TABLE provider_accounts ADD COLUMN balance_alert_below REAL`,
|
||||||
|
`ALTER TABLE server_projects ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE vps ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE payments ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE balance_ledger ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE settings ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE vps_domains ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE notification_log ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE notification_state ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE vps_health_checks ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE audit_log ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE audit_log ADD COLUMN actorUserId TEXT`,
|
||||||
|
`ALTER TABLE sync_log ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE sync_log ADD COLUMN summary TEXT`,
|
||||||
|
`ALTER TABLE active_tariffs ADD COLUMN spaceId TEXT`,
|
||||||
|
`ALTER TABLE tariff_sync_options ADD COLUMN spaceId TEXT`,
|
||||||
|
]
|
||||||
|
|
||||||
const SPACE_BACKFILL_TABLES = [
|
const SPACE_BACKFILL_TABLES = [
|
||||||
'providers',
|
'providers',
|
||||||
'provider_accounts',
|
'provider_accounts',
|
||||||
@@ -165,6 +367,13 @@ function ensureMainSpace(sqlite: Database.Database): void {
|
|||||||
)
|
)
|
||||||
.run(MAIN_SPACE_ID, owner, now)
|
.run(MAIN_SPACE_ID, owner, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sqlite
|
||||||
|
.prepare(
|
||||||
|
`INSERT OR IGNORE INTO settings (id, spaceId, baseCurrency, autoConvert, syncEnabled)
|
||||||
|
VALUES ('settings-main', ?, 'USD', 1, 0)`,
|
||||||
|
)
|
||||||
|
.run(MAIN_SPACE_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
function backfillSpaceIds(sqlite: Database.Database): void {
|
function backfillSpaceIds(sqlite: Database.Database): void {
|
||||||
@@ -189,7 +398,7 @@ export function resetRuntimeMigrate(): void {
|
|||||||
|
|
||||||
export function ensureRuntimeSchema(sqlite: Database.Database): void {
|
export function ensureRuntimeSchema(sqlite: Database.Database): void {
|
||||||
if (migrated) return
|
if (migrated) return
|
||||||
for (const sql of TABLE_MIGRATIONS) {
|
for (const sql of CORE_TABLE_MIGRATIONS) {
|
||||||
sqlite.exec(sql)
|
sqlite.exec(sql)
|
||||||
}
|
}
|
||||||
for (const sql of COLUMN_MIGRATIONS) {
|
for (const sql of COLUMN_MIGRATIONS) {
|
||||||
|
|||||||
Reference in New Issue
Block a user