diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..49016f437 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +.git +.github +infra +temp +npm-debug.log +.DS_Store diff --git a/.github/workflows/deploy-main.yml b/.github/workflows/deploy-main.yml new file mode 100644 index 000000000..6c0b396ac --- /dev/null +++ b/.github/workflows/deploy-main.yml @@ -0,0 +1,42 @@ +name: Deploy To AWS + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + JWT_ACCESS_TOKEN_SECRET: ${{ secrets.JWT_ACCESS_TOKEN_SECRET }} + JWT_REFRESH_TOKEN_SECRET: ${{ secrets.JWT_REFRESH_TOKEN_SECRET }} + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USER }} + key: ${{ secrets.EC2_PRIVATE_KEY }} + port: ${{ secrets.EC2_PORT || 22 }} + envs: DATABASE_URL,JWT_ACCESS_TOKEN_SECRET,JWT_REFRESH_TOKEN_SECRET + script: | + set -e + PROJECT_PATH="${{ secrets.EC2_PROJECT_PATH }}" + if [ -z "$PROJECT_PATH" ]; then + PROJECT_PATH="$HOME/6-sprint-mission" + fi + cd "$PROJECT_PATH" + git fetch origin + git checkout main + git pull origin main + DATABASE_URL="$DATABASE_URL" \ + JWT_ACCESS_TOKEN_SECRET="${JWT_ACCESS_TOKEN_SECRET:-change-me-access-secret}" \ + JWT_REFRESH_TOKEN_SECRET="${JWT_REFRESH_TOKEN_SECRET:-change-me-refresh-secret}" \ + docker compose -f docker-compose.yml down + DATABASE_URL="$DATABASE_URL" \ + JWT_ACCESS_TOKEN_SECRET="${JWT_ACCESS_TOKEN_SECRET:-change-me-access-secret}" \ + JWT_REFRESH_TOKEN_SECRET="${JWT_REFRESH_TOKEN_SECRET:-change-me-refresh-secret}" \ + docker compose -f docker-compose.yml up -d --build diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml new file mode 100644 index 000000000..aee0970e7 --- /dev/null +++ b/.github/workflows/pr-test.yml @@ -0,0 +1,56 @@ +name: PR Test + +on: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16 + ports: + - 5432:5432 + env: + POSTGRES_USER: ci_user + POSTGRES_PASSWORD: ci_password + POSTGRES_DB: ci_test_db + options: >- + --health-cmd "pg_isready -U ci_user -d ci_test_db" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + + env: + POSTGRES_USER: ci_user + POSTGRES_PASSWORD: ci_password + POSTGRES_DB: ci_test_db + DATABASE_URL: postgresql://ci_user:ci_password@localhost:5432/ci_test_db + JWT_ACCESS_TOKEN_SECRET: test_secret_key + JWT_REFRESH_TOKEN_SECRET: test_secret_key + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Generate Prisma Client + run: npx prisma generate + + - name: Apply database migrations + run: npx prisma migrate deploy + + - name: build + run: npm run build + + - name: Run tests + run: npm test diff --git a/.gitignore b/.gitignore index ba541ece7..834158612 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ yarn-error.log* lerna-debug.log* .pnpm-debug.log* + + # Node 진단 리포트 (node --report-on-fatalerror) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -123,20 +125,6 @@ dist # TernJS 포트 파일 .tern-port -### VSCode 관련 ### -# 개인 개발환경 설정 제외 (필요한 일부만 허용) -.vscode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# VSCode Local History -.history/ -*.vsix - # Ionide (F# 관련) .ionide diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..6dcf060ef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:22-bookworm-slim + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY prisma ./prisma +RUN npx prisma generate + +COPY tsconfig.json ./ +COPY src ./src +RUN npm run build + +RUN mkdir -p public + +EXPOSE 3000 + +CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main.js"] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 000000000..c71b3059b --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,38 @@ +services: + db: + image: postgres:16-alpine + container_name: sprint11-db + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: pandamarket + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + + app: + build: + context: . + dockerfile: Dockerfile + container_name: sprint11-app + restart: always + depends_on: + - db + environment: + NODE_ENV: production + PORT: 3000 + DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@db:5432/pandamarket?schema=public} + JWT_ACCESS_TOKEN_SECRET: ${JWT_ACCESS_TOKEN_SECRET:-change-me-access-secret} + JWT_REFRESH_TOKEN_SECRET: ${JWT_REFRESH_TOKEN_SECRET:-change-me-refresh-secret} + JWT_ACCESS_EXPIRES_IN: 1h + JWT_REFRESH_EXPIRES_IN: 14d + ports: + - "3000:3000" + volumes: + - uploads:/app/public + +volumes: + postgres-data: + uploads: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..c71b3059b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +services: + db: + image: postgres:16-alpine + container_name: sprint11-db + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: pandamarket + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + + app: + build: + context: . + dockerfile: Dockerfile + container_name: sprint11-app + restart: always + depends_on: + - db + environment: + NODE_ENV: production + PORT: 3000 + DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@db:5432/pandamarket?schema=public} + JWT_ACCESS_TOKEN_SECRET: ${JWT_ACCESS_TOKEN_SECRET:-change-me-access-secret} + JWT_REFRESH_TOKEN_SECRET: ${JWT_REFRESH_TOKEN_SECRET:-change-me-refresh-secret} + JWT_ACCESS_EXPIRES_IN: 1h + JWT_REFRESH_EXPIRES_IN: 14d + ports: + - "3000:3000" + volumes: + - uploads:/app/public + +volumes: + postgres-data: + uploads: diff --git a/package.json b/package.json index 494e436c3..884deb69c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "scripts": { "start": "node ./dist/main.js", "build": "tsc", + "test": "npm run build", "dev": "tsx src/main.ts", "dev:watch": "tsx watch src/main.ts" }, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4a8400b30..9c9db7334 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -55,18 +55,18 @@ model Comment { } model User { - id Int @id @default(autoincrement()) - email String @unique - nickname String - image String? - password String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - articles Article[] - products Product[] - comments Comment[] - favorites Favorite[] - likes Like[] + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + articles Article[] + products Product[] + comments Comment[] + favorites Favorite[] + likes Like[] notifications Notification[] }