From 7aece22dc840f6085226fe62526a46cdff865802 Mon Sep 17 00:00:00 2001 From: soohyunme Date: Sun, 25 Jan 2026 18:28:42 +0000 Subject: [PATCH] feat(platform): setup devfactory homepage and oracle cloud deployment workflow --- .github/workflows/devfactory-homepage.yml | 43 + README.en.md | 6 +- README.md | 8 +- platform/.env.example | 5 + platform/.gitignore | 5 + platform/PLATFORM.md | 66 + platform/docker-compose.dev.yml | 43 + platform/docker-compose.yml | 61 + platform/frontend/Dockerfile | 23 + platform/frontend/index.html | 237 +++ platform/frontend/main.js | 347 +++++ platform/frontend/package-lock.json | 551 +++++++ platform/frontend/package.json | 17 + platform/frontend/public/brand_logo.png | Bin 0 -> 21598 bytes platform/frontend/public/favicon.png | Bin 0 -> 7346 bytes .../frontend/public}/members/seungkyu.jpg | Bin .../frontend/public}/members/soohyun.png | Bin .../frontend/public}/members/yesin.jpg | Bin platform/frontend/public/pl_symbol.png | Bin 0 -> 7346 bytes platform/frontend/public/pl_symbol_white.png | Bin 0 -> 19757 bytes platform/frontend/style.css | 1273 +++++++++++++++++ platform/frontend/vite.config.js | 15 + platform/server/.env.example | 2 + platform/server/Dockerfile | 16 + platform/server/package-lock.json | 1255 ++++++++++++++++ platform/server/package.json | 23 + platform/server/src/index.js | 72 + 27 files changed, 4061 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/devfactory-homepage.yml create mode 100644 platform/.env.example create mode 100644 platform/.gitignore create mode 100644 platform/PLATFORM.md create mode 100644 platform/docker-compose.dev.yml create mode 100644 platform/docker-compose.yml create mode 100644 platform/frontend/Dockerfile create mode 100644 platform/frontend/index.html create mode 100644 platform/frontend/main.js create mode 100644 platform/frontend/package-lock.json create mode 100644 platform/frontend/package.json create mode 100644 platform/frontend/public/brand_logo.png create mode 100644 platform/frontend/public/favicon.png rename {docs/imgs => platform/frontend/public}/members/seungkyu.jpg (100%) rename {docs/imgs => platform/frontend/public}/members/soohyun.png (100%) rename {docs/imgs => platform/frontend/public}/members/yesin.jpg (100%) create mode 100644 platform/frontend/public/pl_symbol.png create mode 100644 platform/frontend/public/pl_symbol_white.png create mode 100644 platform/frontend/style.css create mode 100644 platform/frontend/vite.config.js create mode 100644 platform/server/.env.example create mode 100644 platform/server/Dockerfile create mode 100644 platform/server/package-lock.json create mode 100644 platform/server/package.json create mode 100644 platform/server/src/index.js diff --git a/.github/workflows/devfactory-homepage.yml b/.github/workflows/devfactory-homepage.yml new file mode 100644 index 0000000..b7b99a9 --- /dev/null +++ b/.github/workflows/devfactory-homepage.yml @@ -0,0 +1,43 @@ +name: ๐Ÿš€ DevFactory Homepage Deploy +run-name: ๐Ÿš€ Deploying to Production by @${{ github.actor }} + +on: + push: + branches: + - main + paths: + - 'platform/**' + - '.github/workflows/devfactory-homepage.yml' + workflow_dispatch: + +# ๊ฐ™์€ ๋ธŒ๋žœ์น˜ ๋™์‹œ ์‹คํ–‰ ์‹œ ์ด์ „ ์žก ์ทจ์†Œ(๊ฒฝ์Ÿ ๋ฐฐํฌ ๋ฐฉ์ง€) +concurrency: + group: DevFactory-homepage-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy-prod: + if: github.ref_name == 'main' + name: ๐Ÿš€ Deploy DF-platform (Production) + runs-on: oracle + environment: platform + defaults: + run: + working-directory: ./platform + steps: + - uses: actions/checkout@v4 + + - name: Write .env (prod) + run: | + cat > .env <<'EOF' + APP_HOST=${{ vars.APP_HOST }} + DATABASE_URL=${{ secrets.DATABASE_URL }} + EOF + + - name: Build & up (prod) + run: | + set -euxo pipefail + docker compose -p df-platform-main config -q + docker compose -p df-platform-main down --remove-orphans + docker compose -p df-platform-main up -d --build --remove-orphans + docker image prune -f --filter "label=org.pseudolab.project=devfactory-platform" diff --git a/README.en.md b/README.en.md index a76e116..ff0f94a 100644 --- a/README.en.md +++ b/README.en.md @@ -65,7 +65,7 @@ Highlight activities of DevFactory ๐Ÿค— Soohyun Kim
-
+

@@ -76,7 +76,7 @@ Highlight activities of DevFactory ๐Ÿค— Yesin Kim
-
+

@@ -86,7 +86,7 @@ Highlight activities of DevFactory ๐Ÿค— Seungkyu Kim
-
+

diff --git a/README.md b/README.md index 05a8142..ea391f9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ ## ๐ŸŒŸ ํ”„๋กœ์ ํŠธ DevFactory์˜ ์ฃผ์š” ํ™œ๋™ ๋‚ด์—ญ์ž…๋‹ˆ๋‹ค ๐Ÿค— * **๐Ÿณ ๊ธฐ์ˆ  ํŠœํ† ๋ฆฌ์–ผ (Tutorials)** - * Docker, Git, LLM ๋“ฑ ์‹ค์Šต ์ค‘์‹ฌ์˜ ์ฝ˜ํ…์ธ ์™€ ์˜คํ”„๋ผ์ธ ์›Œํฌ์ˆ์„ ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. + * Docker, Git, LLM ๋“ฑ ์‹ค์Šต ์ค‘์‹ฌ์˜ ์˜จ๋ผ์ธ ์ฝ˜ํ…์ธ ์™€ ์˜คํ”„๋ผ์ธ ์›Œํฌ์ˆ์„ ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. * [๐Ÿ”— ํŠœํ† ๋ฆฌ์–ผ ๋ณด๊ธฐ](https://pseudo-lab.github.io/DevFactory/intro.html) * **๐ŸŽฎ ๋„คํŠธ์›Œํ‚น ํ”„๋กœ๊ทธ๋žจ (BINGO)** @@ -60,7 +60,7 @@ DevFactory์˜ ์ฃผ์š” ํ™œ๋™ ๋‚ด์—ญ์ž…๋‹ˆ๋‹ค ๐Ÿค— ๊น€์ˆ˜ํ˜„
-
+

@@ -71,7 +71,7 @@ DevFactory์˜ ์ฃผ์š” ํ™œ๋™ ๋‚ด์—ญ์ž…๋‹ˆ๋‹ค ๐Ÿค— ๊น€์˜ˆ์‹ 
-
+

@@ -81,7 +81,7 @@ DevFactory์˜ ์ฃผ์š” ํ™œ๋™ ๋‚ด์—ญ์ž…๋‹ˆ๋‹ค ๐Ÿค— ๊น€์Šน๊ทœ
-
+

