Files
paperlessmanager/paperless-backend/src/auth/api-keys.service.ts
T
bjoernpoettker dad0136365
Build and Push Multi-Platform Images / build-and-push (push) Successful in 41s
chore: apply ESLint auto-fix across entire backend
Reformats code style (line breaks, indentation, type annotations)
without changing logic. Also includes minor feature additions bundled
in the same lint run (stats service, user-settings groups, agrarmonitor
polling improvements).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 09:02:02 +02:00

83 lines
2.1 KiB
TypeScript

import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApiKey } from '../database/entities/api-key.entity';
import * as crypto from 'crypto';
@Injectable()
export class ApiKeysService {
private readonly logger = new Logger(ApiKeysService.name);
constructor(
@InjectRepository(ApiKey)
private readonly apiKeyRepo: Repository<ApiKey>,
) {}
async createApiKey(
name: string,
expiresDays?: number,
): Promise<{ plainKey: string; entity: ApiKey }> {
const prefix = 'pm_';
const randomPart = crypto.randomBytes(24).toString('hex'); // 48 chars hex
const plainKey = `${prefix}${randomPart}`;
const keyHash = this.hashKey(plainKey);
const apiKey = this.apiKeyRepo.create({
name,
keyPrefix: prefix,
keyHash,
expiresAt: expiresDays
? new Date(Date.now() + expiresDays * 24 * 60 * 60 * 1000)
: null,
});
const savedKey = await this.apiKeyRepo.save(apiKey);
return {
plainKey,
entity: savedKey,
};
}
async validateKey(plainKey: string): Promise<ApiKey> {
const keyHash = this.hashKey(plainKey);
const apiKey = await this.apiKeyRepo.findOne({
where: { keyHash },
});
if (!apiKey) {
throw new UnauthorizedException('Invalid API Key');
}
if (apiKey.expiresAt && apiKey.expiresAt < new Date()) {
throw new UnauthorizedException('API Key has expired');
}
// Update last used timestamp (async, don't wait for it to return response faster)
apiKey.lastUsedAt = new Date();
this.apiKeyRepo
.save(apiKey)
.catch((err) =>
this.logger.error('Fehler beim Aktualisieren von lastUsedAt', err),
);
return apiKey;
}
async listKeys(): Promise<ApiKey[]> {
return this.apiKeyRepo.find({
order: { createdAt: 'DESC' },
});
}
async deleteKey(id: string): Promise<void> {
await this.apiKeyRepo.delete(id);
}
private hashKey(key: string): string {
return crypto.createHash('sha256').update(key).digest('hex');
}
}