- Introduced a new `server_projects` table to manage project details. - Updated the `vps` table to include a foreign key reference to `server_projects`. - Enhanced the API to support project-related data retrieval and manipulation. - Implemented project filtering in the Reports and VPS pages. - Added project selection functionality in the UI for better resource management. - Improved data seeding and migration scripts to accommodate new project structure.
235 lines
6.5 KiB
JavaScript
235 lines
6.5 KiB
JavaScript
/**
|
|
* Database migrations — add columns to existing tables
|
|
*/
|
|
|
|
import { randomUUID } from 'node:crypto'
|
|
|
|
export const MIGRATIONS = [
|
|
{
|
|
name: 'provider_accounts_api',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN apiType TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN apiBaseUrl TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN apiCredentials TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'settings_sync',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN syncEnabled INTEGER')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN syncIntervalMinutes INTEGER')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'vps_paidUntil',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE vps ADD COLUMN paidUntil TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'provider_accounts_balance_api',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN balance_api REAL')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN balance_currency TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN balance_updated_at TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE provider_accounts ADD COLUMN enoughmoneyto TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'vps_userOverrides',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE vps ADD COLUMN userOverrides TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'active_tariffs_location_cpu',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE active_tariffs ADD COLUMN location TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE active_tariffs ADD COLUMN cpuModel TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'settings_customFields',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN customFields TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'settings_syncTariffsInterval',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN syncTariffsIntervalMinutes INTEGER')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'active_tariffs_country_datacenter',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE active_tariffs ADD COLUMN country TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE active_tariffs ADD COLUMN datacenterKey TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE active_tariffs ADD COLUMN datacenterName TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'settings_telegram',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN telegramBotToken TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN telegramChatId TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN notifyPaymentExpiryEnabled INTEGER')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN notifyNewTariffsEnabled INTEGER')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'settings_telegram_thread',
|
|
run(db) {
|
|
try {
|
|
db.exec('ALTER TABLE settings ADD COLUMN telegramMessageThreadId TEXT')
|
|
} catch (e) {
|
|
if (!e.message?.includes('duplicate column')) throw e
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'server_projects',
|
|
run(db) {
|
|
db.run(
|
|
`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
|
|
)`,
|
|
)
|
|
try {
|
|
db.run('ALTER TABLE vps ADD COLUMN projectId TEXT')
|
|
} catch (e) {
|
|
if (!String(e.message || e).includes('duplicate column')) throw e
|
|
}
|
|
|
|
const distinctStmt = db.prepare(
|
|
`SELECT DISTINCT trim(project) AS n FROM vps WHERE length(trim(COALESCE(project, ''))) > 0`,
|
|
)
|
|
const seenLower = new Set()
|
|
const findStmt = db.prepare(
|
|
'SELECT id FROM server_projects WHERE LOWER(name) = LOWER(?) LIMIT 1',
|
|
)
|
|
while (distinctStmt.step()) {
|
|
const row = distinctStmt.getAsObject()
|
|
const t = String(row.n ?? '').trim()
|
|
if (!t) continue
|
|
const lk = t.toLowerCase()
|
|
if (seenLower.has(lk)) continue
|
|
seenLower.add(lk)
|
|
|
|
findStmt.bind([t])
|
|
const exists = Boolean(findStmt.step())
|
|
findStmt.reset()
|
|
if (!exists) {
|
|
const id = `proj-${randomUUID()}`
|
|
const now = new Date().toISOString()
|
|
db.run(
|
|
`INSERT INTO server_projects (id, name, color, sortOrder, notes, createdAt) VALUES (?, ?, NULL, 0, NULL, ?)`,
|
|
[id, t, now],
|
|
)
|
|
}
|
|
}
|
|
distinctStmt.free()
|
|
findStmt.free()
|
|
|
|
db.run(`UPDATE vps SET projectId = (
|
|
SELECT sp.id FROM server_projects sp
|
|
WHERE LOWER(sp.name) = LOWER(trim(COALESCE(vps.project, '')))
|
|
LIMIT 1
|
|
) WHERE length(trim(COALESCE(vps.project, ''))) > 0`)
|
|
},
|
|
},
|
|
]
|