diff --git a/platform/.env.example b/platform/.env.example new file mode 100644 index 0000000..8b0e869 --- /dev/null +++ b/platform/.env.example @@ -0,0 +1,5 @@ +# Domain Setting +APP_HOST=your-domain.com + +# Database Setting +DATABASE_URL=postgresql://user:pass@devfactory-postgres:5432/dbname diff --git a/platform/.gitignore b/platform/.gitignore new file mode 100644 index 0000000..a7fce31 --- /dev/null +++ b/platform/.gitignore @@ -0,0 +1,5 @@ +.env +node_modules/ +dist/ +build/ +.DS_Store \ No newline at end of file diff --git a/platform/PLATFORM.md b/platform/PLATFORM.md new file mode 100644 index 0000000..29eddad --- /dev/null +++ b/platform/PLATFORM.md @@ -0,0 +1,66 @@ +# DevFactory Unified Platform + +์†Œ๊ฐœ ํŽ˜์ด์ง€์™€ ๋ฐฑ์—”๋“œ API๊ฐ€ ํ†ตํ•ฉ๋œ DevFactory์˜ ๋ฉ”์ธ ํ”Œ๋žซํผ ๊ด€๋ฆฌ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. +๋ณธ ์„ค์ •์€ **Traefik ์—ญ๋ฐฉํ–ฅ ํ”„๋ก์‹œ**๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ฒ„ ํ™˜๊ฒฝ์„ ์ „์ œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. + +### 1. Production Build +๋กœ์ปฌ ๋˜๋Š” ์„œ๋ฒ„์—์„œ ํ”„๋ก ํŠธ์—”๋“œ ์—์…‹์„ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. +```bash +cd platform/frontend +npm install +npm run build +``` +๊ฒฐ๊ณผ๋ฌผ์€ `platform/frontend/dist` ๋””๋ ‰ํ† ๋ฆฌ์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. + +### 2. Deployment Options + +#### Option A: Static Hosting (GitHub Pages / Vercel) +๋‹จ์ˆœ ์›น ํŽ˜์ด์ง€(Frontend)๋งŒ ๋ฐฐํฌํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. +1. `platform/frontend`์—์„œ `npm run build`๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. +2. `dist` ํด๋” ๋‚ด์˜ ํŒŒ์ผ๋“ค์„ ๋ฐฐํฌ ์„œ๋น„์Šค์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. +> **์ฐธ๊ณ **: ์ด ๋ฐฉ์‹์œผ๋กœ๋Š” ์ž์ฒด DB ๋ฐฉ๋ฌธ์ž ์ถ”์  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +#### Option B: Unified Platform (Full-Stack / Docker) +๋ฐฉ๋ฌธ์ž ์ถ”์  ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ ์ „์ฒด ํ”Œ๋žซํผ ๋ฐฐํฌ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. **(๊ถŒ์žฅ)** + +### 3. Environment Setup +`platform` ๋””๋ ‰ํ† ๋ฆฌ ๋ฃจํŠธ์— `.env` ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ์„œ๋ฒ„ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. +```bash +# .env ํŒŒ์ผ ์˜ˆ์‹œ +APP_HOST=intro.pseudolab-devfactory.com + +# Database +DATABASE_URL=postgresql://user:pass@devfactory-postgres:5432/dbname +``` + +### 3. Traefik ๊ธฐ๋ฐ˜ ๋ฐฐํฌ +Docker Compose๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋น„์Šค๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. +```bash +cd platform +# ์ „์ฒด ์„œ๋น„์Šค ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ +docker-compose up -d --build +``` + +#### ๋ฐฐํฌ ํ™•์ธ +- **Frontend (Web)**: `https://` +- **Backend (API)**: `https:///api/health` + +### 4. Database Schema +์‚ฌ์šฉ ์ค‘์ธ `logging.access_log` ํ…Œ์ด๋ธ”์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. +```sql +CREATE TABLE logging.access_log ( + id SERIAL PRIMARY KEY, + ts TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + path TEXT NOT NULL, + method TEXT NOT NULL, + status INTEGER NOT NULL, + latency_ms INTEGER, + ip_hash TEXT, + user_agent TEXT, + referrer TEXT +); +``` + +### 5. Verification +- Traefik ๋Œ€์‹œ๋ณด๋“œ์—์„œ `df-platform` ๊ด€๋ จ ๋ผ์šฐํ„ฐ๊ฐ€ ํ™œ์„ฑํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. +- ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์ดํŠธ ์ ‘์† ์‹œ HTTPS ์ƒ์‹œ ์—ฐ๊ฒฐ ๋ฐ ๋ฐฉ๋ฌธ ๊ธฐ๋ก ์ ์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜์„ธ์š”. diff --git a/platform/docker-compose.dev.yml b/platform/docker-compose.dev.yml new file mode 100644 index 0000000..8213292 --- /dev/null +++ b/platform/docker-compose.dev.yml @@ -0,0 +1,43 @@ +services: + # Development Overrides with Traefik Support + frontend: + build: + context: ./frontend + target: build-stage + image: devfactory-frontend:dev + command: npm run dev -- --host 0.0.0.0 + volumes: + - ./frontend:/app + - /app/node_modules + labels: + - traefik.enable=true + - traefik.docker.network=traefik + - traefik.http.routers.df-platform-web.rule=Host(`${APP_HOST}`) + - traefik.http.routers.df-platform-web.entrypoints=websecure + - traefik.http.routers.df-platform-web.tls=true + - traefik.http.routers.df-platform-web.tls.certresolver=le + - traefik.http.services.df-platform-web.loadbalancer.server.port=5173 + # HTTP โ†’ HTTPS redirect (re-adding to ensure full functionality) + - traefik.http.routers.df-platform-web-http.rule=Host(`${APP_HOST}`) + - traefik.http.routers.df-platform-web-http.entrypoints=web + - traefik.http.routers.df-platform-web-http.middlewares=redirect-to-https@file + - traefik.http.routers.df-platform-web-http.service=df-platform-web + + server: + command: npm run dev + volumes: + - ./server:/app + - /app/node_modules + labels: + - traefik.enable=true + - traefik.docker.network=traefik + - traefik.http.routers.df-platform-api.rule=Host(`${APP_HOST}`) && PathPrefix(`/api`) + - traefik.http.routers.df-platform-api.entrypoints=websecure + - traefik.http.routers.df-platform-api.tls=true + - traefik.http.routers.df-platform-api.tls.certresolver=le + - traefik.http.services.df-platform-api.loadbalancer.server.port=3000 + # HTTP โ†’ HTTPS redirect + - traefik.http.routers.df-platform-api-http.rule=Host(`${APP_HOST}`) && PathPrefix(`/api`) + - traefik.http.routers.df-platform-api-http.entrypoints=web + - traefik.http.routers.df-platform-api-http.middlewares=redirect-to-https@file + - traefik.http.routers.df-platform-api-http.service=df-platform-api diff --git a/platform/docker-compose.yml b/platform/docker-compose.yml new file mode 100644 index 0000000..96d4834 --- /dev/null +++ b/platform/docker-compose.yml @@ -0,0 +1,61 @@ +services: + # Frontend static server (Nginx) + frontend: + build: + context: ./frontend + labels: + - "org.pseudolab.project=devfactory-platform" + container_name: devfactory-frontend + restart: unless-stopped + networks: + - traefik + - internal + labels: + - traefik.enable=true + - traefik.docker.network=traefik + # --- Traefik Router (frontend) --- + - traefik.http.routers.df-platform-web.rule=Host(`${APP_HOST}`) + - traefik.http.routers.df-platform-web.entrypoints=websecure + - traefik.http.routers.df-platform-web.tls=true + - traefik.http.routers.df-platform-web.tls.certresolver=le + - traefik.http.services.df-platform-web.loadbalancer.server.port=80 + # HTTP โ†’ HTTPS redirect + - traefik.http.routers.df-platform-web-http.rule=Host(`${APP_HOST}`) + - traefik.http.routers.df-platform-web-http.entrypoints=web + - traefik.http.routers.df-platform-web-http.middlewares=redirect-to-https@file + - traefik.http.routers.df-platform-web-http.service=df-platform-web + + # Backend API server + server: + build: + context: ./server + labels: + - "org.pseudolab.project=devfactory-platform" + container_name: devfactory-api + restart: unless-stopped + environment: + - DATABASE_URL=${DATABASE_URL} + - PORT=3000 + networks: + - traefik + - internal + labels: + - traefik.enable=true + - traefik.docker.network=traefik + # --- Traefik Router (backend) --- + - traefik.http.routers.df-platform-api.rule=Host(`${APP_HOST}`) && PathPrefix(`/api`) + - traefik.http.routers.df-platform-api.entrypoints=websecure + - traefik.http.routers.df-platform-api.tls=true + - traefik.http.routers.df-platform-api.tls.certresolver=le + - traefik.http.services.df-platform-api.loadbalancer.server.port=3000 + # HTTP โ†’ HTTPS redirect + - traefik.http.routers.df-platform-api-http.rule=Host(`${APP_HOST}`) && PathPrefix(`/api`) + - traefik.http.routers.df-platform-api-http.entrypoints=web + - traefik.http.routers.df-platform-api-http.middlewares=redirect-to-https@file + - traefik.http.routers.df-platform-api-http.service=df-platform-api + +networks: + traefik: + external: true + internal: + driver: bridge diff --git a/platform/frontend/Dockerfile b/platform/frontend/Dockerfile new file mode 100644 index 0000000..236c2e9 --- /dev/null +++ b/platform/frontend/Dockerfile @@ -0,0 +1,23 @@ +# Stage 1: Build stage +FROM node:20-alpine as build-stage + +WORKDIR /app + +# Copy package files and install dependencies +COPY package*.json ./ +RUN npm install + +# Copy project files and build +COPY . . +RUN npm run build + +# Stage 2: Serve stage +FROM nginx:stable-alpine as production-stage + +# Copy built assets from build-stage to nginx +COPY --from=build-stage /app/dist /usr/share/nginx/html + +# Expose port 80 +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/platform/frontend/index.html b/platform/frontend/index.html new file mode 100644 index 0000000..accb9d4 --- /dev/null +++ b/platform/frontend/index.html @@ -0,0 +1,237 @@ + + + + + + + DevFactory | Team Members + + + + + +
+
+ + + DevFactory + + + +
+
+ + +
+ + +
+ +
+
+ +
+ +
+
+

