216 lines
4.5 KiB
JavaScript
216 lines
4.5 KiB
JavaScript
|
|
// src/controllers/api-key.controller.js
|
||
|
|
// API 키 컨트롤러
|
||
|
|
|
||
|
|
const { ApiKey } = require('../models');
|
||
|
|
const logger = require('../config/logger.config');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* API 키 발급
|
||
|
|
*/
|
||
|
|
exports.create = async (req, res, next) => {
|
||
|
|
try {
|
||
|
|
const { name, expiresInDays, permissions } = req.body;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
// API 키 생성
|
||
|
|
const rawKey = ApiKey.generateKey();
|
||
|
|
const keyHash = ApiKey.hashKey(rawKey);
|
||
|
|
const keyPrefix = rawKey.substring(0, 12);
|
||
|
|
|
||
|
|
// 만료 일시 계산
|
||
|
|
let expiresAt = null;
|
||
|
|
if (expiresInDays) {
|
||
|
|
expiresAt = new Date();
|
||
|
|
expiresAt.setDate(expiresAt.getDate() + expiresInDays);
|
||
|
|
}
|
||
|
|
|
||
|
|
const apiKey = await ApiKey.create({
|
||
|
|
userId,
|
||
|
|
name,
|
||
|
|
keyPrefix,
|
||
|
|
keyHash,
|
||
|
|
permissions: permissions || ['chat:read', 'chat:write'],
|
||
|
|
expiresAt,
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.info(`API 키 발급: ${name} (user: ${userId})`);
|
||
|
|
|
||
|
|
// 주의: 원본 키는 이 응답에서만 반환됨 (다시 조회 불가)
|
||
|
|
return res.status(201).json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
id: apiKey.id,
|
||
|
|
name: apiKey.name,
|
||
|
|
key: rawKey, // 원본 키 (한 번만 표시)
|
||
|
|
keyPrefix: apiKey.keyPrefix,
|
||
|
|
permissions: apiKey.permissions,
|
||
|
|
expiresAt: apiKey.expiresAt,
|
||
|
|
createdAt: apiKey.createdAt,
|
||
|
|
message: '⚠️ API 키는 이 응답에서만 확인할 수 있습니다. 안전한 곳에 저장하세요.',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return next(error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* API 키 목록 조회
|
||
|
|
*/
|
||
|
|
exports.list = async (req, res, next) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const apiKeys = await ApiKey.findAll({
|
||
|
|
where: { userId },
|
||
|
|
attributes: [
|
||
|
|
'id',
|
||
|
|
'name',
|
||
|
|
'keyPrefix',
|
||
|
|
'permissions',
|
||
|
|
'rateLimit',
|
||
|
|
'status',
|
||
|
|
'expiresAt',
|
||
|
|
'lastUsedAt',
|
||
|
|
'totalRequests',
|
||
|
|
'createdAt',
|
||
|
|
],
|
||
|
|
order: [['createdAt', 'DESC']],
|
||
|
|
});
|
||
|
|
|
||
|
|
return res.json({
|
||
|
|
success: true,
|
||
|
|
data: apiKeys,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return next(error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* API 키 상세 조회
|
||
|
|
*/
|
||
|
|
exports.get = async (req, res, next) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const apiKey = await ApiKey.findOne({
|
||
|
|
where: { id, userId },
|
||
|
|
attributes: [
|
||
|
|
'id',
|
||
|
|
'name',
|
||
|
|
'keyPrefix',
|
||
|
|
'permissions',
|
||
|
|
'rateLimit',
|
||
|
|
'status',
|
||
|
|
'expiresAt',
|
||
|
|
'lastUsedAt',
|
||
|
|
'totalRequests',
|
||
|
|
'createdAt',
|
||
|
|
'updatedAt',
|
||
|
|
],
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: {
|
||
|
|
code: 'API_KEY_NOT_FOUND',
|
||
|
|
message: 'API 키를 찾을 수 없습니다.',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return res.json({
|
||
|
|
success: true,
|
||
|
|
data: apiKey,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return next(error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* API 키 수정
|
||
|
|
*/
|
||
|
|
exports.update = async (req, res, next) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const { name, status } = req.body;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const apiKey = await ApiKey.findOne({
|
||
|
|
where: { id, userId },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: {
|
||
|
|
code: 'API_KEY_NOT_FOUND',
|
||
|
|
message: 'API 키를 찾을 수 없습니다.',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (name) apiKey.name = name;
|
||
|
|
if (status) apiKey.status = status;
|
||
|
|
|
||
|
|
await apiKey.save();
|
||
|
|
|
||
|
|
logger.info(`API 키 수정: ${apiKey.name} (id: ${id})`);
|
||
|
|
|
||
|
|
return res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
id: apiKey.id,
|
||
|
|
name: apiKey.name,
|
||
|
|
keyPrefix: apiKey.keyPrefix,
|
||
|
|
status: apiKey.status,
|
||
|
|
updatedAt: apiKey.updatedAt,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return next(error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* API 키 폐기
|
||
|
|
*/
|
||
|
|
exports.revoke = async (req, res, next) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const apiKey = await ApiKey.findOne({
|
||
|
|
where: { id, userId },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: {
|
||
|
|
code: 'API_KEY_NOT_FOUND',
|
||
|
|
message: 'API 키를 찾을 수 없습니다.',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
apiKey.status = 'revoked';
|
||
|
|
await apiKey.save();
|
||
|
|
|
||
|
|
logger.info(`API 키 폐기: ${apiKey.name} (id: ${id})`);
|
||
|
|
|
||
|
|
return res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
message: 'API 키가 폐기되었습니다.',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return next(error);
|
||
|
|
}
|
||
|
|
};
|