51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import {
|
|
CanActivate,
|
|
ExecutionContext,
|
|
Injectable,
|
|
UnauthorizedException,
|
|
} from '@nestjs/common';
|
|
import { BillingService } from '../billing/billing.service';
|
|
import { ApiKeysService } from './api-keys.service';
|
|
|
|
@Injectable()
|
|
export class ApiKeyAuthGuard implements CanActivate {
|
|
constructor(
|
|
private readonly apiKeys: ApiKeysService,
|
|
private readonly billing: BillingService,
|
|
) {}
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const req = context.switchToHttp().getRequest<{
|
|
headers: Record<string, string | string[] | undefined>;
|
|
user?: {
|
|
userId: string;
|
|
organizationId: string;
|
|
role: string;
|
|
apiKeyId?: string;
|
|
};
|
|
}>();
|
|
|
|
const header =
|
|
req.headers['x-api-key'] ??
|
|
(typeof req.headers.authorization === 'string' &&
|
|
req.headers.authorization.toLowerCase().startsWith('bearer mk_')
|
|
? req.headers.authorization.slice(7).trim()
|
|
: undefined);
|
|
|
|
const raw = Array.isArray(header) ? header[0] : header;
|
|
if (!raw?.trim()) throw new UnauthorizedException('کلید API لازم است');
|
|
|
|
const auth = await this.apiKeys.authenticate(raw.trim());
|
|
await this.billing.requireActiveForOrganization(auth.organizationId);
|
|
await this.billing.requireModule(auth.organizationId, 'public_api');
|
|
|
|
req.user = {
|
|
userId: `api-key:${auth.apiKeyId}`,
|
|
organizationId: auth.organizationId,
|
|
role: auth.role,
|
|
apiKeyId: auth.apiKeyId,
|
|
};
|
|
return true;
|
|
}
|
|
}
|