From 8f9b600828f290a5c84ad070c122cea15b4546d2 Mon Sep 17 00:00:00 2001 From: mstfyldz Date: Thu, 12 Mar 2026 23:53:23 +0300 Subject: [PATCH] Feature: Implemented Dynamic Admin Settings for Platform Addresses and Security Guidelines for Private Keys --- app/admin/settings/page.tsx | 280 +++++++++++++++++++------------- app/api/admin/settings/route.ts | 54 ++++++ app/api/crypto-sweep/route.ts | 16 +- lib/settings.ts | 19 +++ 4 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 app/api/admin/settings/route.ts create mode 100644 lib/settings.ts diff --git a/app/admin/settings/page.tsx b/app/admin/settings/page.tsx index c059329..212068e 100644 --- a/app/admin/settings/page.tsx +++ b/app/admin/settings/page.tsx @@ -1,138 +1,188 @@ 'use client'; -import React from 'react'; -import { - Globe, - ShieldCheck, - Bell, - Trash2, - Smartphone, - Monitor, +import React, { useState, useEffect } from 'react'; +import { + Zap, + ShieldCheck, + Save, + RefreshCcw, + Wallet, + Settings, + Server, + Key, + AlertCircle } from 'lucide-react'; -export default function SettingsPage() { +export default function AdminSettingsPage() { + const [settings, setSettings] = useState({ + sol_platform_address: '', + evm_platform_address: '' + }); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [message, setMessage] = useState({ type: '', text: '' }); + + useEffect(() => { + fetchSettings(); + }, []); + + const fetchSettings = async () => { + setIsLoading(true); + try { + const res = await fetch('/api/admin/settings'); + const data = await res.json(); + if (data.error) throw new Error(data.error); + setSettings({ + sol_platform_address: data.sol_platform_address || '', + evm_platform_address: data.evm_platform_address || '' + }); + } catch (err: any) { + setMessage({ type: 'error', text: 'Ayarlar yüklenemedi: ' + err.message }); + } finally { + setIsLoading(false); + } + }; + + const handleSave = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSaving(true); + setMessage({ type: '', text: '' }); + + try { + const res = await fetch('/api/admin/settings', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(settings) + }); + const data = await res.json(); + if (data.error) throw new Error(data.error); + setMessage({ type: 'success', text: 'Ayarlar başarıyla güncellendi.' }); + } catch (err: any) { + setMessage({ type: 'error', text: 'Kaydetme hatası: ' + err.message }); + } finally { + setIsSaving(false); + } + }; + return ( -
+
{/* Header */}
-

Ayarlar

-

Platform tercihlerinizi yönetin

+

Platform Ayarları

+

Sistem Genel Yapılandırması

+
+
+ + Global Kontrol Ünitesi
-
-
- {/* Left Column: Sections */} -
- {/* General Section */} -
-
-
- + {/* Main Form */} +
+ {/* Platform Addresses Card */} +
+
+
+ +
+
+

Merkezi Cüzdanlar (Platform)

+

Süpürülen tüm fonların toplanacağı ana adresler

+
+
+ +
+
+ +
+ + setSettings({ ...settings, sol_platform_address: e.target.value })} + placeholder="Solana Wallet Address" + className="w-full pl-14 pr-6 py-5 bg-gray-50 border-2 border-transparent focus:border-purple-500 focus:bg-white rounded-[24px] outline-none transition-all font-bold text-gray-900 font-mono text-sm" + />
-

Genel Ayarlar

-
-
- - -
-
- - -
-
- - -
-
- - +
+ +
+ + setSettings({ ...settings, evm_platform_address: e.target.value })} + placeholder="0x..." + className="w-full pl-14 pr-6 py-5 bg-gray-50 border-2 border-transparent focus:border-blue-500 focus:bg-white rounded-[24px] outline-none transition-all font-bold text-gray-900 font-mono text-sm" + />
-
+
- {/* Security Section */} -
-
-
- -
-

Güvenlik

-
- -
-
-
-

İki Faktörlü Doğrulama

-

Hesabınıza ekstra bir güvenlik katmanı ekleyin

-
-
-
-
-
- -
-
-

API Erişimi

-

Harici uygulamalar için anahtar yönetimi

-
- -
-
-
-
- - {/* Right Column: Notifications & Danger Zone */} -
-
-
-
- -
-

Bildirimler

-
-
- {['Yeni Satışlar', 'Müşteri Mesajları', 'Sistem Güncellemeleri'].map((item) => ( - - ))} -
-
- -
-
- -

Tehlikeli Bölge

-
-

- Mağaza verilerini kalıcı olarak silmek veya hesabı kapatmak için bu bölümü kullanın. Bu işlem geri alınamaz. +

+ +

+ Bu adresler, merchant'ların yaptığı tahsilatların (komisyonunuz kesildikten sonra kalan kısmın değil, platforma ait ana kesintinin veya tüm fonların) toplanacağı ana havuzunuzdur. Lütfen adreslerin doğruluğunu iki kez kontrol edin.

- -
+
-
- {/* Footer Meta */} -
-

