feat: Meilisearch 검색 기능 및 개발환경 통합
- Meilisearch 기반 일정 검색 API 구현 - 멤버 별명으로 검색 지원 (하냥 → 송하영) - 영문 자판 → 한글 변환 검색 지원 - 검색 응답 구조 개선 (category 객체, datetime 통합, members 배열) - 개발/배포 환경 Dockerfile 통합 (주석 전환 방식) - docker-compose.yml 단일 파일로 통합 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5521c44fa9
commit
abe9687cc8
15 changed files with 946 additions and 517 deletions
46
Dockerfile
46
Dockerfile
|
|
@ -1,27 +1,27 @@
|
||||||
# 빌드 스테이지 - 프론트엔드 빌드
|
# ============================================
|
||||||
FROM node:20-alpine AS frontend-builder
|
# 개발 모드
|
||||||
WORKDIR /frontend
|
# ============================================
|
||||||
COPY frontend/package*.json ./
|
|
||||||
RUN npm install
|
|
||||||
COPY frontend/ ./
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# 프로덕션 스테이지
|
|
||||||
FROM node:20-alpine
|
FROM node:20-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# ffmpeg 설치 (비디오 썸네일 추출용)
|
|
||||||
RUN apk add --no-cache ffmpeg
|
RUN apk add --no-cache ffmpeg
|
||||||
|
CMD ["sh", "-c", "cd /app/backend && npm install && cd /app/frontend && npm install --include=dev && (cd /app/backend && PORT=3000 npm run dev &) && sleep 3 && cd /app/frontend && npm run dev -- --host 0.0.0.0"]
|
||||||
|
|
||||||
# 백엔드 의존성 설치
|
# ============================================
|
||||||
COPY backend/package*.json ./
|
# 배포 모드 (사용 시 위 개발 모드를 주석처리)
|
||||||
RUN npm install --production
|
# ============================================
|
||||||
|
# FROM node:20-alpine AS frontend-builder
|
||||||
# 백엔드 파일 복사
|
# WORKDIR /frontend
|
||||||
COPY backend/ ./
|
# COPY frontend/package*.json ./
|
||||||
|
# RUN npm install
|
||||||
# 프론트엔드 빌드 결과물 복사
|
# COPY frontend/ ./
|
||||||
COPY --from=frontend-builder /frontend/dist ./dist
|
# RUN npm run build
|
||||||
|
#
|
||||||
EXPOSE 80
|
# FROM node:20-alpine
|
||||||
CMD ["npm", "start"]
|
# WORKDIR /app
|
||||||
|
# RUN apk add --no-cache ffmpeg
|
||||||
|
# COPY backend/package*.json ./
|
||||||
|
# RUN npm install --production
|
||||||
|
# COPY backend/ ./
|
||||||
|
# COPY --from=frontend-builder /frontend/dist ./dist
|
||||||
|
# EXPOSE 80
|
||||||
|
# CMD ["npm", "start"]
|
||||||
|
|
|
||||||
163
backend/package-lock.json
generated
163
backend/package-lock.json
generated
|
|
@ -21,6 +21,7 @@
|
||||||
"inko": "^1.1.1",
|
"inko": "^1.1.1",
|
||||||
"ioredis": "^5.4.2",
|
"ioredis": "^5.4.2",
|
||||||
"kiwi-nlp": "^0.22.1",
|
"kiwi-nlp": "^0.22.1",
|
||||||
|
"meilisearch": "^0.44.0",
|
||||||
"mysql2": "^3.12.0",
|
"mysql2": "^3.12.0",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"sharp": "^0.34.5"
|
"sharp": "^0.34.5"
|
||||||
|
|
@ -229,32 +230,32 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-s3": {
|
"node_modules/@aws-sdk/client-s3": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.971.0.tgz",
|
||||||
"integrity": "sha512-mDC792KkFzLZG9PS1Fv9b18lEzmSNBjAdweLJ83D2CZu6ved9+Pr/Dr+FRs0kSxqY+sUUUuIBmvDYHXY8E8EzA==",
|
"integrity": "sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha1-browser": "5.2.0",
|
"@aws-crypto/sha1-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.970.0",
|
"@aws-sdk/credential-provider-node": "3.971.0",
|
||||||
"@aws-sdk/middleware-bucket-endpoint": "3.969.0",
|
"@aws-sdk/middleware-bucket-endpoint": "3.969.0",
|
||||||
"@aws-sdk/middleware-expect-continue": "3.969.0",
|
"@aws-sdk/middleware-expect-continue": "3.969.0",
|
||||||
"@aws-sdk/middleware-flexible-checksums": "3.970.0",
|
"@aws-sdk/middleware-flexible-checksums": "3.971.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.969.0",
|
"@aws-sdk/middleware-host-header": "3.969.0",
|
||||||
"@aws-sdk/middleware-location-constraint": "3.969.0",
|
"@aws-sdk/middleware-location-constraint": "3.969.0",
|
||||||
"@aws-sdk/middleware-logger": "3.969.0",
|
"@aws-sdk/middleware-logger": "3.969.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.969.0",
|
"@aws-sdk/middleware-recursion-detection": "3.969.0",
|
||||||
"@aws-sdk/middleware-sdk-s3": "3.970.0",
|
"@aws-sdk/middleware-sdk-s3": "3.970.0",
|
||||||
"@aws-sdk/middleware-ssec": "3.969.0",
|
"@aws-sdk/middleware-ssec": "3.971.0",
|
||||||
"@aws-sdk/middleware-user-agent": "3.970.0",
|
"@aws-sdk/middleware-user-agent": "3.970.0",
|
||||||
"@aws-sdk/region-config-resolver": "3.969.0",
|
"@aws-sdk/region-config-resolver": "3.969.0",
|
||||||
"@aws-sdk/signature-v4-multi-region": "3.970.0",
|
"@aws-sdk/signature-v4-multi-region": "3.970.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@aws-sdk/util-endpoints": "3.970.0",
|
"@aws-sdk/util-endpoints": "3.970.0",
|
||||||
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.970.0",
|
"@aws-sdk/util-user-agent-node": "3.971.0",
|
||||||
"@smithy/config-resolver": "^4.4.6",
|
"@smithy/config-resolver": "^4.4.6",
|
||||||
"@smithy/core": "^3.20.6",
|
"@smithy/core": "^3.20.6",
|
||||||
"@smithy/eventstream-serde-browser": "^4.2.8",
|
"@smithy/eventstream-serde-browser": "^4.2.8",
|
||||||
|
|
@ -295,9 +296,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-sso": {
|
"node_modules/@aws-sdk/client-sso": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.971.0.tgz",
|
||||||
"integrity": "sha512-ArmgnOsSCXN5VyIvZb4kSP5hpqlRRHolrMtKQ/0N8Hw4MTb7/IeYHSZzVPNzzkuX6gn5Aj8txoUnDPM8O7pc9g==",
|
"integrity": "sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
|
|
@ -311,7 +312,7 @@
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@aws-sdk/util-endpoints": "3.970.0",
|
"@aws-sdk/util-endpoints": "3.970.0",
|
||||||
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.970.0",
|
"@aws-sdk/util-user-agent-node": "3.971.0",
|
||||||
"@smithy/config-resolver": "^4.4.6",
|
"@smithy/config-resolver": "^4.4.6",
|
||||||
"@smithy/core": "^3.20.6",
|
"@smithy/core": "^3.20.6",
|
||||||
"@smithy/fetch-http-handler": "^5.3.9",
|
"@smithy/fetch-http-handler": "^5.3.9",
|
||||||
|
|
@ -418,19 +419,19 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-ini": {
|
"node_modules/@aws-sdk/credential-provider-ini": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.971.0.tgz",
|
||||||
"integrity": "sha512-L5R1hN1FY/xCmH65DOYMXl8zqCFiAq0bAq8tJZU32mGjIl1GzGeOkeDa9c461d81o7gsQeYzXyqFD3vXEbJ+kQ==",
|
"integrity": "sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-env": "3.970.0",
|
"@aws-sdk/credential-provider-env": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-http": "3.970.0",
|
"@aws-sdk/credential-provider-http": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-login": "3.970.0",
|
"@aws-sdk/credential-provider-login": "3.971.0",
|
||||||
"@aws-sdk/credential-provider-process": "3.970.0",
|
"@aws-sdk/credential-provider-process": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-sso": "3.970.0",
|
"@aws-sdk/credential-provider-sso": "3.971.0",
|
||||||
"@aws-sdk/credential-provider-web-identity": "3.970.0",
|
"@aws-sdk/credential-provider-web-identity": "3.971.0",
|
||||||
"@aws-sdk/nested-clients": "3.970.0",
|
"@aws-sdk/nested-clients": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/credential-provider-imds": "^4.2.8",
|
"@smithy/credential-provider-imds": "^4.2.8",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
|
|
@ -443,13 +444,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-login": {
|
"node_modules/@aws-sdk/credential-provider-login": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.971.0.tgz",
|
||||||
"integrity": "sha512-C+1dcLr+p2E+9hbHyvrQTZ46Kj4vC2RoP6N935GEukHQa637ZjXs8VlyHJ2xTvbvwwLZQNiu56Cx7o/OFOqw1A==",
|
"integrity": "sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/nested-clients": "3.970.0",
|
"@aws-sdk/nested-clients": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/protocol-http": "^5.3.8",
|
"@smithy/protocol-http": "^5.3.8",
|
||||||
|
|
@ -462,17 +463,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-node": {
|
"node_modules/@aws-sdk/credential-provider-node": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.971.0.tgz",
|
||||||
"integrity": "sha512-nMM0eeVuiLtw1taLRQ+H/H5Qp11rva8ILrzAQXSvlbDeVmbc7d8EeW5Q2xnCJu+3U+2JNZ1uxqIL22pB2sLEMA==",
|
"integrity": "sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "3.970.0",
|
"@aws-sdk/credential-provider-env": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-http": "3.970.0",
|
"@aws-sdk/credential-provider-http": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-ini": "3.970.0",
|
"@aws-sdk/credential-provider-ini": "3.971.0",
|
||||||
"@aws-sdk/credential-provider-process": "3.970.0",
|
"@aws-sdk/credential-provider-process": "3.970.0",
|
||||||
"@aws-sdk/credential-provider-sso": "3.970.0",
|
"@aws-sdk/credential-provider-sso": "3.971.0",
|
||||||
"@aws-sdk/credential-provider-web-identity": "3.970.0",
|
"@aws-sdk/credential-provider-web-identity": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/credential-provider-imds": "^4.2.8",
|
"@smithy/credential-provider-imds": "^4.2.8",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
|
|
@ -502,14 +503,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-sso": {
|
"node_modules/@aws-sdk/credential-provider-sso": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.971.0.tgz",
|
||||||
"integrity": "sha512-ROb+Aijw8nzkB14Nh2XRH861++SeTZykUzk427y8YtgTLxjAOjgDTchDUFW2Fx6GFWkSjqJ3sY7SZyb33IqyFw==",
|
"integrity": "sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sso": "3.970.0",
|
"@aws-sdk/client-sso": "3.971.0",
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/token-providers": "3.970.0",
|
"@aws-sdk/token-providers": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/shared-ini-file-loader": "^4.4.3",
|
"@smithy/shared-ini-file-loader": "^4.4.3",
|
||||||
|
|
@ -521,13 +522,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-web-identity": {
|
"node_modules/@aws-sdk/credential-provider-web-identity": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.971.0.tgz",
|
||||||
"integrity": "sha512-r7tnYJJg+B6QvnsRHSW5vDol+ks6n+5jBZdCFdGyK63hjcMRMqHx59zEH8O47UR1PFv5hS2Q3uGz6HXvVtP40Q==",
|
"integrity": "sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/nested-clients": "3.970.0",
|
"@aws-sdk/nested-clients": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/shared-ini-file-loader": "^4.4.3",
|
"@smithy/shared-ini-file-loader": "^4.4.3",
|
||||||
|
|
@ -572,9 +573,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/middleware-flexible-checksums": {
|
"node_modules/@aws-sdk/middleware-flexible-checksums": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.971.0.tgz",
|
||||||
"integrity": "sha512-mlKLwX0jWa5EwIvMjJAvVFL/zLAxB/fNLOg4hQCNCUf1qi+XxD+brDopXNPWeA8bSCnpvWfZrQd5yNksG6Fzqg==",
|
"integrity": "sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/crc32": "5.2.0",
|
"@aws-crypto/crc32": "5.2.0",
|
||||||
|
|
@ -681,9 +682,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/middleware-ssec": {
|
"node_modules/@aws-sdk/middleware-ssec": {
|
||||||
"version": "3.969.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.969.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.971.0.tgz",
|
||||||
"integrity": "sha512-9wUYtd5ye4exygKHyl02lPVHUoAFlxxXoqvlw7u2sycfkK6uHLlwdsPru3MkMwj47ZSZs+lkyP/sVKXVMhuaAg==",
|
"integrity": "sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
|
|
@ -713,9 +714,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/nested-clients": {
|
"node_modules/@aws-sdk/nested-clients": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.971.0.tgz",
|
||||||
"integrity": "sha512-RIl8s4DCa31MXtRFw23iU90OqEoWuwQxiZOZshzsPtjyrunhHFjyZJEqb+vuQcYd1o22SMaYa3lPJRp64OH35Q==",
|
"integrity": "sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
|
|
@ -729,7 +730,7 @@
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@aws-sdk/util-endpoints": "3.970.0",
|
"@aws-sdk/util-endpoints": "3.970.0",
|
||||||
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
"@aws-sdk/util-user-agent-browser": "3.969.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.970.0",
|
"@aws-sdk/util-user-agent-node": "3.971.0",
|
||||||
"@smithy/config-resolver": "^4.4.6",
|
"@smithy/config-resolver": "^4.4.6",
|
||||||
"@smithy/core": "^3.20.6",
|
"@smithy/core": "^3.20.6",
|
||||||
"@smithy/fetch-http-handler": "^5.3.9",
|
"@smithy/fetch-http-handler": "^5.3.9",
|
||||||
|
|
@ -795,13 +796,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/token-providers": {
|
"node_modules/@aws-sdk/token-providers": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.971.0.tgz",
|
||||||
"integrity": "sha512-YO8KgJecxHIFMhfoP880q51VXFL9V1ELywK5yzVEqzyrwqoG93IUmnTygBUylQrfkbH+QqS0FxEdgwpP3fcwoQ==",
|
"integrity": "sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "3.970.0",
|
"@aws-sdk/core": "3.970.0",
|
||||||
"@aws-sdk/nested-clients": "3.970.0",
|
"@aws-sdk/nested-clients": "3.971.0",
|
||||||
"@aws-sdk/types": "3.969.0",
|
"@aws-sdk/types": "3.969.0",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/shared-ini-file-loader": "^4.4.3",
|
"@smithy/shared-ini-file-loader": "^4.4.3",
|
||||||
|
|
@ -878,9 +879,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/util-user-agent-node": {
|
"node_modules/@aws-sdk/util-user-agent-node": {
|
||||||
"version": "3.970.0",
|
"version": "3.971.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.970.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.971.0.tgz",
|
||||||
"integrity": "sha512-TNQpwIVD6SxMwkD+QKnaujKVyXy5ljN3O3jrI7nCHJ3GlJu5xJrd8yuBnanYCcrn3e2zwdfOh4d4zJAZvvIvVw==",
|
"integrity": "sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/middleware-user-agent": "3.970.0",
|
"@aws-sdk/middleware-user-agent": "3.970.0",
|
||||||
|
|
@ -1894,9 +1895,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/core": {
|
"node_modules/@smithy/core": {
|
||||||
"version": "3.20.6",
|
"version": "3.20.7",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.6.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.7.tgz",
|
||||||
"integrity": "sha512-BpAffW1mIyRZongoKBbh3RgHG+JDHJek/8hjA/9LnPunM+ejorO6axkxCgwxCe4K//g/JdPeR9vROHDYr/hfnQ==",
|
"integrity": "sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/middleware-serde": "^4.2.9",
|
"@smithy/middleware-serde": "^4.2.9",
|
||||||
|
|
@ -2114,12 +2115,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/middleware-endpoint": {
|
"node_modules/@smithy/middleware-endpoint": {
|
||||||
"version": "4.4.7",
|
"version": "4.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.8.tgz",
|
||||||
"integrity": "sha512-SCmhUG1UwtnEhF5Sxd8qk7bJwkj1BpFzFlHkXqKCEmDPLrRjJyTGM0EhqT7XBtDaDJjCfjRJQodgZcKDR843qg==",
|
"integrity": "sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.20.6",
|
"@smithy/core": "^3.20.7",
|
||||||
"@smithy/middleware-serde": "^4.2.9",
|
"@smithy/middleware-serde": "^4.2.9",
|
||||||
"@smithy/node-config-provider": "^4.3.8",
|
"@smithy/node-config-provider": "^4.3.8",
|
||||||
"@smithy/shared-ini-file-loader": "^4.4.3",
|
"@smithy/shared-ini-file-loader": "^4.4.3",
|
||||||
|
|
@ -2133,15 +2134,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/middleware-retry": {
|
"node_modules/@smithy/middleware-retry": {
|
||||||
"version": "4.4.23",
|
"version": "4.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.23.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.24.tgz",
|
||||||
"integrity": "sha512-lLEmkQj7I7oKfvZ1wsnToGJouLOtfkMXDKRA1Hi6F+mMp5O1N8GcVWmVeNgTtgZtd0OTXDTI2vpVQmeutydGew==",
|
"integrity": "sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/node-config-provider": "^4.3.8",
|
"@smithy/node-config-provider": "^4.3.8",
|
||||||
"@smithy/protocol-http": "^5.3.8",
|
"@smithy/protocol-http": "^5.3.8",
|
||||||
"@smithy/service-error-classification": "^4.2.8",
|
"@smithy/service-error-classification": "^4.2.8",
|
||||||
"@smithy/smithy-client": "^4.10.8",
|
"@smithy/smithy-client": "^4.10.9",
|
||||||
"@smithy/types": "^4.12.0",
|
"@smithy/types": "^4.12.0",
|
||||||
"@smithy/util-middleware": "^4.2.8",
|
"@smithy/util-middleware": "^4.2.8",
|
||||||
"@smithy/util-retry": "^4.2.8",
|
"@smithy/util-retry": "^4.2.8",
|
||||||
|
|
@ -2308,13 +2309,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/smithy-client": {
|
"node_modules/@smithy/smithy-client": {
|
||||||
"version": "4.10.8",
|
"version": "4.10.9",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.8.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.9.tgz",
|
||||||
"integrity": "sha512-wcr3UEL26k7lLoyf9eVDZoD1nNY3Fa1gbNuOXvfxvVWLGkOVW+RYZgUUp/bXHryJfycIOQnBq9o1JAE00ax8HQ==",
|
"integrity": "sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.20.6",
|
"@smithy/core": "^3.20.7",
|
||||||
"@smithy/middleware-endpoint": "^4.4.7",
|
"@smithy/middleware-endpoint": "^4.4.8",
|
||||||
"@smithy/middleware-stack": "^4.2.8",
|
"@smithy/middleware-stack": "^4.2.8",
|
||||||
"@smithy/protocol-http": "^5.3.8",
|
"@smithy/protocol-http": "^5.3.8",
|
||||||
"@smithy/types": "^4.12.0",
|
"@smithy/types": "^4.12.0",
|
||||||
|
|
@ -2415,13 +2416,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/util-defaults-mode-browser": {
|
"node_modules/@smithy/util-defaults-mode-browser": {
|
||||||
"version": "4.3.22",
|
"version": "4.3.23",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.22.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.23.tgz",
|
||||||
"integrity": "sha512-O2WXr6ZRqPnbyoepb7pKcLt1QL6uRfFzGYJ9sGb5hMJQi7v/4RjRmCQa9mNjA0YiXqsc5lBmLXqJPhjM1Vjv5A==",
|
"integrity": "sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/smithy-client": "^4.10.8",
|
"@smithy/smithy-client": "^4.10.9",
|
||||||
"@smithy/types": "^4.12.0",
|
"@smithy/types": "^4.12.0",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
|
|
@ -2430,16 +2431,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/util-defaults-mode-node": {
|
"node_modules/@smithy/util-defaults-mode-node": {
|
||||||
"version": "4.2.25",
|
"version": "4.2.26",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.25.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.26.tgz",
|
||||||
"integrity": "sha512-7uMhppVNRbgNIpyUBVRfjGHxygP85wpXalRvn9DvUlCx4qgy1AB/uxOPSiDx/jFyrwD3/BypQhx1JK7f3yxrAw==",
|
"integrity": "sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/config-resolver": "^4.4.6",
|
"@smithy/config-resolver": "^4.4.6",
|
||||||
"@smithy/credential-provider-imds": "^4.2.8",
|
"@smithy/credential-provider-imds": "^4.2.8",
|
||||||
"@smithy/node-config-provider": "^4.3.8",
|
"@smithy/node-config-provider": "^4.3.8",
|
||||||
"@smithy/property-provider": "^4.2.8",
|
"@smithy/property-provider": "^4.2.8",
|
||||||
"@smithy/smithy-client": "^4.10.8",
|
"@smithy/smithy-client": "^4.10.9",
|
||||||
"@smithy/types": "^4.12.0",
|
"@smithy/types": "^4.12.0",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
|
|
@ -3484,6 +3485,12 @@
|
||||||
"url": "https://github.com/sponsors/wellwelwel"
|
"url": "https://github.com/sponsors/wellwelwel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/meilisearch": {
|
||||||
|
"version": "0.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.44.1.tgz",
|
||||||
|
"integrity": "sha512-ZTZYBmomtRwjaWbvU8U8ct04g/YnrNOlvchogJOPgHcQIQBfjdbAvMJ8mLhuZEzpioYXIT6Cv+FcE150pc2+nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/mime": {
|
"node_modules/mime": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"inko": "^1.1.1",
|
"inko": "^1.1.1",
|
||||||
"ioredis": "^5.4.2",
|
"ioredis": "^5.4.2",
|
||||||
"kiwi-nlp": "^0.22.1",
|
"kiwi-nlp": "^0.22.1",
|
||||||
|
"meilisearch": "^0.44.0",
|
||||||
"mysql2": "^3.12.0",
|
"mysql2": "^3.12.0",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"sharp": "^0.34.5"
|
"sharp": "^0.34.5"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import config from './config/index.js';
|
||||||
import dbPlugin from './plugins/db.js';
|
import dbPlugin from './plugins/db.js';
|
||||||
import redisPlugin from './plugins/redis.js';
|
import redisPlugin from './plugins/redis.js';
|
||||||
import authPlugin from './plugins/auth.js';
|
import authPlugin from './plugins/auth.js';
|
||||||
|
import meilisearchPlugin from './plugins/meilisearch.js';
|
||||||
import youtubeBotPlugin from './services/youtube/index.js';
|
import youtubeBotPlugin from './services/youtube/index.js';
|
||||||
import xBotPlugin from './services/x/index.js';
|
import xBotPlugin from './services/x/index.js';
|
||||||
import schedulerPlugin from './plugins/scheduler.js';
|
import schedulerPlugin from './plugins/scheduler.js';
|
||||||
|
|
@ -44,6 +45,7 @@ export async function buildApp(opts = {}) {
|
||||||
await fastify.register(dbPlugin);
|
await fastify.register(dbPlugin);
|
||||||
await fastify.register(redisPlugin);
|
await fastify.register(redisPlugin);
|
||||||
await fastify.register(authPlugin);
|
await fastify.register(authPlugin);
|
||||||
|
await fastify.register(meilisearchPlugin);
|
||||||
await fastify.register(youtubeBotPlugin);
|
await fastify.register(youtubeBotPlugin);
|
||||||
await fastify.register(xBotPlugin);
|
await fastify.register(xBotPlugin);
|
||||||
await fastify.register(schedulerPlugin);
|
await fastify.register(schedulerPlugin);
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,8 @@ export default {
|
||||||
bucket: process.env.RUSTFS_BUCKET || 'fromis-9',
|
bucket: process.env.RUSTFS_BUCKET || 'fromis-9',
|
||||||
publicUrl: process.env.RUSTFS_PUBLIC_URL,
|
publicUrl: process.env.RUSTFS_PUBLIC_URL,
|
||||||
},
|
},
|
||||||
|
meilisearch: {
|
||||||
|
host: process.env.MEILI_HOST || 'http://fromis9-meilisearch:7700',
|
||||||
|
apiKey: process.env.MEILI_MASTER_KEY,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
92
backend/src/plugins/meilisearch.js
Normal file
92
backend/src/plugins/meilisearch.js
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import fp from 'fastify-plugin';
|
||||||
|
import { MeiliSearch } from 'meilisearch';
|
||||||
|
|
||||||
|
const INDEX_NAME = 'schedules';
|
||||||
|
|
||||||
|
async function meilisearchPlugin(fastify, opts) {
|
||||||
|
const { host, apiKey } = fastify.config.meilisearch;
|
||||||
|
|
||||||
|
const client = new MeiliSearch({
|
||||||
|
host,
|
||||||
|
apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 연결 테스트 및 인덱스 초기화
|
||||||
|
try {
|
||||||
|
await client.health();
|
||||||
|
fastify.log.info('Meilisearch 연결 성공');
|
||||||
|
|
||||||
|
// 인덱스 초기화
|
||||||
|
await initIndex(client, fastify.log);
|
||||||
|
} catch (err) {
|
||||||
|
fastify.log.error('Meilisearch 연결 실패:', err.message);
|
||||||
|
// Meilisearch가 없어도 서버는 동작하도록 에러를 던지지 않음
|
||||||
|
}
|
||||||
|
|
||||||
|
fastify.decorate('meilisearch', client);
|
||||||
|
fastify.decorate('meilisearchIndex', INDEX_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인덱스 초기화 및 설정
|
||||||
|
*/
|
||||||
|
async function initIndex(client, log) {
|
||||||
|
try {
|
||||||
|
// 인덱스 생성 (이미 존재하면 무시)
|
||||||
|
try {
|
||||||
|
await client.createIndex(INDEX_NAME, { primaryKey: 'id' });
|
||||||
|
} catch (err) {
|
||||||
|
// 이미 존재하는 경우 무시
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = client.index(INDEX_NAME);
|
||||||
|
|
||||||
|
// 검색 가능한 필드 설정 (순서가 우선순위 결정)
|
||||||
|
await index.updateSearchableAttributes([
|
||||||
|
'title',
|
||||||
|
'member_names',
|
||||||
|
'description',
|
||||||
|
'source_name',
|
||||||
|
'category_name',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 필터링 가능한 필드 설정
|
||||||
|
await index.updateFilterableAttributes(['category_id', 'date']);
|
||||||
|
|
||||||
|
// 정렬 가능한 필드 설정
|
||||||
|
await index.updateSortableAttributes(['date', 'time']);
|
||||||
|
|
||||||
|
// 랭킹 규칙 설정 (동일 유사도일 때 최신 날짜 우선)
|
||||||
|
await index.updateRankingRules([
|
||||||
|
'words',
|
||||||
|
'typo',
|
||||||
|
'proximity',
|
||||||
|
'attribute',
|
||||||
|
'exactness',
|
||||||
|
'date:desc',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 오타 허용 설정
|
||||||
|
await index.updateTypoTolerance({
|
||||||
|
enabled: true,
|
||||||
|
minWordSizeForTypos: {
|
||||||
|
oneTypo: 2,
|
||||||
|
twoTypos: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 페이징 설정
|
||||||
|
await index.updatePagination({
|
||||||
|
maxTotalHits: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info('Meilisearch 인덱스 초기화 완료');
|
||||||
|
} catch (err) {
|
||||||
|
log.error('Meilisearch 인덱스 초기화 오류:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fp(meilisearchPlugin, {
|
||||||
|
name: 'meilisearch',
|
||||||
|
dependencies: ['db'],
|
||||||
|
});
|
||||||
|
|
@ -3,103 +3,64 @@
|
||||||
* GET: 공개, POST/PUT/DELETE: 인증 필요
|
* GET: 공개, POST/PUT/DELETE: 인증 필요
|
||||||
*/
|
*/
|
||||||
import suggestionsRoutes from './suggestions.js';
|
import suggestionsRoutes from './suggestions.js';
|
||||||
|
import { searchSchedules, syncAllSchedules } from '../../services/meilisearch/index.js';
|
||||||
|
|
||||||
export default async function schedulesRoutes(fastify) {
|
export default async function schedulesRoutes(fastify) {
|
||||||
const { db } = fastify;
|
const { db, meilisearch, redis } = fastify;
|
||||||
|
|
||||||
// 추천 검색어 라우트 등록
|
// 추천 검색어 라우트 등록
|
||||||
fastify.register(suggestionsRoutes, { prefix: '/suggestions' });
|
fastify.register(suggestionsRoutes, { prefix: '/suggestions' });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/schedules
|
* GET /api/schedules
|
||||||
* 월별 일정 목록 조회
|
* 검색 모드: search 파라미터가 있으면 Meilisearch 검색
|
||||||
* @query year - 년도 (필수)
|
* 월별 조회 모드: year, month 파라미터로 월별 조회
|
||||||
* @query month - 월 (필수)
|
|
||||||
*/
|
*/
|
||||||
fastify.get('/', {
|
fastify.get('/', {
|
||||||
schema: {
|
schema: {
|
||||||
tags: ['schedules'],
|
tags: ['schedules'],
|
||||||
summary: '월별 일정 목록 조회',
|
summary: '일정 조회 (검색 또는 월별)',
|
||||||
querystring: {
|
querystring: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: ['year', 'month'],
|
|
||||||
properties: {
|
properties: {
|
||||||
|
search: { type: 'string', description: '검색어' },
|
||||||
year: { type: 'integer', description: '년도' },
|
year: { type: 'integer', description: '년도' },
|
||||||
month: { type: 'integer', minimum: 1, maximum: 12, description: '월' },
|
month: { type: 'integer', minimum: 1, maximum: 12, description: '월' },
|
||||||
|
offset: { type: 'integer', default: 0, description: '페이지 오프셋' },
|
||||||
|
limit: { type: 'integer', default: 100, description: '결과 개수' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, async (request, reply) => {
|
}, async (request, reply) => {
|
||||||
const { year, month } = request.query;
|
const { search, year, month, offset = 0, limit = 100 } = request.query;
|
||||||
|
|
||||||
|
// 검색 모드
|
||||||
|
if (search && search.trim()) {
|
||||||
|
return await handleSearch(fastify, search.trim(), parseInt(offset), parseInt(limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 월별 조회 모드
|
||||||
if (!year || !month) {
|
if (!year || !month) {
|
||||||
return reply.code(400).send({ error: 'year와 month는 필수입니다.' });
|
return reply.code(400).send({ error: 'search 또는 year/month는 필수입니다.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
|
return await handleMonthlySchedules(db, parseInt(year), parseInt(month));
|
||||||
const endDate = new Date(year, month, 0).toISOString().split('T')[0];
|
});
|
||||||
|
|
||||||
const [schedules] = await db.query(`
|
/**
|
||||||
SELECT
|
* POST /api/schedules/sync-search
|
||||||
s.id,
|
* Meilisearch 전체 동기화 (관리자 전용)
|
||||||
s.title,
|
*/
|
||||||
s.date,
|
fastify.post('/sync-search', {
|
||||||
s.time,
|
schema: {
|
||||||
s.category_id,
|
tags: ['schedules'],
|
||||||
c.name as category_name,
|
summary: 'Meilisearch 전체 동기화',
|
||||||
c.color as category_color,
|
security: [{ bearerAuth: [] }],
|
||||||
sy.channel_name as source_name
|
},
|
||||||
FROM schedules s
|
preHandler: [fastify.authenticate],
|
||||||
LEFT JOIN schedule_categories c ON s.category_id = c.id
|
}, async (request, reply) => {
|
||||||
LEFT JOIN schedule_youtube sy ON s.id = sy.schedule_id
|
const count = await syncAllSchedules(meilisearch, db);
|
||||||
WHERE s.date BETWEEN ? AND ?
|
return { success: true, synced: count };
|
||||||
ORDER BY s.date ASC, s.time ASC
|
|
||||||
`, [startDate, endDate]);
|
|
||||||
|
|
||||||
// 날짜별로 그룹화
|
|
||||||
const grouped = {};
|
|
||||||
|
|
||||||
for (const s of schedules) {
|
|
||||||
const dateKey = s.date.toISOString().split('T')[0];
|
|
||||||
|
|
||||||
if (!grouped[dateKey]) {
|
|
||||||
grouped[dateKey] = {
|
|
||||||
categories: [],
|
|
||||||
schedules: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일정 추가
|
|
||||||
const schedule = {
|
|
||||||
id: s.id,
|
|
||||||
title: s.title,
|
|
||||||
time: s.time,
|
|
||||||
category: {
|
|
||||||
id: s.category_id,
|
|
||||||
name: s.category_name,
|
|
||||||
color: s.category_color,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (s.source_name) {
|
|
||||||
schedule.source_name = s.source_name;
|
|
||||||
}
|
|
||||||
grouped[dateKey].schedules.push(schedule);
|
|
||||||
|
|
||||||
// 카테고리 카운트
|
|
||||||
const existingCategory = grouped[dateKey].categories.find(c => c.id === s.category_id);
|
|
||||||
if (existingCategory) {
|
|
||||||
existingCategory.count++;
|
|
||||||
} else {
|
|
||||||
grouped[dateKey].categories.push({
|
|
||||||
id: s.category_id,
|
|
||||||
name: s.category_name,
|
|
||||||
color: s.category_color,
|
|
||||||
count: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grouped;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -150,3 +111,170 @@ export default async function schedulesRoutes(fastify) {
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 검색 처리
|
||||||
|
*/
|
||||||
|
async function handleSearch(fastify, query, offset, limit) {
|
||||||
|
const { db, meilisearch, redis } = fastify;
|
||||||
|
|
||||||
|
// 첫 페이지 검색 시에만 검색어 저장 (bi-gram 학습)
|
||||||
|
if (offset === 0) {
|
||||||
|
// 비동기로 저장 (응답 지연 방지)
|
||||||
|
saveSearchQueryAsync(fastify, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meilisearch 검색
|
||||||
|
const results = await searchSchedules(meilisearch, db, query, { limit: 1000 });
|
||||||
|
|
||||||
|
// 페이징 적용
|
||||||
|
const paginatedHits = results.hits.slice(offset, offset + limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
schedules: paginatedHits,
|
||||||
|
total: results.total,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
hasMore: offset + paginatedHits.length < results.total,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 검색어 비동기 저장
|
||||||
|
*/
|
||||||
|
async function saveSearchQueryAsync(fastify, query) {
|
||||||
|
try {
|
||||||
|
// suggestions 서비스의 saveSearchQuery 사용
|
||||||
|
const { SuggestionService } = await import('../../services/suggestions/index.js');
|
||||||
|
const service = new SuggestionService(fastify.db, fastify.redis);
|
||||||
|
await service.saveSearchQuery(query);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Search] 검색어 저장 실패:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 월별 일정 조회 (생일 포함)
|
||||||
|
*/
|
||||||
|
async function handleMonthlySchedules(db, year, month) {
|
||||||
|
const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
|
||||||
|
const endDate = new Date(year, month, 0).toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// 일정 조회
|
||||||
|
const [schedules] = await db.query(`
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.title,
|
||||||
|
s.date,
|
||||||
|
s.time,
|
||||||
|
s.category_id,
|
||||||
|
c.name as category_name,
|
||||||
|
c.color as category_color,
|
||||||
|
sy.channel_name as source_name
|
||||||
|
FROM schedules s
|
||||||
|
LEFT JOIN schedule_categories c ON s.category_id = c.id
|
||||||
|
LEFT JOIN schedule_youtube sy ON s.id = sy.schedule_id
|
||||||
|
WHERE s.date BETWEEN ? AND ?
|
||||||
|
ORDER BY s.date ASC, s.time ASC
|
||||||
|
`, [startDate, endDate]);
|
||||||
|
|
||||||
|
// 생일 조회
|
||||||
|
const [birthdays] = await db.query(`
|
||||||
|
SELECT m.id, m.name, m.name_en, m.birth_date,
|
||||||
|
i.thumb_url as image_url
|
||||||
|
FROM members m
|
||||||
|
LEFT JOIN images i ON m.image_id = i.id
|
||||||
|
WHERE m.is_former = 0 AND MONTH(m.birth_date) = ?
|
||||||
|
`, [month]);
|
||||||
|
|
||||||
|
// 날짜별로 그룹화
|
||||||
|
const grouped = {};
|
||||||
|
|
||||||
|
// 일정 추가
|
||||||
|
for (const s of schedules) {
|
||||||
|
const dateKey = s.date instanceof Date
|
||||||
|
? s.date.toISOString().split('T')[0]
|
||||||
|
: s.date;
|
||||||
|
|
||||||
|
if (!grouped[dateKey]) {
|
||||||
|
grouped[dateKey] = {
|
||||||
|
categories: [],
|
||||||
|
schedules: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedule = {
|
||||||
|
id: s.id,
|
||||||
|
title: s.title,
|
||||||
|
time: s.time,
|
||||||
|
category: {
|
||||||
|
id: s.category_id,
|
||||||
|
name: s.category_name,
|
||||||
|
color: s.category_color,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (s.source_name) {
|
||||||
|
schedule.source_name = s.source_name;
|
||||||
|
}
|
||||||
|
grouped[dateKey].schedules.push(schedule);
|
||||||
|
|
||||||
|
// 카테고리 카운트
|
||||||
|
const existingCategory = grouped[dateKey].categories.find(c => c.id === s.category_id);
|
||||||
|
if (existingCategory) {
|
||||||
|
existingCategory.count++;
|
||||||
|
} else {
|
||||||
|
grouped[dateKey].categories.push({
|
||||||
|
id: s.category_id,
|
||||||
|
name: s.category_name,
|
||||||
|
color: s.category_color,
|
||||||
|
count: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 생일 일정 추가
|
||||||
|
for (const member of birthdays) {
|
||||||
|
const birthDate = new Date(member.birth_date);
|
||||||
|
const birthdayThisYear = new Date(year, birthDate.getMonth(), birthDate.getDate());
|
||||||
|
const dateKey = birthdayThisYear.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
if (!grouped[dateKey]) {
|
||||||
|
grouped[dateKey] = {
|
||||||
|
categories: [],
|
||||||
|
schedules: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 생일 카테고리 (id: 8)
|
||||||
|
const BIRTHDAY_CATEGORY = {
|
||||||
|
id: 8,
|
||||||
|
name: '생일',
|
||||||
|
color: '#f472b6',
|
||||||
|
};
|
||||||
|
|
||||||
|
const birthdaySchedule = {
|
||||||
|
id: `birthday-${member.id}`,
|
||||||
|
title: `HAPPY ${member.name_en} DAY`,
|
||||||
|
time: null,
|
||||||
|
category: BIRTHDAY_CATEGORY,
|
||||||
|
is_birthday: true,
|
||||||
|
member_name: member.name,
|
||||||
|
member_image: member.image_url,
|
||||||
|
};
|
||||||
|
|
||||||
|
grouped[dateKey].schedules.push(birthdaySchedule);
|
||||||
|
|
||||||
|
// 생일 카테고리 카운트
|
||||||
|
const existingBirthdayCategory = grouped[dateKey].categories.find(c => c.id === 8);
|
||||||
|
if (existingBirthdayCategory) {
|
||||||
|
existingBirthdayCategory.count++;
|
||||||
|
} else {
|
||||||
|
grouped[dateKey].categories.push({
|
||||||
|
...BIRTHDAY_CATEGORY,
|
||||||
|
count: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grouped;
|
||||||
|
}
|
||||||
|
|
|
||||||
249
backend/src/services/meilisearch/index.js
Normal file
249
backend/src/services/meilisearch/index.js
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
/**
|
||||||
|
* Meilisearch 검색 서비스
|
||||||
|
* - 일정 검색 (멤버 별명 → 이름 변환)
|
||||||
|
* - 영문 자판 → 한글 변환
|
||||||
|
* - 유사도 필터링
|
||||||
|
* - 일정 동기화
|
||||||
|
*/
|
||||||
|
import Inko from 'inko';
|
||||||
|
|
||||||
|
const inko = new Inko();
|
||||||
|
const INDEX_NAME = 'schedules';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 영문 자판으로 입력된 검색어인지 확인
|
||||||
|
*/
|
||||||
|
function isEnglishKeyboard(text) {
|
||||||
|
const englishChars = text.match(/[a-zA-Z]/g) || [];
|
||||||
|
const koreanChars = text.match(/[가-힣ㄱ-ㅎㅏ-ㅣ]/g) || [];
|
||||||
|
return englishChars.length > 0 && koreanChars.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 별명/이름으로 멤버 이름 조회
|
||||||
|
*/
|
||||||
|
export async function resolveMemberNames(db, query) {
|
||||||
|
const searchTerm = `%${query}%`;
|
||||||
|
const [members] = await db.query(`
|
||||||
|
SELECT DISTINCT m.name
|
||||||
|
FROM members m
|
||||||
|
LEFT JOIN member_nicknames mn ON m.id = mn.member_id
|
||||||
|
WHERE m.name LIKE ? OR mn.nickname LIKE ?
|
||||||
|
`, [searchTerm, searchTerm]);
|
||||||
|
|
||||||
|
return members.map(m => m.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일정 검색
|
||||||
|
* @param {object} meilisearch - Meilisearch 클라이언트
|
||||||
|
* @param {object} db - DB 연결 풀
|
||||||
|
* @param {string} query - 검색어
|
||||||
|
* @param {object} options - 검색 옵션
|
||||||
|
*/
|
||||||
|
export async function searchSchedules(meilisearch, db, query, options = {}) {
|
||||||
|
const { limit = 1000, offset = 0 } = options;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const index = meilisearch.index(INDEX_NAME);
|
||||||
|
|
||||||
|
const searchOptions = {
|
||||||
|
limit,
|
||||||
|
offset: 0, // 내부적으로 전체 검색 후 필터링
|
||||||
|
attributesToRetrieve: ['*'],
|
||||||
|
showRankingScore: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 검색어 목록 구성
|
||||||
|
const searchQueries = [query];
|
||||||
|
|
||||||
|
// 영문 자판 입력 → 한글 변환
|
||||||
|
if (isEnglishKeyboard(query)) {
|
||||||
|
const koreanQuery = inko.en2ko(query);
|
||||||
|
if (koreanQuery !== query) {
|
||||||
|
searchQueries.push(koreanQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 별명 → 멤버 이름 변환
|
||||||
|
const memberNames = await resolveMemberNames(db, query);
|
||||||
|
for (const name of memberNames) {
|
||||||
|
if (!searchQueries.includes(name)) {
|
||||||
|
searchQueries.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 각 검색어로 검색 후 병합
|
||||||
|
const allHits = new Map(); // id 기준 중복 제거
|
||||||
|
|
||||||
|
for (const q of searchQueries) {
|
||||||
|
const results = await index.search(q, searchOptions);
|
||||||
|
for (const hit of results.hits) {
|
||||||
|
// 더 높은 점수로 업데이트
|
||||||
|
if (!allHits.has(hit.id) || allHits.get(hit.id)._rankingScore < hit._rankingScore) {
|
||||||
|
allHits.set(hit.id, hit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 유사도 0.5 미만 필터링
|
||||||
|
let filteredHits = Array.from(allHits.values())
|
||||||
|
.filter(hit => hit._rankingScore >= 0.5);
|
||||||
|
|
||||||
|
// 유사도 순 정렬
|
||||||
|
filteredHits.sort((a, b) => (b._rankingScore || 0) - (a._rankingScore || 0));
|
||||||
|
|
||||||
|
const total = filteredHits.length;
|
||||||
|
|
||||||
|
// 페이징 적용
|
||||||
|
const paginatedHits = filteredHits.slice(offset, offset + limit);
|
||||||
|
|
||||||
|
// 응답 형식 변환
|
||||||
|
const formattedHits = paginatedHits.map(formatScheduleResponse);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hits: formattedHits,
|
||||||
|
total,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
hasMore: offset + paginatedHits.length < total,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Meilisearch] 검색 오류:', err.message);
|
||||||
|
return { hits: [], total: 0, offset: 0, limit, hasMore: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 검색 결과 응답 형식 변환
|
||||||
|
*/
|
||||||
|
function formatScheduleResponse(hit) {
|
||||||
|
// date + time 합치기
|
||||||
|
let datetime = null;
|
||||||
|
if (hit.date) {
|
||||||
|
const dateStr = hit.date instanceof Date
|
||||||
|
? hit.date.toISOString().split('T')[0]
|
||||||
|
: String(hit.date).split('T')[0];
|
||||||
|
|
||||||
|
if (hit.time) {
|
||||||
|
datetime = `${dateStr}T${hit.time}`;
|
||||||
|
} else {
|
||||||
|
datetime = dateStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// member_names를 배열로 변환
|
||||||
|
const members = hit.member_names
|
||||||
|
? hit.member_names.split(',').map(name => name.trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: hit.id,
|
||||||
|
title: hit.title,
|
||||||
|
datetime,
|
||||||
|
category: {
|
||||||
|
id: hit.category_id,
|
||||||
|
name: hit.category_name,
|
||||||
|
color: hit.category_color,
|
||||||
|
},
|
||||||
|
source_name: hit.source_name || null,
|
||||||
|
members,
|
||||||
|
_rankingScore: hit._rankingScore,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일정 추가/업데이트
|
||||||
|
*/
|
||||||
|
export async function addOrUpdateSchedule(meilisearch, schedule) {
|
||||||
|
try {
|
||||||
|
const index = meilisearch.index(INDEX_NAME);
|
||||||
|
|
||||||
|
const document = {
|
||||||
|
id: schedule.id,
|
||||||
|
title: schedule.title,
|
||||||
|
description: schedule.description || '',
|
||||||
|
date: schedule.date,
|
||||||
|
time: schedule.time || '',
|
||||||
|
category_id: schedule.category_id,
|
||||||
|
category_name: schedule.category_name || '',
|
||||||
|
category_color: schedule.category_color || '',
|
||||||
|
source_name: schedule.source_name || '',
|
||||||
|
member_names: schedule.member_names || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
await index.addDocuments([document]);
|
||||||
|
console.log(`[Meilisearch] 일정 추가/업데이트: ${schedule.id}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Meilisearch] 문서 추가 오류:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일정 삭제
|
||||||
|
*/
|
||||||
|
export async function deleteSchedule(meilisearch, scheduleId) {
|
||||||
|
try {
|
||||||
|
const index = meilisearch.index(INDEX_NAME);
|
||||||
|
await index.deleteDocument(scheduleId);
|
||||||
|
console.log(`[Meilisearch] 일정 삭제: ${scheduleId}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Meilisearch] 문서 삭제 오류:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 일정 동기화
|
||||||
|
*/
|
||||||
|
export async function syncAllSchedules(meilisearch, db) {
|
||||||
|
try {
|
||||||
|
// DB에서 모든 일정 조회
|
||||||
|
const [schedules] = await db.query(`
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.title,
|
||||||
|
s.description,
|
||||||
|
s.date,
|
||||||
|
s.time,
|
||||||
|
s.category_id,
|
||||||
|
c.name as category_name,
|
||||||
|
c.color as category_color,
|
||||||
|
sy.channel_name as source_name,
|
||||||
|
GROUP_CONCAT(DISTINCT m.name ORDER BY m.id SEPARATOR ',') as member_names
|
||||||
|
FROM schedules s
|
||||||
|
LEFT JOIN schedule_categories c ON s.category_id = c.id
|
||||||
|
LEFT JOIN schedule_youtube sy ON s.id = sy.schedule_id
|
||||||
|
LEFT JOIN schedule_members sm ON s.id = sm.schedule_id
|
||||||
|
LEFT JOIN members m ON sm.member_id = m.id
|
||||||
|
GROUP BY s.id
|
||||||
|
`);
|
||||||
|
|
||||||
|
const index = meilisearch.index(INDEX_NAME);
|
||||||
|
|
||||||
|
// 기존 문서 모두 삭제
|
||||||
|
await index.deleteAllDocuments();
|
||||||
|
|
||||||
|
// 문서 변환
|
||||||
|
const documents = schedules.map(s => ({
|
||||||
|
id: s.id,
|
||||||
|
title: s.title,
|
||||||
|
description: s.description || '',
|
||||||
|
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
||||||
|
time: s.time || '',
|
||||||
|
category_id: s.category_id,
|
||||||
|
category_name: s.category_name || '',
|
||||||
|
category_color: s.category_color || '',
|
||||||
|
source_name: s.source_name || '',
|
||||||
|
member_names: s.member_names || '',
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 일괄 추가
|
||||||
|
await index.addDocuments(documents);
|
||||||
|
console.log(`[Meilisearch] ${documents.length}개 일정 동기화 완료`);
|
||||||
|
|
||||||
|
return documents.length;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Meilisearch] 동기화 오류:', err.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
services:
|
|
||||||
# 프론트엔드 - Vite 개발 서버
|
|
||||||
fromis9-frontend:
|
|
||||||
image: node:20-alpine
|
|
||||||
container_name: fromis9-frontend
|
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=false"
|
|
||||||
working_dir: /app
|
|
||||||
command: sh -c "npm install && npm run dev -- --host 0.0.0.0 --port 80"
|
|
||||||
volumes:
|
|
||||||
- ./frontend:/app
|
|
||||||
networks:
|
|
||||||
- app
|
|
||||||
- db
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# 백엔드 - Fastify API 서버
|
|
||||||
fromis9-backend:
|
|
||||||
image: node:20-alpine
|
|
||||||
container_name: fromis9-backend
|
|
||||||
working_dir: /app
|
|
||||||
command: sh -c "apk add --no-cache ffmpeg && npm install && npm run dev"
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
- PORT=3000
|
|
||||||
volumes:
|
|
||||||
- ./backend:/app
|
|
||||||
networks:
|
|
||||||
- app
|
|
||||||
- db
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# Meilisearch - 검색 엔진
|
|
||||||
meilisearch:
|
|
||||||
image: getmeili/meilisearch:v1.6
|
|
||||||
container_name: fromis9-meilisearch
|
|
||||||
environment:
|
|
||||||
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
|
|
||||||
volumes:
|
|
||||||
- ./meilisearch_data:/meili_data
|
|
||||||
networks:
|
|
||||||
- app
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# Redis - 추천 검색어 캐시
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: fromis9-redis
|
|
||||||
volumes:
|
|
||||||
- ./redis_data:/data
|
|
||||||
networks:
|
|
||||||
- app
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
networks:
|
|
||||||
app:
|
|
||||||
external: true
|
|
||||||
db:
|
|
||||||
external: true
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
services:
|
services:
|
||||||
fromis9-web:
|
fromis9-frontend:
|
||||||
build: .
|
build: .
|
||||||
container_name: fromis9-frontend
|
container_name: fromis9-frontend
|
||||||
labels:
|
labels:
|
||||||
- "com.centurylinklabs.watchtower.enable=false"
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
# 개발 모드
|
||||||
|
volumes:
|
||||||
|
- ./backend:/app/backend
|
||||||
|
- ./frontend:/app/frontend
|
||||||
|
- backend_modules:/app/backend/node_modules
|
||||||
|
- frontend_modules:/app/frontend/node_modules
|
||||||
|
# 배포 모드 (사용 시 위 volumes를 주석처리)
|
||||||
|
# volumes: []
|
||||||
networks:
|
networks:
|
||||||
- app
|
- app
|
||||||
- db
|
- db
|
||||||
|
|
@ -25,10 +33,16 @@ services:
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
container_name: fromis9-redis
|
container_name: fromis9-redis
|
||||||
|
volumes:
|
||||||
|
- ./redis_data:/data
|
||||||
networks:
|
networks:
|
||||||
- app
|
- app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
backend_modules:
|
||||||
|
frontend_modules:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
app:
|
app:
|
||||||
external: true
|
external: true
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# fromis_9 Photos 테이블에서 이미지 다운로드 스크립트
|
|
||||||
# 앨범별로 폴더 분류하여 저장
|
|
||||||
|
|
||||||
OUTPUT_DIR="/docker/fromis_9/downloaded_photos"
|
|
||||||
mkdir -p "$OUTPUT_DIR"
|
|
||||||
|
|
||||||
# MariaDB에서 데이터 가져오기
|
|
||||||
docker exec mariadb mariadb -u admin -p'auddnek0403!' fromis_9 -N -e "SELECT photo_id, album_name, photo FROM Photos;" | while IFS=$'\t' read -r photo_id album_name photo_url; do
|
|
||||||
# 앨범명에서 특수문자 제거하여 폴더명 생성
|
|
||||||
folder_name=$(echo "$album_name" | sed 's/[^a-zA-Z0-9가-힣 ]/_/g' | sed 's/ */_/g')
|
|
||||||
|
|
||||||
# 폴더 생성
|
|
||||||
mkdir -p "$OUTPUT_DIR/$folder_name"
|
|
||||||
|
|
||||||
# 파일명 생성 (photo_id 기반)
|
|
||||||
filename="${photo_id}.jpg"
|
|
||||||
filepath="$OUTPUT_DIR/$folder_name/$filename"
|
|
||||||
|
|
||||||
# 이미 다운로드된 파일은 건너뛰기
|
|
||||||
if [ -f "$filepath" ]; then
|
|
||||||
echo "Skip: $filepath (already exists)"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 다운로드
|
|
||||||
echo "Downloading: $album_name/$filename"
|
|
||||||
curl -s -L -o "$filepath" "$photo_url"
|
|
||||||
|
|
||||||
# 다운로드 실패 시 삭제
|
|
||||||
if [ ! -s "$filepath" ]; then
|
|
||||||
rm -f "$filepath"
|
|
||||||
echo "Failed: $filepath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rate limiting (0.2초 대기)
|
|
||||||
sleep 0.2
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Download complete!"
|
|
||||||
echo "Saved to: $OUTPUT_DIR"
|
|
||||||
|
|
||||||
# 결과 요약
|
|
||||||
echo ""
|
|
||||||
echo "=== Summary ==="
|
|
||||||
for dir in "$OUTPUT_DIR"/*/; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
count=$(ls -1 "$dir" 2>/dev/null | wc -l)
|
|
||||||
dirname=$(basename "$dir")
|
|
||||||
echo "$dirname: $count files"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
431
frontend/package-lock.json
generated
431
frontend/package-lock.json
generated
|
|
@ -52,13 +52,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.27.1",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
|
||||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
"integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.27.1",
|
"@babel/helper-validator-identifier": "^7.28.5",
|
||||||
"js-tokens": "^4.0.0",
|
"js-tokens": "^4.0.0",
|
||||||
"picocolors": "^1.1.1"
|
"picocolors": "^1.1.1"
|
||||||
},
|
},
|
||||||
|
|
@ -67,9 +67,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/compat-data": {
|
"node_modules/@babel/compat-data": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz",
|
||||||
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
|
"integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -77,21 +77,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/core": {
|
"node_modules/@babel/core": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
|
||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.28.6",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.6",
|
||||||
"@babel/helper-compilation-targets": "^7.27.2",
|
"@babel/helper-compilation-targets": "^7.28.6",
|
||||||
"@babel/helper-module-transforms": "^7.28.3",
|
"@babel/helper-module-transforms": "^7.28.6",
|
||||||
"@babel/helpers": "^7.28.4",
|
"@babel/helpers": "^7.28.6",
|
||||||
"@babel/parser": "^7.28.5",
|
"@babel/parser": "^7.28.6",
|
||||||
"@babel/template": "^7.27.2",
|
"@babel/template": "^7.28.6",
|
||||||
"@babel/traverse": "^7.28.5",
|
"@babel/traverse": "^7.28.6",
|
||||||
"@babel/types": "^7.28.5",
|
"@babel/types": "^7.28.6",
|
||||||
"@jridgewell/remapping": "^2.3.5",
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
"convert-source-map": "^2.0.0",
|
"convert-source-map": "^2.0.0",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
|
|
@ -108,14 +108,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz",
|
||||||
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
|
"integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.28.5",
|
"@babel/parser": "^7.28.6",
|
||||||
"@babel/types": "^7.28.5",
|
"@babel/types": "^7.28.6",
|
||||||
"@jridgewell/gen-mapping": "^0.3.12",
|
"@jridgewell/gen-mapping": "^0.3.12",
|
||||||
"@jridgewell/trace-mapping": "^0.3.28",
|
"@jridgewell/trace-mapping": "^0.3.28",
|
||||||
"jsesc": "^3.0.2"
|
"jsesc": "^3.0.2"
|
||||||
|
|
@ -125,13 +125,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-compilation-targets": {
|
"node_modules/@babel/helper-compilation-targets": {
|
||||||
"version": "7.27.2",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
|
||||||
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
|
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/compat-data": "^7.27.2",
|
"@babel/compat-data": "^7.28.6",
|
||||||
"@babel/helper-validator-option": "^7.27.1",
|
"@babel/helper-validator-option": "^7.27.1",
|
||||||
"browserslist": "^4.24.0",
|
"browserslist": "^4.24.0",
|
||||||
"lru-cache": "^5.1.1",
|
"lru-cache": "^5.1.1",
|
||||||
|
|
@ -152,29 +152,29 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-module-imports": {
|
"node_modules/@babel/helper-module-imports": {
|
||||||
"version": "7.27.1",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
|
||||||
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/traverse": "^7.27.1",
|
"@babel/traverse": "^7.28.6",
|
||||||
"@babel/types": "^7.27.1"
|
"@babel/types": "^7.28.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-module-transforms": {
|
"node_modules/@babel/helper-module-transforms": {
|
||||||
"version": "7.28.3",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
|
||||||
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
|
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-module-imports": "^7.27.1",
|
"@babel/helper-module-imports": "^7.28.6",
|
||||||
"@babel/helper-validator-identifier": "^7.27.1",
|
"@babel/helper-validator-identifier": "^7.28.5",
|
||||||
"@babel/traverse": "^7.28.3"
|
"@babel/traverse": "^7.28.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
|
|
@ -184,9 +184,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-plugin-utils": {
|
"node_modules/@babel/helper-plugin-utils": {
|
||||||
"version": "7.27.1",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
|
||||||
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
|
"integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -224,27 +224,27 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.28.4",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
|
||||||
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
|
"integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.27.2",
|
"@babel/template": "^7.28.6",
|
||||||
"@babel/types": "^7.28.4"
|
"@babel/types": "^7.28.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
|
||||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
"integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.28.5"
|
"@babel/types": "^7.28.6"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
|
|
@ -286,33 +286,33 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.27.2",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.28.6",
|
||||||
"@babel/parser": "^7.27.2",
|
"@babel/parser": "^7.28.6",
|
||||||
"@babel/types": "^7.27.1"
|
"@babel/types": "^7.28.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz",
|
||||||
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
|
"integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.28.6",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.6",
|
||||||
"@babel/helper-globals": "^7.28.0",
|
"@babel/helper-globals": "^7.28.0",
|
||||||
"@babel/parser": "^7.28.5",
|
"@babel/parser": "^7.28.6",
|
||||||
"@babel/template": "^7.27.2",
|
"@babel/template": "^7.28.6",
|
||||||
"@babel/types": "^7.28.5",
|
"@babel/types": "^7.28.6",
|
||||||
"debug": "^4.3.1"
|
"debug": "^4.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -320,9 +320,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
|
||||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
"integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -813,9 +813,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.23.1",
|
"version": "1.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||||
"integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==",
|
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
|
|
@ -829,9 +829,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
|
||||||
"integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
|
"integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -843,9 +843,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
|
||||||
"integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
|
"integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -857,9 +857,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
|
||||||
"integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
|
"integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -871,9 +871,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
|
||||||
"integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
|
"integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -885,9 +885,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
|
||||||
"integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
|
"integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -899,9 +899,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
|
||||||
"integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
|
"integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -913,9 +913,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
|
||||||
"integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
|
"integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -927,9 +927,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
|
||||||
"integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
|
"integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -941,9 +941,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
|
"integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -955,9 +955,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
|
||||||
"integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
|
"integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -969,9 +969,23 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
|
"integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||||
|
"version": "4.55.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
|
||||||
|
"integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
|
|
@ -983,9 +997,23 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
|
"integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||||
|
"version": "4.55.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
|
||||||
|
"integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
|
|
@ -997,9 +1025,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
|
"integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
|
|
@ -1011,9 +1039,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
|
||||||
"integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
|
"integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
|
|
@ -1025,9 +1053,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
|
"integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
|
|
@ -1039,9 +1067,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
|
"integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1053,9 +1081,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
|
||||||
"integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
|
"integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1066,10 +1094,24 @@
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||||
|
"version": "4.55.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
|
||||||
|
"integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
|
||||||
"integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
|
"integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1081,9 +1123,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
|
||||||
"integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
|
"integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1095,9 +1137,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
|
||||||
"integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
|
"integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
|
@ -1109,9 +1151,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
|
||||||
"integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
|
"integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1123,9 +1165,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
|
||||||
"integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
|
"integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1137,9 +1179,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "5.90.16",
|
"version": "5.90.19",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.16.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.19.tgz",
|
||||||
"integrity": "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==",
|
"integrity": "sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -1147,12 +1189,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-query": {
|
"node_modules/@tanstack/react-query": {
|
||||||
"version": "5.90.16",
|
"version": "5.90.19",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.19.tgz",
|
||||||
"integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
|
"integrity": "sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/query-core": "5.90.16"
|
"@tanstack/query-core": "5.90.19"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -1365,9 +1407,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.9.11",
|
"version": "2.9.15",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz",
|
||||||
"integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
|
"integrity": "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -1445,9 +1487,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001762",
|
"version": "1.0.30001764",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz",
|
||||||
"integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
|
"integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2443,9 +2485,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-intersection-observer": {
|
"node_modules/react-intersection-observer": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.0.2.tgz",
|
||||||
"integrity": "sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg==",
|
"integrity": "sha512-lAMzxVWrBko6SLd1jx6l84fVrzJu91hpxHlvD2as2Wec9mDCjdYXwc5xNOFBchpeBir0Y7AGBW+C/AYMa7CSFg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
|
@ -2521,12 +2563,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.30.2",
|
"version": "6.30.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
|
||||||
"integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==",
|
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.23.1"
|
"@remix-run/router": "1.23.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
|
|
@ -2536,13 +2578,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "6.30.2",
|
"version": "6.30.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
|
||||||
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
|
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.23.1",
|
"@remix-run/router": "1.23.2",
|
||||||
"react-router": "6.30.2"
|
"react-router": "6.30.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
|
|
@ -2553,9 +2595,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-window": {
|
"node_modules/react-window": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-window/-/react-window-2.2.5.tgz",
|
||||||
"integrity": "sha512-gTRqQYC8ojbiXyd9duYFiSn2TJw0ROXCgYjenOvNKITWzK0m0eCvkUsEUM08xvydkMh7ncp+LE0uS3DeNGZxnQ==",
|
"integrity": "sha512-6viWvPSZvVuMIe9hrl4IIZoVfO/npiqOb03m4Z9w+VihmVzBbiudUrtUqDpsWdKvd/Ai31TCR25CBcFFAUm28w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^18.0.0 || ^19.0.0",
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
|
@ -2618,9 +2660,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.54.0",
|
"version": "4.55.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
||||||
"integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
|
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2634,28 +2676,31 @@
|
||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.54.0",
|
"@rollup/rollup-android-arm-eabi": "4.55.1",
|
||||||
"@rollup/rollup-android-arm64": "4.54.0",
|
"@rollup/rollup-android-arm64": "4.55.1",
|
||||||
"@rollup/rollup-darwin-arm64": "4.54.0",
|
"@rollup/rollup-darwin-arm64": "4.55.1",
|
||||||
"@rollup/rollup-darwin-x64": "4.54.0",
|
"@rollup/rollup-darwin-x64": "4.55.1",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.54.0",
|
"@rollup/rollup-freebsd-arm64": "4.55.1",
|
||||||
"@rollup/rollup-freebsd-x64": "4.54.0",
|
"@rollup/rollup-freebsd-x64": "4.55.1",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.54.0",
|
"@rollup/rollup-linux-arm-musleabihf": "4.55.1",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.54.0",
|
"@rollup/rollup-linux-arm64-gnu": "4.55.1",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.54.0",
|
"@rollup/rollup-linux-arm64-musl": "4.55.1",
|
||||||
"@rollup/rollup-linux-loong64-gnu": "4.54.0",
|
"@rollup/rollup-linux-loong64-gnu": "4.55.1",
|
||||||
"@rollup/rollup-linux-ppc64-gnu": "4.54.0",
|
"@rollup/rollup-linux-loong64-musl": "4.55.1",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.54.0",
|
"@rollup/rollup-linux-ppc64-gnu": "4.55.1",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.54.0",
|
"@rollup/rollup-linux-ppc64-musl": "4.55.1",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.54.0",
|
"@rollup/rollup-linux-riscv64-gnu": "4.55.1",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.54.0",
|
"@rollup/rollup-linux-riscv64-musl": "4.55.1",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.54.0",
|
"@rollup/rollup-linux-s390x-gnu": "4.55.1",
|
||||||
"@rollup/rollup-openharmony-arm64": "4.54.0",
|
"@rollup/rollup-linux-x64-gnu": "4.55.1",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.54.0",
|
"@rollup/rollup-linux-x64-musl": "4.55.1",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.54.0",
|
"@rollup/rollup-openbsd-x64": "4.55.1",
|
||||||
"@rollup/rollup-win32-x64-gnu": "4.54.0",
|
"@rollup/rollup-openharmony-arm64": "4.55.1",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.54.0",
|
"@rollup/rollup-win32-arm64-msvc": "4.55.1",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.55.1",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.55.1",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.55.1",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3052,9 +3097,9 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/zustand": {
|
"node_modules/zustand": {
|
||||||
"version": "5.0.9",
|
"version": "5.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.10.tgz",
|
||||||
"integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==",
|
"integrity": "sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.20.0"
|
"node": ">=12.20.0"
|
||||||
|
|
|
||||||
|
|
@ -254,10 +254,30 @@ function Schedule() {
|
||||||
enabled: !!searchTerm && isSearchMode,
|
enabled: !!searchTerm && isSearchMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Flatten search results
|
// Flatten search results and normalize format to match monthly data
|
||||||
const searchResults = useMemo(() => {
|
const searchResults = useMemo(() => {
|
||||||
if (!searchData?.pages) return [];
|
if (!searchData?.pages) return [];
|
||||||
return searchData.pages.flatMap(page => page.schedules);
|
return searchData.pages.flatMap(page =>
|
||||||
|
page.schedules.map(s => {
|
||||||
|
// datetime → date + time 분리
|
||||||
|
const dateTime = s.datetime || '';
|
||||||
|
const [date, time] = dateTime.includes('T')
|
||||||
|
? dateTime.split('T')
|
||||||
|
: [dateTime, null];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
date,
|
||||||
|
time,
|
||||||
|
// category 객체 → flat 구조
|
||||||
|
category_id: s.category?.id,
|
||||||
|
category_name: s.category?.name,
|
||||||
|
category_color: s.category?.color,
|
||||||
|
// members 배열 → 쉼표 구분 문자열 (기존 호환)
|
||||||
|
member_names: Array.isArray(s.members) ? s.members.join(',') : s.member_names,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
}, [searchData]);
|
}, [searchData]);
|
||||||
|
|
||||||
const searchTotal = searchData?.pages?.[0]?.total || 0;
|
const searchTotal = searchData?.pages?.[0]?.total || 0;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,27 @@ export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
port: 5173,
|
port: 80,
|
||||||
allowedHosts: true,
|
allowedHosts: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
|
// 개발 모드 (localhost:3000)
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://fromis9-backend:3000",
|
target: "http://localhost:3000",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
"/docs": {
|
||||||
|
target: "http://localhost:3000",
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
// 배포 모드 (사용 시 위를 주석처리)
|
||||||
|
// "/api": {
|
||||||
|
// target: "http://fromis9-backend:3000",
|
||||||
|
// changeOrigin: true,
|
||||||
|
// },
|
||||||
|
// "/docs": {
|
||||||
|
// target: "http://fromis9-backend:3000",
|
||||||
|
// changeOrigin: true,
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
-- 하얀 그리움 앨범 소개글
|
|
||||||
UPDATE albums SET description = '하얗게 내리는 겨울, 그 안에 다시 피어난 따뜻한 기억.
|
|
||||||
|
|
||||||
프로미스나인이 김민종의 명곡 "하얀 그리움"을 지금의 계절과 감성으로 다시 피워냈다.
|
|
||||||
프로미스나인 특유의 맑고 투명한 음색과 섬세한 감정 표현으로, 한층 현대적이고 따뜻한 감성을 완성했다.
|
|
||||||
부드럽게 흐르는 신스 리프와 맑은 톤의 기타, 리드미컬한 드럼이 어우러져 겨울의 청량함과 온기를 동시에 담은 미디엄 템포 팝 트랙으로 완성되었다.
|
|
||||||
|
|
||||||
눈처럼 흩날리는 멜로디 위로 멤버들의 보컬이 차곡히 쌓이며, "그리움"이라는 단어가 차가움이 아닌 사랑스러운 감정의 잔상으로 다가온다.
|
|
||||||
|
|
||||||
사랑이 끝난 뒤에도 남아 있는 마음의 온기를 노래하며,
|
|
||||||
한때는 사랑이었고 지금은 추억이 된 감정이 겨울 햇살처럼 포근하게 번져 간다.
|
|
||||||
후반부로 갈수록 깊어지는 보컬 하모니와 풍성한 편곡은 원곡의 정서를 그대로 품으면서도, 프로미스나인만의 따뜻하고 서정적인 팝 감성으로 다시 빛난다.
|
|
||||||
|
|
||||||
마치 오래된 사진 속 장면이 현재의 색으로 복원되듯 익숙한 멜로디가 새로운 온기로 피어나며, 단순한 리메이크를 넘어 사랑의 기억을 포근하게 감싸는 계절의 이야기이자 과거와 현재를 잇는 다리로 완성된다.' WHERE id = 2;
|
|
||||||
|
|
||||||
-- From Our 20's 앨범 소개글
|
|
||||||
UPDATE albums SET description = '[From Our 20''s] "어디로든 갈 수 있고 무엇이든 될 수 있는 아름다운 20대의 이야기"
|
|
||||||
|
|
||||||
"여전히 서툴지만, 그래서 더 아름다운 시간.
|
|
||||||
지금 이 순간, 우리는 우리 자신을 살아간다."
|
|
||||||
|
|
||||||
프로미스나인의 6번째 미니 앨범 [From Our 20''s]는 "20대의 우리"가 마주한 감정과 순간들을 솔직하게 풀어낸 기록이다.
|
|
||||||
|
|
||||||
타이틀곡 "LIKE YOU BETTER"를 포함해 총 6개의 트랙으로 구성된 미니 앨범으로, 아름다운 20대를 살아가는 "프로미스나인"의 감정과 이야기를 음악 속에 진솔하게 담아냈다.
|
|
||||||
|
|
||||||
그동안 "프로미스나인"이 꾸준히 구축해온 팀 고유의 음악적 색채를 바탕으로, 보다 팝적인 요소를 세련되게 녹여내며 익숙하면서도 새로운 질감의 완성도를 선보인다.
|
|
||||||
|
|
||||||
이는 팀의 정체성을 견고히 유지하면서도 한층 넓어진 음악적 스펙트럼을 제시하며, "20대의 우리"가 전하는 다채로운 감정의 결을 감각적으로 풀어낸 앨범으로 성장하고 변화한 프로미스나인을 볼 수 있다.
|
|
||||||
|
|
||||||
앨범을 관통하는 메시지는 "20대의 삶" 그 자체이다.
|
|
||||||
|
|
||||||
여전히 덜 익은 마음을 품은 채, 어른들의 세상에 놓여 모든 것이 변할 것을 알면서도 영원을 꿈꾸고, 끝이 있다는 걸 알면서도 온 힘을 다해 사랑하며, 불안과 기대, 순수함과 복잡함이 공존하는 시간 속의 20대 소녀들은 누구보다 자신을 돌보며, 나를 나로서 있게 해줄 수 있는 사랑하는 이들과 함께하는 시간 그 자체가 삶이라 믿는다.
|
|
||||||
|
|
||||||
우리는 이제 어디든 갈 수 있고 무엇이든 될 수 있다.' WHERE id = 1;
|
|
||||||
Loading…
Add table
Reference in a new issue