diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts index 7a2b782..35c9a10 100644 --- a/src/app/api/config/route.ts +++ b/src/app/api/config/route.ts @@ -1,11 +1,12 @@ import { NextRequest, NextResponse } from 'next/server' 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) { const err = await requireAuth(req) if (err) return err - return NextResponse.json(readConfig()) + const config = await readConfig() + return NextResponse.json(config) } export async function POST(req: NextRequest) { @@ -13,16 +14,12 @@ export async function POST(req: NextRequest) { if (err) return err const { type, item } = await req.json() - const config = readConfig() + + if (!['site', 'database', 'service'].includes(type)) { + return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 }) + } - const newItem = { ...item, id: generateId() } - - if (type === 'site') config.sites.push(newItem) - 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) + const newItem = await addConfigItem(type as any, item) return NextResponse.json(newItem) } @@ -31,13 +28,12 @@ export async function DELETE(req: NextRequest) { if (err) return err const { type, id } = await req.json() - const config = readConfig() + + if (!['site', 'database', 'service'].includes(type)) { + return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 }) + } - if (type === 'site') config.sites = config.sites.filter(s => s.id !== id) - else if (type === 'database') config.databases = config.databases.filter(d => d.id !== id) - 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 }) } @@ -46,21 +42,11 @@ export async function PUT(req: NextRequest) { if (err) return err const { type, id, item } = await req.json() - const config = readConfig() - - if (type === 'site') { - 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 { + + if (!['site', 'database', 'service'].includes(type)) { return NextResponse.json({ error: 'Geçersiz type' }, { status: 400 }) } - writeConfig(config) + await updateConfigItem(type as any, id, item) return NextResponse.json({ ok: true }) } diff --git a/src/app/api/db/route.ts b/src/app/api/db/route.ts index 641ec39..16c6236 100644 --- a/src/app/api/db/route.ts +++ b/src/app/api/db/route.ts @@ -12,7 +12,7 @@ export async function GET(req: NextRequest) { 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) 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 config = readConfig() + const config = await readConfig() const db = config.databases.find(d => d.id === dbId) if (!db) return NextResponse.json({ error: 'DB bulunamadı' }, { status: 404 }) diff --git a/src/app/api/ping/route.ts b/src/app/api/ping/route.ts index 01271fe..f7d483c 100644 --- a/src/app/api/ping/route.ts +++ b/src/app/api/ping/route.ts @@ -26,7 +26,7 @@ export async function POST(req: NextRequest) { if (err) return err const { siteId } = await req.json() - const config = readConfig() + const config = await readConfig() const site = config.sites.find(s => s.id === siteId) 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) 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 })))) return NextResponse.json(results) } diff --git a/src/instrumentation.ts b/src/instrumentation.ts index 689bcaa..3ff9911 100644 --- a/src/instrumentation.ts +++ b/src/instrumentation.ts @@ -14,7 +14,7 @@ export async function register() { // Döngü her dakika çalışıp süresi gelenleri kontrol edecek setInterval(async () => { try { - const config = readConfig() + const config = await readConfig() if (!config.sites || config.sites.length === 0) return const now = Date.now() diff --git a/src/lib/appDb.ts b/src/lib/appDb.ts index e18caff..b53a4d9 100644 --- a/src/lib/appDb.ts +++ b/src/lib/appDb.ts @@ -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_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 = { id: number site_id: string diff --git a/src/lib/config.ts b/src/lib/config.ts index 8150064..6a01137 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,5 +1,6 @@ import fs from 'fs' import path from 'path' +import pool from './appDb' const CONFIG_PATH = path.join(process.cwd(), 'config.json') @@ -36,18 +37,75 @@ export interface Config { 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 { return `${Date.now()}-${Math.random().toString(36).slice(2, 7)}` } + +// Migration ve okuma bir arada +export async function readConfig(): Promise { + // 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 { + 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 { + 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 { + 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]) +}