Compare commits
11 Commits
d7e31ae38c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 67f5d815c9 | |||
| f3e97d6c44 | |||
| 126c0169aa | |||
| 8770b1b47b | |||
| dbcda9dd6b | |||
| 1d21d4b49b | |||
| c8dbaf167f | |||
| 0e9e5b0283 | |||
| 539fb8aa61 | |||
| b118a58b8f | |||
| 32b5a8ed0e |
+3
-5
@@ -6,10 +6,12 @@
|
||||
|
||||
# PostgreSQL
|
||||
DB_PORT=5432
|
||||
DB_HOST=postgres
|
||||
DB_HOST=db
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_NAME=report_db
|
||||
# DB 데이터 저장 경로 (프로젝트 폴더 기준 상대 경로, 기본값 ./data/postgres)
|
||||
DB_DATA_DIR=./data/postgres
|
||||
|
||||
# Admin Account
|
||||
ADMIN_USER=
|
||||
@@ -21,7 +23,3 @@ REFRESH_SECRET=
|
||||
|
||||
# Gemini API Key
|
||||
JEMINI_KEY=
|
||||
|
||||
# Ports
|
||||
BACKEND_PORT=54530
|
||||
FRONTEND_PORT=55110
|
||||
|
||||
@@ -43,7 +43,10 @@ jobs:
|
||||
- name: Restart containers
|
||||
run: |
|
||||
cd "$DEPLOY_DIR"
|
||||
docker compose up -d
|
||||
docker compose up -d --remove-orphans
|
||||
# backend/frontend는 항상 새로 기동: DB 교체 시 스키마 재동기화,
|
||||
# nginx의 낡은 upstream IP 캐시 방지
|
||||
docker compose up -d --no-deps --force-recreate backend frontend
|
||||
|
||||
- name: Prune dangling images
|
||||
run: docker image prune -f
|
||||
|
||||
@@ -12,6 +12,9 @@ dist/
|
||||
# Uploads / generated assets
|
||||
backend/img/
|
||||
|
||||
# DB data (bind mount)
|
||||
data/
|
||||
|
||||
# Logs & OS files
|
||||
*.log
|
||||
.DS_Store
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# ============================================================
|
||||
# backend 로컬 개발용 환경 변수 템플릿
|
||||
# 복사해서 .env.development (로컬 개발) 로 만들고 값을 채우세요.
|
||||
# 도커 배포 시에는 이 파일이 아니라 루트 .env 가 사용됩니다 (docker-compose 참고).
|
||||
# ============================================================
|
||||
|
||||
# PostgreSQL (로컬 개발 시 docker-compose의 postgres를 쓰면 host는 localhost, 포트는 5442)
|
||||
DB_PORT=5432
|
||||
DB_HOST=localhost
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=
|
||||
DB_NAME=report_db
|
||||
|
||||
# Admin Account (최초 기동 시 자동 생성되는 관리자 계정)
|
||||
ADMIN_USER=
|
||||
ADMIN_PASS=
|
||||
|
||||
# JWT Secrets (긴 무작위 문자열 사용)
|
||||
ACCESS_SECRET=
|
||||
REFRESH_SECRET=
|
||||
|
||||
# Gemini API Key
|
||||
JEMINI_KEY=
|
||||
Generated
-13
@@ -13,7 +13,6 @@
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.3.1",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"multer": "^2.1.1",
|
||||
@@ -990,18 +989,6 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz",
|
||||
"integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dottie": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.7.tgz",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js",
|
||||
"build": "node build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -14,7 +13,6 @@
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.3.1",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"multer": "^2.1.1",
|
||||
|
||||
+17
-9
@@ -1,6 +1,3 @@
|
||||
require("dotenv").config({
|
||||
path: `.env.${process.env.NODE_ENV || "development"}`,
|
||||
});
|
||||
const express = require("express");
|
||||
const { Sequelize, DataTypes } = require("sequelize");
|
||||
const cors = require("cors");
|
||||
@@ -23,7 +20,7 @@ const PORT = process.env.PORT || 3000;
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME || "report_db",
|
||||
process.env.DB_USER || "postgres",
|
||||
process.env.DB_PASSWORD || "password",
|
||||
process.env.DB_PASSWORD,
|
||||
{
|
||||
host: process.env.DB_HOST || "localhost",
|
||||
port: process.env.DB_PORT || 5432,
|
||||
@@ -89,8 +86,12 @@ const swaggerOptions = {
|
||||
const swaggerDocs = swaggerJsdoc(swaggerOptions);
|
||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocs));
|
||||
|
||||
const ACCESS_SECRET = process.env.ACCESS_SECRET || "access_secret_123";
|
||||
const REFRESH_SECRET = process.env.REFRESH_SECRET || "refresh_secret_123";
|
||||
const ACCESS_SECRET = process.env.ACCESS_SECRET;
|
||||
const REFRESH_SECRET = process.env.REFRESH_SECRET;
|
||||
if (!ACCESS_SECRET || !REFRESH_SECRET) {
|
||||
console.error("ACCESS_SECRET / REFRESH_SECRET 환경변수가 필요합니다.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// OpenAI 추가
|
||||
const { OpenAI } = require("openai");
|
||||
@@ -218,8 +219,12 @@ sequelize
|
||||
.then(async () => {
|
||||
console.log("DB Tables Synced...");
|
||||
// 초기 관리자 계정 생성 (환경 변수 기반)
|
||||
const adminUser = process.env.ADMIN_USER || "admin";
|
||||
const adminPass = process.env.ADMIN_PASS || "admin123!";
|
||||
const adminUser = process.env.ADMIN_USER;
|
||||
const adminPass = process.env.ADMIN_PASS;
|
||||
if (!adminUser || !adminPass) {
|
||||
console.log("ADMIN_USER/ADMIN_PASS 미설정 - 관리자 계정 생성을 건너뜁니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
const adminExists = await User.findOne({ where: { username: adminUser } });
|
||||
const hashedPassword = await bcrypt.hash(adminPass, 10);
|
||||
@@ -243,7 +248,10 @@ sequelize
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log("PostgreSQL Connection/Sync Error:", err));
|
||||
.catch((err) => {
|
||||
console.error("PostgreSQL Connection/Sync Error:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Auth Middleware
|
||||
const authenticateToken = (req, res, next) => {
|
||||
|
||||
+26
-24
@@ -1,20 +1,20 @@
|
||||
services:
|
||||
postgres:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: report-db
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME:-report_db}
|
||||
POSTGRES_USER: ${DB_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-daoblock12!!}
|
||||
ports:
|
||||
- "5442:5432"
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD가 .env에 설정되어야 합니다}
|
||||
expose:
|
||||
- "5432"
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ${DB_DATA_DIR:-./data/postgres}:/var/lib/postgresql/data
|
||||
networks:
|
||||
- report-network
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-report_db}"]
|
||||
test: [ "CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-report_db}" ]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
@@ -24,41 +24,43 @@ services:
|
||||
build: ./backend
|
||||
container_name: report-backend
|
||||
restart: always
|
||||
ports:
|
||||
- "${BACKEND_PORT:-54530}:3000"
|
||||
expose:
|
||||
- "3000"
|
||||
environment:
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_HOST=${DB_HOST:-db}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_USER=${DB_USER:-postgres}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-daoblock12!!}
|
||||
- DB_PASSWORD=${DB_PASSWORD:?DB_PASSWORD가 .env에 설정되어야 합니다}
|
||||
- DB_NAME=${DB_NAME:-report_db}
|
||||
- ACCESS_SECRET=${ACCESS_SECRET:-your_jwt_access_secret_123}
|
||||
- REFRESH_SECRET=${REFRESH_SECRET:-your_jwt_refresh_secret_123}
|
||||
- ADMIN_USER=${ADMIN_USER:-kyeongmi}
|
||||
- ADMIN_PASS=${ADMIN_PASS:-kyeongmi}
|
||||
- ACCESS_SECRET=${ACCESS_SECRET:?ACCESS_SECRET이 .env에 설정되어야 합니다}
|
||||
- REFRESH_SECRET=${REFRESH_SECRET:?REFRESH_SECRET이 .env에 설정되어야 합니다}
|
||||
- ADMIN_USER=${ADMIN_USER:?ADMIN_USER가 .env에 설정되어야 합니다}
|
||||
- ADMIN_PASS=${ADMIN_PASS:?ADMIN_PASS가 .env에 설정되어야 합니다}
|
||||
- JEMINI_KEY=${JEMINI_KEY}
|
||||
depends_on:
|
||||
postgres:
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./backend/img:/app/img
|
||||
networks:
|
||||
- report-network
|
||||
- app-network
|
||||
- proxy
|
||||
|
||||
frontend:
|
||||
build: ./frontend
|
||||
container_name: report-frontend
|
||||
restart: always
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-55110}:80"
|
||||
expose:
|
||||
- "80"
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- report-network
|
||||
- app-network
|
||||
- proxy
|
||||
|
||||
networks:
|
||||
report-network:
|
||||
app-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
# npm(Nginx Proxy Manager)이 컨테이너 이름으로 라우팅하는 공용 네트워크
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# ============================================================
|
||||
# frontend 환경 변수 템플릿 (Vite — 빌드 시점에 번들에 포함됨)
|
||||
# 복사해서 .env.development (로컬) / .env.production (배포 서버) 으로 만드세요.
|
||||
# 배포 서버에서 이 파일을 복사해 .env.production 으로 만드세요.
|
||||
# 주의: VITE_ 변수는 브라우저에 노출되므로 비밀값을 넣으면 안 됩니다.
|
||||
# ============================================================
|
||||
|
||||
# API 서버 주소 (로컬: http://localhost:3000, 운영: 실제 API 도메인)
|
||||
# API 서버 주소 (비워두면 same-origin — nginx가 /api를 backend로 프록시)
|
||||
VITE_API_BASE_URL=
|
||||
|
||||
PASSWORD_ADMIN=
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"chart.js": "^4.4.1",
|
||||
|
||||
Reference in New Issue
Block a user