File size: 1,986 Bytes
bc2ac28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
85
import fs from "fs";
import path from "path";
import crypto from "crypto";

const DATA_DIR = path.join(process.cwd(), "data");
const KEYS_FILE = path.join(DATA_DIR, "api-keys.json");

export interface ApiKey {
  key: string;
  name: string;
  createdAt: string;
  active: boolean;
  usageCount: number;
  lastUsed: string | null;
}

function ensureDataDir() {
  if (!fs.existsSync(DATA_DIR)) {
    fs.mkdirSync(DATA_DIR, { recursive: true });
  }
}

function readKeys(): ApiKey[] {
  ensureDataDir();
  if (!fs.existsSync(KEYS_FILE)) return [];
  try {
    return JSON.parse(fs.readFileSync(KEYS_FILE, "utf-8"));
  } catch {
    return [];
  }
}

function writeKeys(keys: ApiKey[]) {
  ensureDataDir();
  fs.writeFileSync(KEYS_FILE, JSON.stringify(keys, null, 2), "utf-8");
}

export function generateKey(name: string): ApiKey {
  const keys = readKeys();
  const newKey: ApiKey = {
    key: `neo1-${crypto.randomBytes(20).toString("hex")}`,
    name,
    createdAt: new Date().toISOString(),
    active: true,
    usageCount: 0,
    lastUsed: null,
  };
  keys.push(newKey);
  writeKeys(keys);
  return newKey;
}

export function listKeys(): Omit<ApiKey, "key">[] {
  return readKeys().map(({ key, ...rest }) => ({
    ...rest,
    keyPreview: `${key.slice(0, 10)}...${key.slice(-4)}`,
  })) as Omit<ApiKey, "key">[];
}

export function validateKey(key: string): ApiKey | null {
  const keys = readKeys();
  const found = keys.find((k) => k.key === key && k.active);
  if (!found) return null;

  found.usageCount += 1;
  found.lastUsed = new Date().toISOString();
  writeKeys(keys);
  return found;
}

export function revokeKey(keyOrName: string): boolean {
  const keys = readKeys();
  const idx = keys.findIndex(
    (k) => k.key === keyOrName || k.name === keyOrName
  );
  if (idx === -1) return false;
  keys[idx].active = false;
  writeKeys(keys);
  return true;
}

export function getAdminSecret(): string {
  return process.env["NEO_ADMIN_SECRET"] ?? "neo-admin-change-me";
}