Bridging AI Research
through Engineering

+

+ ๊ฐ€์งœ์—ฐ๊ตฌ์†Œ์˜ ๋‹ค์–‘ํ•œ AI ์—ฐ๊ตฌ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง„ ๊ฐœ๋ฐœ ์—ญ๋Ÿ‰์„ ๋ฐ”ํƒ•์œผ๋กœ
+ ์„ธ์ƒ๊ณผ ์—ฐ๊ฒฐํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. +

+ +
+
+
โ†“
+ Scroll Down +
+
+ + +
+
+

Projects

+

DevFactory์˜ ์ฃผ์š” ํ™œ๋™ ๋‚ด์—ญ์ž…๋‹ˆ๋‹ค

+
+
+
+
๐Ÿณ
+

๊ธฐ์ˆ  ํŠœํ† ๋ฆฌ์–ผ

+

Docker, Git, LLM ๋“ฑ ์‹ค์Šต ์ค‘์‹ฌ์˜ ์˜จ๋ผ์ธ ์ฝ˜ํ…์ธ ์™€ ์˜คํ”„๋ผ์ธ ์›Œํฌ์ˆ

+ ํŠœํ† ๋ฆฌ์–ผ ๋ณด๊ธฐ โ†’ +
+
+
๐ŸŽฎ
+

BINGO

+

ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜์˜ ๋น™๊ณ  ๊ฒŒ์ž„์„ ํ†ตํ•œ ๋„คํŠธ์›Œํ‚น ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

+ ์›น์‚ฌ์ดํŠธ ํ™•์ธ โ†’ +
+
+
๐ŸŽฎ
+

์นœํ•ด์ง€๊ธธ๋ฐ”๋ผ

+

๊ฐ„๋‹จํ•œ ํ€ด์ฆˆ๋ฅผ ํ†ตํ•ด ์„œ๋กœ๋ฅผ ์•Œ์•„๊ฐ€๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

+ ์›น์‚ฌ์ดํŠธ ํ™•์ธ + โ†’ +
+
+
๐Ÿ“œ
+

์ˆ˜๋ฃŒ์ฆ ๋ฐœ๊ธ‰ ์‹œ์Šคํ…œ

+

๊ฐ€์งœ์—ฐ๊ตฌ์†Œ์˜ ์„ฑ์žฅ์„ ๊ธฐ๋กํ•˜๋Š” ํ™œ๋™ ์ˆ˜๋ฃŒ์ฆ ๊ด€๋ฆฌ ์„œ๋น„์Šค

+ ์›น์‚ฌ์ดํŠธ ํ™•์ธ โ†’ +
+
+
๐Ÿค–
+

JobPT

+

AI ๊ธฐ๋ฐ˜ ๊ฐœ์ธํ™” ์ทจ์—… ์ง€์› ์†”๋ฃจ์…˜ - ์ด๋ ฅ์„œ ๋ถ„์„๋ถ€ํ„ฐ ๋ฉด์ ‘ ํ”ผ๋“œ๋ฐฑ๊นŒ์ง€

+ ์„œ๋น„์Šค ๋ฐ”๋กœ๊ฐ€๊ธฐ โ†’ +
+
+
+
โ†“
+ Team Members +
+
+ + +
+
+
DEVFACTORY
+
+ +
+

Team Members

+

DevFactory๋ฅผ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ํŒ€์›๋“ค์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค

+
+ + +
+ +
+ + +
+ +
+ +
+
PROFILE
+
BUILDER
+

NAME

+

+ DevFactory๋ฅผ ์ด๋Œ์–ด๊ฐ€๋Š” ํ•ต์‹ฌ ๋ฉค๋ฒ„์˜ ์†Œ๊ฐœ๊ฐ€ ์ด๊ณณ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. +

