Migrate config to PostgreSQL

This commit is contained in:
mstfyldz
2026-05-27 16:53:06 +03:00
parent 10f7ee95dd
commit a5951a97e9
6 changed files with 117 additions and 48 deletions

View File

@@ -1,11 +1,12 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { requireAuth } from '@/lib/auth' import { requireAuth } from '@/lib/auth'
import { readConfig, writeConfig, generateId } from '@/lib/config' import { readConfig, addConfigItem, updateConfigItem, deleteConfigItem } from '@/lib/config'
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
const err = await requireAuth(req) const err = await requireAuth(req)
if (err) return err if (err) return err
return NextResponse.json(readConfig()) const config = await readConfig()
return NextResponse.json(config)
} }
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
@@ -13,16 +14,12 @@ export async function POST(req: NextRequest) {
if (err) return err if (err) return err
const { type, item } = await req.json() const { type, item } = await req.json()
const config = readConfig()
const newItem = { ...item, id: generateId() } if (!['site', 'database', 'service'].includes(type)) {
return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 })
}
if (type === 'site') config.sites.push(newItem) const newItem = await addConfigItem(type as any, item)
else if (type === 'database') config.databases.push(newItem)
else if (type === 'service') config.services.push(newItem)
else return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 })
writeConfig(config)
return NextResponse.json(newItem) return NextResponse.json(newItem)
} }
@@ -31,13 +28,12 @@ export async function DELETE(req: NextRequest) {
if (err) return err if (err) return err
const { type, id } = await req.json() const { type, id } = await req.json()
const config = readConfig()
if (type === 'site') config.sites = config.sites.filter(s => s.id !== id) if (!['site', 'database', 'service'].includes(type)) {
else if (type === 'database') config.databases = config.databases.filter(d => d.id !== id) return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 })
else if (type === 'service') config.services = config.services.filter(s => s.id !== id) }
writeConfig(config) await deleteConfigItem(type as any, id)
return NextResponse.json({ ok: true }) return NextResponse.json({ ok: true })
} }
@@ -46,21 +42,11 @@ export async function PUT(req: NextRequest) {
if (err) return err if (err) return err
const { type, id, item } = await req.json() const { type, id, item } = await req.json()
const config = readConfig()
if (type === 'site') { if (!['site', 'database', 'service'].includes(type)) {
const idx = config.sites.findIndex(s => s.id === id)
if (idx !== -1) config.sites[idx] = { ...config.sites[idx], ...item }
} else if (type === 'database') {
const idx = config.databases.findIndex(d => d.id === id)
if (idx !== -1) config.databases[idx] = { ...config.databases[idx], ...item }
} else if (type === 'service') {
const idx = config.services.findIndex(s => s.id === id)
if (idx !== -1) config.services[idx] = { ...config.services[idx], ...item }
} else {
return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 }) return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 })
} }
writeConfig(config) await updateConfigItem(type as any, id, item)
return NextResponse.json({ ok: true }) return NextResponse.json({ ok: true })
} }

View File

@@ -12,7 +12,7 @@ export async function GET(req: NextRequest) {
if (!dbId) return NextResponse.json({ error: 'dbId gerekli' }, { status: 400 }) if (!dbId) return NextResponse.json({ error: 'dbId gerekli' }, { status: 400 })
const config = readConfig() const config = await readConfig()
const db = config.databases.find(d => d.id === dbId) const db = config.databases.find(d => d.id === dbId)
if (!db) return NextResponse.json({ error: 'DB bulunamadı' }, { status: 404 }) if (!db) return NextResponse.json({ error: 'DB bulunamadı' }, { status: 404 })
@@ -37,7 +37,7 @@ export async function POST(req: NextRequest) {
} }
const { dbId, sql } = body const { dbId, sql } = body
const config = readConfig() const config = await readConfig()
const db = config.databases.find(d => d.id === dbId) const db = config.databases.find(d => d.id === dbId)
if (!db) return NextResponse.json({ error: 'DB bulunamadı' }, { status: 404 }) if (!db) return NextResponse.json({ error: 'DB bulunamadı' }, { status: 404 })

View File

@@ -26,7 +26,7 @@ export async function POST(req: NextRequest) {
if (err) return err if (err) return err
const { siteId } = await req.json() const { siteId } = await req.json()
const config = readConfig() const config = await readConfig()
const site = config.sites.find(s => s.id === siteId) const site = config.sites.find(s => s.id === siteId)
if (!site) return NextResponse.json({ error: 'Site bulunamadı' }, { status: 404 }) if (!site) return NextResponse.json({ error: 'Site bulunamadı' }, { status: 404 })
@@ -40,7 +40,7 @@ export async function PUT(req: NextRequest) {
if (secret !== process.env.PANEL_SECRET) if (secret !== process.env.PANEL_SECRET)
return NextResponse.json({ error: 'Yetkisiz' }, { status: 401 }) return NextResponse.json({ error: 'Yetkisiz' }, { status: 401 })
const config = readConfig() const config = await readConfig()
const results = await Promise.all(config.sites.map(site => pingAndLog(site).then(r => ({ id: site.id, ...r })))) const results = await Promise.all(config.sites.map(site => pingAndLog(site).then(r => ({ id: site.id, ...r }))))
return NextResponse.json(results) return NextResponse.json(results)
} }

View File

