File size: 2,843 Bytes
5ef6e9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { Router } from "express";
import { db, configTable, geminiAccountsTable } from "@workspace/db";
import { eq, sql } from "drizzle-orm";
import { OTP_KEY, SITE_CONFIG_KEYS } from "./admin";

const router = Router();

const TOKEN_KEY = "geminigen_bearer_token";
const REFRESH_TOKEN_KEY = "geminigen_refresh_token";

router.post("/receive-tokens", async (req, res) => {
  const { otp, access_token, refresh_token, label } = req.body as {
    otp?: string;
    access_token?: string;
    refresh_token?: string;
    label?: string;
  };

  if (!otp || !access_token) {
    return res.status(400).json({ error: "otp ε’Œ access_token η‚ΊεΏ…ε‘«" });
  }

  const row = await db
    .select()
    .from(configTable)
    .where(eq(configTable.key, OTP_KEY))
    .limit(1);

  if (!row.length) {
    return res.status(401).json({ error: "OTP η„‘ζ•ˆζˆ–ε·²ιŽζœŸοΌŒθ«‹ι‡ζ–°η”’η”Ÿζ›Έη±€" });
  }

  const [storedOtp, expiresAtStr] = row[0].value.split(":");
  const expiresAt = Number(expiresAtStr);

  if (storedOtp !== otp || Date.now() > expiresAt) {
    return res.status(401).json({ error: "OTP η„‘ζ•ˆζˆ–ε·²ιŽζœŸοΌŒθ«‹ι‡ζ–°η”’η”Ÿζ›Έη±€" });
  }

  // Delete OTP (one-time use)
  await db.delete(configTable).where(eq(configTable.key, OTP_KEY));

  if (label) {
    // Save to pool as a new account
    await db.insert(geminiAccountsTable).values({
      label: label.trim(),
      bearerToken: access_token,
      refreshToken: refresh_token || null,
      isActive: true,
    });
    return res.json({ success: true, message: `εΈ³ζˆΆγ€Œ${label}γ€ε·²εŠ ε…₯ Token 池!` });
  }

  // Legacy: save as single token in config
  await db
    .insert(configTable)
    .values({ key: TOKEN_KEY, value: access_token, updatedAt: new Date() })
    .onConflictDoUpdate({ target: configTable.key, set: { value: access_token, updatedAt: new Date() } });

  if (refresh_token) {
    await db
      .insert(configTable)
      .values({ key: REFRESH_TOKEN_KEY, value: refresh_token, updatedAt: new Date() })
      .onConflictDoUpdate({ target: configTable.key, set: { value: refresh_token, updatedAt: new Date() } });
  }

  return res.json({ success: true, message: "Token 已成功同ζ­₯!" });
});

// ── Public site config (logo, Google Ads) ─────────────────────────────────────
const PUBLIC_SITE_KEYS = ["logo_url", "site_name", "google_ads_enabled", "google_ads_client", "google_ads_slot"];

router.get("/site-config", async (_req, res) => {
  const rows = await db
    .select()
    .from(configTable)
    .where(sql`${configTable.key} = ANY(ARRAY[${sql.raw(PUBLIC_SITE_KEYS.map(k => `'${k}'`).join(","))}]::text[])`);
  const config: Record<string, string> = {};
  for (const row of rows) config[row.key] = row.value;
  res.json(config);
});

export default router;