Compare commits
2 Commits
b34deb3c81
...
7f6694622c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f6694622c | ||
|
|
83705af7f6 |
@@ -1,169 +0,0 @@
|
|||||||
name: CI/CD Pipeline
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, production ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main, production ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
NODE_VERSION: '20'
|
|
||||||
DOCKER_IMAGE: portfolio-app
|
|
||||||
CONTAINER_NAME: portfolio-app
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Run linting
|
|
||||||
run: npm run lint
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: npm run test
|
|
||||||
|
|
||||||
- name: Build application
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
security:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: test
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner
|
|
||||||
uses: aquasecurity/trivy-action@0.30.0
|
|
||||||
with:
|
|
||||||
scan-type: 'fs'
|
|
||||||
scan-ref: '.'
|
|
||||||
format: 'table'
|
|
||||||
output: 'trivy-results.txt'
|
|
||||||
timeout: '10m'
|
|
||||||
ignore-unfixed: true
|
|
||||||
severity: 'CRITICAL,HIGH'
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Run npm audit as fallback
|
|
||||||
if: failure()
|
|
||||||
run: |
|
|
||||||
echo "Trivy failed, running npm audit as fallback..."
|
|
||||||
npm audit --audit-level=high || true
|
|
||||||
echo "Security scan completed with fallback method"
|
|
||||||
|
|
||||||
- name: Upload Trivy scan results
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: trivy-results
|
|
||||||
path: trivy-results.txt
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: test
|
|
||||||
if: github.ref == 'refs/heads/production'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
run: |
|
|
||||||
docker build -t ${{ env.DOCKER_IMAGE }}:latest .
|
|
||||||
docker tag ${{ env.DOCKER_IMAGE }}:latest ${{ env.DOCKER_IMAGE }}:$(date +%Y%m%d-%H%M%S)
|
|
||||||
|
|
||||||
- name: Save Docker image
|
|
||||||
run: |
|
|
||||||
docker save ${{ env.DOCKER_IMAGE }}:latest | gzip > ${{ env.DOCKER_IMAGE }}.tar.gz
|
|
||||||
|
|
||||||
- name: Upload Docker image artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-image
|
|
||||||
path: ${{ env.DOCKER_IMAGE }}.tar.gz
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
if: github.ref == 'refs/heads/production'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Download Docker image artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-image
|
|
||||||
path: ./
|
|
||||||
|
|
||||||
- name: Load Docker image
|
|
||||||
run: |
|
|
||||||
gunzip -c ${{ env.DOCKER_IMAGE }}.tar.gz | docker load
|
|
||||||
|
|
||||||
- name: Stop existing services
|
|
||||||
run: |
|
|
||||||
docker-compose -f docker-compose.workflow.yml down || true
|
|
||||||
|
|
||||||
- name: Verify secrets before deployment
|
|
||||||
run: |
|
|
||||||
echo "🔍 Verifying secrets..."
|
|
||||||
if [ -z "${{ secrets.NEXT_PUBLIC_BASE_URL }}" ]; then
|
|
||||||
echo "❌ NEXT_PUBLIC_BASE_URL secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${{ secrets.MY_EMAIL }}" ]; then
|
|
||||||
echo "❌ MY_EMAIL secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${{ secrets.ADMIN_BASIC_AUTH }}" ]; then
|
|
||||||
echo "❌ ADMIN_BASIC_AUTH secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ All required secrets are present"
|
|
||||||
|
|
||||||
- name: Start services with Docker Compose
|
|
||||||
run: |
|
|
||||||
docker-compose -f docker-compose.workflow.yml up -d
|
|
||||||
env:
|
|
||||||
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
|
|
||||||
MY_EMAIL: ${{ secrets.MY_EMAIL }}
|
|
||||||
MY_INFO_EMAIL: ${{ secrets.MY_INFO_EMAIL }}
|
|
||||||
MY_PASSWORD: ${{ secrets.MY_PASSWORD }}
|
|
||||||
MY_INFO_PASSWORD: ${{ secrets.MY_INFO_PASSWORD }}
|
|
||||||
ADMIN_BASIC_AUTH: ${{ secrets.ADMIN_BASIC_AUTH }}
|
|
||||||
|
|
||||||
- name: Verify container environment
|
|
||||||
run: |
|
|
||||||
echo "🔍 Checking container environment variables..."
|
|
||||||
sleep 10
|
|
||||||
docker exec portfolio-app sh -c 'echo "NODE_ENV: $NODE_ENV" && echo "DATABASE_URL: $DATABASE_URL" && echo "REDIS_URL: $REDIS_URL" && echo "NEXT_PUBLIC_BASE_URL: $NEXT_PUBLIC_BASE_URL" && echo "MY_EMAIL: $MY_EMAIL" && echo "ADMIN_BASIC_AUTH: [HIDDEN]"'
|
|
||||||
|
|
||||||
- name: Wait for container to be ready
|
|
||||||
run: |
|
|
||||||
sleep 10
|
|
||||||
timeout 60 bash -c 'until curl -f http://localhost:3000/api/health; do sleep 2; done'
|
|
||||||
|
|
||||||
- name: Health check
|
|
||||||
run: |
|
|
||||||
curl -f http://localhost:3000/api/health
|
|
||||||
echo "✅ Deployment successful!"
|
|
||||||
|
|
||||||
- name: Cleanup old images
|
|
||||||
run: |
|
|
||||||
docker image prune -f
|
|
||||||
docker system prune -f
|
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Stop existing services
|
- name: Stop existing services
|
||||||
run: |
|
run: |
|
||||||
docker-compose -f docker-compose.workflow.yml down || true
|
docker-compose down || true
|
||||||
|
|
||||||
- name: Verify secrets before deployment
|
- name: Verify secrets before deployment
|
||||||
run: |
|
run: |
|
||||||
@@ -102,7 +102,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Start services with Docker Compose
|
- name: Start services with Docker Compose
|
||||||
run: |
|
run: |
|
||||||
docker-compose -f docker-compose.workflow.yml up -d
|
docker-compose up -d
|
||||||
env:
|
env:
|
||||||
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
|
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
|
||||||
MY_EMAIL: ${{ secrets.MY_EMAIL }}
|
MY_EMAIL: ${{ secrets.MY_EMAIL }}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
name: Quick Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
NODE_VERSION: '20'
|
|
||||||
DOCKER_IMAGE: portfolio-app
|
|
||||||
CONTAINER_NAME: portfolio-app
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
quick-deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build application
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
run: |
|
|
||||||
docker build -t ${{ env.DOCKER_IMAGE }}:latest .
|
|
||||||
|
|
||||||
- name: Stop existing services
|
|
||||||
run: |
|
|
||||||
docker-compose -f docker-compose.workflow.yml down || true
|
|
||||||
|
|
||||||
- name: Verify secrets before deployment
|
|
||||||
run: |
|
|
||||||
echo "🔍 Verifying secrets..."
|
|
||||||
if [ -z "${{ secrets.NEXT_PUBLIC_BASE_URL }}" ]; then
|
|
||||||
echo "❌ NEXT_PUBLIC_BASE_URL secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${{ secrets.MY_EMAIL }}" ]; then
|
|
||||||
echo "❌ MY_EMAIL secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${{ secrets.ADMIN_BASIC_AUTH }}" ]; then
|
|
||||||
echo "❌ ADMIN_BASIC_AUTH secret is missing!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ All required secrets are present"
|
|
||||||
|
|
||||||
- name: Start services with Docker Compose
|
|
||||||
run: |
|
|
||||||
docker-compose -f docker-compose.workflow.yml up -d
|
|
||||||
env:
|
|
||||||
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
|
|
||||||
MY_EMAIL: ${{ secrets.MY_EMAIL }}
|
|
||||||
MY_INFO_EMAIL: ${{ secrets.MY_INFO_EMAIL }}
|
|
||||||
MY_PASSWORD: ${{ secrets.MY_PASSWORD }}
|
|
||||||
MY_INFO_PASSWORD: ${{ secrets.MY_INFO_PASSWORD }}
|
|
||||||
ADMIN_BASIC_AUTH: ${{ secrets.ADMIN_BASIC_AUTH }}
|
|
||||||
|
|
||||||
- name: Verify container environment
|
|
||||||
run: |
|
|
||||||
echo "🔍 Checking container environment variables..."
|
|
||||||
sleep 10
|
|
||||||
docker exec portfolio-app sh -c 'echo "NODE_ENV: $NODE_ENV" && echo "DATABASE_URL: $DATABASE_URL" && echo "REDIS_URL: $REDIS_URL" && echo "NEXT_PUBLIC_BASE_URL: $NEXT_PUBLIC_BASE_URL" && echo "MY_EMAIL: $MY_EMAIL" && echo "ADMIN_BASIC_AUTH: [HIDDEN]"'
|
|
||||||
|
|
||||||
- name: Health check
|
|
||||||
run: |
|
|
||||||
sleep 10
|
|
||||||
curl -f http://localhost:3000/api/health
|
|
||||||
echo "✅ Quick deployment successful!"
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
name: Security Scan
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, production ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main, production ]
|
|
||||||
schedule:
|
|
||||||
- cron: '0 2 * * 1' # Weekly on Monday at 2 AM
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
security:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Run npm audit
|
|
||||||
run: |
|
|
||||||
echo "🔍 Running npm audit for dependency vulnerabilities..."
|
|
||||||
npm audit --audit-level=high --json > npm-audit-results.json || true
|
|
||||||
npm audit --audit-level=high || echo "⚠️ Some vulnerabilities found, but continuing..."
|
|
||||||
|
|
||||||
- name: Run Trivy (with fallback)
|
|
||||||
run: |
|
|
||||||
echo "🔍 Attempting Trivy scan..."
|
|
||||||
# Try to install and run Trivy directly
|
|
||||||
wget -qO- https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
|
||||||
trivy fs --scanners vuln,secret --format table . > trivy-results.txt 2>&1 || {
|
|
||||||
echo "⚠️ Trivy scan failed, but continuing with other checks..."
|
|
||||||
echo "Trivy scan failed due to network issues" > trivy-results.txt
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Check for secrets
|
|
||||||
run: |
|
|
||||||
echo "🔍 Checking for potential secrets..."
|
|
||||||
chmod +x scripts/check-secrets.sh
|
|
||||||
if ./scripts/check-secrets.sh; then
|
|
||||||
echo "✅ No secrets found in code"
|
|
||||||
else
|
|
||||||
echo "❌ Secrets detected - please review"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload security scan results
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: security-scan-results
|
|
||||||
path: |
|
|
||||||
npm-audit-results.json
|
|
||||||
trivy-results.txt
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Security scan summary
|
|
||||||
run: |
|
|
||||||
echo "## Security Scan Summary"
|
|
||||||
echo "### NPM Audit Results"
|
|
||||||
if [ -f npm-audit-results.json ]; then
|
|
||||||
echo "✅ NPM audit completed"
|
|
||||||
else
|
|
||||||
echo "❌ NPM audit failed"
|
|
||||||
fi
|
|
||||||
echo "### Trivy Results"
|
|
||||||
if [ -f trivy-results.txt ]; then
|
|
||||||
echo "✅ Trivy scan completed"
|
|
||||||
else
|
|
||||||
echo "❌ Trivy scan failed"
|
|
||||||
fi
|
|
||||||
78
.githooks/README.md
Normal file
78
.githooks/README.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Git Hooks
|
||||||
|
|
||||||
|
This directory contains Git hooks for the Portfolio project.
|
||||||
|
|
||||||
|
## Pre-Push Hook
|
||||||
|
|
||||||
|
The pre-push hook runs automatically before every `git push` and performs the following checks:
|
||||||
|
|
||||||
|
### Checks Performed:
|
||||||
|
1. **Node.js Version Check** - Ensures Node.js 20+ is installed
|
||||||
|
2. **Dependency Installation** - Installs npm dependencies if needed
|
||||||
|
3. **Linting** - Runs ESLint to check code quality
|
||||||
|
4. **Tests** - Runs Jest test suite
|
||||||
|
5. **Build** - Builds the Next.js application
|
||||||
|
6. **Security Audit** - Runs npm audit for vulnerabilities
|
||||||
|
7. **Secret Detection** - Checks for accidentally committed secrets
|
||||||
|
8. **Docker Configuration** - Validates Dockerfile and docker-compose.yml
|
||||||
|
9. **Production Checks** - Additional checks when pushing to production branch
|
||||||
|
|
||||||
|
### Production Branch Special Checks:
|
||||||
|
- Environment file validation
|
||||||
|
- Docker build test
|
||||||
|
- Deployment readiness check
|
||||||
|
|
||||||
|
### Usage:
|
||||||
|
|
||||||
|
The hook runs automatically on every push. To manually test it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test the hook manually
|
||||||
|
.githooks/pre-push
|
||||||
|
|
||||||
|
# Or push to trigger it
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bypassing the Hook:
|
||||||
|
|
||||||
|
If you need to bypass the hook in an emergency:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push --no-verify origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Only bypass in emergencies. The hook prevents broken code from being pushed.
|
||||||
|
|
||||||
|
### Troubleshooting:
|
||||||
|
|
||||||
|
If the hook fails:
|
||||||
|
|
||||||
|
1. **Fix the reported issues** (linting errors, test failures, etc.)
|
||||||
|
2. **Run the checks manually** to debug:
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
npm run test
|
||||||
|
npm run build
|
||||||
|
npm audit
|
||||||
|
```
|
||||||
|
3. **Check Node.js version**: `node --version` (should be 20+)
|
||||||
|
4. **Reinstall dependencies**: `rm -rf node_modules && npm ci`
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
|
||||||
|
The hook is configured in `.git/config`:
|
||||||
|
```
|
||||||
|
[core]
|
||||||
|
hooksPath = .githooks
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable hooks temporarily:
|
||||||
|
```bash
|
||||||
|
git config core.hooksPath ""
|
||||||
|
```
|
||||||
|
|
||||||
|
To re-enable:
|
||||||
|
```bash
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
```
|
||||||
170
.githooks/pre-push
Executable file
170
.githooks/pre-push
Executable file
@@ -0,0 +1,170 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Pre-push hook for Portfolio
|
||||||
|
# Runs CI/CD checks before allowing push
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Running pre-push checks..."
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if [ ! -f "package.json" ]; then
|
||||||
|
print_error "Not in project root directory!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Node.js is available
|
||||||
|
if ! command -v node &> /dev/null; then
|
||||||
|
print_error "Node.js is not installed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check Node.js version
|
||||||
|
NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ "$NODE_VERSION" -lt 20 ]; then
|
||||||
|
print_error "Node.js version 20+ required, found: $(node --version)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Node.js version: $(node --version)"
|
||||||
|
|
||||||
|
# Install dependencies if node_modules doesn't exist
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
print_status "Installing dependencies..."
|
||||||
|
npm ci
|
||||||
|
else
|
||||||
|
print_status "Dependencies already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run linting
|
||||||
|
print_status "Running ESLint..."
|
||||||
|
if npm run lint; then
|
||||||
|
print_success "Linting passed"
|
||||||
|
else
|
||||||
|
print_error "Linting failed! Please fix the issues before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
print_status "Running tests..."
|
||||||
|
if npm run test; then
|
||||||
|
print_success "Tests passed"
|
||||||
|
else
|
||||||
|
print_error "Tests failed! Please fix the issues before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build application
|
||||||
|
print_status "Building application..."
|
||||||
|
if npm run build; then
|
||||||
|
print_success "Build successful"
|
||||||
|
else
|
||||||
|
print_error "Build failed! Please fix the issues before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Security audit
|
||||||
|
print_status "Running security audit..."
|
||||||
|
if npm audit --audit-level=high; then
|
||||||
|
print_success "Security audit passed"
|
||||||
|
else
|
||||||
|
print_warning "Security audit found issues. Consider running 'npm audit fix'"
|
||||||
|
# Don't fail the push for security warnings, just warn
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for secrets in code
|
||||||
|
print_status "Checking for secrets in code..."
|
||||||
|
if [ -f "scripts/check-secrets.sh" ]; then
|
||||||
|
chmod +x scripts/check-secrets.sh
|
||||||
|
if ./scripts/check-secrets.sh; then
|
||||||
|
print_success "No secrets found in code"
|
||||||
|
else
|
||||||
|
print_error "Secrets detected in code! Please remove them before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "Secret check script not found, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check Docker configuration
|
||||||
|
print_status "Checking Docker configuration..."
|
||||||
|
if [ -f "Dockerfile" ]; then
|
||||||
|
print_success "Dockerfile found"
|
||||||
|
else
|
||||||
|
print_error "Dockerfile not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "docker-compose.yml" ]; then
|
||||||
|
print_success "Docker Compose configuration found"
|
||||||
|
else
|
||||||
|
print_error "Docker Compose configuration not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we're pushing to production branch
|
||||||
|
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
if [ "$CURRENT_BRANCH" = "production" ]; then
|
||||||
|
print_warning "Pushing to production branch - this will trigger deployment!"
|
||||||
|
|
||||||
|
# Additional production checks
|
||||||
|
print_status "Running production-specific checks..."
|
||||||
|
|
||||||
|
# Check if environment file exists
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
print_warning "No .env file found. Make sure secrets are configured in Gitea."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Docker is running
|
||||||
|
if ! docker info > /dev/null 2>&1; then
|
||||||
|
print_warning "Docker is not running. Skipping Docker build test."
|
||||||
|
else
|
||||||
|
# Check Docker image can be built
|
||||||
|
print_status "Testing Docker build..."
|
||||||
|
if docker build -t portfolio-app:test . > /dev/null 2>&1; then
|
||||||
|
print_success "Docker build test passed"
|
||||||
|
docker rmi portfolio-app:test > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
print_warning "Docker build test failed, but continuing..."
|
||||||
|
# Don't fail the push for Docker build issues in pre-push hook
|
||||||
|
# The CI/CD pipeline will catch this
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Final success message
|
||||||
|
echo ""
|
||||||
|
print_success "All pre-push checks passed! ✅"
|
||||||
|
print_status "Ready to push to: $CURRENT_BRANCH"
|
||||||
|
|
||||||
|
# Show what will be pushed
|
||||||
|
echo ""
|
||||||
|
print_status "Files to be pushed:"
|
||||||
|
git diff --name-only HEAD~1 2>/dev/null || git diff --cached --name-only
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "🚀 Push will proceed..."
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
# Automatisches Deployment System
|
|
||||||
|
|
||||||
## Übersicht
|
|
||||||
|
|
||||||
Dieses Portfolio verwendet ein **automatisches Deployment-System**, das bei jedem Git Push die Codebase prüft, den Container erstellt und startet.
|
|
||||||
|
|
||||||
## 🚀 Deployment-Skripte
|
|
||||||
|
|
||||||
### **1. Auto-Deploy (Vollständig)**
|
|
||||||
```bash
|
|
||||||
# Vollständiges automatisches Deployment
|
|
||||||
./scripts/auto-deploy.sh
|
|
||||||
|
|
||||||
# Oder mit npm
|
|
||||||
npm run auto-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
**Was passiert:**
|
|
||||||
- ✅ Git Status prüfen und uncommitted Changes committen
|
|
||||||
- ✅ Latest Changes pullen
|
|
||||||
- ✅ ESLint Linting
|
|
||||||
- ✅ Tests ausführen
|
|
||||||
- ✅ Next.js Build
|
|
||||||
- ✅ Docker Image erstellen
|
|
||||||
- ✅ Container stoppen/starten
|
|
||||||
- ✅ Health Check
|
|
||||||
- ✅ Cleanup alter Images
|
|
||||||
|
|
||||||
### **2. Quick-Deploy (Schnell)**
|
|
||||||
```bash
|
|
||||||
# Schnelles Deployment ohne Tests
|
|
||||||
./scripts/quick-deploy.sh
|
|
||||||
|
|
||||||
# Oder mit npm
|
|
||||||
npm run quick-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
**Was passiert:**
|
|
||||||
- ✅ Docker Image erstellen
|
|
||||||
- ✅ Container stoppen/starten
|
|
||||||
- ✅ Health Check
|
|
||||||
|
|
||||||
### **3. Manuelles Deployment**
|
|
||||||
```bash
|
|
||||||
# Manuelles Deployment mit Docker Compose
|
|
||||||
./scripts/deploy.sh
|
|
||||||
|
|
||||||
# Oder mit npm
|
|
||||||
npm run deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 Automatisches Deployment
|
|
||||||
|
|
||||||
### **Git Hook Setup**
|
|
||||||
Das System verwendet einen Git Post-Receive Hook, der automatisch bei jedem Push ausgeführt wird:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Hook ist bereits konfiguriert in:
|
|
||||||
.git/hooks/post-receive
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Wie es funktioniert:**
|
|
||||||
1. **Git Push** → Hook wird ausgelöst
|
|
||||||
2. **Auto-Deploy Script** wird ausgeführt
|
|
||||||
3. **Vollständige Pipeline** läuft automatisch
|
|
||||||
4. **Deployment** wird durchgeführt
|
|
||||||
5. **Health Check** bestätigt Erfolg
|
|
||||||
|
|
||||||
## 📋 Deployment-Schritte
|
|
||||||
|
|
||||||
### **Automatisches Deployment:**
|
|
||||||
```bash
|
|
||||||
# 1. Code Quality Checks
|
|
||||||
git status --porcelain
|
|
||||||
git pull origin main
|
|
||||||
npm run lint
|
|
||||||
npm run test
|
|
||||||
|
|
||||||
# 2. Build Application
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
# 3. Docker Operations
|
|
||||||
docker build -t portfolio-app:latest .
|
|
||||||
docker tag portfolio-app:latest portfolio-app:$(date +%Y%m%d-%H%M%S)
|
|
||||||
|
|
||||||
# 4. Deployment
|
|
||||||
docker stop portfolio-app || true
|
|
||||||
docker rm portfolio-app || true
|
|
||||||
docker run -d --name portfolio-app -p 3000:3000 portfolio-app:latest
|
|
||||||
|
|
||||||
# 5. Health Check
|
|
||||||
curl -f http://localhost:3000/api/health
|
|
||||||
|
|
||||||
# 6. Cleanup
|
|
||||||
docker system prune -f
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Verwendung
|
|
||||||
|
|
||||||
### **Für Entwicklung:**
|
|
||||||
```bash
|
|
||||||
# Schnelles Deployment während der Entwicklung
|
|
||||||
npm run quick-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Für Production:**
|
|
||||||
```bash
|
|
||||||
# Vollständiges Deployment mit Tests
|
|
||||||
npm run auto-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Automatisch bei Push:**
|
|
||||||
```bash
|
|
||||||
# Einfach committen und pushen
|
|
||||||
git add .
|
|
||||||
git commit -m "Update feature"
|
|
||||||
git push origin main
|
|
||||||
# → Automatisches Deployment läuft
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Monitoring
|
|
||||||
|
|
||||||
### **Container Status:**
|
|
||||||
```bash
|
|
||||||
# Status prüfen
|
|
||||||
npm run monitor status
|
|
||||||
|
|
||||||
# Health Check
|
|
||||||
npm run monitor health
|
|
||||||
|
|
||||||
# Logs anzeigen
|
|
||||||
npm run monitor logs
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Deployment Logs:**
|
|
||||||
```bash
|
|
||||||
# Deployment-Logs anzeigen
|
|
||||||
tail -f /var/log/portfolio-deploy.log
|
|
||||||
|
|
||||||
# Git-Deployment-Logs
|
|
||||||
tail -f /var/log/git-deploy.log
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Konfiguration
|
|
||||||
|
|
||||||
### **Ports:**
|
|
||||||
- **Standard Port:** 3000
|
|
||||||
- **Backup Port:** 3001 (falls 3000 belegt)
|
|
||||||
|
|
||||||
### **Container:**
|
|
||||||
- **Name:** portfolio-app
|
|
||||||
- **Image:** portfolio-app:latest
|
|
||||||
- **Restart Policy:** unless-stopped
|
|
||||||
|
|
||||||
### **Logs:**
|
|
||||||
- **Deployment Logs:** `/var/log/portfolio-deploy.log`
|
|
||||||
- **Git Logs:** `/var/log/git-deploy.log`
|
|
||||||
|
|
||||||
## 🚨 Troubleshooting
|
|
||||||
|
|
||||||
### **Deployment schlägt fehl:**
|
|
||||||
```bash
|
|
||||||
# Logs prüfen
|
|
||||||
docker logs portfolio-app
|
|
||||||
|
|
||||||
# Container-Status prüfen
|
|
||||||
docker ps -a
|
|
||||||
|
|
||||||
# Manuell neu starten
|
|
||||||
npm run quick-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Port bereits belegt:**
|
|
||||||
```bash
|
|
||||||
# Ports prüfen
|
|
||||||
lsof -i :3000
|
|
||||||
|
|
||||||
# Anderen Port verwenden
|
|
||||||
docker run -d --name portfolio-app -p 3001:3000 portfolio-app:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Tests schlagen fehl:**
|
|
||||||
```bash
|
|
||||||
# Tests lokal ausführen
|
|
||||||
npm run test
|
|
||||||
|
|
||||||
# Linting prüfen
|
|
||||||
npm run lint
|
|
||||||
|
|
||||||
# Build testen
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📈 Features
|
|
||||||
|
|
||||||
### **Automatische Features:**
|
|
||||||
- ✅ **Git Integration** - Automatisch bei Push
|
|
||||||
- ✅ **Code Quality** - Linting und Tests
|
|
||||||
- ✅ **Health Checks** - Automatische Verifikation
|
|
||||||
- ✅ **Rollback** - Alte Container werden gestoppt
|
|
||||||
- ✅ **Cleanup** - Alte Images werden entfernt
|
|
||||||
- ✅ **Logging** - Vollständige Deployment-Logs
|
|
||||||
|
|
||||||
### **Sicherheits-Features:**
|
|
||||||
- ✅ **Non-root Container**
|
|
||||||
- ✅ **Resource Limits**
|
|
||||||
- ✅ **Health Monitoring**
|
|
||||||
- ✅ **Error Handling**
|
|
||||||
- ✅ **Rollback bei Fehlern**
|
|
||||||
|
|
||||||
## 🎉 Vorteile
|
|
||||||
|
|
||||||
1. **Automatisierung** - Keine manuellen Schritte nötig
|
|
||||||
2. **Konsistenz** - Immer gleiche Deployment-Prozesse
|
|
||||||
3. **Sicherheit** - Tests vor jedem Deployment
|
|
||||||
4. **Monitoring** - Vollständige Logs und Health Checks
|
|
||||||
5. **Schnell** - Quick-Deploy für Entwicklung
|
|
||||||
6. **Zuverlässig** - Automatische Rollbacks bei Fehlern
|
|
||||||
|
|
||||||
## 📞 Support
|
|
||||||
|
|
||||||
Bei Problemen:
|
|
||||||
1. **Logs prüfen:** `tail -f /var/log/portfolio-deploy.log`
|
|
||||||
2. **Container-Status:** `npm run monitor status`
|
|
||||||
3. **Health Check:** `npm run monitor health`
|
|
||||||
4. **Manueller Neustart:** `npm run quick-deploy`
|
|
||||||
385
DEPLOYMENT.md
385
DEPLOYMENT.md
@@ -1,272 +1,229 @@
|
|||||||
# Portfolio Deployment Guide
|
# Portfolio Deployment Guide
|
||||||
|
|
||||||
## Übersicht
|
## Overview
|
||||||
|
|
||||||
Dieses Portfolio verwendet ein **optimiertes CI/CD-System** mit Docker für Production-Deployment. Das System ist darauf ausgelegt, hohen Traffic zu bewältigen und automatische Tests vor dem Deployment durchzuführen.
|
This document covers all aspects of deploying the Portfolio application, including local development, CI/CD, and production deployment.
|
||||||
|
|
||||||
## 🚀 Features
|
## Prerequisites
|
||||||
|
|
||||||
### ✅ **CI/CD Pipeline**
|
- Docker and Docker Compose installed
|
||||||
- **Automatische Tests** vor jedem Deployment
|
- Node.js 20+ for local development
|
||||||
- **Security Scanning** mit Trivy
|
- Access to Gitea repository with Actions enabled
|
||||||
- **Multi-Architecture Docker Builds** (AMD64 + ARM64)
|
|
||||||
- **Health Checks** und Deployment-Verifikation
|
|
||||||
- **Automatische Cleanup** alter Images
|
|
||||||
|
|
||||||
### ⚡ **Performance-Optimierungen**
|
## Environment Setup
|
||||||
- **Multi-Stage Docker Build** für kleinere Images
|
|
||||||
- **Nginx Load Balancer** mit Caching
|
|
||||||
- **Gzip Compression** und optimierte Headers
|
|
||||||
- **Rate Limiting** für API-Endpoints
|
|
||||||
- **Resource Limits** für Container
|
|
||||||
|
|
||||||
### 🔒 **Sicherheit**
|
### Required Secrets in Gitea
|
||||||
- **Non-root User** im Container
|
|
||||||
- **Security Headers** (HSTS, CSP, etc.)
|
|
||||||
- **SSL/TLS Termination** mit Nginx
|
|
||||||
- **Vulnerability Scanning** in CI/CD
|
|
||||||
|
|
||||||
## 📁 Dateistruktur
|
Configure these secrets in your Gitea repository (Settings → Secrets):
|
||||||
|
|
||||||
```
|
| Secret Name | Description | Example |
|
||||||
├── .github/workflows/
|
|-------------|-------------|---------|
|
||||||
│ └── ci-cd.yml # CI/CD Pipeline
|
| `NEXT_PUBLIC_BASE_URL` | Public URL of your website | `https://dk0.dev` |
|
||||||
├── scripts/
|
| `MY_EMAIL` | Main email for contact form | `contact@dk0.dev` |
|
||||||
│ ├── deploy.sh # Deployment-Skript
|
| `MY_INFO_EMAIL` | Info email address | `info@dk0.dev` |
|
||||||
│ └── monitor.sh # Monitoring-Skript
|
| `MY_PASSWORD` | Password for main email | `your_email_password` |
|
||||||
├── docker-compose.prod.yml # Production Docker Compose
|
| `MY_INFO_PASSWORD` | Password for info email | `your_info_email_password` |
|
||||||
├── nginx.conf # Nginx Konfiguration
|
| `ADMIN_BASIC_AUTH` | Admin basic auth for protected areas | `admin:your_secure_password` |
|
||||||
├── Dockerfile # Optimiertes Dockerfile
|
|
||||||
└── env.example # Environment Template
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ Setup
|
### Local Environment
|
||||||
|
|
||||||
### 1. **Environment Variables**
|
1. Copy environment template:
|
||||||
```bash
|
```bash
|
||||||
# Kopiere die Beispiel-Datei
|
|
||||||
cp env.example .env
|
cp env.example .env
|
||||||
|
|
||||||
# Bearbeite die .env Datei mit deinen Werten
|
|
||||||
nano .env
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. **GitHub Secrets & Variables**
|
2. Update `.env` with your values:
|
||||||
Konfiguriere in deinem GitHub Repository:
|
|
||||||
|
|
||||||
**Secrets:**
|
|
||||||
- `GITHUB_TOKEN` (automatisch verfügbar)
|
|
||||||
- `GHOST_API_KEY`
|
|
||||||
- `MY_PASSWORD`
|
|
||||||
- `MY_INFO_PASSWORD`
|
|
||||||
|
|
||||||
**Variables:**
|
|
||||||
- `NEXT_PUBLIC_BASE_URL`
|
|
||||||
- `GHOST_API_URL`
|
|
||||||
- `MY_EMAIL`
|
|
||||||
- `MY_INFO_EMAIL`
|
|
||||||
|
|
||||||
### 3. **SSL-Zertifikate**
|
|
||||||
```bash
|
```bash
|
||||||
# Erstelle SSL-Verzeichnis
|
NEXT_PUBLIC_BASE_URL=https://dk0.dev
|
||||||
mkdir -p ssl
|
MY_EMAIL=contact@dk0.dev
|
||||||
|
MY_INFO_EMAIL=info@dk0.dev
|
||||||
# Kopiere deine SSL-Zertifikate
|
MY_PASSWORD=your_email_password
|
||||||
cp your-cert.pem ssl/cert.pem
|
MY_INFO_PASSWORD=your_info_email_password
|
||||||
cp your-key.pem ssl/key.pem
|
ADMIN_BASIC_AUTH=admin:your_secure_password
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 Deployment
|
## Deployment Methods
|
||||||
|
|
||||||
### **Automatisches Deployment**
|
### 1. Local Development
|
||||||
Das System deployt automatisch bei Push auf den `production` Branch:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Code auf production Branch pushen
|
# Start all services
|
||||||
git push origin production
|
docker-compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f portfolio
|
||||||
|
|
||||||
|
# Stop services
|
||||||
|
docker-compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Manuelles Deployment**
|
### 2. CI/CD Pipeline (Automatic)
|
||||||
|
|
||||||
|
The CI/CD pipeline runs automatically on:
|
||||||
|
- **Push to `main`**: Runs tests, linting, build, and security checks
|
||||||
|
- **Push to `production`**: Full deployment including Docker build and deployment
|
||||||
|
|
||||||
|
#### Pipeline Steps:
|
||||||
|
1. **Install dependencies** (`npm ci`)
|
||||||
|
2. **Run linting** (`npm run lint`)
|
||||||
|
3. **Run tests** (`npm run test`)
|
||||||
|
4. **Build application** (`npm run build`)
|
||||||
|
5. **Security scan** (`npm audit`)
|
||||||
|
6. **Build Docker image** (production only)
|
||||||
|
7. **Deploy with Docker Compose** (production only)
|
||||||
|
|
||||||
|
### 3. Manual Deployment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Lokales Deployment
|
# Build and start services
|
||||||
./scripts/deploy.sh production
|
docker-compose up -d --build
|
||||||
|
|
||||||
# Oder mit npm
|
# Check service status
|
||||||
npm run deploy
|
docker-compose ps
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Docker Commands**
|
## Service Configuration
|
||||||
|
|
||||||
|
### Portfolio App
|
||||||
|
- **Port**: 3000 (configurable via `PORT` environment variable)
|
||||||
|
- **Health Check**: `http://localhost:3000/api/health`
|
||||||
|
- **Environment**: Production
|
||||||
|
- **Resources**: 512M memory limit, 0.5 CPU limit
|
||||||
|
|
||||||
|
### PostgreSQL Database
|
||||||
|
- **Port**: 5432 (internal)
|
||||||
|
- **Database**: `portfolio_db`
|
||||||
|
- **User**: `portfolio_user`
|
||||||
|
- **Password**: `portfolio_pass`
|
||||||
|
- **Health Check**: `pg_isready`
|
||||||
|
|
||||||
|
### Redis Cache
|
||||||
|
- **Port**: 6379 (internal)
|
||||||
|
- **Health Check**: `redis-cli ping`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Secrets not loading**:
|
||||||
|
- Run the debug workflow: Actions → Debug Secrets
|
||||||
|
- Verify all secrets are set in Gitea
|
||||||
|
- Check secret names match exactly
|
||||||
|
|
||||||
|
2. **Container won't start**:
|
||||||
```bash
|
```bash
|
||||||
# Container starten
|
# Check logs
|
||||||
npm run docker:compose
|
docker-compose logs portfolio
|
||||||
|
|
||||||
# Container stoppen
|
# Check service status
|
||||||
npm run docker:down
|
docker-compose ps
|
||||||
|
|
||||||
# Health Check
|
# Restart services
|
||||||
npm run health
|
docker-compose restart
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📊 Monitoring
|
3. **Database connection issues**:
|
||||||
|
|
||||||
### **Container Status**
|
|
||||||
```bash
|
```bash
|
||||||
# Status anzeigen
|
# Check PostgreSQL status
|
||||||
./scripts/monitor.sh status
|
docker-compose exec postgres pg_isready -U portfolio_user -d portfolio_db
|
||||||
|
|
||||||
# Oder mit npm
|
# Check database logs
|
||||||
npm run monitor status
|
docker-compose logs postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Health Check**
|
4. **Redis connection issues**:
|
||||||
```bash
|
```bash
|
||||||
# Application Health
|
# Test Redis connection
|
||||||
./scripts/monitor.sh health
|
docker-compose exec redis redis-cli ping
|
||||||
|
|
||||||
# Oder direkt
|
# Check Redis logs
|
||||||
curl http://localhost:3000/api/health
|
docker-compose logs redis
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Logs anzeigen**
|
### Debug Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Letzte 50 Zeilen
|
# Check environment variables in container
|
||||||
./scripts/monitor.sh logs 50
|
docker exec portfolio-app env | grep -E "(DATABASE_URL|REDIS_URL|NEXT_PUBLIC_BASE_URL)"
|
||||||
|
|
||||||
# Live-Logs folgen
|
# Test health endpoints
|
||||||
./scripts/monitor.sh logs 100
|
curl -f http://localhost:3000/api/health
|
||||||
|
|
||||||
|
# View all service logs
|
||||||
|
docker-compose logs --tail=50
|
||||||
|
|
||||||
|
# Check resource usage
|
||||||
|
docker stats
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Metriken**
|
## Monitoring
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
- **Portfolio App**: `http://localhost:3000/api/health`
|
||||||
|
- **PostgreSQL**: `pg_isready` command
|
||||||
|
- **Redis**: `redis-cli ping` command
|
||||||
|
|
||||||
|
### Logs
|
||||||
```bash
|
```bash
|
||||||
# Detaillierte Metriken
|
# Follow all logs
|
||||||
./scripts/monitor.sh metrics
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Follow specific service logs
|
||||||
|
docker-compose logs -f portfolio
|
||||||
|
docker-compose logs -f postgres
|
||||||
|
docker-compose logs -f redis
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔧 Wartung
|
## Security
|
||||||
|
|
||||||
### **Container neustarten**
|
### Security Scans
|
||||||
|
- **NPM Audit**: Runs automatically in CI/CD
|
||||||
|
- **Dependency Check**: Checks for known vulnerabilities
|
||||||
|
- **Secret Detection**: Prevents accidental secret commits
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
- Never commit secrets to repository
|
||||||
|
- Use environment variables for sensitive data
|
||||||
|
- Regularly update dependencies
|
||||||
|
- Monitor security advisories
|
||||||
|
|
||||||
|
## Backup and Recovery
|
||||||
|
|
||||||
|
### Database Backup
|
||||||
```bash
|
```bash
|
||||||
./scripts/monitor.sh restart
|
# Create backup
|
||||||
|
docker-compose exec postgres pg_dump -U portfolio_user portfolio_db > backup.sql
|
||||||
|
|
||||||
|
# Restore backup
|
||||||
|
docker-compose exec -T postgres psql -U portfolio_user portfolio_db < backup.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Cleanup**
|
### Volume Backup
|
||||||
```bash
|
```bash
|
||||||
# Docker-Ressourcen bereinigen
|
# Backup volumes
|
||||||
./scripts/monitor.sh cleanup
|
docker run --rm -v portfolio_postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres_backup.tar.gz /data
|
||||||
|
docker run --rm -v portfolio_redis_data:/data -v $(pwd):/backup alpine tar czf /backup/redis_backup.tar.gz /data
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Updates**
|
## Performance Optimization
|
||||||
```bash
|
|
||||||
# Neues Image pullen und deployen
|
|
||||||
./scripts/deploy.sh production
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📈 Performance-Tuning
|
### Resource Limits
|
||||||
|
- **Portfolio App**: 512M memory, 0.5 CPU
|
||||||
|
- **PostgreSQL**: 256M memory, 0.25 CPU
|
||||||
|
- **Redis**: Default limits
|
||||||
|
|
||||||
### **Nginx Optimierungen**
|
### Caching
|
||||||
- **Gzip Compression** aktiviert
|
- **Next.js**: Built-in caching
|
||||||
- **Static Asset Caching** (1 Jahr)
|
- **Redis**: Session and analytics caching
|
||||||
- **API Rate Limiting** (10 req/s)
|
- **Static Assets**: Served from CDN
|
||||||
- **Load Balancing** bereit für Skalierung
|
|
||||||
|
|
||||||
### **Docker Optimierungen**
|
## Support
|
||||||
- **Multi-Stage Build** für kleinere Images
|
|
||||||
- **Non-root User** für Sicherheit
|
|
||||||
- **Health Checks** für automatische Recovery
|
|
||||||
- **Resource Limits** (512MB RAM, 0.5 CPU)
|
|
||||||
|
|
||||||
### **Next.js Optimierungen**
|
For issues or questions:
|
||||||
- **Standalone Output** für Docker
|
1. Check the troubleshooting section above
|
||||||
- **Image Optimization** (WebP, AVIF)
|
2. Review CI/CD pipeline logs
|
||||||
- **CSS Optimization** aktiviert
|
3. Run the debug workflow
|
||||||
- **Package Import Optimization**
|
4. Check service health endpoints
|
||||||
|
|
||||||
## 🚨 Troubleshooting
|
|
||||||
|
|
||||||
### **Container startet nicht**
|
|
||||||
```bash
|
|
||||||
# Logs prüfen
|
|
||||||
./scripts/monitor.sh logs
|
|
||||||
|
|
||||||
# Status prüfen
|
|
||||||
./scripts/monitor.sh status
|
|
||||||
|
|
||||||
# Neustarten
|
|
||||||
./scripts/monitor.sh restart
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Health Check schlägt fehl**
|
|
||||||
```bash
|
|
||||||
# Manueller Health Check
|
|
||||||
curl -v http://localhost:3000/api/health
|
|
||||||
|
|
||||||
# Container-Logs prüfen
|
|
||||||
docker compose -f docker-compose.prod.yml logs portfolio
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Performance-Probleme**
|
|
||||||
```bash
|
|
||||||
# Resource-Usage prüfen
|
|
||||||
./scripts/monitor.sh metrics
|
|
||||||
|
|
||||||
# Nginx-Logs prüfen
|
|
||||||
docker compose -f docker-compose.prod.yml logs nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
### **SSL-Probleme**
|
|
||||||
```bash
|
|
||||||
# SSL-Zertifikate prüfen
|
|
||||||
openssl x509 -in ssl/cert.pem -text -noout
|
|
||||||
|
|
||||||
# Nginx-Konfiguration testen
|
|
||||||
docker compose -f docker-compose.prod.yml exec nginx nginx -t
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 CI/CD Pipeline
|
|
||||||
|
|
||||||
### **Workflow-Schritte**
|
|
||||||
1. **Test** - Linting, Tests, Build
|
|
||||||
2. **Security** - Trivy Vulnerability Scan
|
|
||||||
3. **Build** - Multi-Arch Docker Image
|
|
||||||
4. **Deploy** - Automatisches Deployment
|
|
||||||
|
|
||||||
### **Trigger**
|
|
||||||
- **Push auf `main`** - Build nur
|
|
||||||
- **Push auf `production`** - Build + Deploy
|
|
||||||
- **Pull Request** - Test + Security
|
|
||||||
|
|
||||||
### **Monitoring**
|
|
||||||
- **GitHub Actions** - Pipeline-Status
|
|
||||||
- **Container Health** - Automatische Checks
|
|
||||||
- **Resource Usage** - Monitoring-Skript
|
|
||||||
|
|
||||||
## 🔄 Skalierung
|
|
||||||
|
|
||||||
### **Horizontal Scaling**
|
|
||||||
```yaml
|
|
||||||
# In nginx.conf - weitere Backend-Server hinzufügen
|
|
||||||
upstream portfolio_backend {
|
|
||||||
least_conn;
|
|
||||||
server portfolio:3000 max_fails=3 fail_timeout=30s;
|
|
||||||
server portfolio-2:3000 max_fails=3 fail_timeout=30s;
|
|
||||||
server portfolio-3:3000 max_fails=3 fail_timeout=30s;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Vertical Scaling**
|
|
||||||
```yaml
|
|
||||||
# In docker-compose.prod.yml - Resource-Limits erhöhen
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 1G
|
|
||||||
cpus: '1.0'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📞 Support
|
|
||||||
|
|
||||||
Bei Problemen:
|
|
||||||
1. **Logs prüfen**: `./scripts/monitor.sh logs`
|
|
||||||
2. **Status prüfen**: `./scripts/monitor.sh status`
|
|
||||||
3. **Health Check**: `./scripts/monitor.sh health`
|
|
||||||
4. **Container neustarten**: `./scripts/monitor.sh restart`
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# Gitea Secrets Setup
|
|
||||||
|
|
||||||
Um die GitHub Actions Workflows korrekt zu verwenden, müssen die folgenden Secrets in deinem Gitea Repository konfiguriert werden:
|
|
||||||
|
|
||||||
## Secrets konfigurieren
|
|
||||||
|
|
||||||
1. Gehe zu deinem Repository in Gitea
|
|
||||||
2. Klicke auf **Settings** → **Secrets**
|
|
||||||
3. Füge die folgenden Secrets hinzu:
|
|
||||||
|
|
||||||
### Erforderliche Secrets:
|
|
||||||
|
|
||||||
| Secret Name | Beschreibung | Beispiel |
|
|
||||||
|-------------|--------------|----------|
|
|
||||||
| `NEXT_PUBLIC_BASE_URL` | Die öffentliche URL deiner Website | `https://dk0.dev` |
|
|
||||||
| `MY_EMAIL` | Haupt-Email-Adresse für Kontaktformular | `contact@dk0.dev` |
|
|
||||||
| `MY_INFO_EMAIL` | Info-Email-Adresse | `info@dk0.dev` |
|
|
||||||
| `MY_PASSWORD` | Passwort für Haupt-Email | `dein_email_passwort` |
|
|
||||||
| `MY_INFO_PASSWORD` | Passwort für Info-Email | `dein_info_email_passwort` |
|
|
||||||
| `ADMIN_BASIC_AUTH` | Admin-Basic-Auth für geschützte Bereiche | `admin:dein_sicheres_passwort` |
|
|
||||||
|
|
||||||
## Docker Compose Setup
|
|
||||||
|
|
||||||
Die Workflows verwenden jetzt `docker-compose.workflow.yml` für eine vollständige Service-Konfiguration:
|
|
||||||
|
|
||||||
- **PostgreSQL**: Datenbank für die Anwendung
|
|
||||||
- **Redis**: Caching und Session-Management
|
|
||||||
- **Portfolio App**: Die Hauptanwendung
|
|
||||||
|
|
||||||
## Netzwerk-Konfiguration
|
|
||||||
|
|
||||||
Die Services sind im `portfolio_net` Netzwerk konfiguriert, damit sie miteinander kommunizieren können.
|
|
||||||
|
|
||||||
## Health Checks
|
|
||||||
|
|
||||||
Alle Services haben Health Checks konfiguriert:
|
|
||||||
- PostgreSQL: `pg_isready`
|
|
||||||
- Redis: `redis-cli ping`
|
|
||||||
- Portfolio App: HTTP Health Check auf `/api/health`
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
Falls die Workflows fehlschlagen:
|
|
||||||
|
|
||||||
1. **Secrets prüfen**: Stelle sicher, dass alle Secrets korrekt konfiguriert sind
|
|
||||||
2. **Netzwerk prüfen**: Überprüfe, ob das `portfolio_net` Netzwerk existiert
|
|
||||||
3. **Ports prüfen**: Stelle sicher, dass Port 3000 frei ist
|
|
||||||
4. **Logs prüfen**: Schaue in die Container-Logs für Fehlermeldungen
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Container-Logs anzeigen
|
|
||||||
docker-compose -f docker-compose.workflow.yml logs
|
|
||||||
|
|
||||||
# Services-Status prüfen
|
|
||||||
docker-compose -f docker-compose.workflow.yml ps
|
|
||||||
```
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
# Secrets Verification Guide
|
|
||||||
|
|
||||||
## Wie du überprüfst, ob Secrets korrekt geladen werden
|
|
||||||
|
|
||||||
### 1. **Debug-Workflow ausführen**
|
|
||||||
|
|
||||||
Ich habe einen speziellen Debug-Workflow erstellt (`.gitea/workflows/debug-secrets.yml`):
|
|
||||||
|
|
||||||
1. Gehe zu deinem Repository in Gitea
|
|
||||||
2. Klicke auf **Actions** → **Debug Secrets**
|
|
||||||
3. Klicke auf **Run workflow** → **Run workflow**
|
|
||||||
4. Der Workflow wird dir zeigen:
|
|
||||||
- ✅ Welche Secrets gesetzt sind
|
|
||||||
- ❌ Welche Secrets fehlen
|
|
||||||
- 🔐 Ob die Formate korrekt sind
|
|
||||||
|
|
||||||
### 2. **Secrets in Gitea überprüfen**
|
|
||||||
|
|
||||||
**Manuell in Gitea:**
|
|
||||||
1. Gehe zu **Settings** → **Secrets**
|
|
||||||
2. Überprüfe, ob alle Secrets vorhanden sind:
|
|
||||||
- `NEXT_PUBLIC_BASE_URL`
|
|
||||||
- `MY_EMAIL`
|
|
||||||
- `MY_INFO_EMAIL`
|
|
||||||
- `MY_PASSWORD`
|
|
||||||
- `MY_INFO_PASSWORD`
|
|
||||||
- `ADMIN_BASIC_AUTH`
|
|
||||||
|
|
||||||
### 3. **Workflow-Logs überprüfen**
|
|
||||||
|
|
||||||
Nach dem Ausführen eines Workflows:
|
|
||||||
|
|
||||||
1. Gehe zu **Actions** → **Workflow runs**
|
|
||||||
2. Klicke auf den neuesten Run
|
|
||||||
3. Schaue dir die Logs an, besonders:
|
|
||||||
- "Verify secrets before deployment"
|
|
||||||
- "Verify container environment"
|
|
||||||
|
|
||||||
### 4. **Container direkt überprüfen**
|
|
||||||
|
|
||||||
Falls der Container läuft, kannst du ihn direkt überprüfen:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Container-Environment anzeigen
|
|
||||||
docker exec portfolio-app env | grep -E "(NEXT_PUBLIC_BASE_URL|MY_EMAIL|ADMIN_BASIC_AUTH)"
|
|
||||||
|
|
||||||
# Container-Logs anzeigen
|
|
||||||
docker logs portfolio-app
|
|
||||||
|
|
||||||
# Services-Status prüfen
|
|
||||||
docker-compose -f docker-compose.workflow.yml ps
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. **Häufige Probleme**
|
|
||||||
|
|
||||||
**❌ Secret nicht gesetzt:**
|
|
||||||
- Gehe zu Gitea → Settings → Secrets
|
|
||||||
- Füge das fehlende Secret hinzu
|
|
||||||
|
|
||||||
**❌ Falsches Format:**
|
|
||||||
- `NEXT_PUBLIC_BASE_URL`: Muss mit `http://` oder `https://` beginnen
|
|
||||||
- `MY_EMAIL`: Muss eine gültige Email-Adresse sein
|
|
||||||
- `ADMIN_BASIC_AUTH`: Muss Format `username:password` haben
|
|
||||||
|
|
||||||
**❌ Container kann nicht starten:**
|
|
||||||
- Überprüfe die Docker-Logs
|
|
||||||
- Stelle sicher, dass alle Services (PostgreSQL, Redis) laufen
|
|
||||||
|
|
||||||
### 6. **Test-Schritte**
|
|
||||||
|
|
||||||
1. **Führe den Debug-Workflow aus**
|
|
||||||
2. **Überprüfe die Ausgabe**
|
|
||||||
3. **Korrigiere fehlende/falsche Secrets**
|
|
||||||
4. **Führe einen normalen Workflow aus**
|
|
||||||
5. **Überprüfe die Container-Environment**
|
|
||||||
|
|
||||||
### 7. **Erwartete Ausgabe**
|
|
||||||
|
|
||||||
**Bei korrekten Secrets:**
|
|
||||||
```
|
|
||||||
✅ NEXT_PUBLIC_BASE_URL: Set (length: 15)
|
|
||||||
✅ MY_EMAIL: Set (length: 20)
|
|
||||||
✅ MY_INFO_EMAIL: Set (length: 18)
|
|
||||||
✅ MY_PASSWORD: Set (length: 12)
|
|
||||||
✅ MY_INFO_PASSWORD: Set (length: 12)
|
|
||||||
✅ ADMIN_BASIC_AUTH: Set (length: 15)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Bei fehlenden Secrets:**
|
|
||||||
```
|
|
||||||
❌ NEXT_PUBLIC_BASE_URL: Not set
|
|
||||||
❌ MY_EMAIL: Not set
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8. **Schnelltest**
|
|
||||||
|
|
||||||
Führe diesen Befehl aus, um schnell zu testen:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug-Workflow manuell ausführen
|
|
||||||
curl -X POST "https://your-gitea-instance.com/api/v1/repos/your-username/portfolio/actions/workflows/debug-secrets.yml/dispatches" \
|
|
||||||
-H "Authorization: token YOUR_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"ref":"main"}'
|
|
||||||
```
|
|
||||||
@@ -101,9 +101,9 @@ const Hero = () => {
|
|||||||
<Image
|
<Image
|
||||||
src="/images/me.jpg"
|
src="/images/me.jpg"
|
||||||
alt="Dennis Konkol - Software Engineer"
|
alt="Dennis Konkol - Software Engineer"
|
||||||
fill={true}
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
priority={true}
|
priority
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Hover overlay effect */}
|
{/* Hover overlay effect */}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
services:
|
|
||||||
portfolio:
|
|
||||||
image: portfolio-app:latest
|
|
||||||
container_name: portfolio-app
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "4000:3000"
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
- DATABASE_URL=postgresql://portfolio_user:portfolio_pass@postgres:5432/portfolio_db?schema=public
|
|
||||||
- REDIS_URL=redis://redis-redis-shared-1:6379
|
|
||||||
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
|
||||||
- MY_EMAIL=${MY_EMAIL}
|
|
||||||
- MY_INFO_EMAIL=${MY_INFO_EMAIL}
|
|
||||||
- MY_PASSWORD=${MY_PASSWORD}
|
|
||||||
- MY_INFO_PASSWORD=${MY_INFO_PASSWORD}
|
|
||||||
- ADMIN_BASIC_AUTH=${ADMIN_BASIC_AUTH}
|
|
||||||
volumes:
|
|
||||||
- portfolio_data:/app/.next/cache
|
|
||||||
networks:
|
|
||||||
- portfolio_net
|
|
||||||
- proxy
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 512M
|
|
||||||
cpus: '0.5'
|
|
||||||
reservations:
|
|
||||||
memory: 256M
|
|
||||||
cpus: '0.25'
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
container_name: portfolio-postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- POSTGRES_DB=portfolio_db
|
|
||||||
- POSTGRES_USER=portfolio_user
|
|
||||||
- POSTGRES_PASSWORD=portfolio_pass
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
networks:
|
|
||||||
- portfolio_net
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U portfolio_user -d portfolio_db"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 30s
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 256M
|
|
||||||
cpus: '0.25'
|
|
||||||
reservations:
|
|
||||||
memory: 128M
|
|
||||||
cpus: '0.1'
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
portfolio_data:
|
|
||||||
driver: local
|
|
||||||
postgres_data:
|
|
||||||
driver: local
|
|
||||||
redis_data:
|
|
||||||
driver: local
|
|
||||||
|
|
||||||
networks:
|
|
||||||
portfolio_net:
|
|
||||||
external: true
|
|
||||||
proxy:
|
|
||||||
external: true
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Docker Compose configuration for GitHub Actions workflows
|
# Unified Docker Compose configuration for Portfolio
|
||||||
# This ensures all required services are running before deployment
|
# Supports both local development and production deployment
|
||||||
|
|
||||||
services:
|
services:
|
||||||
portfolio:
|
portfolio:
|
||||||
@@ -7,9 +7,9 @@ services:
|
|||||||
container_name: portfolio-app
|
container_name: portfolio-app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "${PORT:-3000}:3000" # Configurable port, defaults to 3000
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=${NODE_ENV:-production}
|
||||||
- DATABASE_URL=postgresql://portfolio_user:portfolio_pass@postgres:5432/portfolio_db?schema=public
|
- DATABASE_URL=postgresql://portfolio_user:portfolio_pass@postgres:5432/portfolio_db?schema=public
|
||||||
- REDIS_URL=redis://redis:6379
|
- REDIS_URL=redis://redis:6379
|
||||||
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
||||||
@@ -22,6 +22,7 @@ services:
|
|||||||
- portfolio_data:/app/.next/cache
|
- portfolio_data:/app/.next/cache
|
||||||
networks:
|
networks:
|
||||||
- portfolio_net
|
- portfolio_net
|
||||||
|
- proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -33,6 +34,14 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 40s
|
start_period: 40s
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.5'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -52,6 +61,14 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
reservations:
|
||||||
|
memory: 128M
|
||||||
|
cpus: '0.1'
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
@@ -79,3 +96,5 @@ volumes:
|
|||||||
networks:
|
networks:
|
||||||
portfolio_net:
|
portfolio_net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
@@ -25,8 +25,14 @@ jest.mock('next/link', () => {
|
|||||||
|
|
||||||
// Mock next/image
|
// Mock next/image
|
||||||
jest.mock('next/image', () => {
|
jest.mock('next/image', () => {
|
||||||
const ImageComponent = ({ src, alt, ...props }: Record<string, unknown>) =>
|
const ImageComponent = ({ src, alt, fill, priority, ...props }: Record<string, unknown>) => {
|
||||||
React.createElement('img', { src, alt, ...props });
|
// Convert boolean props to strings for DOM compatibility
|
||||||
|
const domProps: Record<string, unknown> = { src, alt };
|
||||||
|
if (fill) domProps.style = { width: '100%', height: '100%', objectFit: 'cover' };
|
||||||
|
if (priority) domProps.loading = 'eager';
|
||||||
|
|
||||||
|
return React.createElement('img', { ...domProps, ...props });
|
||||||
|
};
|
||||||
ImageComponent.displayName = 'Image';
|
ImageComponent.displayName = 'Image';
|
||||||
return ImageComponent;
|
return ImageComponent;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user