feat(center): enforce API key on all APIs except node register

This commit is contained in:
nav
2026-05-13 08:17:42 +00:00
parent a924bf656d
commit cfa5ccdfaf
4 changed files with 63 additions and 1 deletions

View File

@@ -0,0 +1,43 @@
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { GuildNode } from '../entities/guild-node.entity';
@Injectable()
export class CenterApiKeyGuard implements CanActivate {
constructor(
@InjectRepository(GuildNode)
private readonly nodeRepo: Repository<GuildNode>,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest<{
path?: string;
method?: string;
headers: Record<string, string | string[] | undefined>;
}>();
const path = req.path ?? '';
const method = (req.method ?? 'GET').toUpperCase();
// only guild registration is exempt from API key; it is protected by HMAC secret
if (method === 'POST' && (path === '/nodes/register' || path.endsWith('/nodes/register'))) {
return true;
}
const received = req.headers['x-api-key'];
const apiKey = Array.isArray(received) ? received[0] : received;
if (!apiKey) throw new UnauthorizedException('missing api key');
const nodes = await this.nodeRepo.find({ where: { status: 'active' } });
for (const node of nodes) {
if (!node.apiKeyHash) continue;
const ok = await bcrypt.compare(apiKey, node.apiKeyHash);
if (ok) return true;
}
throw new UnauthorizedException('invalid api key');
}
}