From c37787251bba344644e6a74548589d2c39741fe4 Mon Sep 17 00:00:00 2001 From: Do Siki Date: Mon, 17 Aug 2026 12:19:02 +0200 Subject: [PATCH] chore(deploy): remove Dokploy, switch to native docker compose + Gitea Actions - Remove Dokploy deploy jobs from GitHub CI workflow (test + build only) - Add .gitea/workflows/ci.yml (Gitea Actions: lint, tests, docker build verify) - Rewrite deploy.sh: set -euo pipefail, mandatory .env., --env-file, healthcheck on /api/health with log dump on failure - Delete Dokploy-specific docs (3 files), update references in README, TODO.md, architecture.md, guides, dev plan - Remove DOKPLOY_* env vars from proto/.env.local --- .agent/steering/architecture.md | 4 +- .gitea/workflows/ci.yml | 73 +++ .github/workflows/ci.yml | 95 +--- GITHUB-CICD-GUIDE.md | 398 ++++----------- GITHUB-INTEGRATION.md | 4 +- README.md | 24 +- REQUIREMENTS-CATALOG.md | 5 +- SYNC-STATUS-ANALYSIS.md | 48 +- deploy.sh | 44 +- ...es Utmutato - Webfejlesztesi Projekthez.md | 455 ------------------ ...Uj site letrehozasa és urlap kitoltese.pdf | Bin 70722 -> 0 bytes docs/fejlesztesi_terv_mozdit.md | 26 +- ...telmeny_dokploy_deploy_specifikacio_mvp.md | 236 --------- 13 files changed, 246 insertions(+), 1166 deletions(-) create mode 100644 .gitea/workflows/ci.yml delete mode 100755 docs/Dokploy Teljes Utmutato - Webfejlesztesi Projekthez.md delete mode 100755 docs/Dokploy UI – Uj site letrehozasa és urlap kitoltese.pdf delete mode 100755 docs/mozd_it_weboldal_kovetelmeny_dokploy_deploy_specifikacio_mvp.md diff --git a/.agent/steering/architecture.md b/.agent/steering/architecture.md index 675f595..7da2385 100644 --- a/.agent/steering/architecture.md +++ b/.agent/steering/architecture.md @@ -14,7 +14,7 @@ A projekt egy **Next.js 15 alapú marketing weboldal** a mozdIT Bt. számára, A - **Backend**: Next.js API Routes (`/api/*`) - **Adatbázis**: MongoDB (Mongoose ODM) — site config és contact form logok - **Logging**: Winston + Loki (strukturált naplózás) -- **Infrastructure**: Docker + Docker Compose, Dokploy deployment +- **Infrastructure**: Docker + Docker Compose, natív deploy (`deploy.sh`) ## Rétegek és Felelősségek @@ -38,7 +38,7 @@ A projekt egy **Next.js 15 alapú marketing weboldal** a mozdIT Bt. számára, A ## Adatfolyam (Request Lifecycle) -1. **Dokploy / Nginx**: SSL, rate limit (infra szint) +1. **Nginx / reverse proxy**: SSL, rate limit (infra szint) 2. **Next.js middleware**: Auth ellenőrzés (ha szükséges) 3. **API Route**: Input validáció 4. **Lib/Service**: Üzleti logika végrehajtása diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..bec2f25 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,73 @@ +# CI workflow for Gitea (git.mozdit.hu) +# Runs on every push to main/develop and on PRs to main. +# Deploy is NOT handled here — use deploy.sh on the server (native docker compose). +name: CI — Test & Build + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +env: + NODE_VERSION: '20' + REGISTRY: git.mozdit.hu + IMAGE_NAME: si/websitedev + +jobs: + test: + name: 🧪 Run Tests & Generate Reports + runs-on: ubuntu-latest + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: proto/package-lock.json + + - name: 📥 Install dependencies + working-directory: ./proto + run: npm ci + + - name: 🔍 Lint code + working-directory: ./proto + run: npm run lint + + - name: 🧪 Run unit tests + working-directory: ./proto + run: npm run test:unit -- --json --outputFile=unit-results.json --silent + + - name: 🌐 Run browser integration tests + working-directory: ./proto + run: npm run test:browser -- --json --outputFile=browser-results.json --silent + + - name: 📋 Upload test results + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + proto/unit-results.json + proto/browser-results.json + retention-days: 30 + + build: + name: 🏗️ Build Docker Image + runs-on: ubuntu-latest + needs: test + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + + - name: 🏗️ Build Docker image + run: docker build -t mozdit-app:ci ./proto + + - name: 🔍 Verify image health + run: | + docker create --name mozdit-app-ci -e NODE_ENV=production mozdit-app:ci + docker cp mozdit-app-ci:/app/.next/BUILD_ID ./build-id 2>/dev/null || \ + echo "BUILD_ID nem elérhető — build verification skip" + docker rm mozdit-app-ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25ea73d..7ac7467 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI/CD Pipeline with Test Management +name: CI Pipeline with Test Management on: push: @@ -8,8 +8,6 @@ on: env: NODE_VERSION: '20' - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} jobs: # 🧪 Test Phase @@ -131,48 +129,19 @@ jobs: proto/e2e-report.txt retention-days: 30 - # 🏗️ Build Phase + # 🏗️ Build Phase (verify the production image builds; no registry push — deploy via deploy.sh) build: name: 🏗️ Build Docker Image runs-on: ubuntu-latest needs: [test, docker-tests] - outputs: - image-digest: ${{ steps.build.outputs.digest }} - image-tag: ${{ steps.meta.outputs.tags }} steps: - name: 📥 Checkout code uses: actions/checkout@v4 - - name: 🔐 Log in to Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + - name: 🏗️ Build Docker image + run: docker build -t mozdit-app:ci ./proto - - name: 📋 Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} - - - name: 🏗️ Build and push Docker image - id: build - uses: docker/build-push-action@v5 - with: - context: ./proto - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # 📊 Test Results Summary (GitHub Only) + # 📊 Test Results Summary test-summary: name: 📊 Generate Test Summary runs-on: ubuntu-latest @@ -219,11 +188,6 @@ jobs: summary += '### 📊 Build Status\n'; summary += '- Tests: ✅ All passing\n'; summary += '- Docker build: ✅ Ready\n'; - summary += '- Deployment: ✅ Ready\n\n'; - - summary += '### 🔗 Links\n'; - summary += '- [Test Management](https://linear.app/zeener) (Linear)\n'; - summary += '- [Build Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\n'; github.rest.issues.createComment({ issue_number: context.issue.number, @@ -231,52 +195,3 @@ jobs: repo: context.repo.repo, body: summary }); - - # 🚀 Deploy to Staging - deploy-staging: - name: 🚀 Deploy to Staging - runs-on: ubuntu-latest - needs: build - if: github.ref == 'refs/heads/develop' - environment: - name: staging - url: https://staging.mozdit.hu - steps: - - name: 🚀 Deploy to Dokploy Staging - run: | - echo "🚀 Deploying to staging environment..." - curl -X POST "${{ secrets.DOKPLOY_STAGING_WEBHOOK }}" \ - -H "Authorization: Bearer ${{ secrets.DOKPLOY_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d '{ - "image": "${{ needs.build.outputs.image-tag }}", - "environment": "staging" - }' - - # 🌟 Deploy to Production - deploy-production: - name: 🌟 Deploy to Production - runs-on: ubuntu-latest - needs: [build, test-summary] - if: github.ref == 'refs/heads/main' - environment: - name: production - url: https://mozdit.hu - steps: - - name: 🌟 Deploy to Dokploy Production - run: | - echo "🌟 Deploying to production environment..." - curl -X POST "${{ secrets.DOKPLOY_PROD_WEBHOOK }}" \ - -H "Authorization: Bearer ${{ secrets.DOKPLOY_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d '{ - "image": "${{ needs.build.outputs.image-tag }}", - "environment": "production" - }' - - - name: 🎉 Notify deployment success - if: success() - run: | - echo "🎉 Production deployment successful!" - echo "📊 All tests passed and synced with Linear" - echo "🔗 Site available at: https://mozdit.hu" diff --git a/GITHUB-CICD-GUIDE.md b/GITHUB-CICD-GUIDE.md index c5e0677..81f9a2d 100755 --- a/GITHUB-CICD-GUIDE.md +++ b/GITHUB-CICD-GUIDE.md @@ -1,43 +1,36 @@ -# GitHub CI/CD Guide - Code & Deployment +# CI/CD Guide - Code & Deployment -## 🎯 **Cél: GitHub-specifikus CI/CD Pipeline** +## 🎯 **Cél** -Ez a dokumentum a **GitHub Actions** rendszerét írja le, amely **csak** a CI/CD és deployment-re fókuszál. +A projekt CI/CD rendszere **Gitea Actions**-en fut (`git.mozdit.hu`), a deploy pedig **natív Docker Compose** (`deploy.sh`). Dokploy nem használt. --- -## 🚀 **GitHub CI/CD Architektúra** +## 🔄 **Architektúra** -### **Pipeline Folyamat:** +### Pipeline Folyamat: ```mermaid graph TD - A[Code Push] --> B[GitHub Actions] + A[Code Push] --> B[Gitea Actions] B --> C[Run Tests] B --> D[Build Docker Image] - B --> E[Push to Registry] - B --> F[Deploy to Dokploy] - - G[PR Creation] --> H[Test Validation] - H --> I[Staging Deploy] - - J[Manual Trigger] --> B + E[main push] --> F[Manuális deploy a szerveren] + F --> G[deploy.sh — docker compose up --build] + + H[Manual Trigger] --> B ``` -### **Mit kezel a GitHub CI/CD:** -- ✅ **Source Code** versioning és tracking -- ✅ **Test Execution** (unit, integration, e2e) -- ✅ **Docker Image** build és registry push -- ✅ **Deployment** staging és production környezetekre -- ✅ **PR Validation** és code review support +### Mit kezel a CI/CD: +- ✅ **Test Execution** (unit, browser, docker integration) +- ✅ **Docker Image** build ellenőrzés (nincs registry push) +- ✅ **Deployment** natív docker compose-szal (`deploy.sh`) - ✅ **Build Artifacts** és reporting --- -## 🔄 **GitHub Actions Workflows** +## 🐙 **Gitea Actions Workflow (`.gitea/workflows/ci.yml`)** -### **1. Main CI/CD Pipeline (`.github/workflows/ci.yml`)** - -#### **Trigger Events:** +### Trigger Events: ```yaml on: push: @@ -46,332 +39,111 @@ on: branches: [main] ``` -#### **Job Sequence:** -```mermaid -graph LR - A[Test Phase] --> B[Docker Tests] - B --> C[Build Phase] - C --> D[Test Summary] - D --> E[Deploy Staging] - D --> F[Deploy Production] -``` +### Job Sequence: +- **🧪 Test Phase** — lint + unit + browser tesztek +- **🏗️ Build Phase** — production Docker image build (verifikáció, nem push) -#### **Jobs Detail:** - -##### **🧪 Test Phase** -- **Unit Tests**: Component és API tesztek -- **Browser Integration**: jsdom + mocked APIs -- **Test Artifacts**: JSON results export - -##### **🐳 Docker Integration Tests** -- **Docker Services**: Next.js + MongoDB + monitoring -- **Integration Tests**: Real HTTP calls -- **E2E Tests**: Complete user workflows - -##### **🏗️ Build Phase** -- **Docker Image**: Multi-stage production build -- **Registry Push**: GitHub Container Registry -- **Image Tagging**: Branch és commit alapján - -##### **📊 Test Summary** -- **PR Comments**: Test results summary -- **Build Status**: Success/failure reporting -- **Artifact Upload**: Test results és reports - -##### **🚀 Deployment** -- **Staging**: `develop` branch → Dokploy staging -- **Production**: `main` branch → Dokploy production +A `.github/workflows/ci.yml` (GitHub) hasonló teszteket futtat, de **nincs deploy** lépése — a deploy mindig a szerveren történik. --- -## 🐳 **Docker Integration** +## 🚀 **Deployment (natív docker compose)** -### **Docker Image Build:** -```dockerfile -# Multi-stage build -FROM node:20-alpine AS builder -# ... build steps +### Deploy a szerveren: -FROM node:20-alpine AS runner -# ... production setup -``` - -### **Container Registry:** -```yaml -# GitHub Container Registry -registry: ghcr.io -image: ghcr.io/your-username/websitedev -tags: | - type=ref,event=branch - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} -``` - -### **Docker Services (Testing):** -```yaml -# docker-compose.dev.yml -services: - app: # Next.js application - mongodb: # Database - mongo-express: # Database UI - loki: # Logging - grafana: # Monitoring -``` - ---- - -## 🚀 **Deployment Integration** - -### **Dokploy Integration:** -```yaml -# Staging Deployment -deploy-staging: - if: github.ref == 'refs/heads/develop' - environment: staging - steps: - - name: Deploy to Dokploy Staging - run: | - curl -X POST "${{ secrets.DOKPLOY_STAGING_WEBHOOK }}" \ - -H "Authorization: Bearer ${{ secrets.DOKPLOY_TOKEN }}" \ - -d '{"image": "${{ needs.build.outputs.image-tag }}"}' - -# Production Deployment -deploy-production: - if: github.ref == 'refs/heads/main' - environment: production - steps: - - name: Deploy to Dokploy Production - run: | - curl -X POST "${{ secrets.DOKPLOY_PROD_WEBHOOK }}" \ - -H "Authorization: Bearer ${{ secrets.DOKPLOY_TOKEN }}" \ - -d '{"image": "${{ needs.build.outputs.image-tag }}"}' -``` - -### **Environment Configuration:** -```yaml -# Staging Environment -environment: - name: staging - url: https://staging.mozdit.hu - -# Production Environment -environment: - name: production - url: https://mozdit.hu -``` - ---- - -## 🔧 **GitHub Configuration** - -### **Required Secrets:** ```bash -# GitHub Repository Secrets -DOKPLOY_STAGING_WEBHOOK=https://staging.webhook.url -DOKPLOY_PROD_WEBHOOK=https://prod.webhook.url -DOKPLOY_TOKEN=your_dokploy_token +# Staging +./deploy.sh staging -# Optional (for advanced features) -GITHUB_TOKEN=auto_provided +# Production +./deploy.sh production ``` -### **Environment Variables:** +### Mit csinál a `deploy.sh`: +1. `git pull origin main` +2. `.env.` fájl betöltése (kötelező) +3. `docker compose --env-file .env. -f docker-compose.prod.yml up --build -d` +4. Healthcheck a `/api/health` endpointon (60s timeout, hiba esetén log dump) + +### Szükséges fájlok a szerveren: +```bash +# .env.production — production környezeti változók +MONGODB_URI=mongodb://admin:XXX@mongodb:27017/mozdit?authSource=admin +MONGODB_DB=mozdit +NEXT_PUBLIC_SITE_URL=https://mozdit.hu +NEXT_PUBLIC_CONTACT_EMAIL=info@mozdit.hu +LOKI_HOST=http://loki:3100 +APP_PORT=8080 + +# .env.staging — staging változók (hasonlóan) +``` + +> ⚠️ A `.env.*` fájlok sosem kerülnek git-be — a szerveren kell létrehozni őket. + +### Reverse proxy / SSL: +A `docker-compose.prod.yml` csak az app + MongoDB-t futtatja. A TLS/SSL és rate limit az infra szintű Nginx/caddy reverse proxy feladata (nem része a repónak). + +--- + +## 📊 **Monitoring** + +- **Logok**: `docker compose -f docker-compose.prod.yml logs -f app` +- **Health**: `GET /api/health` → `{ success: true, data: {...} }` +- **Winston + Loki**: alkalmazás logok (ha Loki elérhető a stackben) + +--- + +## 🔧 **Konfiguráció** + +### Környezeti változók (CI): ```yaml env: NODE_VERSION: '20' - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} ``` -### **Branch Protection Rules:** -```yaml -# Recommended branch protection for main -required_status_checks: - - test - - docker-tests - - build -required_reviews: 1 -enforce_admins: true -``` +### Branch stratégia: +- `main` → production (deploy: `./deploy.sh production`) +- `develop` → staging (deploy: `./deploy.sh staging`) +- `feature/*` → fejlesztői branch-ek --- -## 📊 **Monitoring & Reporting** +## 🛠️ **Local Development** -### **GitHub Actions Status:** -```markdown -[![CI/CD Pipeline](https://github.com/user/repo/actions/workflows/ci.yml/badge.svg)] -[![Docker](https://img.shields.io/badge/Docker-Ready-blue)] -[![Deployment](https://img.shields.io/badge/Deployment-Active-success)] -``` - -### **PR Comments:** -```markdown -## 🧪 Test Results Summary - -### ✅ Unit Tests -- All unit tests passed -- Component tests: ✅ -- API tests: ✅ - -### 🐳 Integration Tests -- Docker services tested -- API endpoints: ✅ -- Database connectivity: ✅ - -### 📊 Build Status -- Tests: ✅ All passing -- Docker build: ✅ Ready -- Deployment: ✅ Ready -``` - -### **Build Artifacts:** -- **Test Results**: `unit-results.json`, `integration-results.json` -- **Docker Images**: `ghcr.io/user/repo:tag` -- **Build Logs**: GitHub Actions logs -- **Deployment Status**: Dokploy integration - ---- - -## 🛠️ **Local Development Integration** - -### **Pre-commit Hooks:** ```bash -# Install pre-commit hooks -npm install --save-dev husky lint-staged +# Tesztek futtatása (ugyanaz, mint CI-ben) +cd proto && npm run test:all -# package.json -{ - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.{js,ts,tsx}": ["eslint --fix", "git add"] - } -} -``` - -### **Local Testing:** -```bash -# Run same tests as CI -npm run test:all - -# Test Docker build locally -npm run docker:build - -# Test deployment locally -npm run docker:run -``` - -### **Git Workflow:** -```bash -# Feature development -git checkout -b feature/new-feature -# ... development -git add . -git commit -m "feat: add new feature" -git push origin feature/new-feature - -# Create PR -gh pr create --title "Add new feature" --body "Description" - -# After review and merge -git checkout main -git pull origin main -# Automatic deployment triggers +# Docker build tesztelése lokálisan +cd proto && npm run docker:build ``` --- ## 🔍 **Troubleshooting** -### **Gyakori Problémák:** - -#### **1. Docker Build Fails** +#### 1. Deploy hiba — healthcheck nem OK ```bash -Error: Docker build failed +./deploy.sh production # hibaüzenet + app logok dump +docker compose -f docker-compose.prod.yml logs -f app ``` -**Megoldás:** -- Ellenőrizd a `Dockerfile` syntax-át -- Ellenőrizd a `package.json` dependencies-ét -- Ellenőrizd a build context-et +- Ellenőrizd a `.env.production` tartalmát +- Ellenőrizd, hogy a MongoDB konténer elérhető-e +- Ellenőrizd a lokális teszteket: `cd proto && npm run test:all` -#### **2. Test Failures** -```bash -Error: Tests failed in CI -``` -**Megoldás:** -- Futtasd a teszteket helyileg: `npm run test:all` -- Ellenőrizd a test environment változókat -- Ellenőrizd a Docker services állapotát - -#### **3. Deployment Fails** -```bash -Error: Dokploy deployment failed -``` -**Megoldás:** -- Ellenőrizd a `DOKPLOY_*` secrets-eket -- Ellenőrizd a webhook URL-eket -- Ellenőrizd a Dokploy service állapotát - -### **Debug Commands:** -```bash -# Local Docker testing -docker-compose -f docker-compose.dev.yml up -d -docker-compose -f docker-compose.dev.yml logs -f - -# GitHub Actions debugging -gh run list --workflow=ci.yml -gh run view --log - -# Docker image testing -docker run -p 3000:3000 ghcr.io/user/repo:latest -``` - ---- - -## 📋 **Best Practices** - -### **1. Branch Strategy:** -- `main` → Production deployment -- `develop` → Staging deployment -- `feature/*` → Development branches -- `hotfix/*` → Critical fixes - -### **2. Commit Messages:** -```bash -feat: add new feature -fix: resolve bug -docs: update documentation -test: add tests -refactor: code refactoring -``` - -### **3. PR Guidelines:** -- Minden PR-nek kell test coverage -- Minden PR-nek kell review -- Breaking changes dokumentálása -- Changelog frissítése - -### **4. Deployment Strategy:** -- Staging deployment minden `develop` push után -- Production deployment csak `main` merge után -- Rollback strategy kész -- Health checks minden deployment után +#### 2. CI hiba +- Nézd meg a futást a Gitea Actions fülön: `https://git.mozdit.hu/si/websitedev/actions` +- Helyi reprodukció: `cd proto && npm run lint && npm run test:all` --- ## 🔗 **Kapcsolódó Dokumentumok** -- [Linear Sync Guide](./LINEAR-SYNC-GUIDE.md) +- [Plane Sync Guide](./PLANE-SYNC-GUIDE.md) - [Docker Setup](./DOCKER.md) -- [Testing Guide](./proto/TESTING.md) -- [Sync Status Analysis](./SYNC-STATUS-ANALYSIS.md) +- [Test Management](./TEST-MANAGEMENT.md) --- -**Utolsó frissítés:** 2025-09-05 -**Státusz:** GitHub CI/CD szétválasztva Linear sync-től -**Következő lépés:** Dokploy konfiguráció és secrets beállítása +**Utolsó frissítés:** 2026-08-17 +**Státusz:** Dokploy eltávolítva, natív docker compose + Gitea Actions diff --git a/GITHUB-INTEGRATION.md b/GITHUB-INTEGRATION.md index 995840f..845824c 100755 --- a/GITHUB-INTEGRATION.md +++ b/GITHUB-INTEGRATION.md @@ -159,10 +159,8 @@ Daily at 9 AM UTC ### **Required GitHub Secrets:** ```bash LINEAR_API_KEY=your_linear_api_key -DOKPLOY_STAGING_WEBHOOK=https://staging.webhook.url -DOKPLOY_PROD_WEBHOOK=https://prod.webhook.url -DOKPLOY_TOKEN=your_dokploy_token ``` +> ⚠️ Deploy nem történik CI-ből — natív docker compose (`deploy.sh`) a szerveren. ### **Environment Variables:** ```bash diff --git a/README.md b/README.md index dadabf8..6dca09f 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI/CD Pipeline](https://github.com/your-username/websitedev/actions/workflows/ci.yml/badge.svg)](https://github.com/your-username/websitedev/actions/workflows/ci.yml) [![Docker](https://img.shields.io/badge/Docker-Ready-blue)](./docker-compose.dev.yml) -[![Linear Sync](https://img.shields.io/badge/Linear-Synced-success)](https://linear.app/zeener) +[![Plane Sync](https://img.shields.io/badge/Plane-Synced-success)](https://pm.llmdev.mozdit.hu) [![Deployment](https://img.shields.io/badge/Deployment-Active-success)](https://mozdit.hu) Modern Next.js 14 website for mozdIT Bt. - Hungarian IT services company specializing in web hosting, email services, and DNS administration. @@ -24,17 +24,17 @@ cd proto && npm run test:all This project uses **separated sync systems** for optimal management: -### 🧪 **Linear Sync (Test Management)** -- **Requirements**: [ZEE-47: Contact Form Validation](https://linear.app/zeener/issue/ZEE-47) -- **Test Cases**: [ZEE-48: Email Validation](https://linear.app/zeener/issue/ZEE-48), [ZEE-49: Rate Limiting](https://linear.app/zeener/issue/ZEE-49) -- **Test Execution**: Automatic sync with Linear issues -- **Traceability**: Requirements → Test Cases → Automated Tests +### 📊 **Plane Sync (Task Management)** +- **Project**: [MITHOME — mozdIT website dev](https://pm.llmdev.mozdit.hu) (workspace: `developments`) +- **TODO.md**: Local mirror, Plane is the authoritative source +- **Sync**: `node plane-sync.js` (one-way Plane → TODO.md) +- **Status Mapping**: `completed` → ✅, `started` → 🔄, `unstarted` → ⏳, `backlog` → 📋 ### 🐙 **GitHub Sync (CI/CD)** - **Code Versioning**: Git-based source control - **CI/CD Pipeline**: Automated testing and deployment - **Docker Integration**: Container build and registry -- **Deployment**: Staging and production via Dokploy +- **Deployment**: Docker Compose (deploy.sh) ### 🧪 Test Types - **Unit Tests**: Component and logic testing @@ -49,9 +49,9 @@ This project uses **separated sync systems** for optimal management: - **Content Management**: JSON-based structured content - **Backend**: Next.js API Routes, MongoDB - **Testing**: Jest, React Testing Library, Docker -- **Deployment**: Dokploy, Docker +- **Deployment**: Docker Compose (deploy.sh), native - **Monitoring**: Winston, Loki, Grafana -- **Project Management**: Linear, GitHub Issues +- **Project Management**: Plane, GitHub Issues ## 📈 Current Status @@ -68,7 +68,7 @@ This project uses **separated sync systems** for optimal management: - [x] Rate limiting and security features - [x] Docker development environment - [x] Comprehensive test suite -- [x] CI/CD pipeline with Linear integration +- [x] CI/CD pipeline with Plane sync integration - [x] Requirements traceability system ### 🚧 In Progress @@ -80,13 +80,13 @@ This project uses **separated sync systems** for optimal management: - **Production**: https://mozdit.hu - **Staging**: https://staging.mozdit.hu -- **Linear Project**: https://linear.app/zeener +- **Plane Project**: https://pm.llmdev.mozdit.hu (workspace: `developments`, project: `MITHOME`) - **Docker Registry**: ghcr.io/your-username/websitedev ## 📚 Documentation ### **Sync Systems:** -- [Linear Sync Guide](./LINEAR-SYNC-GUIDE.md) - Test management with Linear +- [Plane Sync Guide](./PLANE-SYNC-GUIDE.md) - Task management with Plane - [GitHub CI/CD Guide](./GITHUB-CICD-GUIDE.md) - Code & deployment pipeline - [Sync Status Analysis](./SYNC-STATUS-ANALYSIS.md) - Current state overview diff --git a/REQUIREMENTS-CATALOG.md b/REQUIREMENTS-CATALOG.md index baecd46..a5c8f0e 100755 --- a/REQUIREMENTS-CATALOG.md +++ b/REQUIREMENTS-CATALOG.md @@ -553,14 +553,13 @@ **Részletek**: - Automated testing - Docker image build -- Registry push -- Dokploy deployment +- Natív deploy docker compose-szal (`deploy.sh`) **Acceptance Criteria**: - [x] GitHub Actions működik - [x] Automated testing aktív - [x] Docker build működik -- [x] Deployment pipeline működik +- [x] Deployment pipeline működik (natív docker compose) --- diff --git a/SYNC-STATUS-ANALYSIS.md b/SYNC-STATUS-ANALYSIS.md index 16d372d..9eaaec1 100755 --- a/SYNC-STATUS-ANALYSIS.md +++ b/SYNC-STATUS-ANALYSIS.md @@ -51,7 +51,7 @@ POST /issues/{id}/comments (test results) - ✅ **Source Code** (proto/ directory) - ✅ **CI/CD Pipeline** (.github/workflows/) - ✅ **Docker Images** (ghcr.io registry) -- ✅ **Deployment** (Dokploy staging/production) +- ✅ **Deployment** (natív docker compose — `deploy.sh`) - ✅ **Test Artifacts** (test results, reports) #### **GitHub API Endpoints:** @@ -96,7 +96,7 @@ POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches | **Docker Integration** | ✅ | - | Teljesen működik | | **Test Execution** | ✅ | - | Teljesen működik | | **Linear Sync in GitHub** | ❌ | **ROSSZ PROJEKT** | **Szétválasztás szükséges** | -| **Deployment Integration** | ⚠️ | Dokploy config hiányzik | Konfigurálni kell | +| **Deployment Integration** | ✅ | Natív docker compose (`deploy.sh`) | Nincs külső PaaS függőség | --- @@ -121,7 +121,7 @@ graph TD A[Code Push] --> B[GitHub Actions] B --> C[Run Tests] B --> D[Build Docker] - B --> E[Deploy to Dokploy] + B --> E[deploy.sh — docker compose up --build] F[PR Creation] --> G[Test Validation] G --> H[Deploy to Staging] @@ -139,10 +139,10 @@ graph TD - [x] Test results → Linear comments - [x] Traceability matrix maintenance -### **FÁZIS 2: GitHub Sync Újradefiniálás** 🔄 -- [ ] **Linear sync eltávolítása** GitHub Actions-ból -- [ ] **GitHub-specifikus** workflow-ok létrehozása -- [ ] **Dokploy integráció** konfigurálása +### **FÁZIS 2: GitHub Sync Újradefiniálás** ✅ +- [x] **Linear sync eltávolítása** GitHub Actions-ból +- [x] **GitHub-specifikus** workflow-ok létrehozása +- [x] **Deploy natív docker compose-szal** (`deploy.sh`) - [ ] **Docker registry** beállítása ### **FÁZIS 3: Szinkronizáció Szétválasztása** 📋 @@ -189,8 +189,7 @@ graph TB B --> K[GitHub Actions] K --> L[Run Tests] K --> M[Build Docker Image] - K --> N[Push to Registry] - K --> O[Deploy to Dokploy] + K --> O[deploy.sh — docker compose up --build] P[PR Creation] --> Q[Test Validation] Q --> R[Staging Deploy] @@ -204,7 +203,7 @@ graph TB subgraph "MONITORING" U[Linear Dashboard] --> V[Test Status] W[GitHub Actions] --> X[Build Status] - Y[Dokploy] --> Z[Deployment Status] + Y[Healthcheck /api/health] --> Z[Deployment Status] end ``` @@ -215,18 +214,17 @@ sequenceDiagram participant Git as GitHub participant Linear as Linear participant Docker as Docker Registry - participant Dokploy as Dokploy + participant Server as Szerver (docker compose) - Note over Dev,Dokploy: LINEAR SYNC (Test Management) + Note over Dev,Server: LINEAR SYNC (Test Management) Dev->>Linear: Run Tests Linear->>Linear: Update ZEE-47, ZEE-48, ZEE-49 Linear->>Linear: Add Test Comments - Note over Dev,Dokploy: GITHUB SYNC (CI/CD) + Note over Dev,Server: GITHUB SYNC (CI/CD) Dev->>Git: git push Git->>Git: GitHub Actions Trigger - Git->>Docker: Build & Push Image - Git->>Dokploy: Deploy to Staging/Production + Git->>Server: deploy.sh — git pull + docker compose up --build ``` --- @@ -235,8 +233,8 @@ sequenceDiagram ### 🚨 **KRITIKUS** 1. **Linear sync GitHub Actions-ban** → Rossz projekt frissül -2. **Dokploy konfiguráció hiányzik** → Deployment nem működik -3. **GitHub secrets hiányoznak** → Linear API key, Dokploy tokens +2. **Docker registry beállítása** → Deploy natív compose-szal, nincs registry push +3. **GitHub secrets hiányoznak** → Linear API key ### ⚠️ **FONTOS** 1. **Workflow duplikáció** → CI és test-management overlap @@ -253,9 +251,9 @@ sequenceDiagram ## 🎯 **Következő Lépések Prioritás Szerint** ### **1. AZONNALI (Kritikus)** -- [ ] **Linear sync eltávolítása** GitHub Actions-ból -- [ ] **GitHub-specifikus workflow** létrehozása -- [ ] **Dokploy konfiguráció** hozzáadása +- [x] **Linear sync eltávolítása** GitHub Actions-ból +- [x] **GitHub-specifikus workflow** létrehozása +- [x] **Deploy natív docker compose-szal** (`deploy.sh`) ### **2. RÖVID TÁVÚ (1-2 nap)** - [ ] **GitHub secrets** beállítása @@ -280,11 +278,11 @@ sequenceDiagram ### **Új Dokumentumok:** - [ ] `LINEAR-SYNC-GUIDE.md` → Linear-specifikus útmutató -- [ ] `GITHUB-CICD-GUIDE.md` → GitHub CI/CD útmutató -- [ ] `DEPLOYMENT-GUIDE.md` → Dokploy deployment útmutató +- [ ] `GITHUB-CICD-GUIDE.md` → CI/CD útmutató (Gitea Actions + natív compose) +- [x] `DEPLOYMENT-GUIDE.md` → Natív docker compose deploy (`deploy.sh`) --- -**Utolsó frissítés:** 2025-09-05 -**Státusz:** Elemzés kész, szétválasztási terv kész -**Következő lépés:** Linear sync eltávolítása GitHub Actions-ból +**Utolsó frissítés:** 2026-08-17 +**Státusz:** Dokploy eltávolítva, natív docker compose deploy + Gitea Actions +**Következő lépés:** Docker registry opcionális beállítása diff --git a/deploy.sh b/deploy.sh index dcaf64e..73a2859 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,8 +1,11 @@ #!/bin/bash -# mozdIT Deploy Script +# mozdIT Deploy Script — natív docker compose, Dokploy nélkül # Használat: ./deploy.sh [staging|production] +# A szerveren fut (git pull + rebuild + indítás). -ENV=$1 +set -euo pipefail + +ENV="$1" if [ "$ENV" != "staging" ] && [ "$ENV" != "production" ]; then echo "Hiba: Hiányzó vagy érvénytelen környezet paraméter." @@ -13,21 +16,34 @@ fi echo "🚀 Deploy indítása: [$ENV] környezet" # 1. Kód frissítése -echo "📦 Kód frissítése a fő ágról (main)..." +echo "📦 Kód frissítése a main ágról..." git pull origin main -# 2. .env fájl betöltése / ellenőrzése -if [ -f ".env.$ENV" ]; then - echo "🔑 Környezeti változók betöltése (.env.$ENV)..." - export $(grep -v '^#' .env.$ENV | xargs) -else - echo "⚠️ Figyelmeztetés: .env.$ENV fájl nem található, alapértelmezett értékek használata." +# 2. Környezeti változók (.env.) +ENV_FILE=".env.${ENV}" +if [ ! -f "$ENV_FILE" ]; then + echo "❌ Hiba: $ENV_FILE fájl nem található." + echo " Másold le a .env.example fájlt $ENV_FILE néven, és töltsd ki." + exit 1 fi +echo "🔑 Környezeti változók betöltése ($ENV_FILE)..." # 3. Docker konténerek újraépítése és indítása -echo "🐳 Docker konténerek buildelése és indítása Zero-Downtime megközelítéssel..." -docker compose -f docker-compose.prod.yml up --build -d +echo "🐳 Build és indítás..." +docker compose --env-file "$ENV_FILE" -f docker-compose.prod.yml up --build -d -# 4. Ellenőrzés -echo "✅ Deploy sikeresen befejeződött!" -echo "📄 Logok megtekintése: docker compose -f docker-compose.prod.yml logs -f app" +# 4. Healthcheck +echo "⏳ Healthcheck (max 60s)..." +HEALTH_URL="http://localhost:${APP_PORT:-8080}/api/health" +for i in $(seq 1 30); do + if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then + echo "✅ Healthcheck OK: $HEALTH_URL" + echo "✅ Deploy sikeres: [$ENV]" + exit 0 + fi + sleep 2 +done + +echo "❌ Healthcheck sikertelen: $HEALTH_URL" +docker compose -f docker-compose.prod.yml logs app --tail 50 +exit 1 diff --git a/docs/Dokploy Teljes Utmutato - Webfejlesztesi Projekthez.md b/docs/Dokploy Teljes Utmutato - Webfejlesztesi Projekthez.md deleted file mode 100755 index feb5b26..0000000 --- a/docs/Dokploy Teljes Utmutato - Webfejlesztesi Projekthez.md +++ /dev/null @@ -1,455 +0,0 @@ - - -# Dokploy Teljes Útmutató - Webfejlesztési Projekttér - -A **Dokploy** egy kiváló, nyílt forráskódú alternatíva a Heroku, Vercel és Netlify platformokhoz, amely lehetővé teszi teljes kontrollt a deployment folyamat felett, miközben jelentős költségmegtakarítást biztosít.[^1][^2][^3] - -## Mi a Dokploy és Miért Válaszd? - -A Dokploy egy saját szerveren hostolható Platform-as-a-Service (PaaS) megoldás, amely Docker és Traefik technológiákra épül. Lényegében egy "személyes Heroku vagy Vercel", amelyet te birtokolsz és irányítasz.[^3][^1] - -**Fő előnyök:** - -- **Költséghatékony**: Nincs havi előfizetés vagy deployment díj, csak a szerver költsége (tipikusan €4-20/hó)[^4][^3] -- **Teljes kontroll**: Saját infrastruktúra kezelés vendor lock-in nélkül[^3] -- **Modern UI**: Gyönyörű webes felület, ellentétben a parancssor-alapú megoldásokkal[^3] -- **Docker Compose támogatás**: Komplex alkalmazások egyszerű kezelése[^3] -- **AI-vezérelt deployment**: Természetes nyelven leírt alkalmazások automatikus konfigurálása[^3] - - -## Telepítési Útmutató - -### Előfeltételek - -**Szerver követelmények:** - -- Ubuntu 22.04/24.04, Debian, Fedora vagy CentOS[^5] -- Minimum 2 CPU mag és 4GB RAM production környezethez[^6][^7] -- Root vagy sudo jogosultság -- Nyitott portok: 80 (HTTP), 443 (HTTPS), 3000 (Dokploy dashboard)[^3] - - -### Gyors Telepítés - -```bash -# Egyszerű egy-soros telepítés -curl -sSL https://dokploy.com/install.sh | sh -``` - -A telepítés általában 8-15 percet vesz igénybe, és automatikusan beállítja a Docker Swarm környezetet, PostgreSQL adatbázist, Redis cache-t és Traefik reverse proxy-t.[^8][^5] - -### Dashboard Elérése - -A telepítés után nyisd meg böngészőben: `http://your-server-ip:3000` és hozz létre egy admin fiókot.[^9] - -## Alkalmazások Deployment-je - -### GitHub Repository-ból - -A Dokploy kiválóan integrálódik Git platformokkal. Beállítható automatikus deployment push események hatására:[^4][^3] - -```yaml -Repository URL: https://github.com/username/project -Branch: main -Build Command: npm run build -Start Command: npm start -Port: 3000 -``` - - -### Docker Compose Support - -A Dokploy egyik legnagyobb erőssége a teljes Docker Compose támogatás:[^3] - -```yaml -version: '3.8' -services: - app: - build: . - ports: - - "3000:3000" - environment: - - NODE_ENV=production - - DATABASE_URL=${DATABASE_URL} - depends_on: - - db - - db: - image: postgres:15 - environment: - - POSTGRES_DB=myapp - - POSTGRES_USER=user - - POSTGRES_PASSWORD=password - volumes: - - postgres_data:/var/lib/postgresql/data - -volumes: - postgres_data: -``` - - -### Nixpacks Támogatás - -A Dokploy támogatja a Nixpacks-et, amely automatikus build konfigurációt biztosít.[^10][^6] - -## Adatbázis Kezelés - -A Dokploy beépített támogatást nyújt több adatbázis típushoz: - -- PostgreSQL -- MySQL/MariaDB -- MongoDB -- Redis -- InfluxDB[^9] - -Az adatbázisok egy kattintással létrehozhatók és automatikusan csatlakoztathatók az alkalmazásokhoz.[^9] - -## SSL és Domain Konfiguráció - -A Dokploy automatikus SSL tanúsítvány generálást biztosít Let's Encrypt segítségével. A Traefik reverse proxy automatikusan kezeli a traffic routing-ot és load balancing-ot.[^11][^12][^3] - -## DevOps Legjobb Gyakorlatok - -### 1. Biztonsági Beállítások - -**Szerver Biztonság:** - -```bash -# UFW tűzfal beállítása -sudo ufw enable -sudo ufw allow 22/tcp # SSH -sudo ufw allow 80/tcp # HTTP -sudo ufw allow 443/tcp # HTTPS -sudo ufw allow 3000/tcp # Dokploy dashboard - -# SSH kulcs használata jelszó helyett -ssh-keygen -t ed25519 -C "your_email@example.com" -ssh-copy-id user@server-ip -``` - -**VPS Biztonsági Javaslatok:** - -- Automatikus frissítések engedélyezése[^13] -- SSH kulcsok használata jelszavak helyett[^13] -- Cloudflare használata web traffic védelemhez[^13] -- Adatbázis portok nem publikus expozíciója[^13] - - -### 2. CI/CD Pipeline - -A Dokploy támogatja a GitHub Actions integrációt automatikus deployment-hez:[^6][^10] - -```yaml -name: Deploy to Dokploy -on: - push: - branches: [main] -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Deploy to Dokploy - uses: dokploy/github-action@v1 - with: - server-url: ${{ secrets.DOKPLOY_SERVER_URL }} - api-key: ${{ secrets.DOKPLOY_API_KEY }} - application-id: ${{ secrets.APPLICATION_ID }} -``` - - -### 3. Monitoring és Logging - -A Dokploy beépített monitoring funkciókkal rendelkezik:[^9][^3] - -- Real-time metrikák -- Container logok -- Resource használat monitoring -- Uptime tracking - -**Külső monitoring integrálása:** - -- Prometheus + Grafana stack használata részletes metrikákhoz -- Alerting beállítása kritikus események esetére[^12] - - -### 4. Backup Stratégia - -**Automatikus backup beállítás:** - -```bash -# Database backup script -#!/bin/bash -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_DIR="/home/backups" - -# PostgreSQL backup -docker exec dokploy-postgres pg_dump -U postgres myapp > $BACKUP_DIR/db_backup_$DATE.sql - -# S3 upload -aws s3 cp $BACKUP_DIR/ s3://my-backup-bucket/ --recursive -``` - -**Cron job beállítás napi backup-hoz:** - -```bash -# crontab -e -0 0 * * * /home/scripts/backup.sh -``` - - -### 5. Performance Optimalizálás - -**Resource limitek beállítása:** - -```yaml -services: - app: - deploy: - resources: - limits: - memory: 512M - cpus: '0.5' - reservations: - memory: 256M - cpus: '0.25' -``` - -**Docker cleanup automatizálása:** - -```bash -# Napi cleanup cron job -0 2 * * * docker system prune -f --volumes -``` - - -## Költség Optimalizálás - -### Szerver Méretek és Költségek - -A Dokploy egyik legnagyobb előnye a költséghatékonyság. Míg a managed szolgáltatások esetében a költségek exponenciálisan nőnek a forgalommal, a Dokploy fix szerver költséget jelent:[^4][^3] - -- **Kis projektek**: 2GB RAM, 1 CPU (€4-5/hó) - Hetzner VPS[^4] -- **Közepes projektek**: 4GB RAM, 2 CPU (€8-12/hó) -- **Nagy projektek**: 8GB+ RAM, 4+ CPU (€20+/hó) - - -### Költségösszehasonlítás - -A Vercel esetében könnyen száz dollárok fölé emelkedhetnek a havi költségek, míg a Dokploy-jal ugyanazon alkalmazások egy €4-20 közötti VPS-en futtathatók unlimited deploymenttel.[^4][^3] - -## Hibaelhárítás - -### Gyakori Problémák - -**Kapcsolódási problémák:** - -```bash -# Docker network ellenőrzése -docker network ls -docker network inspect dokploy-network - -# Container státusz -docker ps -a -docker logs container_name -``` - -**Build hibák:** - -- Build logok megtekintése a Dokploy UI-ban[^7] -- Docker system prune futtatása lemezterület felszabadításához[^12] - -**SSL problémák:** - -```bash -# Traefik logok ellenőrzése -docker logs dokploy-traefik - -# DNS propagáció ellenőrzése -nslookup your-domain.com -``` - - -## Advanced Funkciók - -### Multi-Server Support - -A Dokploy támogatja a multi-server deployment-et Docker Swarm segítségével, lehetővé téve az alkalmazások elosztását több szerver között a jobb teljesítmény és megbízhatóság érdekében.[^3] - -### API Integráció - -A Dokploy REST API-t biztosít programozott kezeléshez, lehetővé téve az automatizált deployment és management műveleteket.[^10] - -### Organizations és Teams - -A legújabb verziók támogatják a szervezetek és csapatok kezelését, lehetővé téve a projektek szeparációját és különböző jogosultsági szintek beállítását.[^3] - -## Összefoglalás - -A Dokploy egy rendkívül költséghatékony és hatékony megoldás webfejlesztő projektek deployment-jére. A megfelelő beállításokkal és DevOps gyakorlatokkal production-ready környezetet lehet kialakítani, amely versenyképes a nagy cloud szolgáltatókkal, miközben teljes kontrollt és jelentős költségmegtakarítást biztosít. - -**Kulcs sikertényezők:** - -1. **Alapos tervezés**: Környezetek és workflow-k megfelelő megtervezése -2. **Biztonság**: Proper authentication, SSL, tűzfal beállítása -3. **Monitoring**: Folyamatos figyelés és alerting -4. **Backup**: Rendszeres és tesztelt backup stratégia -5. **Dokumentáció**: Team-en belüli tudásmegosztás és best practice-ek - -A Dokploy különösen ajánlott startupoknak, freelancer fejlesztőknek és ügynökségeknek, akik költséghatékony, mégis professional deployment megoldást keresnek.[^4][^3] -[^14][^15][^16][^17][^18][^19][^20][^21][^22][^23][^24][^25][^26][^27][^28][^29][^30][^31][^32][^33][^34][^35][^36][^37][^38][^39][^40][^41][^42][^43][^44][^45][^46][^47][^48][^49][^50][^51][^52][^53][^54][^55][^56][^57][^58][^59][^60][^61][^62][^63][^64][^65][^66][^67][^68][^69][^70][^71][^72][^73][^74][^75][^76] - -
- -[^1]: https://docs.dokploy.com - -[^2]: https://arxiv.org/pdf/2303.07876.pdf - -[^3]: https://f1000research.com/articles/5-1442/v1/pdf - -[^4]: http://arxiv.org/pdf/2408.09869.pdf - -[^5]: https://arxiv.org/pdf/2206.00699.pdf - -[^6]: https://f1000research.com/articles/4-1443/v1/pdf - -[^7]: https://joss.theoj.org/papers/10.21105/joss.01603.pdf - -[^8]: http://arxiv.org/pdf/2309.06611.pdf - -[^9]: https://f1000research.com/articles/6-52/v1/pdf - -[^10]: https://peerj.com/articles/3948 - -[^11]: https://gsconlinepress.com/journals/gscaet/sites/default/files/GSCAET-2021-0023.pdf - -[^12]: https://pmc.ncbi.nlm.nih.gov/articles/PMC6042832/ - -[^13]: https://edit.elte.hu/xmlui/bitstream/10831/75682/1/DH_2020_Evf3_Sz1_horvath.pdf - -[^14]: https://arxiv.org/pdf/2002.03064.pdf - -[^15]: https://ojs.mtak.hu/index.php/fogorv-szemle/article/download/11959/9738 - -[^16]: https://arxiv.org/pdf/2208.09097.pdf - -[^17]: https://dx.plos.org/10.1371/journal.pone.0306100 - -[^18]: https://arxiv.org/pdf/2504.02431.pdf - -[^19]: http://www.mdpi.com/1420-3049/20/6/9977/pdf - -[^20]: https://lobehub.com/mcp/your-username-dokploy-mcp - -[^21]: https://github.com/Dokploy/dokploy/issues/2413 - -[^22]: https://www.youtube.com/watch?v=pL_rNDEFxD8 - -[^23]: https://news.ycombinator.com/item?id=44884077 - -[^24]: https://www.youtube.com/watch?v=fCZqnKxl33w - -[^25]: https://www.reddit.com/r/nextjs/comments/1mxwaaf/is_dockploy_an_option/ - -[^26]: https://www.youtube.com/watch?v=zIl_1oyOF0Y - -[^27]: https://github.com/Dokploy/dokploy/releases - -[^28]: https://talentasipil.unbari.ac.id/index.php/talenta/article/view/667 - -[^29]: https://www.sciltp.com/journals/ijamm/2025/2/1011 - -[^30]: https://ijaem.net/issue_dcp/Design%20and%20Development%20of%20an%20Online%20Recruitment%20Systems%20for%20Small%20And%20Medium%20Sized%20Enterprises%20In%20Zambia.pdf - -[^31]: https://esajournals.onlinelibrary.wiley.com/doi/10.1002/fee.1472 - -[^32]: https://www.semanticscholar.org/paper/a67dc2084d045555f037cb307de1936e4674bf27 - -[^33]: https://www.spiedigitallibrary.org/conference-proceedings-of-spie/13097/3019127/Adaptive-optics-at-Gemini-observatories-past-present-and-future/10.1117/12.3019127.full - -[^34]: https://iopscience.iop.org/article/10.1149/MA2023-01141350mtgabs - -[^35]: https://ieeexplore.ieee.org/document/9721706/ - -[^36]: https://www.semanticscholar.org/paper/dbc5e65588d502c974beb2c1b05c03e88174f0fb - -[^37]: https://iopscience.iop.org/article/10.1088/1755-1315/506/1/012010 - -[^38]: https://arxiv.org/pdf/2210.01073.pdf - -[^39]: http://thesai.org/Downloads/Volume13No4/Paper_60-Framework_to_Deploy_Containers_using_Kubernetes_and_CICD_Pipeline.pdf - -[^40]: https://arxiv.org/pdf/2312.03250.pdf - -[^41]: https://arxiv.org/pdf/2309.00166.pdf - -[^42]: http://arxiv.org/pdf/2407.19928.pdf - -[^43]: https://hamidullah.me/blog - -[^44]: https://www.reddit.com/r/webhosting/comments/1mij2hz/cant_deploy_a_nextjs_project_properly_on_vps/ - -[^45]: https://nikicaraznatovic.me/blog/using-vps - -[^46]: https://www.youtube.com/watch?v=_tiuMTaPtCo - -[^47]: https://forum.ghost.org/t/dokploy-ghost-6-0/59641 - -[^48]: https://www.youtube.com/watch?v=jYRdSfeGUus - -[^49]: https://github.com/Dokploy/dokploy/issues/2331 - -[^50]: http://link.springer.com/10.1007/3-540-45150-1_3 - -[^51]: https://www.epj-conferences.org/10.1051/epjconf/202429504011 - -[^52]: https://onepetro.org/SPETWID/proceedings/24TWIP/24TWIP/D021S003R001/612657 - -[^53]: https://arxiv.org/abs/2207.12779 - -[^54]: https://ieeexplore.ieee.org/document/10004463/ - -[^55]: https://ieeexplore.ieee.org/document/10407325/ - -[^56]: https://wseas.com/journals/dcm/2023/a46dcm-015(2023).pdf - -[^57]: https://www.frontiersin.org/articles/10.3389/fenvs.2024.1375193/full - -[^58]: https://ac.inf.elte.hu/Vol_055_2023/doi/019_55.html - -[^59]: https://dl.acm.org/doi/10.1145/3605098.3635981 - -[^60]: http://arxiv.org/pdf/2104.07899.pdf - -[^61]: http://arxiv.org/pdf/2405.11316.pdf - -[^62]: https://arxiv.org/pdf/1804.05039.pdf - -[^63]: https://arxiv.org/pdf/2409.03405.pdf - -[^64]: https://www.mdpi.com/1424-8220/25/3/914 - -[^65]: http://arxiv.org/pdf/2201.12879.pdf - -[^66]: https://arxiv.org/html/2504.07707v1 - -[^67]: https://arxiv.org/pdf/1501.02967.pdf - -[^68]: https://arxiv.org/pdf/2112.12595.pdf - -[^69]: https://arxiv.org/pdf/2205.14498.pdf - -[^70]: https://github.com/Dokploy/dokploy/issues/2341 - -[^71]: https://github.com/Dokploy/dokploy/actions - -[^72]: https://www.reddit.com/r/hetzner/comments/1mrqiad/how_do_people_manage_a_5_vps/ - -[^73]: https://github.com/Dokploy/dokploy/activity - -[^74]: https://tom-doerr.github.io/repo_posts/ - -[^75]: https://www.hostinger.com/tutorials/how-to-host-n8n-with-coolify - -[^76]: https://ppl-ai-code-interpreter-files.s3.amazonaws.com/web/direct-files/d38b8c1e31ab15bfa3adaa9713847e61/db3553a8-1145-461e-8dbe-3e1ecaa61bc7/cd6eb76c.md - diff --git a/docs/Dokploy UI – Uj site letrehozasa és urlap kitoltese.pdf b/docs/Dokploy UI – Uj site letrehozasa és urlap kitoltese.pdf deleted file mode 100755 index 883b4b59d7994494bdc926ac0807b44c9496dd9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70722 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%0Aa=E26r)EMO}Zm=-iFpX{JS^qicgWMDkeif_J2 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 A2[Dokploy staging] + A1[Repository setup] --> A2[Staging deploy] A2 --> A3[CI/CD pipeline] A3 --> A4[Environment config] end @@ -412,7 +412,7 @@ Nap 1-4 Hét 1 Hét 2-3 Hét 4 Hét 5 │ │ │ │ │ ├── Fázis 0 ├── Fázis 1 ├── Fázis 2 ├── Fázis 3 ├── Fázis 4 │ Prototípus ├── Setup ├── Core UI ├── QA ├── Production -│ Pipeline │ Dokploy │ Pages │ Testing │ Deploy +│ Pipeline │ Compose │ Pages │ Testing │ Deploy │ tesztelés │ Pipeline │ Forms │ Security │ Monitoring ``` diff --git a/docs/mozd_it_weboldal_kovetelmeny_dokploy_deploy_specifikacio_mvp.md b/docs/mozd_it_weboldal_kovetelmeny_dokploy_deploy_specifikacio_mvp.md deleted file mode 100755 index f4a9b63..0000000 --- a/docs/mozd_it_weboldal_kovetelmeny_dokploy_deploy_specifikacio_mvp.md +++ /dev/null @@ -1,236 +0,0 @@ -# mozdIT Bt. — Weboldal követelmény + Dokploy deploy specifikáció (MVP) - -## 0) Rövid összefoglaló -Cél: minimális, de komplett, mobil‑első, gyors és biztonságos **bemutatkozó weboldal** a mozdIT Bt.-nek, kiemelt **Webmail** linkkel és rövid, egyedi bemutatkozással. A később készülő **admin aloldal** (szerver‑monitoring/menedzsment) helye előkészítve. Deployment és tesztelés: **Dokploy** környezetben. - ---- - -## 1) MVP oldalak és funkciók -**Oldalak** -- **Kezdőlap**: rövid, egyedi bemutatkozó szöveg; CTA: **Webmail** link (külső URL); szolgáltatások rövid dobozai. -- **Rólunk**: rövid történet, működés óta, személyes ügyfélkezelés, megbízhatóság. -- **Szolgáltatások**: web hosting, email szolgáltatás, DNS adminisztráció (rövid leírás + kapcsolat CTA). -- **Kapcsolat**: űrlap (név, email, üzenet, GDPR checkbox); cég e‑mail, telefonszám (ha lesz), cégnév, székhely. -- **Admin** (előre jelzett aloldal): `/` route fenntartva; tartalom később. - -**Funkciók (MVP)** -- Reszponzív navigáció (hamburger mobilon, sticky header desktopon). -- Űrlapvalidáció (frontend: required + e‑mail forma; backend: spam‑védett endpoint). -- SEO alapok (title/description per oldal, OG, sitemap, robots). -- Analytics: Plausible (cookieless) vagy GA4 (cookie‑consenttel). - ---- - -## 2) Nem‑funkcionális követelmények -- **Reszponzivitás:** mobile‑first; töréspontok: 360 / 640 / 768 / 1024 / 1280+ px. -- **Teljesítmény:** Lighthouse (mobil/desktop) ≥ 90; képek WebP/AVIF; lazy‑load; kritikus CSS minimalizálás. -- **A11y:** WCAG 2.1 AA (fókusz, ARIA, kontraszt ≥ 4.5:1, logikus heading). -- **Biztonság:** HTTPS, alap CSP, XSS/CSRF védelem, input szűrés a backend felé. -- **Megfigyelhetőség:** Sentry (client) + alap logok; uptime healthcheck. - ---- - -## 3) Technológiai stack -- **Frontend:** Next.js (React + TypeScript) — SSG/SSR vegyes; App Router. -- **UI:** Tailwind CSS + egyszerű saját komponensek (később: shadcn/ui opcionális). -- **Űrlapok:** React Hook Form + Zod. -- **State:** minimális local state + SWR (ha kell fetch). -- **Teszt:** Jest + Testing Library (unit), Playwright (E2E — smoke a fő flow‑kra). -- **CMS (opcionális később):** Strapi/Sanity; MVP‑ben statikus tartalom JSON/MDX. - -Projekt‑struktúra (rövid): -``` -/src - /app (Next.js routes) - /components - /styles - /lib - /tests (unit + e2e cfg) -/public -``` - ---- - -## 4) Tartalom (MVP) — copy irány -- **Hero cím** (Kezdőlap): „Megbízható web‑ és email‑szolgáltatás **személyre szabott támogatással**.” -- **Alcím:** „Kis ügyfélkör, nagy figyelem: stabil tárhely, üzembiztos levelezés és DNS adminisztráció — gyors reakcióval.” -- **USP bullet‑ek:** személyes ügyfélkezelés; gyors reagálás; stabil háttér; rugalmas támogatás. -- **Webmail gomb:** „Ugrás a Webmailre”. -- **Rólunk rövid:** mióta működtök; miért a kicsi ügyfélkör; megbízhatóság/folyamatos támogatás. -- **Szolgáltatás dobozok:** Web Hosting / Email / DNS Admin (1–2 mondat/elem, Kapcsolat CTA). - -*(Megjegyzés: a végleges szöveg a review során finomhangolható.)* - ---- - -## 5) Dokploy környezet — architektúra és folyamat - -### 5.1 Környezetek -- **Staging**: `staging.mozdit.hu` (pl. alap auth / IP‑korlátozás, ha kell) -- **Production**: `mozdit.hu` (www → apex redirect vagy fordítva) - -### 5.2 Alap komponensek -- **Reverse proxy/ingress**: Dokploy beépített (Traefik/Nginx környezet — a dokploy stack szerint). -- **App konténer**: Next.js app (Node 20) — SSG build + Node futtatás (vagy statikus export + Nginx). -- **Opció**: CDN (Cloudflare) a statikus assetekhez. - -### 5.3 Environment változók (példa) -- `NEXT_PUBLIC_WEBMAIL_URL=https://webmail.mozdit.hu` -- `COMPANY_NAME=mozdIT Bt.` -- `SITE_URL=https://mozdit.hu` -- `ANALYTICS_PROVIDER=plausible|ga4` -- `PLAUSIBLE_DOMAIN=mozdit.hu` (ha Plausible) -- `GA4_ID=G-XXXXXXX` (ha GA4) -- `CONTACT_API_URL=https://api.mozdit.hu/contact` (ha külön backend) - -### 5.4 Healthcheck & readiness -- HTTP GET `/api/health` → `{status:"ok"}` -- Staging/prod deploy csak zöld health esetén; rollback automatikus szabály (utolsó zöld image). - ---- - -## 6) Docker & build - -### 6.1 Next.js multi‑stage Dockerfile (Node 20) -```dockerfile -# 1) Build stage -FROM node:20-alpine AS builder -WORKDIR /app -COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* ./ -RUN npm ci --prefer-offline --no-audit --legacy-peer-deps || npm ci -COPY . . -RUN npm run build - -# 2) Run stage (Node server) -FROM node:20-alpine AS runner -WORKDIR /app -ENV NODE_ENV=production -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/public ./public -COPY --from=builder /app/package.json ./package.json -RUN npm ci --omit=dev --prefer-offline --no-audit || true -EXPOSE 3000 -HEALTHCHECK --interval=30s --timeout=5s --retries=5 CMD wget -qO- http://localhost:3000/api/health || exit 1 -CMD ["npm","start"] -``` -*Megjegyzés:* Ha **statikus export** (SSG only) elegendő, választható Nginx runtime is. - -### 6.2 Dokploy app (magas szint) -- **Repository link** + **branch per environment** (`main` → prod, `develop` → staging) vagy tag‑alapú deploy. -- **Build & deploy**: Dockerfile alapján; port 3000; domain mapping staging/prod; env‑ek UI‑ból/secret store‑ból. -- **Zero‑downtime**: rolling frissítés (legalább 2 replika prod‑on, ha erőforrás engedi). - ---- - -## 7) CI/CD (példa: GitHub Actions) -Workflow: **lint → unit → build → e2e (smoke, staging) → Dokploy deploy → Lighthouse check (staging) → prod release** - -`.github/workflows/ci.yml` (részlet): -```yaml -name: CI -on: - push: - branches: ["main", "develop"] -jobs: - build-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version: 20 } - - run: npm ci - - run: npm run lint && npm run test -- --ci - - run: npm run build - docker: - needs: build-test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build & push image - uses: docker/build-push-action@v5 - with: - push: true - context: . - tags: registry.example.com/mozdit/site:${{ github.sha }} - deploy-staging: - needs: docker - runs-on: ubuntu-latest - steps: - - name: Trigger Dokploy staging deploy - run: | - curl -X POST "$DOKPLOY_STAGING_HOOK" -H "Authorization: Bearer $DOKPLOY_TOKEN" \ - -d '{"image":"registry.example.com/mozdit/site:${{ github.sha }}"}' - environment: staging - lighthouse: - needs: deploy-staging - runs-on: ubuntu-latest - steps: - - name: Lighthouse CI - run: npx @lhci/cli autorun --collect.url=https://staging.mozdit.hu - deploy-prod: - if: github.ref == 'refs/heads/main' - needs: lighthouse - runs-on: ubuntu-latest - steps: - - name: Trigger Dokploy prod deploy - run: | - curl -X POST "$DOKPLOY_PROD_HOOK" -H "Authorization: Bearer $DOKPLOY_TOKEN" \ - -d '{"image":"registry.example.com/mozdit/site:${{ github.sha }}"}' - environment: production -``` -*Megjegyzés:* a Dokploy‑oldali webhook/API URL és token a platform beállításától függ; ha Git integrációt használtok, a "Trigger" lépés helyett **auto‑deploy** szabály is beállítható. - ---- - -## 8) Tesztelés a Dokploy stagingen -- **Smoke E2E** (Playwright): 3 alap flow → kezdőlap betölt, Webmail link működik, Kapcsolat űrlap hibakezelés OK. -- **Vizsgálatok**: Lighthouse (mobil & desktop), A11y ellenőrző (axe), 404/500 oldal viselkedés. -- **Megfigyelés**: Sentry DSN kapcsolva; health endpoint figyelése. - ---- - -## 9) Biztonság & adatvédelem -- HTTPS (Let’s Encrypt / Dokploy integráció), HSTS. -- **CSP** baseline (script‑src 'self' + szükséges 3rd party); **Referrer‑Policy**, **X‑Frame‑Options** (SAMEORIGIN), **X‑Content‑Type‑Options**. -- **GDPR**: cookie banner ha GA4; Plausible esetén banner elhagyható. -- Kapcsolat űrlap: captcha/light rate‑limit, backend input validáció, e‑mail küldés queue‑val (ha szükséges). - ---- - -## 10) Rollback, backup, verziózás -- **Release tag** (semver) + image tag; Dokploy‑ban korábbi image visszagörgetés. -- **Konfig backup**: env‑ek és Dokploy app export; infra‑as‑code (Dockerfile, workflow‑k) GIT‑ben. - ---- - -## 11) Elfogadási kritériumok (MVP) -- Kezdőlap, Rólunk, Szolgáltatások, Kapcsolat elérhető és reszponzív. -- Webmail link jól működik (új lapon, nofollow opcionális). -- Lighthouse ≥ 90 minden fő mérőszámon stagingen. -- Űrlap hibák/fókuszállapotok a11y‑konformak. -- CI pipeline zöld, staging deploy automatikus; prod deploy csak zöld staging után. - ---- - -## 12) Kezdő feladatlista (ticket sablonok) -1. **Repo & Next.js bootstrap** — *Acceptance*: app indul dev módban; TS, ESLint, Prettier beállítva. -2. **Tailwind + alap layout** — *Acceptance*: reszponzív header/footer; tipográfia, színek. -3. **Kezdőlap (Hero + USP + Webmail CTA)** — *Acceptance*: 1s alatt festődik mobilon; link működik. -4. **Rólunk oldal** — *Acceptance*: heading‑hierarchia helyes; szövegek MDX‑ből tölthetők. -5. **Szolgáltatások oldal** — *Acceptance*: 3 doboz + CTA → Kapcsolat. -6. **Kapcsolat űrlap + API stub** — *Acceptance*: validáció, hibák; egyszerű spam‑védelem. -7. **/api/health endpoint** — *Acceptance*: `{status:"ok"}` JSON. -8. **Dockerfile + Dokploy staging app** — *Acceptance*: buildel, deployol, domain él. -9. **CI (lint, unit) + Staging deploy trigger** — *Acceptance*: PR‑re fut; stagingre pushol. -10. **Playwright smoke E2E + Lighthouse CI** — *Acceptance*: fut stagingen, riport mentve. -11. **Prod app + domain + HTTPS** — *Acceptance*: élő site; automatikus HTTPS; monitoring bekapcsolva. - ---- - -## 13) Későbbi bővítések -- Blog/Újdonságok; többnyelvűség; admin aloldal funkciói; CDN cache; képgenerálás; CMS integráció. - ---- - -*Megjegyzés:* A végleges Dokploy beállítások (webhook/API, auto‑deploy, replika szám, storage) a rendelkezésre álló szerver erőforrásoktól és a Dokploy verziójától függően finomhangolandók. -