// src/models/api-key.model.js // API 키 모델 const { DataTypes } = require('sequelize'); const crypto = require('crypto'); module.exports = (sequelize) => { const ApiKey = sequelize.define('ApiKey', { id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true, }, userId: { type: DataTypes.UUID, allowNull: false, references: { model: 'users', key: 'id', }, comment: '소유자 사용자 ID', }, name: { type: DataTypes.STRING(100), allowNull: false, comment: 'API 키 이름 (사용자 지정)', }, keyPrefix: { type: DataTypes.STRING(12), allowNull: false, comment: 'API 키 접두사 (표시용)', }, keyHash: { type: DataTypes.STRING(64), allowNull: false, unique: true, comment: 'API 키 해시 (SHA-256)', }, permissions: { type: DataTypes.JSONB, defaultValue: ['chat:read', 'chat:write'], comment: '권한 목록', }, rateLimit: { type: DataTypes.INTEGER, defaultValue: 60, // 분당 60회 comment: '분당 요청 제한', }, status: { type: DataTypes.ENUM('active', 'revoked', 'expired'), defaultValue: 'active', comment: 'API 키 상태', }, expiresAt: { type: DataTypes.DATE, allowNull: true, comment: '만료 일시 (null이면 무기한)', }, lastUsedAt: { type: DataTypes.DATE, allowNull: true, comment: '마지막 사용 시간', }, totalRequests: { type: DataTypes.INTEGER, defaultValue: 0, comment: '총 요청 수', }, }, { tableName: 'api_keys', timestamps: true, underscored: true, indexes: [ { fields: ['key_hash'], unique: true, }, { fields: ['user_id'], }, { fields: ['status'], }, ], }); // 클래스 메서드: API 키 생성 ApiKey.generateKey = function() { const prefix = process.env.API_KEY_PREFIX || 'sk-'; const length = parseInt(process.env.API_KEY_LENGTH, 10) || 48; const randomPart = crypto.randomBytes(length).toString('base64url').slice(0, length); return `${prefix}${randomPart}`; }; // 클래스 메서드: API 키 해시 생성 ApiKey.hashKey = function(key) { return crypto.createHash('sha256').update(key).digest('hex'); }; // 클래스 메서드: API 키로 조회 ApiKey.findByKey = async function(key) { const keyHash = this.hashKey(key); const apiKey = await this.findOne({ where: { keyHash, status: 'active' }, }); if (apiKey) { // 사용자 정보 별도 조회 const { User } = require('./index'); apiKey.user = await User.findByPk(apiKey.userId); } return apiKey; }; // 인스턴스 메서드: 사용 기록 업데이트 ApiKey.prototype.recordUsage = async function() { this.lastUsedAt = new Date(); this.totalRequests += 1; await this.save(); }; // 인스턴스 메서드: 만료 여부 확인 ApiKey.prototype.isExpired = function() { if (!this.expiresAt) return false; return new Date() > this.expiresAt; }; return ApiKey; };