Files
telegram-shop/admin-next/src/lib/auth.ts
NW a4a5fd449d feat(admin): integrate Next.js admin panel (admin-next/) from feat/nextjs-admin
- Add admin-next/: full Next.js + Prisma + shadcn admin panel (45 API routes, 76 components)
- docker-compose: tg_shop_admin service (port 3000, shared db/shop.db, prisma), bot talks to it via ADMIN_CHAT_URL=http://tg_shop_admin:3000/api/chat
- tor-proxy now proxies onion admin to tg_shop_admin:3000 (new panel)
- chatbotService: ADMIN_CHAT_URL default = http://tg_shop_admin:3000/api/chat (removed localhost:3100 anachronism)
- .env admin secrets gitignored (admin-next/.env)
2026-08-08 01:31:45 +01:00

70 lines
1.9 KiB
TypeScript
Executable File

import crypto from 'crypto';
const ADMIN_SECRET = process.env.ADMIN_SECRET || 'changeme';
const SUPER_ADMIN_SECRET = process.env.SUPER_ADMIN_SECRET || '';
export type AdminRole = 'admin' | 'super_admin';
export interface AdminPayload {
role: AdminRole;
jti: string;
iat: number;
exp: number;
}
export function createToken(secret: string): string {
const isSuper = SUPER_ADMIN_SECRET && secret === SUPER_ADMIN_SECRET;
const isAdmin = secret === ADMIN_SECRET;
if (!isAdmin && !isSuper) return '';
// If no super secret set, all admins are super
const role: AdminRole = (!SUPER_ADMIN_SECRET || isSuper) ? 'super_admin' : 'admin';
const payload: AdminPayload = {
role,
jti: crypto.randomUUID(),
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 86400,
};
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
const signingKey = SUPER_ADMIN_SECRET || ADMIN_SECRET;
const sig = crypto
.createHmac('sha256', signingKey)
.update(payloadB64)
.digest('base64url');
return `${payloadB64}.${sig}`;
}
export function verifyToken(token: string): AdminPayload | null {
try {
const [payloadB64, sig] = token.split('.');
if (!payloadB64 || !sig) return null;
const signingKey = SUPER_ADMIN_SECRET || ADMIN_SECRET;
const expected = crypto
.createHmac('sha256', signingKey)
.update(payloadB64)
.digest('base64url');
if (sig !== expected) return null;
const payload: AdminPayload = JSON.parse(
Buffer.from(payloadB64, 'base64url').toString()
);
if (payload.exp < Math.floor(Date.now() / 1000)) return null;
return payload;
} catch {
return null;
}
}
export function verifyReAuth(token: string): boolean {
const isSuper = SUPER_ADMIN_SECRET && token === SUPER_ADMIN_SECRET;
const isAdmin = token === ADMIN_SECRET;
return isSuper || isAdmin;
}