@@ -14,7 +14,7 @@ export async function register() {
// Döngü her dakika çalışıp süresi gelenleri kontrol edecek // Döngü her dakika çalışıp süresi gelenleri kontrol edecek
setInterval(async () => { setInterval(async () => {
try { try {
const config = readConfig() const config = await readConfig()
if (!config.sites || config.sites.length === 0) return if (!config.sites || config.sites.length === 0) return
const now = Date.now() const now = Date.now()

View File

@@ -33,8 +33,33 @@ pool.query(`
); );
CREATE INDEX IF NOT EXISTS idx_pv_domain ON pageviews(domain, ts DESC); CREATE INDEX IF NOT EXISTS idx_pv_domain ON pageviews(domain, ts DESC);
CREATE INDEX IF NOT EXISTS idx_pv_ts ON pageviews(ts DESC); CREATE INDEX IF NOT EXISTS idx_pv_ts ON pageviews(ts DESC);
`).catch(console.error) CREATE TABLE IF NOT EXISTS config_sites (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
interval_min INTEGER
);
CREATE TABLE IF NOT EXISTS config_databases (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
host TEXT NOT NULL,
port INTEGER NOT NULL,
database TEXT NOT NULL,
username TEXT NOT NULL,
password TEXT NOT NULL,
ssl BOOLEAN NOT NULL DEFAULT FALSE,
color TEXT
);
CREATE TABLE IF NOT EXISTS config_services (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
icon TEXT,
description TEXT
);
`).catch(console.error)
export type PingLog = { export type PingLog = {
id: number id: number
site_id: string site_id: string

View File

@@ -1,5 +1,6 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import pool from './appDb'
const CONFIG_PATH = path.join(process.cwd(), 'config.json') const CONFIG_PATH = path.join(process.cwd(), 'config.json')
@@ -36,18 +37,75 @@ export interface Config {
services: Service[] services: Service[]
} }
export function readConfig(): Config {
if (!fs.existsSync(CONFIG_PATH)) {
return { sites: [], databases: [], services: [] }
}
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')
return JSON.parse(raw)
}
export function writeConfig(config: Config): void {
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8')
}
export function generateId(): string { export function generateId(): string {
return `${Date.now()}-${Math.random().toString(36).slice(2, 7)}` return `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`
} }
// Migration ve okuma bir arada
export async function readConfig(): Promise<Config> {
// Migration kontrolü
if (fs.existsSync(CONFIG_PATH)) {
try {
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')
const oldConfig = JSON.parse(raw) as Config
// Veritabanına aktar
for (const site of oldConfig.sites || []) {
await pool.query(`INSERT INTO config_sites (id, name, url, interval_min) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING`, [site.id, site.name, site.url, site.interval_min || 5])
}
for (const db of oldConfig.databases || []) {
await pool.query(`INSERT INTO config_databases (id, name, host, port, database, username, password, ssl, color) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT DO NOTHING`, [db.id, db.name, db.host, db.port, db.database, db.username, db.password, db.ssl || false, db.color])
}
for (const svc of oldConfig.services || []) {
await pool.query(`INSERT INTO config_services (id, name, url, icon, description) VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING`, [svc.id, svc.name, svc.url, svc.icon, svc.description])
}
// Dosyanın uzantısını değiştirerek migration'ın bittiğini işaretle
fs.renameSync(CONFIG_PATH, CONFIG_PATH + '.bak')
console.log('[Migration] config.json verileri PostgreSQL tablolalarına aktarıldı ve dosya yedeklendi.')
} catch (e) {
console.error('[Migration] config.json veritabanına aktarılırken hata oluştu:', e)
}
}
// DB'den oku
const [sitesRes, dbsRes, svcsRes] = await Promise.all([
pool.query(`SELECT * FROM config_sites`),
pool.query(`SELECT * FROM config_databases`),
pool.query(`SELECT * FROM config_services`),
])
return {
sites: sitesRes.rows as Site[],
databases: dbsRes.rows as Database[],
services: svcsRes.rows as Service[]
}
}
export async function addConfigItem(type: 'site' | 'database' | 'service', item: any): Promise<any> {
const newItem = { ...item, id: generateId() }
if (type === 'site') {
await pool.query(`INSERT INTO config_sites (id, name, url, interval_min) VALUES ($1, $2, $3, $4)`, [newItem.id, newItem.name, newItem.url, newItem.interval_min || 5])
} else if (type === 'database') {
await pool.query(`INSERT INTO config_databases (id, name, host, port, database, username, password, ssl, color) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [newItem.id, newItem.name, newItem.host, newItem.port, newItem.database, newItem.username, newItem.password, newItem.ssl || false, newItem.color])
} else if (type === 'service') {
await pool.query(`INSERT INTO config_services (id, name, url, icon, description) VALUES ($1, $2, $3, $4, $5)`, [newItem.id, newItem.name, newItem.url, newItem.icon, newItem.description])
}
return newItem
}
export async function updateConfigItem(type: 'site' | 'database' | 'service', id: string, item: any): Promise<void> {
if (type === 'site') {
await pool.query(`UPDATE config_sites SET name=$1, url=$2, interval_min=$3 WHERE id=$4`, [item.name, item.url, item.interval_min || 5, id])
} else if (type === 'database') {
await pool.query(`UPDATE config_databases SET name=$1, host=$2, port=$3, database=$4, username=$5, password=$6, ssl=$7, color=$8 WHERE id=$9`, [item.name, item.host, item.port, item.database, item.username, item.password, item.ssl || false, item.color, id])
} else if (type === 'service') {
await pool.query(`UPDATE config_services SET name=$1, url=$2, icon=$3, description=$4 WHERE id=$5`, [item.name, item.url, item.icon, item.description, id])
}
}
export async function deleteConfigItem(type: 'site' | 'database' | 'service', id: string): Promise<void> {
if (type === 'site') await pool.query(`DELETE FROM config_sites WHERE id=$1`, [id])
else if (type === 'database') await pool.query(`DELETE FROM config_databases WHERE id=$1`, [id])
else if (type === 'service') await pool.query(`DELETE FROM config_services WHERE id=$1`, [id])
}