+
+
+
Position
+
-
+
+
+
Contribution
+
-
+
+
+
Generation
+
-
+
+
+ +
+ + +
+
+ Member Visual +
+
+ +
+
+ +
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/platform/frontend/main.js b/platform/frontend/main.js new file mode 100644 index 0000000..b51e8c7 --- /dev/null +++ b/platform/frontend/main.js @@ -0,0 +1,347 @@ +// Mobile Menu Toggle +const menuToggle = document.getElementById('menu-toggle'); +const closeBtn = document.getElementById('close-menu'); +const menuOverlay = document.getElementById('mobile-menu-overlay'); +const mobileLinks = document.querySelectorAll('.mobile-nav-link'); + +function openMenu() { + menuOverlay.classList.add('active'); + document.body.style.overflow = 'hidden'; // Prevent scroll +} + +function closeMenu() { + menuOverlay.classList.remove('active'); + document.body.style.overflow = ''; // Restore scroll +} + +if (menuToggle) menuToggle.addEventListener('click', openMenu); +if (closeBtn) closeBtn.addEventListener('click', closeMenu); + +mobileLinks.forEach(link => { + link.addEventListener('click', closeMenu); +}); + +// Member Data & Core Functions +const teamMembers = [ + { + name: "๊น€์ˆ˜ํ˜„", + enName: "KIM SOOHYUN", + role: "BUILDER", + position: "TPM / INFRA", + status: "Active", + contribution: "Builder", + gen: "10th, 11th", + desc: "DevFactory์˜ ๋นŒ๋”๋กœ์„œ ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ์™€ ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ๋ฅผ ์ด๊ด„ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์งœ์—ฐ๊ตฌ์†Œ์˜ ๊ฐœ๋ฐœ ๋ฌธํ™”๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ์šด์˜ํ•ฉ๋‹ˆ๋‹ค.", + image: "/members/soohyun.png", + github: "https://github.com/soohyunme", + linkedin: "https://www.linkedin.com/in/soohyun-dev" + }, + { + name: "๊น€์˜ˆ์‹ ", + enName: "KIM YESIN", + role: "BUILDER", + position: "Backend Developer", + status: "Active", + contribution: "Builder", + gen: "10th, 11th", + desc: "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์„ ๋‹ด๋‹นํ•˜๋ฉฐ ๊ฒฌ๊ณ ํ•œ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค. 10๊ธฐ์™€ 11๊ธฐ ๋นŒ๋”๋กœ์„œ ํ™œ๋™ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.", + image: "/members/yesin.jpg", + github: "https://github.com/yesinkim", + linkedin: "https://www.linkedin.com/in/bailando/" + }, + { + name: "๊น€์Šน๊ทœ", + enName: "KIM SEUNGKYU", + role: "BUILDER", + position: "Frontend Developer", + status: "Active", + contribution: "Builder", + gen: "10th, 11th", + desc: "์‚ฌ์šฉ์ž ์ค‘์‹ฌ์˜ ํ”„๋ก ํŠธ์—”๋“œ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. DevFactory์˜ ๋‹ค์–‘ํ•œ ์›น ์„œ๋น„์Šค๋ฅผ ๋งค๋ ฅ์ ์œผ๋กœ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.", + image: "/members/seungkyu.jpg", + github: "https://github.com/ed-kyu", + linkedin: "https://www.linkedin.com/in/seungkyu-kim-9088a21b1/" + }, + { + name: "ํ™ฉ์œคํฌ", + enName: "HWANG YUNHEE", + role: "BUILDER", + position: "Product Owner", + status: "Active", + contribution: "Builder", + gen: "11th", + desc: "ํ”„๋กœ๋•ํŠธ์˜ ๋ฐฉํ–ฅ์„ฑ์„ ์ œ์‹œํ•˜๊ณ  ๊ธฐํš์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. 11๊ธฐ ๋นŒ๋”๋กœ์„œ DevFactory์˜ ์„ฑ์žฅ์„ ์ด๋•๋‹ˆ๋‹ค.", + image: "https://github.com/yunhee1.png", + github: "https://github.com/yunhee1", + linkedin: "https://www.linkedin.com/in/uni-po/" + }, + { + name: "์ตœ์œ ์ง„", + enName: "CHOI YUJIN", + role: "RUNNER", + position: "Product Owner", + status: "Active", + contribution: "Runner", + gen: "11th", + desc: "11๊ธฐ ๋Ÿฌ๋„ˆ๋กœ์„œ ํ”„๋กœ๋•ํŠธ ์˜ค๋„ˆ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ์•„์ด๋””์–ด๋ฅผ ๊ตฌ์ฒดํ™”ํ•ฉ๋‹ˆ๋‹ค.", + image: "https://github.com/yujin37.png", + github: "https://github.com/yujin37", + linkedin: "https://www.linkedin.com/in/yujin37/" + }, + { + name: "์„์ข…์ผ", + enName: "SEOK JONGIL", + role: "RUNNER", + position: "Frontend Developer", + status: "Active", + contribution: "Runner", + gen: "11th", + desc: "์›น ๊ธฐ์ˆ ์— ์—ด์ •์„ ๊ฐ€์ง„ 11๊ธฐ ๋Ÿฌ๋„ˆ์ž…๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ์„œ ํ”„๋กœ์ ํŠธ์— ๊ธฐ์—ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.", + image: "https://github.com/daclouds.png", + github: "https://github.com/daclouds", + linkedin: "https://www.linkedin.com/in/daclouds/" + }, + { + name: "์ •ํ˜„์ค€", + enName: "JUNG HYUNJUN", + role: "RUNNER", + position: "Backend Developer", + status: "Active", + contribution: "Runner", + gen: "11th", + desc: "์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” 11๊ธฐ ๋Ÿฌ๋„ˆ์ž…๋‹ˆ๋‹ค. ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค ์ œ๊ณต์— ํž˜์”๋‹ˆ๋‹ค.", + image: "https://github.com/hu6r1s.png", + github: "https://github.com/hu6r1s", + linkedin: "https://www.linkedin.com/in/hu6r1s/" + }, + { + name: "ํ•œ๋‚˜์—ฐ", + enName: "HAN NAYEON", + role: "RUNNER", + position: "Backend Developer", + status: "Active", + contribution: "Runner", + gen: "11th", + desc: "11๊ธฐ ๋Ÿฌ๋„ˆ๋กœ์„œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์— ์ฐธ์—ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํšจ์œจ์ ์ธ ์ฝ”๋“œ์™€ ํ˜‘์—…์„ ์ค‘์š”ํ•˜๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.", + image: "https://github.com/HanNayeoniee.png", + github: "https://github.com/HanNayeoniee", + linkedin: "https://www.linkedin.com/in/nayeon-han/" + }, +]; + +let currentIndex = 0; + +function updateDisplay(index) { + const member = teamMembers[index]; + + // Select elements + const visual = document.getElementById('member-visual'); + const name = document.getElementById('member-name'); + const bgName = document.getElementById('member-bg-name'); + const role = document.getElementById('role-tag'); + const desc = document.getElementById('member-desc'); + const pos = document.getElementById('stat-position'); + const contr = document.getElementById('stat-contribution'); + const gen = document.getElementById('stat-gen'); + const socialContainer = document.getElementById('member-social'); + + // Add animation classes + const panel = document.getElementById('info-panel'); + const visualContainer = visual.parentElement; + + panel.classList.remove('fade-in'); + visualContainer.classList.remove('fade-in'); + if (bgName) bgName.classList.remove('fade-in'); + + // Trigger reflow to restart animation + void panel.offsetWidth; + void visualContainer.offsetWidth; + if (bgName) void bgName.offsetWidth; + + // Update content + visual.src = member.image; + name.textContent = member.name; + if (bgName) bgName.textContent = member.enName; + role.textContent = member.role; + + // Add runner class for styling (Builder keeps default cyan) + role.className = 'role-tag'; + if (member.role === 'RUNNER') { + role.classList.add('runner'); + } + + desc.textContent = member.desc; + pos.textContent = member.position; + contr.textContent = member.contribution; + gen.textContent = member.gen; + + // Update social links (restore bar buttons) + socialContainer.innerHTML = ''; + if (member.github) { + const githubLink = document.createElement('a'); + githubLink.href = member.github; + githubLink.target = '_blank'; + githubLink.className = 'social-btn'; + githubLink.title = 'GitHub Profile'; + githubLink.innerHTML = ` + + + + GitHub + `; + socialContainer.appendChild(githubLink); + } + if (member.linkedin) { + const linkedinLink = document.createElement('a'); + linkedinLink.href = member.linkedin; + linkedinLink.target = '_blank'; + linkedinLink.className = 'social-btn'; + linkedinLink.title = 'LinkedIn Profile'; + linkedinLink.innerHTML = ` + + + + LinkedIn + `; + socialContainer.appendChild(linkedinLink); + } + + panel.classList.add('fade-in'); + visualContainer.classList.add('fade-in'); + if (bgName) bgName.classList.add('fade-in'); + + // Highlight thumbnail + document.querySelectorAll('.thumb-btn').forEach((btn, i) => { + if (i === index) btn.classList.add('active'); + else btn.classList.remove('active'); + }); +} + +function initThumbnails() { + const container = document.getElementById('thumb-grid'); + teamMembers.forEach((member, index) => { + const btn = document.createElement('button'); + btn.className = 'thumb-btn'; + if (index === 0) btn.classList.add('active'); + + const img = document.createElement('img'); + img.src = member.image; + img.alt = member.name; + + btn.appendChild(img); + btn.addEventListener('click', () => { + if (currentIndex === index) return; + currentIndex = index; + updateDisplay(index); + }); + + container.appendChild(btn); + }); +} + +function updateTime() { + const timeEl = document.getElementById('system-time'); + if (timeEl) { + const now = new Date(); + timeEl.textContent = now.toLocaleTimeString('en-US', { hour12: false }); + } +} + +function nextMember() { + currentIndex = (currentIndex + 1) % teamMembers.length; + updateDisplay(currentIndex); +} + +function prevMember() { + currentIndex = (currentIndex - 1 + teamMembers.length) % teamMembers.length; + updateDisplay(currentIndex); +} + +// Initialize +initThumbnails(); +updateDisplay(0); +setInterval(updateTime, 1000); +updateTime(); + +// Visitor Tracking +async function logVisit() { + try { + await fetch('/api/stats/visit', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + path: window.location.pathname + window.location.hash, + userAgent: navigator.userAgent + }) + }); + } catch (err) { + console.warn('Visitor tracking failed (server probably not running yet)'); + } +} + +logVisit(); + +// Prev/Next Navigation +document.getElementById('prev-member').addEventListener('click', prevMember); +document.getElementById('next-member').addEventListener('click', nextMember); + +// Cross-Platform Swipe Navigation (Touch & Mouse) +let startX = 0; +let isDragging = false; + +const teamSect = document.getElementById('team'); + +// Touch Events +teamSect.addEventListener('touchstart', e => { + startX = e.changedTouches[0].screenX; +}, { passive: true }); + +teamSect.addEventListener('touchend', e => { + const endX = e.changedTouches[0].screenX; + handleSwipe(startX, endX); +}, { passive: true }); + +// Mouse Events for PC +teamSect.addEventListener('mousedown', e => { + startX = e.screenX; + isDragging = true; +}); + +teamSect.addEventListener('mouseup', e => { + if (!isDragging) return; + const endX = e.screenX; + handleSwipe(startX, endX); + isDragging = false; +}); + +teamSect.addEventListener('mouseleave', () => { + isDragging = false; +}); + +function handleSwipe(sX, eX) { + const swipeThreshold = 50; + if (eX < sX - swipeThreshold) { + animateArrowFeedback('next-member'); + nextMember(); // Swipe Left -> Next + } else if (eX > sX + swipeThreshold) { + animateArrowFeedback('prev-member'); + prevMember(); // Swipe Right -> Prev + } +} + +function animateArrowFeedback(buttonId) { + const btn = document.getElementById(buttonId); + if (btn) { + btn.classList.add('active'); + setTimeout(() => btn.classList.remove('active'), 200); + } +} + +// Global Keyboard Navigation +document.addEventListener('keydown', (e) => { + if (e.key === 'ArrowLeft') { + prevMember(); + } else if (e.key === 'ArrowRight') { + nextMember(); + } +}); diff --git a/platform/frontend/package-lock.json b/platform/frontend/package-lock.json new file mode 100644 index 0000000..fd1ab03 --- /dev/null +++ b/platform/frontend/package-lock.json @@ -0,0 +1,551 @@ +{ + "name": "intro_page", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "intro_page", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "vite": "^4.5.14" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", + "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/platform/frontend/package.json b/platform/frontend/package.json new file mode 100644 index 0000000..d549f18 --- /dev/null +++ b/platform/frontend/package.json @@ -0,0 +1,17 @@ +{ + "name": "intro_page", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "vite --host", + "build": "vite build", + "preview": "vite preview" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "vite": "^4.5.14" + } +} \ No newline at end of file diff --git a/platform/frontend/public/brand_logo.png b/platform/frontend/public/brand_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..585e7be9b4adfa2bd4056c544643060d4cddc1ec GIT binary patch literal 21598 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 literal 0 HcmV?d00001 diff --git a/platform/frontend/public/favicon.png b/platform/frontend/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2fee67fcaa7d138ef36b55a240e58a6b10586c9 GIT binary patch literal 7346 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 literal 0 HcmV?d00001 diff --git a/docs/imgs/members/seungkyu.jpg b/platform/frontend/public/members/seungkyu.jpg similarity index 100% rename from docs/imgs/members/seungkyu.jpg rename to platform/frontend/public/members/seungkyu.jpg diff --git a/docs/imgs/members/soohyun.png b/platform/frontend/public/members/soohyun.png similarity index 100% rename from docs/imgs/members/soohyun.png rename to platform/frontend/public/members/soohyun.png diff --git a/docs/imgs/members/yesin.jpg b/platform/frontend/public/members/yesin.jpg similarity index 100% rename from docs/imgs/members/yesin.jpg rename to platform/frontend/public/members/yesin.jpg diff --git a/platform/frontend/public/pl_symbol.png b/platform/frontend/public/pl_symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..b2fee67fcaa7d138ef36b55a240e58a6b10586c9 GIT binary patch literal 7346 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 literal 0 HcmV?d00001 diff --git a/platform/frontend/public/pl_symbol_white.png b/platform/frontend/public/pl_symbol_white.png new file mode 100644 index 0000000000000000000000000000000000000000..f4262f7d9bf1156d728c8c45e0a4c75172227f67 GIT binary patch literal 19757 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 OJPPpHz^!2m(*Fa;(Xf30 literal 0 HcmV?d00001 diff --git a/platform/frontend/style.css b/platform/frontend/style.css new file mode 100644 index 0000000..36dfaec --- /dev/null +++ b/platform/frontend/style.css @@ -0,0 +1,1273 @@ +@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Space+Grotesk:wght@300;500;700&display=swap'); + +:root { + --bg-dark: #050b15; + --bg-card: rgba(10, 20, 35, 0.8); + --accent-cyan: #00f2ff; + --accent-magenta: #ff00ff; + --accent-yellow: #f8fb3c; + --text-main: #e0e6ed; + --text-dim: #94a3b8; + --glass-border: rgba(255, 255, 255, 0.1); + --neon-shadow: 0 0 15px rgba(0, 242, 255, 0.4); + --transition-smooth: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + background-color: var(--bg-dark); + color: var(--text-main); + font-family: 'Outfit', sans-serif; + overflow-x: hidden; + overflow-y: auto; +} + +/* Brand Header */ +.brand-header { + position: fixed; + top: 0; + left: 0; + width: 100%; + padding: 0.8rem 4rem; + /* Balanced padding */ + z-index: 1000; + background: rgba(5, 11, 21, 0.7); + /* Translucent dark background */ + backdrop-filter: blur(15px); + /* Premium blur effect */ + -webkit-backdrop-filter: blur(15px); + border-bottom: 1px solid rgba(255, 255, 255, 0.03); + pointer-events: auto; +} + +.header-content { + max-width: 1400px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo-container { + display: inline-flex; + align-items: center; + gap: 1.2rem; + text-decoration: none; +} + +.brand-logo.mobile-logo { + display: none; + /* Hidden on desktop */ +} + +.brand-logo { + height: 40px; + width: auto; + filter: brightness(1.2); +} + +.team-label { + font-family: 'Space Grotesk', sans-serif; + font-size: 1.6rem; + font-weight: 700; + color: #fff; + letter-spacing: -0.01rem; +} + +.header-nav { + display: flex; + gap: 2.5rem; +} + +.menu-toggle { + display: none; + /* Hidden on desktop */ + flex-direction: column; + gap: 5px; + background: transparent; + border: none; + cursor: pointer; + padding: 10px; + z-index: 1001; +} + +.menu-toggle .bar { + width: 25px; + height: 2px; + background: var(--accent-cyan); + border-radius: 2px; + transition: var(--transition-smooth); +} + +/* Mobile Menu Overlay */ +.mobile-menu-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(5, 10, 20, 0.95); + backdrop-filter: blur(15px); + z-index: 2000; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; +} + +.mobile-menu-overlay.active { + opacity: 1; + pointer-events: auto; +} + +.close-menu { + position: absolute; + top: 2rem; + right: 2rem; + background: transparent; + border: none; + color: #fff; + font-size: 3rem; + cursor: pointer; +} + +.mobile-nav-links { + display: flex; + flex-direction: column; + gap: 2.5rem; + text-align: center; +} + +.mobile-nav-link { + font-family: 'Space Grotesk', sans-serif; + font-size: 2rem; + font-weight: 700; + color: #fff; + text-decoration: none; + text-transform: uppercase; + letter-spacing: 0.2rem; + transition: var(--transition-smooth); +} + +.mobile-nav-link:hover { + color: var(--accent-cyan); + text-shadow: 0 0 20px rgba(0, 242, 255, 0.5); +} + +.nav-link { + text-decoration: none; + font-family: 'Outfit', sans-serif; + font-size: 0.9rem; + font-weight: 600; + color: var(--text-dim); + text-transform: uppercase; + letter-spacing: 0.1rem; + transition: var(--transition-smooth); + position: relative; +} + +.nav-link:hover { + color: var(--accent-cyan); +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 2px; + background: var(--accent-cyan); + transition: var(--transition-smooth); +} + +.nav-link:hover::after { + width: 100%; +} + +/* Background Effects */ +.bg-grid { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + linear-gradient(rgba(0, 242, 255, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 242, 255, 0.05) 1px, transparent 1px); + background-size: 50px 50px; + z-index: -1; + pointer-events: none; +} + +.bg-glow { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80vw; + height: 80vh; + background: radial-gradient(circle, rgba(0, 242, 255, 0.05) 0%, transparent 70%); + filter: blur(100px); + z-index: -1; + pointer-events: none; +} + +/* Full-page Section Layout */ +.full-section { + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 2rem; + position: relative; + overflow: hidden; +} + +/* Section 1: Intro/Vision */ +#intro { + text-align: center; +} + +.intro-content { + max-width: 850px; + /* Slightly wider for longer words */ +} + +.intro-badge { + display: inline-block; + padding: 0.5rem 1rem; + border: 1px solid var(--accent-cyan); + color: var(--accent-cyan); + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.2rem; + margin-bottom: 2rem; + background: rgba(0, 242, 255, 0.05); +} + +.intro-title { + font-family: 'Space Grotesk', sans-serif; + font-size: 4rem; + /* Increased from 3.8rem */ + font-weight: 800; + line-height: 1.1; + padding: 0.1em 0; + background: linear-gradient(to bottom, #00f2ff, #fff); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 2rem; + text-shadow: 0 0 40px rgba(0, 242, 255, 0.3); +} + +.intro-desc { + font-size: 1.1rem; + color: var(--text-dim); + line-height: 1.8; + margin-bottom: 3rem; +} + +.intro-cta { + display: flex; + gap: 1.5rem; + justify-content: center; +} + +.btn-primary, +.btn-secondary { + padding: 1rem 2.5rem; + font-size: 1rem; + font-weight: 600; + text-decoration: none; + border-radius: 4px; + transition: var(--transition-smooth); + text-transform: uppercase; + letter-spacing: 0.1rem; +} + +.btn-primary { + background: var(--accent-cyan); + color: var(--bg-dark); + box-shadow: var(--neon-shadow); +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 0 25px rgba(0, 242, 255, 0.6); +} + +.btn-secondary { + border: 2px solid var(--accent-magenta); + color: var(--accent-magenta); + background: transparent; +} + +.btn-secondary:hover { + background: rgba(255, 0, 255, 0.1); + transform: translateY(-3px); +} + +/* Section 2: Projects */ +#projects { + padding: 1rem 2rem 10rem 2rem; + /* Reduced top (from 3rem), increased bottom (from 8rem) */ +} + +.section-header { + text-align: center; + margin-bottom: 4rem; +} + +.section-title { + font-family: 'Space Grotesk', sans-serif; + font-size: 3.5rem; + font-weight: 700; + background: linear-gradient(to right, var(--accent-cyan), var(--accent-magenta)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 1rem; +} + +.section-subtitle { + font-size: 1.2rem; + color: var(--text-dim); +} + +.projects-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + /* Fixed 3-col for scannability */ + gap: 1.5rem; + max-width: 1100px; + width: 100%; +} + +.project-card { + background: var(--bg-card); + border: 1px solid var(--glass-border); + padding: 1.5rem; + /* Reduced padding */ + border-radius: 8px; + transition: var(--transition-smooth); + display: flex; + flex-direction: column; + text-align: left; +} + +.project-card:hover { + border-color: var(--accent-cyan); + transform: translateY(-5px); + box-shadow: var(--neon-shadow); +} + +.project-icon { + font-size: 2rem; + /* Shrunk from 3rem */ + margin-bottom: 0.8rem; +} + +.project-name { + font-size: 1.2rem; + /* Shrunk from 1.5rem */ + font-weight: 700; + margin-bottom: 0.5rem; +} + +.project-desc { + font-size: 0.9rem; + /* Shrunk from 1rem */ + color: var(--text-dim); + line-height: 1.5; + margin-bottom: 1.2rem; + flex: 1; + /* Match heights naturally */ +} + +.project-link { + color: var(--accent-cyan); + text-decoration: none; + font-weight: 600; + font-size: 0.85rem; +} + +.btn-primary, +.btn-secondary { + white-space: nowrap; + /* Prevent team-member break */ +} + +/* Section 3: Team Members - Centralized & Gathered */ +#team { + padding: 6rem 2rem; + /* Increased top padding from 4rem */ +} + +/* Giant Background Name */ +.member-bg-name-container { + position: absolute; + bottom: 20%; + left: 0; + width: 100%; + display: flex; + justify-content: center; + z-index: 1; + pointer-events: none; +} + +.member-bg-name { + font-family: 'Space Grotesk', sans-serif; + font-size: 10vw; + /* Reduced from 12vw */ + font-weight: 800; + color: rgba(255, 255, 255, 0.02); + /* Made slightly subtler */ + -webkit-text-stroke: 1px rgba(0, 242, 255, 0.08); + white-space: nowrap; + text-transform: uppercase; + letter-spacing: 0.5rem; +} + +.team-navigation { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + max-width: 1200px; + /* Limits the "Whole area" width on huge screens */ + margin: 0 auto; + position: relative; + z-index: 10; + user-select: none; + /* Prevent text selection while swiping */ +} + +.team-navigation img { + pointer-events: none; + /* Prevent image dragging while swiping */ +} + +.team-container { + display: flex; + justify-content: center; + align-items: center; + flex: 1; + gap: 4rem; + /* Restored original spacing */ +} + +/* Navigation Arrow Styling - Smart Placement */ +.arrows-wrapper { + position: absolute; + top: 50%; + left: 0; + right: 0; + transform: translateY(-50%); + width: 100%; + /* Spans the entire 1200px on PC */ + display: flex; + justify-content: space-between; + pointer-events: none; + z-index: 30; +} + +.nav-arrow { + pointer-events: auto; + /* Re-enable clicks for buttons */ + background: rgba(10, 20, 35, 0.4); + border: 1px solid rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + border-radius: 50px; + color: var(--text-dim); + padding: 1.2rem 1.5rem; + cursor: pointer; + transition: var(--transition-smooth); + display: flex; + flex-direction: column; + align-items: center; + gap: 0.2rem; +} + +.nav-arrow:hover, +.nav-arrow.active { + background: rgba(0, 242, 255, 0.2); + color: var(--accent-cyan); + box-shadow: 0 0 30px rgba(0, 242, 255, 0.3); + transform: scale(1.05); + /* Added slight pop for feedback */ +} + +.nav-arrow .nav-label { + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.2rem; +} + +.nav-arrow:hover { + background: rgba(0, 242, 255, 0.1); + color: var(--accent-cyan); + box-shadow: 0 0 30px rgba(0, 242, 255, 0.2); +} + +/* Info Panel (Left) */ +.hero-info { + max-width: 380px; + /* Reduced from 450px */ + text-align: left; + display: flex; + flex-direction: column; + gap: 0.8rem; +} + +.role-tag { + display: inline-block; + padding: 0.2rem 0.6rem; + background: rgba(0, 242, 255, 0.1); + border: 1px solid var(--accent-cyan); + color: var(--accent-cyan); + font-size: 0.75rem; + font-weight: 600; + align-self: flex-start; +} + +/* Runner - Subtle green variant */ +.role-tag.runner { + background: rgba(52, 211, 153, 0.15); + border: 1px solid rgba(52, 211, 153, 0.5); + color: #34d399; +} + +.member-name { + font-family: 'Space Grotesk', sans-serif; + font-size: 3.5rem; + /* Reduced from 4.5rem */ + font-weight: 700; + line-height: 1.1; + padding: 0.1em 0; + background: linear-gradient(to bottom, #fff, #94a3b8); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.member-desc { + font-size: 0.95rem; + /* Reduced from 1.1rem */ + color: var(--text-dim); + line-height: 1.7; +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.2rem; + margin-top: 0.5rem; +} + +.stat-item { + border-left: 2px solid var(--accent-magenta); + padding-left: 1rem; +} + +.stat-label { + font-size: 0.7rem; + color: var(--accent-magenta); + text-transform: uppercase; +} + +.stat-value { + font-size: 1.1rem; + font-weight: 600; + color: #fff; + word-break: keep-all; + /* Keep Korean together if possible */ + overflow-wrap: break-word; + /* Break long English words like Backend... */ +} + +/* Member Social Links (Profile Panel) */ +.member-social-links { + display: flex; + gap: 1.2rem; + margin-top: 1.5rem; +} + +.social-btn { + display: inline-flex; + align-items: center; + gap: 0.6rem; + padding: 0.6rem 1.2rem; + background: rgba(0, 242, 255, 0.05); + border: 1px solid rgba(0, 242, 255, 0.2); + border-radius: 4px; + color: var(--accent-cyan); + text-decoration: none; + font-size: 0.85rem; + font-weight: 600; + transition: var(--transition-smooth); +} + +.social-btn:hover { + background: rgba(0, 242, 255, 0.1); + border-color: var(--accent-cyan); + transform: translateY(-2px); + box-shadow: 0 0 15px rgba(0, 242, 255, 0.3); +} + +/* Visual (Right) */ +.hero-visual { + position: relative; + width: 320px; + /* Reduced from 420px */ + height: 320px; + /* Reduced from 420px */ + display: flex; + justify-content: center; + align-items: center; +} + +.visual-orbit { + position: absolute; + width: 280px; + /* Reduced from 380px */ + height: 280px; + /* Reduced from 380px */ + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 50%; +} + +.member-image { + max-height: 100%; + max-width: 100%; + object-fit: contain; + z-index: 5; + filter: drop-shadow(0 0 50px rgba(0, 242, 255, 0.2)); +} + +/* Navigation - Thumbnails Grid at Bottom */ +.nav-footer { + display: grid; + grid-template-columns: repeat(4, 1fr); + /* Default 2 rows for 8 people */ + gap: 1.2rem; + margin-top: 3rem; + width: fit-content; + max-width: 1000px; + margin-left: auto; + margin-right: auto; + position: relative; + z-index: 10; +} + +/* Wide Desktop: Show everyone in one line */ +@media (min-width: 1200px) { + .nav-footer { + grid-template-columns: repeat(8, 1fr); + } +} + +.thumb-btn { + width: 80px; + height: 80px; + border-radius: 50%; + border: none; + background: transparent; + cursor: pointer; + position: relative; + padding: 4px; + /* Space for the ring */ + transition: var(--transition-smooth); +} + +/* Slotted Circle Effect (Inspired by reference) */ +.thumb-btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 50%; + /* Create the "notches" using linear-gradients or clip-path */ + mask: radial-gradient(circle at 0 50%, transparent 10%, black 10.5%), + radial-gradient(circle at 100% 50%, transparent 10%, black 10.5%), + radial-gradient(circle at 50% 0, transparent 10%, black 10.5%), + radial-gradient(circle at 50% 100%, transparent 10%, black 10.5%); + -webkit-mask: radial-gradient(circle at 0 50%, transparent 10%, black 10.5%), + radial-gradient(circle at 100% 50%, transparent 10%, black 10.5%), + radial-gradient(circle at 50% 0, transparent 10%, black 10.5%), + radial-gradient(circle at 50% 100%, transparent 10%, black 10.5%); + transition: var(--transition-smooth); +} + +.thumb-btn img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; + opacity: 0.4; + transition: var(--transition-smooth); +} + +.thumb-btn:hover::before, +.thumb-btn.active::before { + border-color: var(--accent-cyan); + transform: rotate(45deg); + /* Subtle spin on active */ + box-shadow: 0 0 20px rgba(0, 242, 255, 0.4); +} + +.thumb-btn:hover img, +.thumb-btn.active img { + opacity: 1; + transform: scale(0.9); + /* Inset feel */ +} + +.thumb-btn.active { + transform: scale(1.1); +} + +@media (max-width: 480px) { + .nav-footer { + grid-template-columns: repeat(4, 1fr); + /* Keep 4 for small avatars */ + gap: 1rem; + max-width: 320px; + } + + .thumb-btn { + width: 65px; + height: 65px; + } +} + +/* Scroll Indicator */ +.scroll-indicator { + position: absolute; + bottom: 2rem; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + color: var(--accent-cyan); + animation: bounce 2s infinite; +} + +.scroll-arrow { + font-size: 2rem; +} + +@keyframes bounce { + + 0%, + 100% { + transform: translateX(-50%) translateY(0); + } + + 50% { + transform: translateX(-50%) translateY(-10px); + } +} + +.fade-in { + animation: fadeInUp 0.6s forwards; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Responsive */ +@media (max-width: 1024px) { + .team-container { + gap: 2rem; + } + + .member-name { + font-size: 3rem; + } + + .hero-visual { + width: 350px; + height: 350px; + } + + .visual-orbit { + width: 300px; + height: 300px; + } +} + +/* Site Footer */ +.site-footer { + background: #000; + padding: 3rem 3rem 1.5rem; + /* Reduced from 6rem 4rem 2rem */ + border-top: 1px solid rgba(0, 242, 255, 0.1); + position: relative; + z-index: 100; + margin-top: 8rem; + /* Add spacing between content and footer */ +} + +.footer-container { + max-width: 1400px; + margin: 0 auto; + display: flex; + justify-content: flex-start; + align-items: flex-start; + margin-bottom: 2rem; + /* Reduced from 4rem */ +} + +.footer-logo { + font-family: 'Space Grotesk', sans-serif; + font-size: 1.8rem; + /* Reduced from 2.5rem */ + font-weight: 800; + letter-spacing: -0.05rem; + color: #fff; + margin-bottom: 1.2rem; + /* Reduced from 2rem */ +} + +.footer-social-wrapper { + display: flex; + flex-direction: row; + gap: 3rem; +} + +.footer-social { + display: flex; + align-items: center; + gap: 1.2rem; +} + +.social-label { + font-size: 0.65rem; + /* Slightly smaller for multi-tier */ + min-width: 90px; + /* Ensure labels align */ + font-weight: 700; + letter-spacing: 0.15rem; + color: var(--text-dim); +} + +.social-link { + width: 32px; + height: 32px; + display: flex; + justify-content: center; + align-items: center; + text-decoration: none; + color: #fff; + font-size: 0.7rem; + font-weight: 700; + transition: var(--transition-smooth); +} + +.social-link img { + width: 20px; + height: auto; + opacity: 0.8; + transition: var(--transition-smooth); +} + +.social-link:hover img { + opacity: 1; + filter: drop-shadow(0 0 8px var(--accent-cyan)); +} + +.footer-right { + display: flex; + gap: 6rem; + align-items: flex-start; +} + +.footer-nav { + display: flex; + gap: 2.5rem; +} + +.footer-nav-link { + writing-mode: vertical-rl; + text-decoration: none; + color: #fff; + font-size: 1rem; + font-weight: 600; + letter-spacing: 0.3rem; + transition: var(--transition-smooth); + padding-bottom: 1rem; + border-right: 1px solid transparent; +} + +.footer-nav-link:hover { + color: var(--accent-cyan); + border-right-color: var(--accent-cyan); +} + +.to-top-container { + position: relative; +} + +.to-top-btn { + display: flex; + flex-direction: column; + align-items: center; + text-decoration: none; + gap: 1.5rem; +} + +.to-top-text { + writing-mode: vertical-rl; + color: #fff; + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.2rem; +} + +.to-top-line { + width: 1px; + height: 80px; + background: linear-gradient(to bottom, var(--accent-cyan), transparent); + position: relative; +} + +.to-top-line::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translate(-50%, 50%); + width: 8px; + height: 8px; + background: var(--accent-magenta); + border-radius: 50%; + box-shadow: 0 0 10px var(--accent-magenta); +} + +.footer-bottom { + max-width: 1400px; + margin: 0 auto; + border-top: 1px solid rgba(255, 255, 255, 0.05); + padding-top: 0.8rem; + /* Reduced from 2rem */ + display: flex; + justify-content: space-between; + align-items: center; +} + +.copyright { + font-size: 0.8rem; + color: var(--text-dim); +} + +.footer-meta-links { + display: flex; + gap: 2rem; +} + +.footer-meta-links a { + text-decoration: none; + color: var(--text-dim); + font-size: 0.8rem; + transition: var(--transition-smooth); +} + +.footer-meta-links a:hover { + color: #fff; +} + +@media (max-width: 768px) { + .brand-header { + padding: 0.8rem 1.5rem; + /* Reduced from 4rem */ + } + + .full-section { + padding: 3rem 1.2rem; + /* Reduced horizontal padding for better flow */ + } + + /* Section Header Compactness */ + .section-header { + margin-bottom: 1.5rem; + } + + .section-title { + font-size: 2.2rem; + } + + .section-subtitle { + font-size: 0.9rem; + } + + /* Team Section Mobile Fixes (Photo on Top) */ + #team { + min-height: 100vh; + height: auto; + padding: 6rem 1.2rem 4rem 1.2rem; + display: flex; + flex-direction: column; + justify-content: center; + /* Better tall screen centering */ + gap: 2rem; + } + + .team-navigation { + flex-direction: column; + gap: 1.5rem; + align-items: center; + width: 100%; + margin: 0; + } + + .team-container { + flex-direction: column-reverse; + /* PHOTO ON TOP */ + text-align: center; + gap: 2.5rem; + width: 100%; + order: 1; + } + + /* Arrows tightly framing the photo on mobile */ + .arrows-wrapper { + position: absolute; + top: 130px; + /* Centered vertically on the 140px photo */ + left: 50%; + transform: translate(-50%, -50%); + width: 100vw; + /* Spans full screen width */ + display: flex; + justify-content: space-between; + padding: 0 0.4rem; + /* Tight gap from the wall */ + pointer-events: none; + z-index: 100; + } + + .nav-arrow { + pointer-events: auto; + display: flex; + padding: 0.6rem 0.8rem; + width: auto; + min-width: 45px; + background: rgba(10, 20, 35, 0.7); + backdrop-filter: blur(8px); + border-radius: 4px; + font-size: 0.9rem; + } + + .nav-prev { + order: 1; + } + + .nav-next { + order: 2; + } + + .hero-info { + max-width: 100%; + align-items: center; + text-align: center; + } + + .member-name { + font-size: 2rem; + } + + .stat-value { + font-size: 0.9rem; + /* Reduced from 1.1rem for mobile */ + } + + .member-desc { + font-size: 0.9rem; + line-height: 1.5; + margin-bottom: 2rem; + min-height: auto; + /* Allow content to dictate height */ + display: block; + /* Remove flex/clamp behavior */ + } + + .stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem 0.5rem; + width: 100%; + max-width: 260px; + /* Unified width to keep pink bars aligned */ + margin: 0 auto; + text-align: left; + } + + .stat-item { + min-height: 3rem; + /* Standardized height for all stat boxes */ + display: flex; + flex-direction: column; + justify-content: flex-start; + } + + .hero-visual { + width: 100%; + max-width: 140px; + /* Reduced further from 150px */ + height: 140px; + margin: 0 auto; + } + + .visual-orbit { + width: 130px; + height: 130px; + } + + .nav-footer { + margin-top: 1rem; + /* Heavily reduced margin */ + gap: 0.5rem; + } + + .thumb-btn { + width: 50px; + /* Shrunk from 65px/80px */ + height: 50px; + } + + .role-tag { + align-self: center; + font-size: 0.7rem; + padding: 0.2rem 0.6rem; + } + + .intro-title { + font-size: 2.4rem; + margin-bottom: 1.5rem; + } + + .intro-title br { + display: none; + /* Hide line break on mobile for better flow */ + } + + .intro-desc { + font-size: 1rem; + } + + .intro-cta { + flex-wrap: wrap; + /* Allow stacking if extremely narrow */ + gap: 1rem; + } + + .btn-primary, + .btn-secondary { + padding: 0.8rem 1.2rem; + /* Tighter padding for mobile */ + font-size: 0.85rem; + text-align: center; + min-width: 130px; + /* Uniform width for symmetry */ + } + + .projects-grid { + grid-template-columns: 1fr; + gap: 1.2rem; + } + + .project-card { + padding: 1.2rem; + } + + #projects { + padding: 6rem 1.2rem; + } + + .footer-container { + flex-direction: column; + gap: 2rem; + text-align: center; + } + + .footer-social-wrapper { + flex-direction: column; + gap: 1.5rem; + align-items: center; + } + + .footer-left { + align-items: center; + display: flex; + flex-direction: column; + } + + .footer-bottom { + flex-direction: column; + gap: 1.5rem; + } + + .logo-container { + gap: 0.8rem; + } + + .brand-logo.standard-logo { + display: block; + /* Show full logo again */ + height: 22px; + /* Adjusted size to fit menu button */ + } + + .brand-logo.mobile-logo { + display: none; + } + + .team-label { + display: block !important; + /* Restore 'DevFactory' text */ + font-size: 1.1rem; + /* Smaller for mobile header */ + } + + .header-nav { + display: none; + /* Hide direct links on mobile */ + } + + .menu-toggle { + display: flex; + /* Show menu button */ + } + + .nav-link { + font-size: 0.75rem; + } +} \ No newline at end of file diff --git a/platform/frontend/vite.config.js b/platform/frontend/vite.config.js new file mode 100644 index 0000000..c587c08 --- /dev/null +++ b/platform/frontend/vite.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + server: { + allowedHosts: [ + 'private.pseudolab-devfactory.com' + ], + host: '0.0.0.0', + port: 5173, + // Add HMR port if needed for Traefik + hmr: { + clientPort: 443 + } + } +}); diff --git a/platform/server/.env.example b/platform/server/.env.example new file mode 100644 index 0000000..78b89a2 --- /dev/null +++ b/platform/server/.env.example @@ -0,0 +1,2 @@ +DATABASE_URL=postgresql://username:password@localhost:5432/devfactory +PORT=3000 diff --git a/platform/server/Dockerfile b/platform/server/Dockerfile new file mode 100644 index 0000000..270de5c --- /dev/null +++ b/platform/server/Dockerfile @@ -0,0 +1,16 @@ +FROM node:20-alpine + +WORKDIR /app + +# Copy package files and install dependencies +COPY package*.json ./ +RUN npm install + +# Copy source files +COPY . . + +# Expose port +EXPOSE 3000 + +# Run the server +CMD ["npm", "start"] diff --git a/platform/server/package-lock.json b/platform/server/package-lock.json new file mode 100644 index 0000000..519b62f --- /dev/null +++ b/platform/server/package-lock.json @@ -0,0 +1,1255 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "cors": "^2.8.6", + "dotenv": "^17.2.3", + "express": "^5.2.1", + "pg": "^8.17.2" + }, + "devDependencies": { + "nodemon": "^3.1.11" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pg": { + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.17.2.tgz", + "integrity": "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw==", + "dependencies": { + "pg-connection-string": "^2.10.1", + "pg-pool": "^3.11.0", + "pg-protocol": "^1.11.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.10.1.tgz", + "integrity": "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", + "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", + "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/platform/server/package.json b/platform/server/package.json new file mode 100644 index 0000000..7a5a43e --- /dev/null +++ b/platform/server/package.json @@ -0,0 +1,23 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node src/index.js", + "dev": "nodemon src/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "cors": "^2.8.6", + "dotenv": "^17.2.3", + "express": "^5.2.1", + "pg": "^8.17.2" + }, + "devDependencies": { + "nodemon": "^3.1.11" + } +} \ No newline at end of file diff --git a/platform/server/src/index.js b/platform/server/src/index.js new file mode 100644 index 0000000..504b6c2 --- /dev/null +++ b/platform/server/src/index.js @@ -0,0 +1,72 @@ +require('dotenv').config(); +const express = require('express'); +const { Pool } = require('pg'); +const cors = require('cors'); + +const app = express(); +const port = process.env.PORT || 3000; + +// Middleware +app.use(cors()); +app.use(express.json()); + +// PostgreSQL Connection +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, +}); + +// Test DB Connection +pool.query('SELECT NOW()', (err, res) => { + if (err) { + console.error('Error connecting to the database:', err); + } else { + console.log('Connected to PostgreSQL successfully'); + } +}); + +// API Routes +app.get('/api/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +// Register a visit +app.post('/api/stats/visit', async (req, res) => { + try { + const { path, userAgent } = req.body; + // ๊ธฐ์กด ๋กœ๊ทธ ํฌ๋งท์— ๋งž์ถฐ method๋Š” 'PAGEVIEW'๋กœ, referrer๋Š” ํ˜„์žฌ ํ˜ธ์ŠคํŠธ๋กœ ๊ธฐ๋ก + const referrer = req.headers.referer || ''; + + await pool.query( + 'INSERT INTO logging.access_log (path, method, status, user_agent, referrer, ts) VALUES ($1, $2, $3, $4, $5, NOW())', + [path || '/', 'PAGEVIEW', 200, userAgent, referrer] + ); + res.status(201).json({ message: 'Visit logged successfully' }); + } catch (err) { + console.error('Error logging visit:', err); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Get total visit count (filtered by current site if requested) +app.get('/api/stats/count', async (req, res) => { + try { + const { site } = req.query; + let query = 'SELECT COUNT(*) FROM logging.access_log'; + let params = []; + + if (site) { + query += ' WHERE referrer LIKE $1'; + params.push(`%${site}%`); + } + + const result = await pool.query(query, params); + res.json({ count: parseInt(result.rows[0].count) }); + } catch (err) { + console.error('Error fetching count:', err); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +app.listen(port, () => { + console.log(`Server running on port ${port}`); +});