import pg from 'pg';
import { AuthService } from './auth.js';
export interface ApiKeyRecord {
id: number;
name: string;
api_key: string;
created_at: Date;
last_used: Date | null;
}
export class ApiKeyService {
private authService: AuthService;
constructor(private pool: pg.Pool) {
this.authService = new AuthService(pool);
}
async createApiKey(userId: number, name: string = 'Default Key'): Promise<string> {
const apiKey = this.authService.generateApiKey();
await this.pool.query(
`INSERT INTO api_keys (user_id, api_key, name) VALUES ($1, $2, $3)`,
[userId, apiKey, name]
);
return apiKey;
}
async getApiKeys(userId: number): Promise<ApiKeyRecord[]> {
const result = await this.pool.query(
`SELECT id, name, api_key, created_at, last_used FROM api_keys
WHERE user_id = $1 AND is_active = true ORDER BY created_at DESC`,
[userId]
);
return result.rows;
}
async getFullApiKey(userId: number, keyId: number): Promise<string | null> {
const result = await this.pool.query(
`SELECT api_key FROM api_keys WHERE id = $1 AND user_id = $2 AND is_active = true`,
[keyId, userId]
);
return result.rows[0]?.api_key || null;
}
async validateApiKey(apiKey: string): Promise<{ userId: number; keyId: number } | null> {
const result = await this.pool.query(
`SELECT ak.id, ak.user_id FROM api_keys ak
JOIN users u ON ak.user_id = u.id
WHERE ak.api_key = $1 AND ak.is_active = true AND u.is_active = true
AND (ak.expires_at IS NULL OR ak.expires_at > CURRENT_TIMESTAMP)`,
[apiKey]
);
if (result.rows.length === 0) return null;
// Update last_used timestamp
await this.pool.query('UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = $1', [result.rows[0].id]);
return { userId: result.rows[0].user_id, keyId: result.rows[0].id };
}
async revokeApiKey(userId: number, keyId: number): Promise<boolean> {
const result = await this.pool.query(
'UPDATE api_keys SET is_active = false WHERE id = $1 AND user_id = $2',
[keyId, userId]
);
return result.rowCount !== null && result.rowCount > 0;
}
}