ERP-node/ai-assistant/src/models/api-key.model.js

131 lines
3.2 KiB
JavaScript

// 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;
};