v1.0.4 Platinum Enterprise Edition

-
+ {/* Info Card: Gas Tank Secrets */} +
+
+ +
+ +
+
+ +
+

Kritik Güvenlik: Gas Tank Private Key

+

+ Süpürme (Sweep) işlemlerinin blokzinciri ücretlerini ödeyen "Gaz Tankı" cüzdanınızın Private Key'i, en yüksek güvenlik için veritabanında değil, sunucu tarafında Coolify Environment Variables (`CRYPTO_GAS_TANK_KEY`) olarak saklanmalıdır. +

+
+ +
+
+ Coolify Panelden Yönetildi +
+
+ + {/* Status and Action */} + {message.text && ( +
+ {message.type === 'success' ? : } + {message.text} +
+ )} + +
+ + +
+ ); } diff --git a/app/api/admin/settings/route.ts b/app/api/admin/settings/route.ts new file mode 100644 index 0000000..4b63d9d --- /dev/null +++ b/app/api/admin/settings/route.ts @@ -0,0 +1,54 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +export async function GET() { + try { + // Ensure table exists (Safe initialization) + await db.query(` + CREATE TABLE IF NOT EXISTS system_settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + updated_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + const result = await db.query('SELECT * FROM system_settings'); + + // Convert to key-value object + const settings: Record = {}; + result.rows.forEach(row => { + settings[row.key] = row.value; + }); + + // Fill defaults if empty + if (!settings.sol_platform_address) settings.sol_platform_address = process.env.SOL_PLATFORM_ADDRESS || ''; + if (!settings.evm_platform_address) settings.evm_platform_address = process.env.EVM_PLATFORM_ADDRESS || ''; + + return NextResponse.json(settings); + } catch (err: any) { + return NextResponse.json({ error: err.message }, { status: 500 }); + } +} + +export async function POST(req: NextRequest) { + try { + const body = await req.json(); + const { sol_platform_address, evm_platform_address } = body; + + const queries = [ + { key: 'sol_platform_address', value: sol_platform_address }, + { key: 'evm_platform_address', value: evm_platform_address } + ]; + + for (const q of queries) { + await db.query( + 'INSERT INTO system_settings (key, value, updated_at) VALUES ($1, $2, NOW()) ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = NOW()', + [q.key, q.value] + ); + } + + return NextResponse.json({ success: true }); + } catch (err: any) { + return NextResponse.json({ error: err.message }, { status: 500 }); + } +} diff --git a/app/api/crypto-sweep/route.ts b/app/api/crypto-sweep/route.ts index 730b0a6..69617ae 100644 --- a/app/api/crypto-sweep/route.ts +++ b/app/api/crypto-sweep/route.ts @@ -45,10 +45,18 @@ export async function POST(request: Request) { return NextResponse.json({ success: false, error: `No temporary wallet found for ${walletType}` }, { status: 500 }); } - // 3. Define Platform Address (In production, load from env/settings) - const platformAddress = selectedNetwork === 'SOLANA' - ? process.env.SOL_PLATFORM_ADDRESS || "5pLH1tqZhx8p8WpZ18yr28N42KXB3FXVPzZ9ceCtpBVe" - : process.env.EVM_PLATFORM_ADDRESS || "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; + // 3. Define Platform Address (Fetch from dynamic settings) + const platformAddresses = await (async () => { + const result = await db.query('SELECT key, value FROM system_settings WHERE key IN (\'sol_platform_address\', \'evm_platform_address\')'); + const map: Record = {}; + result.rows.forEach(r => map[r.key] = r.value); + return { + sol: map.sol_platform_address || process.env.SOL_PLATFORM_ADDRESS || "5pLH1tqZhx8p8WpZ18yr28N42KXB3FXVPzZ9ceCtpBVe", + evm: map.evm_platform_address || process.env.EVM_PLATFORM_ADDRESS || "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" + }; + })(); + + const platformAddress = selectedNetwork === 'SOLANA' ? platformAddresses.sol : platformAddresses.evm; // 4. Define Merchant Address (Fetch from transaction's merchant) const merchantResult = await db.query('SELECT * FROM merchants WHERE id = $1', [transaction.merchant_id]); diff --git a/lib/settings.ts b/lib/settings.ts new file mode 100644 index 0000000..72146d2 --- /dev/null +++ b/lib/settings.ts @@ -0,0 +1,19 @@ +import { db } from './db'; + +export async function getSystemSetting(key: string, defaultValue: string = ''): Promise { + try { + const result = await db.query('SELECT value FROM system_settings WHERE key = $1', [key]); + if (result.rows.length > 0) { + return result.rows[0].value; + } + } catch (err) { + console.error(`[Settings] Failed to fetch key ${key}:`, err); + } + return defaultValue; +} + +export async function getPlatformAddresses() { + const sol = await getSystemSetting('sol_platform_address', process.env.SOL_PLATFORM_ADDRESS || ''); + const evm = await getSystemSetting('evm_platform_address', process.env.EVM_PLATFORM_ADDRESS || ''); + return { sol, evm }; +}