diff --git a/server/redis/redis.service.ts b/server/redis/redis.service.ts index 92e7e602..015177f5 100644 --- a/server/redis/redis.service.ts +++ b/server/redis/redis.service.ts @@ -18,4 +18,9 @@ export class RedisService { publishToChannel(channel: string, message: string) { this.redisPublisher.publish(channel, message); } + psubscribeToPattern(pattern: string, callback: Function) { + this.redisSubscriber.pSubscribe(pattern, (message, channel) => { + callback(message, channel); + }); + } } diff --git a/server/src/admin/admin.controller.ts b/server/src/admin/admin.controller.ts index 68b30d1e..566d3670 100644 --- a/server/src/admin/admin.controller.ts +++ b/server/src/admin/admin.controller.ts @@ -8,7 +8,6 @@ import { import { Response } from 'express'; import { RedisService } from 'redis/redis.service'; import { UserId } from 'src/users/decorator/userId.decorator'; -import { channels } from './const/channels.const'; @Controller('admin') export class AdminController { @@ -31,10 +30,9 @@ export class AdminController { }; res.write(`data: ${changeFormat('notice', 'Server connected')}\n\n`); - channels.forEach((channel) => { - this.redisService.subscribeToChannel(channel, (message) => { - res.write(`data: ${changeFormat(channel, message)}\n\n`); - }); + + this.redisService.psubscribeToPattern('*', (message, channel) => { + res.write(`data: ${changeFormat(channel, message)}\n\n`); }); req.on('close', () => { diff --git a/server/src/admin/const/channels.const.ts b/server/src/admin/const/channels.const.ts deleted file mode 100644 index a4e3ed4a..00000000 --- a/server/src/admin/const/channels.const.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const channels = [ - 'channel', - 'sharedChecklist', - 'ai_result', - 'httpLog', - 'wsLog', -]; diff --git a/server/src/admin/html/admin.html b/server/src/admin/html/admin.html index 491ea157..a0f07fcf 100644 --- a/server/src/admin/html/admin.html +++ b/server/src/admin/html/admin.html @@ -84,6 +84,15 @@ [data-channel='wsLog'] { color: #fdd7ac; /* 연갈색 */ } + [data-channel='system-stats'] { + color: #f4b6c2; /* 연분홍색 */ + } + [data-channel='ai_evaluate'] { + color: #ffdfba; /* 연살구색 */ + } + [data-channel='ai_evaluate_error'] { + color: #ff0000; /* 빨간색 */ + } /* 로그인 폼 스타일 */ #loginForm { @@ -102,7 +111,33 @@ color: red; /* 오류 메시지 색상 */ margin-bottom: 10px; /* 간격 */ } + + /* 차트 컨테이너 스타일 */ + .chart-container { + display: flex; /* Flexbox 레이아웃 사용 */ + flex-wrap: wrap; /* 줄 바꿈 허용 */ + justify-content: space-around; /* 요소들 사이에 공간을 균등하게 배분 */ + align-items: center; /* 세로 방향 중앙 정렬 */ + margin-bottom: 20px; /* 아래쪽 여백 */ + } + + .chart { + flex-basis: 30%; /* 각 차트가 차지할 너비 */ + margin-bottom: 20px; /* 아래쪽 여백 */ + } + + /* 화면 너비가 768픽셀 미만일 때의 스타일 */ + @media (max-width: 768px) { + .chart-container { + flex-direction: column; /* 요소들을 세로로 정렬 */ + } + + .chart { + flex-basis: 100%; /* 각 차트가 전체 너비를 차지하도록 설정 */ + } + } +