/** * Presale Configuration Database Helpers * * Manages the presale_config table — a key-value store for * admin-editable presale parameters. * * Default values are seeded on first access if not present. */ import { eq } from "drizzle-orm"; import { getDb } from "./db"; import { presaleConfig } from "../drizzle/schema"; // ─── Default configuration values ───────────────────────────────────────────── export const DEFAULT_CONFIG: Array<{ key: string; value: string; label: string; description: string; type: string; }> = [ { key: "presaleEndDate", value: "2026-06-30T23:59:59Z", label: "预售结束时间 (Presale End Date)", description: "ISO 8601 格式,例如:2026-06-30T23:59:59Z", type: "date", }, { key: "tokenPrice", value: "0.02", label: "代币价格 (Token Price, USDT)", description: "每枚 XIC 的 USDT 价格", type: "number", }, { key: "hardCap", value: "5000000", label: "硬顶 (Hard Cap, USDT)", description: "预售最大募资额(USDT)", type: "number", }, { key: "listingPrice", value: "0.10", label: "上市目标价格 (Target Listing Price, USDT)", description: "预计上市价格(仅展示用)", type: "number", }, { key: "totalSupply", value: "100000000000", label: "总供应量 (Total Supply)", description: "XIC 代币总供应量", type: "number", }, { key: "maxPurchaseUsdt", value: "50000", label: "单笔最大购买额 (Max Purchase, USDT)", description: "单笔购买最大 USDT 金额", type: "number", }, { key: "presaleStatus", value: "live", label: "预售状态 (Presale Status)", description: "live = 进行中,paused = 暂停,ended = 已结束", type: "text", }, { key: "heroTitle", value: "XIC Token Presale", label: "首页标题 (Hero Title)", description: "首页大标题文字", type: "text", }, { key: "heroSubtitle", value: "New AssetChain — The next-generation RWA native blockchain with AI-native compliance, CBPP consensus, and Charter smart contracts.", label: "首页副标题 (Hero Subtitle)", description: "首页副标题文字", type: "text", }, { key: "tronReceivingAddress", value: "TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp", label: "TRC20 收款地址 (TRON Receiving Address)", description: "接收 TRC20 USDT 的 TRON 地址", type: "text", }, { key: "telegramBotToken", value: "", label: "Telegram Bot Token", description: "从 @BotFather 获取的 Bot Token,用于发送购买通知", type: "text", }, { key: "telegramChatId", value: "", label: "Telegram Chat ID", description: "接收通知的 Chat ID(个人账号或群组)", type: "text", }, ]; export interface ConfigMap { [key: string]: string; } /** * Seed default config values if not already present. */ export async function seedDefaultConfig(): Promise { const db = await getDb(); if (!db) return; for (const item of DEFAULT_CONFIG) { try { const existing = await db .select() .from(presaleConfig) .where(eq(presaleConfig.key, item.key)) .limit(1); if (existing.length === 0) { await db.insert(presaleConfig).values({ key: item.key, value: item.value, label: item.label, description: item.description, type: item.type, }); } } catch (e) { console.warn(`[Config] Failed to seed ${item.key}:`, e); } } } /** * Get all config values as a key-value map. * Falls back to DEFAULT_CONFIG values if DB is unavailable. */ export async function getAllConfig(): Promise { // Build defaults map const defaults: ConfigMap = {}; for (const item of DEFAULT_CONFIG) { defaults[item.key] = item.value; } const db = await getDb(); if (!db) return defaults; try { const rows = await db.select().from(presaleConfig); const result: ConfigMap = { ...defaults }; for (const row of rows) { result[row.key] = row.value; } return result; } catch (e) { console.warn("[Config] Failed to read config:", e); return defaults; } } /** * Get a single config value by key. */ export async function getConfig(key: string): Promise { const db = await getDb(); if (!db) { const def = DEFAULT_CONFIG.find((c) => c.key === key); return def?.value ?? null; } try { const rows = await db .select() .from(presaleConfig) .where(eq(presaleConfig.key, key)) .limit(1); if (rows.length > 0) return rows[0].value; // Fall back to default const def = DEFAULT_CONFIG.find((c) => c.key === key); return def?.value ?? null; } catch (e) { console.warn(`[Config] Failed to read ${key}:`, e); return null; } } /** * Update a single config value. */ export async function setConfig(key: string, value: string): Promise { const db = await getDb(); if (!db) throw new Error("DB unavailable"); const existing = await db .select() .from(presaleConfig) .where(eq(presaleConfig.key, key)) .limit(1); if (existing.length > 0) { await db .update(presaleConfig) .set({ value }) .where(eq(presaleConfig.key, key)); } else { const def = DEFAULT_CONFIG.find((c) => c.key === key); await db.insert(presaleConfig).values({ key, value, label: def?.label ?? key, description: def?.description ?? "", type: def?.type ?? "text", }); } }