- CORS origin 검증 로직 추가 (운영 도메인 + localhost + 192.168.x.x) - Redis 기반 세션/토큰 관리 유틸 추가 - departments 테이블 JOIN 지원 (findByUsername, findById) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
86 lines
2.0 KiB
JavaScript
86 lines
2.0 KiB
JavaScript
/**
|
|
* Redis 클라이언트 (로그인 시도 제한, 토큰 블랙리스트)
|
|
*
|
|
* Redis 미연결 시 메모리 폴백으로 동작
|
|
*/
|
|
|
|
const { createClient } = require('redis');
|
|
|
|
const REDIS_HOST = process.env.REDIS_HOST || 'redis';
|
|
const REDIS_PORT = process.env.REDIS_PORT || 6379;
|
|
|
|
let client = null;
|
|
let connected = false;
|
|
|
|
// 메모리 폴백 (Redis 미연결 시)
|
|
const memoryStore = new Map();
|
|
|
|
async function initRedis() {
|
|
try {
|
|
client = createClient({ url: `redis://${REDIS_HOST}:${REDIS_PORT}` });
|
|
client.on('error', () => { connected = false; });
|
|
client.on('connect', () => { connected = true; });
|
|
await client.connect();
|
|
console.log('Redis 연결 성공');
|
|
} catch {
|
|
console.warn('Redis 연결 실패 - 메모리 폴백 사용');
|
|
connected = false;
|
|
}
|
|
}
|
|
|
|
async function get(key) {
|
|
if (connected) {
|
|
return await client.get(key);
|
|
}
|
|
const entry = memoryStore.get(key);
|
|
if (!entry) return null;
|
|
if (entry.expiry && entry.expiry < Date.now()) {
|
|
memoryStore.delete(key);
|
|
return null;
|
|
}
|
|
return entry.value;
|
|
}
|
|
|
|
async function set(key, value, ttlSeconds) {
|
|
if (connected) {
|
|
await client.set(key, value, { EX: ttlSeconds });
|
|
} else {
|
|
memoryStore.set(key, {
|
|
value,
|
|
expiry: ttlSeconds ? Date.now() + ttlSeconds * 1000 : null,
|
|
});
|
|
}
|
|
}
|
|
|
|
async function del(key) {
|
|
if (connected) {
|
|
await client.del(key);
|
|
} else {
|
|
memoryStore.delete(key);
|
|
}
|
|
}
|
|
|
|
async function incr(key) {
|
|
if (connected) {
|
|
return await client.incr(key);
|
|
}
|
|
const entry = memoryStore.get(key);
|
|
const current = entry ? parseInt(entry.value) || 0 : 0;
|
|
const next = current + 1;
|
|
memoryStore.set(key, { value: String(next), expiry: entry?.expiry || null });
|
|
return next;
|
|
}
|
|
|
|
async function expire(key, ttlSeconds) {
|
|
if (connected) {
|
|
await client.expire(key, ttlSeconds);
|
|
} else {
|
|
const entry = memoryStore.get(key);
|
|
if (entry) {
|
|
entry.expiry = Date.now() + ttlSeconds * 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { initRedis, get, set, del, incr, expire };
|