243 lines
7.1 KiB
JavaScript
243 lines
7.1 KiB
JavaScript
|
|
const dbConnection = require('./connection');
|
||
|
|
|
||
|
|
// API 로그 테이블 생성
|
||
|
|
async function createApiLogTable() {
|
||
|
|
const createTableQuery = `
|
||
|
|
CREATE TABLE API_LOGS (
|
||
|
|
ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||
|
|
API_KEY_ID VARCHAR2(255),
|
||
|
|
API_KEY_NAME VARCHAR2(255),
|
||
|
|
USER_ID NUMBER,
|
||
|
|
USERNAME VARCHAR2(100),
|
||
|
|
METHOD VARCHAR2(10) NOT NULL,
|
||
|
|
ENDPOINT VARCHAR2(500) NOT NULL,
|
||
|
|
REQUEST_BODY CLOB,
|
||
|
|
RESPONSE_STATUS NUMBER,
|
||
|
|
RESPONSE_TIME NUMBER,
|
||
|
|
IP_ADDRESS VARCHAR2(45),
|
||
|
|
USER_AGENT VARCHAR2(1000),
|
||
|
|
CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
|
|
)
|
||
|
|
`;
|
||
|
|
|
||
|
|
let connection;
|
||
|
|
try {
|
||
|
|
connection = await dbConnection.getConnection();
|
||
|
|
await connection.execute(createTableQuery);
|
||
|
|
console.log('API_LOGS 테이블이 생성되었습니다.');
|
||
|
|
} catch (error) {
|
||
|
|
if (error.errorNum !== 955) { // ORA-00955: name is already used by an existing object
|
||
|
|
console.error('API_LOGS 테이블 생성 실패:', error);
|
||
|
|
throw error;
|
||
|
|
} else {
|
||
|
|
console.log('API_LOGS 테이블이 이미 존재합니다.');
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
if (connection) {
|
||
|
|
try {
|
||
|
|
await connection.close();
|
||
|
|
} catch (err) {
|
||
|
|
console.error('연결 종료 실패:', err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// API 로그 저장
|
||
|
|
async function saveApiLog(logData) {
|
||
|
|
const query = `
|
||
|
|
INSERT INTO API_LOGS (
|
||
|
|
API_KEY_ID, API_KEY_NAME, USER_ID, USERNAME, METHOD, ENDPOINT,
|
||
|
|
REQUEST_BODY, RESPONSE_STATUS, RESPONSE_TIME, IP_ADDRESS, USER_AGENT
|
||
|
|
) VALUES (
|
||
|
|
:apiKeyId, :apiKeyName, :userId, :username, :method, :endpoint,
|
||
|
|
:requestBody, :responseStatus, :responseTime, :ipAddress, :userAgent
|
||
|
|
)
|
||
|
|
`;
|
||
|
|
|
||
|
|
const binds = {
|
||
|
|
apiKeyId: logData.apiKeyId || null,
|
||
|
|
apiKeyName: logData.apiKeyName || null,
|
||
|
|
userId: logData.userId || null,
|
||
|
|
username: logData.username || null,
|
||
|
|
method: logData.method,
|
||
|
|
endpoint: logData.endpoint,
|
||
|
|
requestBody: logData.requestBody ? JSON.stringify(logData.requestBody) : null,
|
||
|
|
responseStatus: logData.responseStatus,
|
||
|
|
responseTime: logData.responseTime,
|
||
|
|
ipAddress: logData.ipAddress,
|
||
|
|
userAgent: logData.userAgent
|
||
|
|
};
|
||
|
|
|
||
|
|
let connection;
|
||
|
|
try {
|
||
|
|
connection = await dbConnection.getConnection();
|
||
|
|
const result = await connection.execute(query, binds, { autoCommit: true });
|
||
|
|
return result;
|
||
|
|
} catch (error) {
|
||
|
|
console.error('API 로그 저장 실패:', error);
|
||
|
|
throw error;
|
||
|
|
} finally {
|
||
|
|
if (connection) {
|
||
|
|
try {
|
||
|
|
await connection.close();
|
||
|
|
} catch (err) {
|
||
|
|
console.error('연결 종료 실패:', err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// API 로그 조회 (페이징)
|
||
|
|
async function getApiLogs(page = 1, limit = 50, filters = {}) {
|
||
|
|
let whereClause = '';
|
||
|
|
const binds = {};
|
||
|
|
|
||
|
|
if (filters.startDate) {
|
||
|
|
whereClause += ' AND CREATED_AT >= :startDate';
|
||
|
|
binds.startDate = new Date(filters.startDate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (filters.endDate) {
|
||
|
|
whereClause += ' AND CREATED_AT <= :endDate';
|
||
|
|
binds.endDate = new Date(filters.endDate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (filters.method) {
|
||
|
|
whereClause += ' AND METHOD = :method';
|
||
|
|
binds.method = filters.method;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (filters.endpoint) {
|
||
|
|
whereClause += ' AND ENDPOINT LIKE :endpoint';
|
||
|
|
binds.endpoint = `%${filters.endpoint}%`;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (filters.username) {
|
||
|
|
whereClause += ' AND USERNAME LIKE :username';
|
||
|
|
binds.username = `%${filters.username}%`;
|
||
|
|
}
|
||
|
|
|
||
|
|
const offset = (page - 1) * limit;
|
||
|
|
binds.limit = limit;
|
||
|
|
binds.offset = offset;
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
SELECT * FROM (
|
||
|
|
SELECT a.*, ROW_NUMBER() OVER (ORDER BY CREATED_AT DESC) as rn
|
||
|
|
FROM API_LOGS a
|
||
|
|
WHERE 1=1 ${whereClause}
|
||
|
|
) WHERE rn > :offset AND rn <= :offset + :limit
|
||
|
|
`;
|
||
|
|
|
||
|
|
const countQuery = `
|
||
|
|
SELECT COUNT(*) as total
|
||
|
|
FROM API_LOGS
|
||
|
|
WHERE 1=1 ${whereClause}
|
||
|
|
`;
|
||
|
|
|
||
|
|
let connection;
|
||
|
|
try {
|
||
|
|
connection = await dbConnection.getConnection();
|
||
|
|
const [logsResult, countResult] = await Promise.all([
|
||
|
|
connection.execute(query, binds),
|
||
|
|
connection.execute(countQuery, binds)
|
||
|
|
]);
|
||
|
|
|
||
|
|
return {
|
||
|
|
logs: logsResult.rows.map(row => ({
|
||
|
|
id: row[0],
|
||
|
|
apiKeyId: row[1],
|
||
|
|
apiKeyName: row[2],
|
||
|
|
userId: row[3],
|
||
|
|
username: row[4],
|
||
|
|
method: row[5],
|
||
|
|
endpoint: row[6],
|
||
|
|
requestBody: row[7],
|
||
|
|
responseStatus: row[8],
|
||
|
|
responseTime: row[9],
|
||
|
|
ipAddress: row[10],
|
||
|
|
userAgent: row[11],
|
||
|
|
createdAt: row[12]
|
||
|
|
})),
|
||
|
|
total: countResult.rows[0][0],
|
||
|
|
page,
|
||
|
|
limit,
|
||
|
|
totalPages: Math.ceil(countResult.rows[0][0] / limit)
|
||
|
|
};
|
||
|
|
} catch (error) {
|
||
|
|
console.error('API 로그 조회 실패:', error);
|
||
|
|
throw error;
|
||
|
|
} finally {
|
||
|
|
if (connection) {
|
||
|
|
try {
|
||
|
|
await connection.close();
|
||
|
|
} catch (err) {
|
||
|
|
console.error('연결 종료 실패:', err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// API 로그 통계
|
||
|
|
async function getApiLogStats() {
|
||
|
|
const queries = {
|
||
|
|
totalRequests: 'SELECT COUNT(*) as count FROM API_LOGS',
|
||
|
|
todayRequests: `SELECT COUNT(*) as count FROM API_LOGS WHERE CREATED_AT >= TRUNC(SYSDATE)`,
|
||
|
|
topEndpoints: `
|
||
|
|
SELECT ENDPOINT, COUNT(*) as count
|
||
|
|
FROM API_LOGS
|
||
|
|
GROUP BY ENDPOINT
|
||
|
|
ORDER BY count DESC
|
||
|
|
FETCH FIRST 10 ROWS ONLY
|
||
|
|
`,
|
||
|
|
topUsers: `
|
||
|
|
SELECT USERNAME, COUNT(*) as count
|
||
|
|
FROM API_LOGS
|
||
|
|
WHERE USERNAME IS NOT NULL
|
||
|
|
GROUP BY USERNAME
|
||
|
|
ORDER BY count DESC
|
||
|
|
FETCH FIRST 10 ROWS ONLY
|
||
|
|
`
|
||
|
|
};
|
||
|
|
|
||
|
|
let connection;
|
||
|
|
try {
|
||
|
|
connection = await dbConnection.getConnection();
|
||
|
|
const results = {};
|
||
|
|
|
||
|
|
for (const [key, query] of Object.entries(queries)) {
|
||
|
|
const result = await connection.execute(query);
|
||
|
|
|
||
|
|
if (key === 'totalRequests' || key === 'todayRequests') {
|
||
|
|
results[key] = result.rows[0][0];
|
||
|
|
} else {
|
||
|
|
results[key] = result.rows.map(row => ({
|
||
|
|
name: row[0],
|
||
|
|
count: row[1]
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return results;
|
||
|
|
} catch (error) {
|
||
|
|
console.error('API 로그 통계 조회 실패:', error);
|
||
|
|
throw error;
|
||
|
|
} finally {
|
||
|
|
if (connection) {
|
||
|
|
try {
|
||
|
|
await connection.close();
|
||
|
|
} catch (err) {
|
||
|
|
console.error('연결 종료 실패:', err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
createApiLogTable,
|
||
|
|
saveApiLog,
|
||
|
|
getApiLogs,
|
||
|
|
getApiLogStats
|
||
|
|
};
|