dad0136365
Build and Push Multi-Platform Images / build-and-push (push) Successful in 41s
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>
83 lines
2.1 KiB
TypeScript
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');
|
|
}
|
|
}
|