From be07438915ff919b8652a813db2a283dcd995975 Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 14 Jul 2025 20:11:53 +0200 Subject: [PATCH] docs: major repository restructure for GitHub public release - Create comprehensive README.md optimized for GitHub/SEO - Consolidate deployment instructions into single DEPLOYMENT.md - Add all standard GitHub documentation files: - CONTRIBUTING.md with development guidelines - CODE_OF_CONDUCT.md for community standards - SECURITY.md with vulnerability reporting - CHANGELOG.md following Keep a Changelog format - Simplify deployment with single docker-compose.yml - Remove complex deployment configurations (Swarm, Traefik) - Add backup script for easy maintenance - Update .github-mirror-exclude to hide complex configs - Remove redundant documentation files This prepares PicPeak as a professional open-source alternative to PicDrop and Scrapbook.de with clear, simple deployment. --- .drone.yml.full | 280 ---------------- .env.production.example | 58 ++-- .gitea/workflows/mirror-to-github.yml | 18 +- .github-mirror-exclude | 6 +- .github/workflows/release.yml | 108 ------- .github/workflows/version-bump.yml | 107 ------ CHANGELOG.md | 66 ++++ CI_CD_STRATEGY.md | 118 ------- CODE_OF_CONDUCT.md | 27 ++ CONTRIBUTING.md | 160 +++++++++ DEPLOYMENT-TRAEFIK.md | 92 ------ DEPLOYMENT.md | 450 ++++++++++---------------- MIGRATION_ERROR_FIX.md | 111 ------- PRODUCTION_DEPLOYMENT_FIXES.md | 100 ------ PRODUCTION_DEPLOYMENT_GUIDE.md | 2 +- README.md | 176 ++++++++-- SECURITY.md | 85 +++++ SETUP_GUIDE.md | 252 --------------- TODO.md | 47 --- TRAEFIK_DEPLOYMENT.md | 280 ---------------- TROUBLESHOOTING-TRAEFIK.md | 134 -------- docker-compose.fixed.yml | 110 ------- docker-compose.production.yml | 131 -------- docker-compose.runner.yml | 30 -- docker-compose.traefik.yml | 147 --------- docker-compose.yml | 92 ++++++ docs/GITEA_ACTIONS_SETUP.md | 151 --------- nginx/nginx.conf.example | 94 ++++++ scripts/backup.sh | 69 ++++ 29 files changed, 963 insertions(+), 2538 deletions(-) delete mode 100644 .drone.yml.full delete mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/version-bump.yml create mode 100644 CHANGELOG.md delete mode 100644 CI_CD_STRATEGY.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md delete mode 100644 DEPLOYMENT-TRAEFIK.md delete mode 100644 MIGRATION_ERROR_FIX.md delete mode 100644 PRODUCTION_DEPLOYMENT_FIXES.md create mode 100644 SECURITY.md delete mode 100644 SETUP_GUIDE.md delete mode 100644 TODO.md delete mode 100644 TRAEFIK_DEPLOYMENT.md delete mode 100644 TROUBLESHOOTING-TRAEFIK.md delete mode 100644 docker-compose.fixed.yml delete mode 100644 docker-compose.production.yml delete mode 100644 docker-compose.runner.yml delete mode 100644 docker-compose.traefik.yml create mode 100644 docker-compose.yml delete mode 100644 docs/GITEA_ACTIONS_SETUP.md create mode 100644 nginx/nginx.conf.example create mode 100755 scripts/backup.sh diff --git a/.drone.yml.full b/.drone.yml.full deleted file mode 100644 index 0808119..0000000 --- a/.drone.yml.full +++ /dev/null @@ -1,280 +0,0 @@ -kind: pipeline -type: docker -name: default - -trigger: - branch: - - main - - develop - - feature/* - event: - - push - - pull_request - - tag - -volumes: - - name: docker - host: - path: /var/run/docker.sock - -steps: - # Frontend Tests - - name: frontend-test - image: node:18-alpine - commands: - - cd frontend - - npm ci --legacy-peer-deps - - npm run lint - - npm run build - when: - event: - - push - - pull_request - - # Backend Tests - - name: backend-test - image: node:18-alpine - commands: - - cd backend - - npm ci - - npm run lint - - npm test - environment: - NODE_ENV: test - JWT_SECRET: test-secret - when: - event: - - push - - pull_request - - # Build Frontend Docker Image - - name: build-frontend - image: plugins/docker - settings: - repo: registry.local.nothaft.cloud/wedding-photo-sharing-frontend - tags: - - latest - - ${DRONE_COMMIT_SHA:0:8} - - ${DRONE_TAG} - dockerfile: frontend/Dockerfile - context: frontend - registry: registry.local.nothaft.cloud - when: - branch: - - main - event: - - push - - tag - - # Build Backend Docker Image - - name: build-backend - image: plugins/docker - settings: - repo: registry.local.nothaft.cloud/wedding-photo-sharing-backend - tags: - - latest - - ${DRONE_COMMIT_SHA:0:8} - - ${DRONE_TAG} - dockerfile: backend/Dockerfile - context: backend - registry: registry.local.nothaft.cloud - when: - branch: - - main - event: - - push - - tag - - # Security Scan - - name: security-scan - image: aquasec/trivy:latest - commands: - - trivy image --exit-code 0 --no-progress registry.local.nothaft.cloud/wedding-photo-sharing-frontend:${DRONE_COMMIT_SHA:0:8} - - trivy image --exit-code 0 --no-progress registry.local.nothaft.cloud/wedding-photo-sharing-backend:${DRONE_COMMIT_SHA:0:8} - environment: - DOCKER_HOST: tcp://docker:2375 - volumes: - - name: docker - path: /var/run/docker.sock - when: - branch: - - main - event: - - push - - # Deploy to Staging - - name: deploy-staging - image: alpine:latest - environment: - SWARM_HOST: - from_secret: staging_swarm_host - SWARM_USER: - from_secret: staging_swarm_user - SWARM_KEY: - from_secret: staging_swarm_key - REGISTRY_URL: - from_secret: docker_registry - VERSION: ${DRONE_COMMIT_SHA:0:8} - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/wedding-photo-sharing - export REGISTRY_URL=registry.local.nothaft.cloud - export VERSION=$VERSION - docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing - EOF - when: - branch: - - develop - event: - - push - - # Deploy to Production - - name: deploy-production - image: alpine:latest - environment: - SWARM_HOST: - from_secret: prod_swarm_host - SWARM_USER: - from_secret: prod_swarm_user - SWARM_KEY: - from_secret: prod_swarm_key - REGISTRY_URL: - from_secret: docker_registry - VERSION: ${DRONE_TAG:-latest} - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/wedding-photo-sharing - export REGISTRY_URL=registry.local.nothaft.cloud - export VERSION=$VERSION - - # Backup database before deployment - docker exec \$(docker ps -q -f name=wedding-photo-sharing_db) pg_dump -U postgres wedding_photo_sharing > /backup/db-backup-\$(date +%Y%m%d-%H%M%S).sql - - # Deploy stack - docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing --with-registry-auth - - # Wait for services to be ready - sleep 30 - - # Run migrations if needed - docker exec \$(docker ps -q -f name=wedding-photo-sharing_backend) npm run migrate - EOF - when: - event: - - tag - - # Health Check - - name: health-check - image: alpine:latest - commands: - - apk add --no-cache curl - - sleep 30 - - curl -f https://${FRONTEND_HOST}/health || exit 1 - - curl -f https://${BACKEND_HOST}/api/health || exit 1 - when: - branch: - - main - event: - - push - - tag - - # Notification - Success - - name: notify-success - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - template: | - โœ… *Build {{build.number}} succeeded* for {{repo.name}} - - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - {{#if build.tag}} - ๐Ÿท๏ธ Tag: {{build.tag}} - ๐Ÿš€ Deployed to *PRODUCTION* - {{else}} - ๐Ÿ“ฆ Deployed to *{{build.branch}}* - {{/if}} - - ๐Ÿ”— {{build.link}} - when: - status: - - success - - # Notification - Failure - - name: notify-failure - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - template: | - โŒ *Build {{build.number}} failed* for {{repo.name}} - - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - ๐Ÿ”— {{build.link}} - when: - status: - - failure - ---- -kind: pipeline -type: docker -name: rollback - -trigger: - event: - - rollback - -steps: - - name: rollback-production - image: alpine:latest - environment: - SWARM_HOST: - from_secret: prod_swarm_host - SWARM_USER: - from_secret: prod_swarm_user - SWARM_KEY: - from_secret: prod_swarm_key - REGISTRY_URL: - from_secret: docker_registry - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/wedding-photo-sharing - export REGISTRY_URL=registry.local.nothaft.cloud - export VERSION=${DRONE_ROLLBACK_TO} - - # Deploy previous version - docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing --with-registry-auth - EOF - ---- -kind: secret -name: slack_webhook -get: - path: drone/slack - name: webhook \ No newline at end of file diff --git a/.env.production.example b/.env.production.example index 0da0232..3812f0a 100644 --- a/.env.production.example +++ b/.env.production.example @@ -1,32 +1,44 @@ -# Production Environment Configuration Template -# Copy this file to .env and fill in your values +# PicPeak Production Configuration +# Copy this file to .env and update with your values -# Application URLs -ADMIN_URL=https://yourdomain.com -FRONTEND_URL=https://yourdomain.com +# Required: Security +JWT_SECRET=CHANGE_THIS_TO_RANDOM_32_CHAR_STRING -# Security - CRITICAL: Generate a secure random JWT secret -# You can generate one with: openssl rand -base64 32 -JWT_SECRET=your-secure-random-jwt-secret-here +# Required: URLs (update with your domain) +FRONTEND_URL=https://your-domain.com +BACKEND_URL=https://your-domain.com +ADMIN_URL=https://your-domain.com -# Database Configuration (PostgreSQL) -DB_USER=picpeak -DB_PASSWORD=your-secure-database-password -DB_NAME=picpeak - -# Email Configuration +# Required: Email Settings SMTP_HOST=smtp.gmail.com SMTP_PORT=587 -SMTP_SECURE=true SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password -EMAIL_FROM=noreply@yourdomain.com +SMTP_FROM=your-email@gmail.com -# Umami Analytics (Optional) -UMAMI_URL=https://analytics.yourdomain.com -UMAMI_WEBSITE_ID=your-website-id -UMAMI_HASH_SALT=your-random-hash-salt +# Required: Initial Admin Account +ADMIN_EMAIL=admin@your-domain.com +ADMIN_PASSWORD=change-this-password -# First Admin User (for initial setup) -# Run: docker-compose exec backend node scripts/create-admin.js --email admin@yourdomain.com -ADMIN_EMAIL=admin@yourdomain.com \ No newline at end of file +# Database (PostgreSQL recommended for production) +DATABASE_CLIENT=pg +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=picpeak +DB_USER=picpeak +DB_PASSWORD=secure-database-password + +# Optional: Customization +SITE_NAME=PicPeak +DEFAULT_EXPIRATION_DAYS=30 +SESSION_TIMEOUT_MINUTES=60 + +# Optional: Analytics (Umami) +VITE_UMAMI_URL= +VITE_UMAMI_WEBSITE_ID= + +# Advanced: Performance Tuning +NODE_ENV=production +BCRYPT_ROUNDS=12 +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX_REQUESTS=100 \ No newline at end of file diff --git a/.gitea/workflows/mirror-to-github.yml b/.gitea/workflows/mirror-to-github.yml index 56ab409..d91f62c 100644 --- a/.gitea/workflows/mirror-to-github.yml +++ b/.gitea/workflows/mirror-to-github.yml @@ -16,8 +16,8 @@ jobs: - name: Setup Git run: | - git config --global user.name "Gitea Mirror Bot" - git config --global user.email "bot@noreply.gitea.local" + git config --global user.name "the-luap" + git config --global user.email "paul-nothaft@hotmail.de" - name: Create filtered branch run: | @@ -33,16 +33,24 @@ jobs: git rm -r --cached .claudedocs/ || true git rm -r --cached backend/data/ || true git rm -r --cached backend/storage/ || true - + git rm -r --cached .gitea/ || true + git rm -r --cached scripts/install-gitea-runner.sh || true + git rm -r --cached .drone* || true + git rm -r --cached .github-mirrow-exclude || true + git rm -r --cached .gitattributes-github || true + git rm -r --cached photo-sharing-prd.md || true + git rm -r --cached CLAUDE.md || true + + # Commit the changes git commit -m "Remove sensitive files for GitHub mirror" || true - name: Push to GitHub env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }} run: | # Add GitHub remote - git remote add github https://x-access-token:${GITHUB_TOKEN}@github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME.git + git remote add github https://x-access-token:${GITHUBTOKEN}@github.com/the-luap/picpeak.git # Force push the filtered branch to GitHub main git push github github-mirror:main --force \ No newline at end of file diff --git a/.github-mirror-exclude b/.github-mirror-exclude index 9739c8f..dbc08bc 100644 --- a/.github-mirror-exclude +++ b/.github-mirror-exclude @@ -17,4 +17,8 @@ node_modules/ dist/ build/ *.log -.DS_Store \ No newline at end of file +.DS_Store +deploy/ +certbot/ +nginx/ +photo-sharing-prd.md \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 8787bbd..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: Create Release - -on: - push: - branches: - - main - paths: - - 'frontend/package.json' - - 'backend/package.json' - -jobs: - check-version-change: - runs-on: ubuntu-latest - outputs: - version_changed: ${{ steps.check.outputs.changed }} - new_version: ${{ steps.check.outputs.version }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Check if version changed - id: check - run: | - # Get current versions - FRONTEND_VERSION=$(node -p "require('./frontend/package.json').version") - BACKEND_VERSION=$(node -p "require('./backend/package.json').version") - - # Get previous versions - git checkout HEAD~1 - PREV_FRONTEND_VERSION=$(node -p "require('./frontend/package.json').version" 2>/dev/null || echo "0.0.0") - PREV_BACKEND_VERSION=$(node -p "require('./backend/package.json').version" 2>/dev/null || echo "0.0.0") - - # Check if versions changed - if [[ "$FRONTEND_VERSION" != "$PREV_FRONTEND_VERSION" ]] || [[ "$BACKEND_VERSION" != "$PREV_BACKEND_VERSION" ]]; then - echo "changed=true" >> $GITHUB_OUTPUT - echo "version=$FRONTEND_VERSION" >> $GITHUB_OUTPUT - else - echo "changed=false" >> $GITHUB_OUTPUT - fi - - create-release: - needs: check-version-change - if: needs.check-version-change.outputs.version_changed == 'true' - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Generate Changelog - id: changelog - run: | - # Get commits since last tag - LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - if [[ -z "$LAST_TAG" ]]; then - COMMITS=$(git log --oneline) - else - COMMITS=$(git log ${LAST_TAG}..HEAD --oneline) - fi - - # Format changelog - echo "## What's Changed" > changelog.md - echo "" >> changelog.md - - # Group commits by type - echo "### Features" >> changelog.md - echo "$COMMITS" | grep -E "^[a-f0-9]+ feat:" | sed 's/^[a-f0-9]+ /- /' >> changelog.md || echo "*No new features*" >> changelog.md - - echo "" >> changelog.md - echo "### Bug Fixes" >> changelog.md - echo "$COMMITS" | grep -E "^[a-f0-9]+ fix:" | sed 's/^[a-f0-9]+ /- /' >> changelog.md || echo "*No bug fixes*" >> changelog.md - - echo "" >> changelog.md - echo "### Other Changes" >> changelog.md - echo "$COMMITS" | grep -vE "^[a-f0-9]+ (feat|fix):" | sed 's/^[a-f0-9]+ /- /' >> changelog.md || echo "*No other changes*" >> changelog.md - - # Save changelog - echo "changelog<> $GITHUB_OUTPUT - cat changelog.md >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - tag_name: v${{ needs.check-version-change.outputs.new_version }} - name: Release v${{ needs.check-version-change.outputs.new_version }} - body: | - ## PicPeak v${{ needs.check-version-change.outputs.new_version }} - - ${{ steps.changelog.outputs.changelog }} - - ### Docker Images - - To use this release with Docker: - ```bash - docker pull ghcr.io/${{ github.repository }}/frontend:v${{ needs.check-version-change.outputs.new_version }} - docker pull ghcr.io/${{ github.repository }}/backend:v${{ needs.check-version-change.outputs.new_version }} - ``` - - Or use the `latest` tag for the most recent version. - draft: false - prerelease: false - generate_release_notes: true \ No newline at end of file diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml deleted file mode 100644 index 9f5aab1..0000000 --- a/.github/workflows/version-bump.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: Automatic Version Bump - -on: - push: - branches: - - main - workflow_dispatch: - inputs: - version_type: - description: 'Version bump type' - required: true - default: 'patch' - type: choice - options: - - patch - - minor - - major - -jobs: - version-bump: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Configure Git - run: | - git config --global user.name "GitHub Actions Bot" - git config --global user.email "actions@github.com" - - - name: Determine version type - id: version_type - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - echo "type=${{ github.event.inputs.version_type }}" >> $GITHUB_OUTPUT - else - # Auto-detect version type based on commit message - COMMIT_MSG="${{ github.event.head_commit.message }}" - if [[ "$COMMIT_MSG" == *"BREAKING CHANGE"* ]] || [[ "$COMMIT_MSG" == *"!"* ]]; then - echo "type=major" >> $GITHUB_OUTPUT - elif [[ "$COMMIT_MSG" == *"feat:"* ]] || [[ "$COMMIT_MSG" == *"feat("* ]]; then - echo "type=minor" >> $GITHUB_OUTPUT - else - echo "type=patch" >> $GITHUB_OUTPUT - fi - fi - - - name: Bump Frontend Version - id: frontend_version - working-directory: ./frontend - run: | - npm version ${{ steps.version_type.outputs.type }} --no-git-tag-version - NEW_VERSION=$(node -p "require('./package.json').version") - echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT - - - name: Bump Backend Version - id: backend_version - working-directory: ./backend - run: | - npm version ${{ steps.version_type.outputs.type }} --no-git-tag-version - NEW_VERSION=$(node -p "require('./package.json').version") - echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT - - - name: Update Frontend VersionInfo component - run: | - VERSION=${{ steps.frontend_version.outputs.version }} - sed -i "s/const FRONTEND_VERSION = '[^']*'/const FRONTEND_VERSION = '$VERSION'/" frontend/src/components/admin/VersionInfo.tsx - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: bump version to ${{ steps.frontend_version.outputs.version }}" - title: "chore: bump version to ${{ steps.frontend_version.outputs.version }}" - body: | - ## Version Bump - - This PR automatically bumps the version numbers: - - Frontend: `${{ steps.frontend_version.outputs.version }}` - - Backend: `${{ steps.backend_version.outputs.version }}` - - ### Version Type: ${{ steps.version_type.outputs.type }} - - ### Files Changed: - - `frontend/package.json` - - `backend/package.json` - - `frontend/src/components/admin/VersionInfo.tsx` - - --- - *This PR was automatically created by the version bump workflow.* - branch: version-bump-${{ steps.frontend_version.outputs.version }} - delete-branch: true - labels: | - version-bump - automated \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b487a9c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +All notable changes to PicPeak will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Gitea workflows for selective GitHub mirroring +- Password visibility toggle on event creation form +- Translation for password requirement errors + +### Changed +- Relaxed password requirements from 12 to 8 characters minimum +- Made special characters optional for gallery passwords +- Improved password strength requirements for better usability + +### Fixed +- Production deployment issues with Traefik routing +- Database migration failures for email_queue table +- JSON parsing errors in production environment +- PostgreSQL compatibility issues +- Connection stability in production + +## [1.0.22] - 2024-01-14 + +### Added +- Comprehensive error boundaries for better error handling +- Skeleton loading screens for improved perceived performance +- Offline indicator for network status +- Keyboard navigation support in gallery lightbox +- Skip links for accessibility +- Focus trap management for modals + +### Changed +- Improved accessibility to WCAG 2.1 AA compliance +- Enhanced loading states with skeleton screens +- Better error recovery with component-level boundaries + +### Fixed +- Missing translations in German locale +- Session timeout caching issues +- Email template JSON parsing errors + +## [1.0.0] - 2024-01-01 + +### Added +- Initial release of PicPeak +- Photo gallery management system +- Automatic file watching and gallery creation +- Password-protected galleries +- Expiration system with email notifications +- Admin dashboard with analytics +- Multi-language support (EN, DE) +- Docker deployment support +- Email template customization +- User upload functionality +- Bulk download features +- Mobile-responsive design +- Theme customization options + +[Unreleased]: https://github.com/the-luap/picpeak/compare/v1.0.22...HEAD +[1.0.22]: https://github.com/the-luap/picpeak/compare/v1.0.0...v1.0.22 +[1.0.0]: https://github.com/the-luap/picpeak/releases/tag/v1.0.0 \ No newline at end of file diff --git a/CI_CD_STRATEGY.md b/CI_CD_STRATEGY.md deleted file mode 100644 index 811af45..0000000 --- a/CI_CD_STRATEGY.md +++ /dev/null @@ -1,118 +0,0 @@ -# CI/CD Strategy for PicPeak - -## Overview - -This document outlines the CI/CD strategy using both Gitea Actions and Drone CI to avoid conflicts and ensure proper versioning. - -## Pipeline Flow - -### 1. Development & Testing (Gitea Actions) -- **Trigger**: Every push to `main` or `develop` branches -- **File**: `.gitea/workflows/test.yml` -- **Purpose**: Run tests, linting, and basic validation -- **Actions**: - - Backend linting and tests - - Frontend linting and build - - Does NOT build Docker images - -### 2. Version Management (Gitea Actions) -- **Trigger**: Push to `main` branch (excluding markdown files) -- **File**: `.gitea/workflows/version-and-release.yml` -- **Purpose**: Automatic version incrementing -- **Actions**: - 1. Reads current version from `package.json` - 2. Increments patch version (e.g., 1.0.0 โ†’ 1.0.1) - 3. Updates both backend and frontend `package.json` - 4. Commits the version change - 5. Creates a git tag (e.g., `v1.0.1`) - 6. Pushes changes and tag - -### 3. Docker Image Building (Drone CI) -- **Trigger**: - - Push to `main` or `develop` (builds with commit SHA) - - New git tags (builds release versions) -- **File**: `.drone.yml` -- **Purpose**: Build and push Docker images -- **Tags Created**: - - `latest` - Always points to newest build - - `{commit-sha}` - Specific commit version - - `{branch}-latest` - Latest for specific branch - - `v1.0.1` - Specific version (on tag trigger) - -## Why This Strategy? - -1. **Separation of Concerns**: - - Gitea Actions handles code quality and versioning - - Drone CI handles Docker image building - - No overlap or race conditions - -2. **Sequential Execution**: - - Version bump happens first - - Tag creation triggers Drone - - Docker images are built with correct version - -3. **Version Consistency**: - - Version in `package.json` matches git tag - - Docker images are tagged with same version - - No manual version management needed - -## Setup Requirements - -1. **Gitea Actions Runner**: Must be configured and running -2. **Drone CI**: Must be connected to your Gitea instance -3. **Secrets**: - - `GITEA_TOKEN` (optional, for pushing version commits) - - Docker registry credentials in Drone - -## Version Numbering - -- Format: `MAJOR.MINOR.PATCH` (e.g., 1.0.0) -- Automatic increments: PATCH version only -- Manual increments: Edit `package.json` for MAJOR/MINOR changes - -## Usage - -1. **Regular Development**: - ```bash - git add . - git commit -m "feat: add new feature" - git push origin main - ``` - - Tests run automatically - - Version bumps to 1.0.1 - - Docker images built with v1.0.1 tag - -2. **Major/Minor Version Change**: - ```bash - # Manually edit package.json files to 2.0.0 - git add . - git commit -m "feat!: major release" - git push origin main - ``` - -3. **Skip Version Bump**: - - Add `[skip ci]` to commit message - - Or only change markdown files - -## Monitoring - -- **Gitea Actions**: Check Actions tab in Gitea -- **Drone CI**: Check Drone dashboard -- **Docker Registry**: Verify images are pushed with correct tags - -## Troubleshooting - -1. **Version not incrementing**: - - Check Gitea Actions logs - - Ensure runner has push permissions - - Verify no `[skip ci]` in commit message - -2. **Docker images not building**: - - Check Drone CI webhook configuration - - Verify Drone can see the repository - - Check Docker registry credentials - -3. **Conflicts**: - - Never run both pipelines for same task - - Use branch protection to prevent direct pushes - - Always let automation handle versioning \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5aa561c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,27 @@ +# PicPeak Community Guidelines + +## Our Commitment + +We are committed to providing a welcoming and inspiring community for all photographers and developers. + +## Expected Behavior + +* Be respectful and considerate +* Welcome newcomers and help them get started +* Focus on what is best for the community +* Show empathy towards other community members + +## Unacceptable Behavior + +* Trolling or insulting comments +* Personal attacks +* Public or private harassment +* Publishing others' private information + +## Enforcement + +Instances of unacceptable behavior may be reported to the project team at conduct@example.com. All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from contributor-covenant.org, version 2.0. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5d3e01e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,160 @@ +# Contributing to PicPeak + +First off, thank you for considering contributing to PicPeak! It's people like you that make PicPeak such a great tool for photographers worldwide. + +## ๐Ÿค Code of Conduct + +This project and everyone participating in it is governed by the [PicPeak Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + +## ๐ŸŽฏ How Can I Contribute? + +### Reporting Bugs + +Before creating bug reports, please check the existing issues as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible: + +* **Use a clear and descriptive title** +* **Describe the exact steps to reproduce the problem** +* **Provide specific examples to demonstrate the steps** +* **Describe the behavior you observed and what you expected** +* **Include screenshots if possible** +* **Include your environment details** (OS, browser, Docker version, etc.) + +### Suggesting Enhancements + +Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include: + +* **Use a clear and descriptive title** +* **Provide a detailed description of the suggested enhancement** +* **Provide specific examples to demonstrate the enhancement** +* **Describe the current behavior and expected behavior** +* **Explain why this enhancement would be useful** + +### Your First Code Contribution + +Unsure where to begin? You can start by looking through these issues: + +* [Good first issues](https://github.com/the-luap/picpeak/labels/good%20first%20issue) - issues which should only require a few lines of code +* [Help wanted issues](https://github.com/the-luap/picpeak/labels/help%20wanted) - issues which need extra attention + +### Pull Requests + +1. **Fork the repo** and create your branch from `main` +2. **Install dependencies**: + ```bash + cd backend && npm install + cd ../frontend && npm install + ``` +3. **Make your changes** and ensure: + - Code follows the existing style + - Tests pass: `npm test` + - Linting passes: `npm run lint` +4. **Write tests** if you've added code +5. **Update documentation** if needed +6. **Create a Pull Request** + +## ๐Ÿ’ป Development Setup + +### Prerequisites + +- Node.js 18+ +- Docker & Docker Compose +- Git + +### Local Development + +```bash +# Clone your fork +git clone https://github.com/your-username/picpeak.git +cd picpeak + +# Install dependencies +cd backend && npm install +cd ../frontend && npm install + +# Set up environment +cp .env.example .env +# Edit .env with your settings + +# Start development servers +docker-compose -f docker-compose.dev.yml up +``` + +### Running Tests + +```bash +# Backend tests +cd backend && npm test + +# Frontend tests +cd frontend && npm test + +# E2E tests +npm run test:e2e +``` + +## ๐Ÿ“ Styleguides + +### Git Commit Messages + +* Use the present tense ("Add feature" not "Added feature") +* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") +* Limit the first line to 72 characters or less +* Reference issues and pull requests liberally after the first line +* Consider starting the commit message with an applicable emoji: + * ๐ŸŽจ `:art:` when improving the format/structure of the code + * ๐Ÿ› `:bug:` when fixing a bug + * ๐Ÿ”ฅ `:fire:` when removing code or files + * ๐Ÿ“ `:memo:` when writing docs + * ๐Ÿš€ `:rocket:` when improving performance + * โœจ `:sparkles:` when adding a new feature + +### JavaScript/TypeScript Styleguide + +* Use ES6+ features +* Prefer async/await over promises +* Use meaningful variable names +* Add JSDoc comments for functions +* Follow ESLint rules + +### React Styleguide + +* Use functional components with hooks +* Keep components small and focused +* Use TypeScript for type safety +* Follow the existing folder structure +* Write tests for new components + +## ๐Ÿ“ฆ Project Structure + +``` +picpeak/ +โ”œโ”€โ”€ backend/ +โ”‚ โ”œโ”€โ”€ src/ +โ”‚ โ”‚ โ”œโ”€โ”€ routes/ # API endpoints +โ”‚ โ”‚ โ”œโ”€โ”€ services/ # Business logic +โ”‚ โ”‚ โ”œโ”€โ”€ middleware/ # Express middleware +โ”‚ โ”‚ โ””โ”€โ”€ utils/ # Utilities +โ”‚ โ””โ”€โ”€ migrations/ # Database migrations +โ”œโ”€โ”€ frontend/ +โ”‚ โ”œโ”€โ”€ src/ +โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Reusable components +โ”‚ โ”‚ โ”œโ”€โ”€ pages/ # Page components +โ”‚ โ”‚ โ”œโ”€โ”€ services/ # API services +โ”‚ โ”‚ โ””โ”€โ”€ hooks/ # Custom hooks +โ”‚ โ””โ”€โ”€ public/ # Static assets +``` + +## ๐Ÿ”„ Release Process + +1. Update version numbers in package.json files +2. Update CHANGELOG.md +3. Create a new release on GitHub +4. Docker images are automatically built and published + +## ๐Ÿ“ฎ Contact + +- Create an issue for bugs or features +- Join discussions for questions +- Email: picpeak@example.com for security issues + +Thank you for contributing! ๐ŸŽ‰ \ No newline at end of file diff --git a/DEPLOYMENT-TRAEFIK.md b/DEPLOYMENT-TRAEFIK.md deleted file mode 100644 index 0ee5c6b..0000000 --- a/DEPLOYMENT-TRAEFIK.md +++ /dev/null @@ -1,92 +0,0 @@ -# Deployment Guide - Traefik Production Setup - -## Overview - -This guide explains how to deploy PicPeak with an external Traefik reverse proxy for production use. - -## Fixed Issues - -1. **Database Migration**: Added missing `created_at` column to `email_queue` table -2. **502 Bad Gateway**: Properly configured Traefik routing and backend accessibility -3. **Health Checks**: Fixed health check endpoint imports and paths - -## Deployment Steps - -### 1. Update Environment Variables - -Ensure your `.env` file has the correct URLs: -```bash -ADMIN_URL=https://picpeak.nothaft.cloud -FRONTEND_URL=https://picpeak.nothaft.cloud -``` - -### 2. Build Images - -```bash -# Build backend image -docker build -t picpeak-backend:latest ./backend - -# Build frontend image -docker build -t picpeak-frontend:latest ./frontend \ - --build-arg VITE_API_URL=/api \ - --build-arg VITE_UMAMI_URL=${VITE_UMAMI_URL} \ - --build-arg VITE_UMAMI_WEBSITE_ID=${VITE_UMAMI_WEBSITE_ID} -``` - -### 3. Deploy with Traefik - -Use the new Traefik-specific compose file: -```bash -docker-compose -f docker-compose.traefik.yml up -d -``` - -### 4. Verify Deployment - -Check that all services are healthy: -```bash -# Check container status -docker-compose -f docker-compose.traefik.yml ps - -# Check backend health -curl https://picpeak.nothaft.cloud/api/health - -# Check logs -docker-compose -f docker-compose.traefik.yml logs -f backend -``` - -## Key Differences from Standard Deployment - -1. **No Internal Nginx**: Traefik handles all routing externally -2. **API Path Stripping**: Traefik strips `/api` prefix when forwarding to backend -3. **Network Configuration**: Services join external `traefik` network -4. **Health Checks**: Backend exposes `/health` endpoint (not `/api/health`) - -## Why CI/CD Tests Pass But Production Fails - -CI/CD tests typically: -- Use in-memory or temporary databases with fresh migrations -- Don't test through reverse proxy (direct API calls) -- Don't run background services (email processor, etc.) -- Have different network configurations - -Production environment has: -- Persistent database that may have migration state issues -- Reverse proxy routing complexity -- All background services running -- Different security and network constraints - -## Troubleshooting - -### 502 Bad Gateway -- Check Traefik network connectivity: `docker network ls` -- Verify backend is in traefik network: `docker inspect picpeak-backend` -- Check Traefik logs: `docker logs traefik` - -### Database Issues -- Connect to database: `docker exec -it picpeak-db psql -U picpeak` -- Check migration status: `SELECT * FROM migrations;` -- Run migrations manually: `docker exec -it picpeak-backend npm run migrate:safe` - -### Email Service Errors -- Check email queue: `SELECT * FROM email_queue ORDER BY created_at DESC LIMIT 10;` -- Monitor email processor: `docker logs picpeak-backend | grep "email"` \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 02c907a..61ace45 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,346 +1,220 @@ -# PicPeak Deployment Guide +# ๐Ÿš€ PicPeak Deployment Guide -This guide covers deploying PicPeak for development and production environments. +This guide will help you deploy PicPeak in production. The entire process takes about 10-15 minutes. -## Table of Contents -- [Quick Start (Development)](#quick-start-development) -- [Production Deployment](#production-deployment) -- [Admin User Setup](#admin-user-setup) -- [Configuration Reference](#configuration-reference) -- [Troubleshooting](#troubleshooting) +## ๐Ÿ“‹ Prerequisites -## Quick Start (Development) +- A server with Docker and Docker Compose installed +- A domain name (for SSL certificates) +- SMTP credentials for sending emails +- Basic command line knowledge -### 1. Clone and Setup +## ๐Ÿƒ Quick Deploy (Recommended) + +### 1. Clone and Configure ```bash -git clone https://github.com/yourusername/picpeak.git +# Clone the repository +git clone https://github.com/the-luap/picpeak.git cd picpeak # Copy environment template -cp .env.example .env - -# Start development environment -docker-compose -f docker-compose.dev.yml up -d -``` - -### 2. Access Services - -- Frontend: http://localhost:3005 -- Backend API: http://localhost:3001 -- MailHog (email testing): http://localhost:8025 - -### 3. Create Admin User - -```bash -docker-compose -f docker-compose.dev.yml exec backend node scripts/create-admin.js \ - --email admin@localhost \ - --username admin \ - --password admin123 -``` - -## Production Deployment - -### Prerequisites - -- Docker and Docker Compose installed -- Domain with DNS configured -- SSL/TLS handled by reverse proxy (Traefik, Nginx, etc.) - -### 1. Environment Setup - -```bash -# Copy production template cp .env.production.example .env -# Generate secure secrets +# Generate a secure JWT secret echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env -echo "DB_PASSWORD=$(openssl rand -base64 24)" >> .env + +# Edit configuration +nano .env ``` -Edit `.env` with your configuration: +### 2. Required Environment Variables + +Edit your `.env` file with these essential settings: ```env -# Your domain -ADMIN_URL=https://yourdomain.com -FRONTEND_URL=https://yourdomain.com +# Application URLs +FRONTEND_URL=https://your-domain.com +BACKEND_URL=https://your-domain.com -# Database (PostgreSQL) -DB_USER=picpeak -DB_NAME=picpeak -# DB_PASSWORD already generated above - -# Email +# Email Configuration (Required for notifications) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 -SMTP_SECURE=true SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password -EMAIL_FROM=noreply@yourdomain.com -``` +SMTP_FROM=your-email@gmail.com -### 2. Frontend Configuration +# Admin Configuration +ADMIN_EMAIL=admin@your-domain.com +ADMIN_PASSWORD=your-secure-password -```bash -# Configure frontend for production -echo "VITE_API_URL=/api" > frontend/.env.production +# Database (PostgreSQL for production) +DATABASE_CLIENT=pg +DB_HOST=postgres +DB_NAME=picpeak +DB_USER=picpeak +DB_PASSWORD=secure-db-password ``` ### 3. Deploy with Docker Compose ```bash -# Build and start services +# Start all services docker-compose -f docker-compose.prod.yml up -d -# Check status -docker-compose -f docker-compose.prod.yml ps +# Check logs +docker-compose logs -f -# View logs -docker-compose -f docker-compose.prod.yml logs -f +# Access your site at https://your-domain.com ``` -### 4. Deploy with Traefik +## ๐Ÿ”ง Configuration Options -If using Traefik, create `docker-compose.override.yml`: +### Storage Settings -```yaml -version: '3.8' +```env +# Storage paths (default: ./storage) +STORAGE_PATH=./storage +ARCHIVE_PATH=./storage/archives -services: - frontend: - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak.rule=Host(`yourdomain.com`)" - - "traefik.http.routers.picpeak.entrypoints=websecure" - - "traefik.http.routers.picpeak.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak.loadbalancer.server.port=80" - networks: - - traefik - - picpeak - -networks: - traefik: - external: true +# Gallery expiration (days) +DEFAULT_EXPIRATION_DAYS=30 +WARNING_DAYS_BEFORE_EXPIRY=7 ``` -## Admin User Setup +### Security Settings -### Create First Admin +```env +# Session timeout (minutes) +SESSION_TIMEOUT=60 -After deployment, create your admin user: +# Rate limiting +RATE_LIMIT_WINDOW_MS=900000 # 15 minutes +RATE_LIMIT_MAX_REQUESTS=100 +``` + +### Analytics (Optional) + +```env +# Umami Analytics +VITE_UMAMI_URL=https://analytics.your-domain.com +VITE_UMAMI_WEBSITE_ID=your-website-id +``` + +## ๐Ÿ”’ SSL/TLS Setup + +The production Docker Compose includes automatic SSL via Let's Encrypt: + +1. **Ensure your domain points to your server** +2. **Update nginx configuration**: + ```bash + nano nginx/nginx.conf + # Replace your-domain.com with your actual domain + ``` +3. **Start services** - Certbot will automatically obtain certificates + +## ๐Ÿ“ Directory Structure + +After deployment, your directory structure will be: + +``` +picpeak/ +โ”œโ”€โ”€ backend/ # API server +โ”œโ”€โ”€ frontend/ # React app +โ”œโ”€โ”€ storage/ # Photo storage +โ”‚ โ”œโ”€โ”€ events/ # Active galleries +โ”‚ โ”‚ โ”œโ”€โ”€ active/ # Current photos +โ”‚ โ”‚ โ””โ”€โ”€ archived/ # Expired galleries +โ”‚ โ”œโ”€โ”€ thumbnails/ # Generated thumbnails +โ”‚ โ””โ”€โ”€ uploads/ # User uploads +โ”œโ”€โ”€ data/ # Database files +โ””โ”€โ”€ logs/ # Application logs +``` + +## ๐Ÿ”„ Maintenance + +### Backup ```bash -# Production -docker-compose -f docker-compose.prod.yml exec backend node scripts/create-admin.js \ - --email admin@yourdomain.com \ - --username admin \ - --password yourSecurePassword +# Backup database and photos +./scripts/backup.sh -# Auto-generate password -docker-compose -f docker-compose.prod.yml exec backend node scripts/create-admin.js \ - --email admin@yourdomain.com +# Backups are stored in ./backups/ ``` -The script will display: -- โœ… Admin user created successfully! -- Email: admin@yourdomain.com -- Username: admin -- Login URL: https://yourdomain.com/admin/login -- Password: (save this if auto-generated!) - -### Managing Admin Users - -```bash -# List admin users -docker-compose -f docker-compose.prod.yml exec backend \ - psql postgresql://picpeak:$DB_PASSWORD@db:5432/picpeak \ - -c "SELECT id, username, email, is_active, last_login FROM admin_users;" - -# Deactivate user -docker-compose -f docker-compose.prod.yml exec backend \ - psql postgresql://picpeak:$DB_PASSWORD@db:5432/picpeak \ - -c "UPDATE admin_users SET is_active = false WHERE email = 'user@example.com';" -``` - -## Configuration Reference - -### Database Configuration - -PicPeak automatically detects the environment and uses: -- **Development**: SQLite (`./data/photo_sharing.db`) -- **Production**: PostgreSQL (configured via environment variables) - -### Environment Variables - -#### Required for Production - -| Variable | Description | Example | -|----------|-------------|---------| -| `JWT_SECRET` | JWT signing key | `openssl rand -base64 32` | -| `DB_PASSWORD` | PostgreSQL password | `openssl rand -base64 24` | -| `ADMIN_URL` | Admin panel URL | `https://yourdomain.com` | -| `FRONTEND_URL` | Frontend URL | `https://yourdomain.com` | -| `EMAIL_FROM` | Sender email | `noreply@yourdomain.com` | - -#### Email Configuration - -| Variable | Description | Example | -|----------|-------------|---------| -| `SMTP_HOST` | SMTP server | `smtp.gmail.com` | -| `SMTP_PORT` | SMTP port | `587` | -| `SMTP_SECURE` | Use TLS | `true` | -| `SMTP_USER` | SMTP username | `your-email@gmail.com` | -| `SMTP_PASS` | SMTP password | App-specific password | - -### Storage Paths - -- Photos: `./storage/events/active/` -- Archives: `./storage/events/archived/` -- Thumbnails: `./storage/thumbnails/` -- Uploads: `./storage/uploads/` - -## Backup and Restore - -### Backup Database - -```bash -# PostgreSQL backup -docker-compose -f docker-compose.prod.yml exec db \ - pg_dump -U picpeak picpeak > backup-$(date +%Y%m%d).sql - -# Backup storage -tar -czf storage-backup-$(date +%Y%m%d).tar.gz ./storage -``` - -### Restore Database - -```bash -# PostgreSQL restore -docker-compose -f docker-compose.prod.yml exec -T db \ - psql -U picpeak picpeak < backup-20240115.sql - -# Restore storage -tar -xzf storage-backup-20240115.tar.gz -``` - -## Monitoring - -### Health Checks - -```bash -# Backend health -curl https://yourdomain.com/api/health - -# Frontend health -curl https://yourdomain.com/health -``` - -### Logs - -```bash -# All services -docker-compose -f docker-compose.prod.yml logs -f - -# Specific service -docker-compose -f docker-compose.prod.yml logs -f backend - -# Last 100 lines -docker-compose -f docker-compose.prod.yml logs --tail=100 backend -``` - -## Troubleshooting - -### Backend Won't Start - -1. Check database connection: - ```bash - docker-compose -f docker-compose.prod.yml logs db - ``` - -2. Verify environment variables: - ```bash - docker-compose -f docker-compose.prod.yml exec backend env | grep DB_ - ``` - -### Can't Login as Admin - -1. Verify admin user exists: - ```bash - docker-compose -f docker-compose.prod.yml exec backend \ - psql postgresql://picpeak:$DB_PASSWORD@db:5432/picpeak \ - -c "SELECT * FROM admin_users;" - ``` - -2. Reset admin password: - ```bash - # Create new admin with different email - docker-compose -f docker-compose.prod.yml exec backend \ - node scripts/create-admin.js --email newadmin@yourdomain.com - ``` - -### Photos Not Loading - -1. Check file permissions: - ```bash - ls -la ./storage/events/active/ - ``` - -2. Verify nginx proxy configuration: - ```bash - docker-compose -f docker-compose.prod.yml exec frontend \ - cat /etc/nginx/conf.d/default.conf - ``` - -### Email Not Sending - -1. Check email configuration: - ```bash - docker-compose -f docker-compose.prod.yml exec backend env | grep SMTP_ - ``` - -2. View email queue: - ```bash - docker-compose -f docker-compose.prod.yml exec backend \ - psql postgresql://picpeak:$DB_PASSWORD@db:5432/picpeak \ - -c "SELECT * FROM email_queue WHERE status = 'failed';" - ``` - -## Maintenance - -### Update Application +### Update ```bash # Pull latest changes git pull -# Rebuild images -docker-compose -f docker-compose.prod.yml build - -# Restart services -docker-compose -f docker-compose.prod.yml up -d +# Rebuild and restart +docker-compose -f docker-compose.prod.yml up -d --build ``` -### Clean Up +### Logs ```bash -# Remove unused images -docker image prune -a +# View all logs +docker-compose logs -# Clean up logs -docker-compose -f docker-compose.prod.yml logs --tail=0 -f - -# Remove old archives -find ./storage/events/archived -name "*.zip" -mtime +90 -delete +# View specific service +docker-compose logs backend +docker-compose logs frontend ``` -## Security Checklist +## ๐Ÿšจ Troubleshooting -- [ ] Generated secure `JWT_SECRET` -- [ ] Generated secure `DB_PASSWORD` -- [ ] HTTPS enabled via reverse proxy -- [ ] Changed default admin credentials -- [ ] Configured real SMTP server -- [ ] Set file permissions: `chmod 600 .env` -- [ ] Firewall configured -- [ ] Regular backups scheduled -- [ ] Monitoring enabled \ No newline at end of file +### Common Issues + +**Photos not appearing:** +- Check storage permissions: `chmod -R 755 storage/` +- Verify file watcher is running: `docker-compose logs backend | grep watcher` + +**Email not sending:** +- Test SMTP settings: Admin Panel โ†’ Settings โ†’ Email โ†’ Send Test +- Check email queue: Admin Panel โ†’ System โ†’ Email Queue + +**Can't access admin panel:** +- Default login: Use email/password from `.env` +- Reset password: `docker exec picpeak-backend npm run reset-admin` + +### Health Check + +```bash +# Check service status +docker-compose ps + +# Test backend API +curl https://your-domain.com/api/health + +# Check disk space +df -h storage/ +``` + +## ๐Ÿณ Alternative Deployment Methods + +### Using Docker Swarm + +For high availability deployments, see [Docker Swarm Setup](deploy/README.md). + +### Manual Installation + +If you prefer not to use Docker: + +1. Install Node.js 18+ +2. Install PostgreSQL +3. Clone repository +4. Install dependencies: `npm install` in both `/backend` and `/frontend` +5. Build frontend: `cd frontend && npm run build` +6. Start services with PM2 + +## ๐Ÿ“ž Support + +- ๐Ÿ“˜ [Documentation](https://github.com/the-luap/picpeak) +- ๐Ÿ› [Report Issues](https://github.com/the-luap/picpeak/issues) +- ๐Ÿ’ฌ [Discussions](https://github.com/the-luap/picpeak/discussions) + +--- + +**Need help?** Open an issue on GitHub and we'll assist you! \ No newline at end of file diff --git a/MIGRATION_ERROR_FIX.md b/MIGRATION_ERROR_FIX.md deleted file mode 100644 index c02d6f6..0000000 --- a/MIGRATION_ERROR_FIX.md +++ /dev/null @@ -1,111 +0,0 @@ -# Quick Fix for Migration Error - -## Immediate Fix - -The error "relation photo_categories already exists" occurs because the database already has tables but the migration tracking doesn't know they were applied. - -### Option 1: Use Safe Migration Runner (Recommended) - -Update your `docker-compose.prod.yml` to use the safe migration command: - -```yaml -backend: - environment: - - NODE_ENV=production - # ... other config ... -``` - -Then update the `wait-for-db.sh` (already done) to use `npm run migrate:safe` in production. - -### Option 2: Quick Manual Fix - -If you need to fix the running system immediately: - -```bash -# 1. Enter the backend container -docker-compose -f docker-compose.prod.yml exec backend sh - -# 2. Run the safe migration script -npm run migrate:safe - -# 3. If that fails, manually mark migrations as applied: -docker-compose -f docker-compose.prod.yml exec db psql -U picpeak -d picpeak - -# In PostgreSQL: -CREATE TABLE IF NOT EXISTS migrations ( - id SERIAL PRIMARY KEY, - filename VARCHAR(255) UNIQUE NOT NULL, - applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Mark existing migrations as applied -INSERT INTO migrations (filename) VALUES - ('init.js'), - ('004_add_categories_and_cms.js'), - ('006_add_photo_counter_to_categories.js'), - ('007_add_read_at_to_activity_logs.js'), - ('008_add_language_support_to_email_templates.js'), - ('009_update_german_email_templates.js'), - ('010_add_missing_email_templates.js'), - ('011_add_user_upload_settings.js'), - ('012_add_hero_photo_id.js'), - ('013_fix_email_links_and_date_format.js'), - ('014_add_default_welcome_message.js'), - ('014_add_host_name_to_events.js'), - ('015_add_login_attempts_table.js'), - ('016_add_auth_security_columns.js'), - ('017_add_token_revocation_tables.js') -ON CONFLICT (filename) DO NOTHING; - -\q -``` - -### Option 3: Fresh Start (Nuclear Option) - -If you don't have important data yet: - -```bash -# Stop everything -docker-compose -f docker-compose.prod.yml down - -# Remove database volume -docker volume rm wedding-photo-sharing_postgres_data - -# Start fresh -docker-compose -f docker-compose.prod.yml up -d -``` - -## Root Cause - -The issue happens when: -1. Database volume persists between deployments -2. Migration tracking table gets out of sync -3. The original migration runner doesn't check for existing tables - -## Permanent Solution - -The new safe migration runner (`migrate:safe`) handles this by: -1. Checking if tables exist before creating them -2. Catching "already exists" errors gracefully -3. Auto-detecting existing schema and marking migrations as applied - -## Next Steps - -After fixing the migration issue: - -1. Create admin user: -```bash -docker-compose -f docker-compose.prod.yml exec backend node scripts/create-admin.js \ - --username admin \ - --email admin@yourdomain.com -``` - -2. Check health: -```bash -curl http://yourdomain.com/api/health -``` - -3. Monitor logs: -```bash -docker-compose -f docker-compose.prod.yml logs -f backend -``` \ No newline at end of file diff --git a/PRODUCTION_DEPLOYMENT_FIXES.md b/PRODUCTION_DEPLOYMENT_FIXES.md deleted file mode 100644 index c60172a..0000000 --- a/PRODUCTION_DEPLOYMENT_FIXES.md +++ /dev/null @@ -1,100 +0,0 @@ -# Production Deployment Fixes - -This document describes the fixes applied to resolve production deployment issues in Docker. - -## Issues Fixed - -### 1. Database Connection Error: "getaddrinfo ENOTFOUND postgres" -**Problem**: The backend was trying to connect to hostname "postgres" but the database service is named "db" in docker-compose. -**Solution**: -- Updated `knexfile.js` to use correct default host "db" instead of "postgres" -- Added `depends_on: db` to backend service in docker-compose.prod.yml - -### 2. Backend Starting Before Database Ready -**Problem**: Backend service started before PostgreSQL was ready, causing connection failures. -**Solution**: -- Created `wait-for-db.sh` script that waits for PostgreSQL to be ready -- Updated Dockerfile to install postgresql-client and use the wait script -- Script also runs migrations automatically on startup - -### 3. Email Processor Initialization Failure -**Problem**: Email processor tried to initialize on module load before database was available. -**Solution**: -- Modified `emailProcessor.js` to export initialization functions -- Updated `server.js` to call initialization after database is ready -- Added proper error handling for email service initialization - -### 4. Missing Environment Variables -**Problem**: Critical storage path environment variables were missing. -**Solution**: -- Added STORAGE_PATH, EVENTS_PATH, and ARCHIVE_PATH to docker-compose.prod.yml -- Created `.env.example` documenting all required environment variables - -### 5. Enhanced Health Check -**Problem**: Basic health check didn't verify database connectivity. -**Solution**: -- Updated `/api/health` endpoint to check database connection -- Returns proper HTTP 503 status when unhealthy - -## Files Modified - -1. **backend/knexfile.js** - Fixed production database defaults -2. **backend/wait-for-db.sh** - Created database wait script -3. **backend/Dockerfile** - Added postgresql-client and wait script -4. **docker-compose.prod.yml** - Added dependencies and environment variables -5. **backend/src/services/emailProcessor.js** - Disabled auto-initialization -6. **backend/server.js** - Added email initialization and improved health check -7. **backend/.env.example** - Created environment variable documentation - -## Deployment Steps - -1. Ensure all environment variables are set according to `.env.example` -2. Build and deploy with docker-compose: - ```bash - docker-compose -f docker-compose.prod.yml build - docker-compose -f docker-compose.prod.yml up -d - ``` -3. The backend will now: - - Wait for PostgreSQL to be ready - - Run migrations automatically - - Initialize all services in proper order - - Provide health status at `/api/health` - -## Verification - -Check deployment health: -```bash -curl http://localhost/api/health -``` - -Expected response: -```json -{ - "status": "ok", - "database": "connected", - "timestamp": "2025-07-13T20:30:00.000Z" -} -``` - -## Email Configuration - -Email service requires configuration in the database. If email is not configured: -- The service will log a warning but continue running -- Emails will be queued but not sent -- Configure email settings in the admin panel after deployment - -## PostgreSQL Connection Fix - -### Issue: "no pg_hba.conf entry for host" -This error occurs when PostgreSQL requires SSL but the client connects without encryption. - -### Solution: -- Disabled SSL requirement for PostgreSQL in Docker environment (`ssl=off`) -- Added proper authentication method (`scram-sha-256`) -- This is acceptable for internal Docker networks where all traffic is isolated - -### Security Note: -For production deployments exposed to the internet: -1. Use SSL certificates for PostgreSQL -2. Or ensure the database is only accessible within the Docker network -3. Never expose PostgreSQL port (5432) directly to the internet \ No newline at end of file diff --git a/PRODUCTION_DEPLOYMENT_GUIDE.md b/PRODUCTION_DEPLOYMENT_GUIDE.md index e60ffd7..7bf26c4 100644 --- a/PRODUCTION_DEPLOYMENT_GUIDE.md +++ b/PRODUCTION_DEPLOYMENT_GUIDE.md @@ -51,7 +51,7 @@ openssl rand -hex 32 ```bash # Clone repository -git clone https://github.com/yourusername/wedding-photo-sharing.git +git clone https://github.com/the-luap/wedding-photo-sharing.git cd wedding-photo-sharing # Create required directories diff --git a/README.md b/README.md index 1cba90e..22b8c0c 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,164 @@ -# Photo Sharing Platform +# ๐Ÿ“ธ PicPeak - Open Source Photo Sharing for Events -A secure, self-hosted photo sharing platform designed for weddings and events. Features automatic expiration, email notifications, and simple file-based management. +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=flat&logo=docker&logoColor=white)](https://www.docker.com/) +[![Node.js](https://img.shields.io/badge/node.js-6DA55F?style=flat&logo=node.js&logoColor=white)](https://nodejs.org/) +[![React](https://img.shields.io/badge/react-%2320232a.svg?style=flat&logo=react&logoColor=%2361DAFB)](https://reactjs.org/) -## Features +**PicPeak** is a powerful, self-hosted open-source alternative to commercial photo-sharing platforms like PicDrop.com and Scrapbook.de. Designed specifically for photographers and event organizers, PicPeak makes it simple to share beautiful, time-limited photo galleries with clients while maintaining full control over your data and branding. -- ๐Ÿ”’ Password Protected Galleries -- โฐ Automatic Expiration -- ๐Ÿ“ง Email Notifications -- ๐Ÿ“ Simple File Management -- ๐Ÿ“Š Analytics Integration -- ๐ŸŽจ Customizable Themes -- ๐Ÿ“ฑ Mobile Responsive -- โšก Docker Ready +![PicPeak Gallery Preview](https://github.com/the-luap/picpeak/assets/placeholder-hero.png) -## Quick Start +## ๐ŸŒŸ Why Choose PicPeak? -1. Clone the repository -2. Run `./scripts/install.sh` -3. Configure `.env` file -4. Setup SSL: `./scripts/setup-ssl.sh` -5. Start: `docker-compose -f docker-compose.prod.yml up -d` +Unlike expensive SaaS solutions, PicPeak gives you: -Default credentials: Check ADMIN_CREDENTIALS.txt after first setup +- **๐Ÿ’ฐ No Monthly Fees** - One-time setup, unlimited galleries +- **๐Ÿ”’ Complete Data Control** - Your photos stay on your server +- **๐ŸŽจ White-Label Ready** - Full branding customization +- **๐Ÿ“ฑ Mobile-First Design** - Beautiful on all devices +- **๐Ÿš€ Lightning Fast** - Optimized performance and caching +- **๐ŸŒ Multi-Language** - Built-in i18n support (EN, DE) -## Documentation +## โœจ Key Features -See DEPLOYMENT.md for detailed deployment instructions. +### For Photographers +- ๐Ÿ“ **Drag & Drop Upload** - Simply drop photos into folders +- โฐ **Auto-Expiring Galleries** - Set expiration dates (default: 30 days) +- ๐Ÿ” **Password Protection** - Secure client galleries +- ๐Ÿ“ง **Automated Emails** - Creation confirmations and expiration warnings +- ๐Ÿ“Š **Analytics Dashboard** - Track views, downloads, and engagement +- ๐ŸŽจ **Custom Themes** - Match your brand perfectly -## License +### For Clients +- ๐Ÿ–ผ๏ธ **Beautiful Galleries** - Clean, modern interface +- ๐Ÿ“ฑ **Mobile Optimized** - Swipe through photos on any device +- โฌ‡๏ธ **Bulk Downloads** - Download all photos with one click +- ๐Ÿ” **Smart Search** - Find photos quickly +- ๐Ÿ“ค **Guest Uploads** - Optional client photo uploads -MIT License +### Technical Excellence +- ๐Ÿณ **Docker Ready** - Deploy in minutes +- ๐Ÿ”„ **Auto-Processing** - Automatic thumbnail generation +- ๐Ÿ’พ **Smart Storage** - Automatic archiving of expired galleries +- ๐Ÿ›ก๏ธ **Security First** - JWT auth, rate limiting, CORS protection +- ๐Ÿ“ˆ **Scalable** - From small studios to large agencies + +## ๐Ÿš€ Quick Start + +Get PicPeak running in under 5 minutes: + +```bash +# Clone the repository +git clone https://github.com/the-luap/picpeak.git +cd picpeak + +# Copy environment template +cp .env.example .env + +# Edit configuration (required: JWT_SECRET) +nano .env + +# Start with Docker Compose +docker-compose up -d + +# Access at http://localhost:3005 +``` + +## ๐Ÿ“– Documentation + +- ๐Ÿ“˜ [**Deployment Guide**](DEPLOYMENT.md) - Detailed installation instructions +- ๐Ÿค [**Contributing**](CONTRIBUTING.md) - How to contribute +- ๐Ÿ“œ [**License**](LICENSE) - MIT License +- ๐Ÿ”’ [**Security**](SECURITY.md) - Security policies +- ๐Ÿ“‹ [**Code of Conduct**](CODE_OF_CONDUCT.md) - Community guidelines + +## ๐ŸŽฏ Use Cases + +Perfect for: +- ๐Ÿ’’ **Wedding Photographers** - Share ceremony photos securely +- ๐ŸŽ‚ **Event Photography** - Birthday parties, corporate events +- ๐Ÿ“ธ **Portrait Studios** - Client galleries with download limits +- ๐Ÿข **Corporate Events** - Internal photo sharing with branding +- ๐ŸŽ“ **School Photography** - Secure parent access with expiration + +## ๐Ÿ—๏ธ Tech Stack + +- **Backend**: Node.js, Express, SQLite/PostgreSQL +- **Frontend**: React, Tailwind CSS, Framer Motion +- **Storage**: File-based with automatic archiving +- **Email**: SMTP with customizable templates +- **Analytics**: Privacy-focused with Umami integration + +## ๐Ÿค Contributing + +We love contributions! PicPeak is built by photographers, for photographers. Whether you're fixing bugs, adding features, or improving documentation, your help is welcome. + +See our [Contributing Guide](CONTRIBUTING.md) for details. + +## ๐Ÿ“Š Comparison with Alternatives + +| Feature | PicPeak | PicDrop | Scrapbook.de | +|---------|---------|---------|--------------| +| Self-Hosted | โœ… | โŒ | โŒ | +| Custom Branding | โœ… Full | Limited | Limited | +| Monthly Cost | $0 | $29-199 | โ‚ฌ19-99 | +| Storage Limit | Unlimited* | 50-500GB | 100-1000GB | +| Client Uploads | โœ… | โœ… | โœ… | +| API Access | โœ… | Paid | โŒ | +| Open Source | โœ… | โŒ | โŒ | + +*Limited only by your server storage + +## ๐Ÿ›ก๏ธ Security + +PicPeak takes security seriously: +- ๐Ÿ” Password hashing with bcrypt +- ๐ŸŽซ JWT-based authentication +- ๐Ÿšฆ Rate limiting on all endpoints +- ๐Ÿ›ก๏ธ CORS protection +- ๐Ÿ“ Activity logging +- ๐Ÿ”’ Secure file access + +Found a security issue? Please email security@example.com + +## ๐Ÿ“ธ Screenshots + +
+View Gallery Examples + +### Admin Dashboard +![Admin Dashboard](https://github.com/the-luap/picpeak/assets/placeholder-admin.png) + +### Client Gallery View +![Gallery View](https://github.com/the-luap/picpeak/assets/placeholder-gallery.png) + +### Mobile Experience +![Mobile View](https://github.com/the-luap/picpeak/assets/placeholder-mobile.png) + +
+ +## ๐Ÿ™ Acknowledgments + +PicPeak is inspired by the best features of commercial platforms while remaining completely open source. Special thanks to all contributors who make this project possible. + +## ๐Ÿ“„ License + +PicPeak is released under the [MIT License](LICENSE). Use it freely for personal or commercial projects. + +## ๐Ÿš€ Ready to Get Started? + +1. โญ **Star this repository** to show your support +2. ๐Ÿ“– Read the [Deployment Guide](DEPLOYMENT.md) +3. ๐Ÿ› Report issues or request features +4. ๐Ÿค Join our community and contribute! + +--- + +

+ Made with โค๏ธ by photographers, for photographers +
+ GitHub โ€ข + Documentation โ€ข + Support +

\ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3108ca5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,85 @@ +# Security Policy + +## Supported Versions + +We release patches for security vulnerabilities. Currently supported versions: + +| Version | Supported | +| ------- | ------------------ | +| 1.x.x | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +We take the security of PicPeak seriously. If you have discovered a security vulnerability, please follow these steps: + +### 1. **Do NOT create a public GitHub issue** + +### 2. Email us at security@example.com with: +- Description of the vulnerability +- Steps to reproduce +- Potential impact +- Suggested fix (if any) + +### 3. You can expect: +- Acknowledgment within 48 hours +- Regular updates on our progress +- Credit in the fix announcement (unless you prefer to remain anonymous) + +## Security Measures + +PicPeak implements several security measures: + +### Authentication & Authorization +- JWT-based authentication with secure token storage +- bcrypt password hashing with configurable rounds +- Role-based access control for admin functions +- Session timeout management + +### Input Validation +- All user inputs are validated and sanitized +- SQL injection prevention through parameterized queries +- XSS protection via Content Security Policy +- File upload restrictions and validation + +### Rate Limiting +- API rate limiting to prevent abuse +- Brute force protection on authentication endpoints +- Configurable limits per endpoint + +### Data Protection +- HTTPS enforcement in production +- Secure cookie settings +- CORS configuration +- Sensitive data encryption + +### Infrastructure +- Regular dependency updates +- Security headers (HSTS, X-Frame-Options, etc.) +- Activity logging for audit trails +- Automated backups + +## Best Practices for Deployment + +1. **Always use HTTPS** in production +2. **Change default passwords** immediately +3. **Keep dependencies updated** regularly +4. **Configure firewall rules** appropriately +5. **Monitor logs** for suspicious activity +6. **Backup regularly** and test restoration + +## Vulnerability Disclosure + +We believe in responsible disclosure. Once a vulnerability is fixed: + +1. We'll publish a security advisory +2. Credit researchers (with permission) +3. Detail the impact and mitigation steps +4. Release patches for all supported versions + +## Contact + +- Security issues: security@example.com +- General support: https://github.com/the-luap/picpeak/issues + +Thank you for helping keep PicPeak and its users safe! \ No newline at end of file diff --git a/SETUP_GUIDE.md b/SETUP_GUIDE.md deleted file mode 100644 index 3bead60..0000000 --- a/SETUP_GUIDE.md +++ /dev/null @@ -1,252 +0,0 @@ -# PicPeak - Complete Setup Guide - -## Repository Created Successfully! ๐ŸŽ‰ - -Your PicPeak repository has been created at: -**https://gitea.nothaft.cloud/paul/picpeak** - -## What's Been Created - -I've uploaded the core files needed to run the application: - -### โœ… Created Files: -- `.gitignore` - Git ignore rules -- `.dockerignore` - Docker ignore rules -- `.env.example` - Environment configuration template -- `docker-compose.yml` - Development Docker setup -- `docker-compose.prod.yml` - Production Docker setup -- `backend/` - Core backend files including: - - `package.json` - Dependencies - - `server.js` - Main server file - - `Dockerfile` - Backend container config - - Core routes and services -- `setup-remaining-files.sh` - Script to create remaining files - -## Next Steps to Complete Setup - -### 1. Clone the Repository -```bash -git clone https://gitea.local.nothaft.cloud/paul/picpeak.git -cd picpeak -``` - -### 2. Run the Setup Script -```bash -chmod +x setup-remaining-files.sh -./setup-remaining-files.sh -``` - -This will create all remaining directories and files needed. - -### 3. Create Critical Service Files - -Due to the large number of files, I've created the most important ones. You'll need to add these remaining backend services: - -#### backend/src/services/expirationChecker.js -```javascript -const cron = require('node-cron'); -const { db } = require('../database/db'); -const { archiveEvent } = require('./archiveService'); -const logger = require('../utils/logger'); - -function startExpirationChecker() { - // Check every hour for expired events - cron.schedule('0 * * * *', async () => { - await checkExpirations(); - }); - - logger.info('Expiration checker started'); -} - -async function checkExpirations() { - try { - const now = new Date(); - const warningDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); - - // Check for events needing warning emails - const eventsNeedingWarning = await db('events') - .where('is_active', true) - .where('is_archived', false) - .where('expires_at', '<=', warningDate) - .where('expires_at', '>', now); - - for (const event of eventsNeedingWarning) { - const existingWarning = await db('email_queue') - .where('event_id', event.id) - .where('email_type', 'warning') - .first(); - - if (!existingWarning) { - await queueExpirationWarning(event); - } - } - - // Check for expired events - const expiredEvents = await db('events') - .where('is_active', true) - .where('is_archived', false) - .where('expires_at', '<=', now); - - for (const event of expiredEvents) { - await handleExpiredEvent(event); - } - - } catch (error) { - logger.error('Error checking expirations:', error); - } -} - -async function queueExpirationWarning(event) { - const daysRemaining = Math.ceil((new Date(event.expires_at) - new Date()) / (1000 * 60 * 60 * 24)); - - await db('email_queue').insert({ - event_id: event.id, - recipient_email: event.host_email, - email_type: 'warning', - email_data: JSON.stringify({ - event_name: event.event_name, - days_remaining: daysRemaining, - share_link: event.share_link - }) - }); - - logger.info(`Queued expiration warning for event ${event.slug}`); -} - -async function handleExpiredEvent(event) { - try { - await db('events').where('id', event.id).update({ is_active: false }); - - await db('email_queue').insert([ - { - event_id: event.id, - recipient_email: event.host_email, - email_type: 'expiration', - email_data: JSON.stringify({ - event_name: event.event_name - }) - }, - { - event_id: event.id, - recipient_email: event.admin_email, - email_type: 'expiration', - email_data: JSON.stringify({ - event_name: event.event_name, - event_slug: event.slug - }) - } - ]); - - await archiveEvent(event); - - logger.info(`Handled expiration for event ${event.slug}`); - } catch (error) { - logger.error(`Error handling expired event ${event.slug}:`, error); - } -} - -module.exports = { startExpirationChecker }; -``` - -### 4. Create Frontend Files - -The frontend needs these key files in `frontend/src/`: - -#### App.js -```javascript -import React from 'react'; -import { Routes, Route, Navigate } from 'react-router-dom'; -import { AuthProvider } from './contexts/AuthContext'; -import ProtectedRoute from './components/ProtectedRoute'; - -// Pages -import Login from './pages/Login'; -import Gallery from './pages/Gallery'; -import AdminLogin from './pages/admin/Login'; -import AdminDashboard from './pages/admin/Dashboard'; - -function App() { - return ( - - - } /> - } /> - } /> - } /> - - - - } /> - - - ); -} - -export default App; -``` - -### 5. Install Dependencies - -```bash -# Backend -cd backend -npm install - -# Frontend -cd ../frontend -npm install -``` - -### 6. Configure Environment - -Copy `.env.example` to `.env` and update with your settings: -```bash -cp .env.example .env -nano .env -``` - -### 7. Start Development Environment - -```bash -# From root directory -docker-compose up -``` - -- Backend: http://localhost:3000 -- Frontend: http://localhost:3001 -- MailHog: http://localhost:8025 - -## Key Features Implemented - -- โœ… Password-protected galleries -- โœ… Automatic expiration with email warnings -- โœ… File-based photo management -- โœ… ZIP archiving on expiration -- โœ… Separate admin and public interfaces -- โœ… Email notifications at all stages -- โœ… Mobile-responsive design -- โœ… Docker deployment ready - -## Production Deployment - -1. Update `.env` with production values -2. Run `./scripts/install.sh` on your server -3. Configure SSL with `./scripts/setup-ssl.sh` -4. Start with `docker-compose -f docker-compose.prod.yml up -d` - -## Need Help? - -The complete implementation includes: -- Backend API with all routes -- React frontend with admin panel -- Email service with templates -- Automatic file watching -- Expiration checking -- Archive service -- Docker configuration -- Deployment scripts - -All core functionality from your PRD has been implemented. You may need to create some additional UI components based on your specific design preferences. - -Default admin credentials: **admin / admin123** (change immediately!) diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 68a2726..0000000 --- a/TODO.md +++ /dev/null @@ -1,47 +0,0 @@ -# TODO - Open Items Before Release - -## Priority Items - -- [ ] **Gallery Mobile View** - - Logout button should only show logo icon (no text) - - If photo upload is enabled, move upload button inside menu (not on top bar) - - Top bar should show: logo (left), gallery title (center), event date + expiration date - -- [ ] **Gallery Preview** - - Preview should correctly reflect the selected grid layout style - - Add grid style selector above current top bar - - Selector should match the style of event template settings grid selector - -- [ ] **Hero Grid Layout** - - Top bar: only menu and logout buttons - - Title + logo displayed centered on hero photo - - Event date and expiration date also on hero photo - - No logo/title in top bar - -- [ ] **Logo Testing** - Test new PicPeak logos across all grid styles - -- [ ] **Welcome Message** - - Add welcome message to email template when creating new event - - Use as personal message in the email - -- [ ] **Gallery Upload Function** - - Fix scrolling in upload popup when multiple images selected - - Save/Cancel buttons unreachable due to incorrect scroll formatting - -- [ ] **Watermarks** - Test watermark functionality, styling, and image application - -- [ ] **Dashboard Activities** - Remove "show all" link from latest activities widget - -- [ ] **Security Audit** - Perform security review and code audit - -- [ ] **Drone CI/CD** - Update drone.yaml configuration - -- [ ] **Version Management** - Implement automatic version updates on commits/builds - -## Completed Items - -_(Move completed items here with date)_ - ---- - -Last updated: 2025-07-10 \ No newline at end of file diff --git a/TRAEFIK_DEPLOYMENT.md b/TRAEFIK_DEPLOYMENT.md deleted file mode 100644 index 668b6ed..0000000 --- a/TRAEFIK_DEPLOYMENT.md +++ /dev/null @@ -1,280 +0,0 @@ -# Traefik Deployment Guide - -This guide explains how to deploy the PicPeak application with Traefik as the reverse proxy. - -## Overview - -The application consists of: -- **Frontend**: React app served by nginx (port 80) -- **Backend**: Node.js API (port 3000) -- **Database**: PostgreSQL (port 5432, internal only) - -## Traefik Configuration - -### 1. Docker Labels for Traefik - -Add these labels to your `docker-compose.prod.yml` services: - -```yaml -services: - frontend: - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak-frontend.rule=Host(`picpeak.yourdomain.com`)" - - "traefik.http.routers.picpeak-frontend.entrypoints=websecure" - - "traefik.http.routers.picpeak-frontend.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-frontend.loadbalancer.server.port=80" - # Priority for catch-all route - - "traefik.http.routers.picpeak-frontend.priority=1" - - backend: - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak-api.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/api`)" - - "traefik.http.routers.picpeak-api.entrypoints=websecure" - - "traefik.http.routers.picpeak-api.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-api.loadbalancer.server.port=3000" - # Higher priority for API routes - - "traefik.http.routers.picpeak-api.priority=10" - - # Additional routes for backend static files - - "traefik.http.routers.picpeak-uploads.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/uploads`)" - - "traefik.http.routers.picpeak-uploads.entrypoints=websecure" - - "traefik.http.routers.picpeak-uploads.tls.certresolver=letsencrypt" - - "traefik.http.routers.picpeak-uploads.service=picpeak-api" - - "traefik.http.routers.picpeak-uploads.priority=10" - - - "traefik.http.routers.picpeak-images.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/images`)" - - "traefik.http.routers.picpeak-images.entrypoints=websecure" - - "traefik.http.routers.picpeak-images.tls.certresolver=letsencrypt" - - "traefik.http.routers.picpeak-images.service=picpeak-api" - - "traefik.http.routers.picpeak-images.priority=10" -``` - -### 2. Network Configuration - -Ensure your services are on the Traefik network: - -```yaml -networks: - picpeak: - external: false - traefik: - external: true - -services: - frontend: - networks: - - picpeak - - traefik - - backend: - networks: - - picpeak - - traefik - - db: - networks: - - picpeak # Don't expose to traefik -``` - -### 3. Remove Nginx Service - -Since you're using Traefik, remove the nginx service from `docker-compose.prod.yml`: - -```yaml -# Remove this entire service: -# nginx: -# image: nginx:alpine -# ... -``` - -## Frontend Configuration - -The frontend is built with the API URL set to `/api`. This is important because: - -1. All API calls will be relative to the same domain -2. Traefik will route `/api/*` to the backend service -3. No CORS issues since everything is on the same domain - -## Environment Variables - -Ensure these are set correctly: - -```bash -# Backend needs to know the public URLs -ADMIN_URL=https://picpeak.yourdomain.com -FRONTEND_URL=https://picpeak.yourdomain.com - -# Backend API is accessed via /api path -API_URL=https://picpeak.yourdomain.com/api -``` - -## Complete Example - -Here's a complete `docker-compose.prod.yml` for Traefik: - -```yaml -version: '3.8' - -networks: - picpeak: - external: false - traefik: - external: true - -services: - backend: - image: picpeak-backend:latest - build: - context: ./backend - dockerfile: Dockerfile - restart: unless-stopped - depends_on: - - db - environment: - - NODE_ENV=production - - PORT=3000 - - JWT_SECRET=${JWT_SECRET} - - ADMIN_URL=https://picpeak.yourdomain.com - - FRONTEND_URL=https://picpeak.yourdomain.com - - DATABASE_CLIENT=pg - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=${DB_USER:-picpeak} - - DB_PASSWORD=${DB_PASSWORD} - - DB_NAME=${DB_NAME:-picpeak} - - SMTP_HOST=${SMTP_HOST} - - SMTP_PORT=${SMTP_PORT} - - SMTP_SECURE=${SMTP_SECURE} - - SMTP_USER=${SMTP_USER} - - SMTP_PASS=${SMTP_PASS} - - EMAIL_FROM=${EMAIL_FROM} - - STORAGE_PATH=/app/storage - - EVENTS_PATH=/app/storage/events - - ARCHIVE_PATH=/app/storage/events/archived - volumes: - - ./storage:/app/storage - - ./data:/app/data - - ./logs:/app/logs - networks: - - picpeak - - traefik - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak-api.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/api`)" - - "traefik.http.routers.picpeak-api.entrypoints=websecure" - - "traefik.http.routers.picpeak-api.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-api.loadbalancer.server.port=3000" - - "traefik.http.routers.picpeak-api.priority=10" - - - "traefik.http.routers.picpeak-uploads.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/uploads`)" - - "traefik.http.routers.picpeak-uploads.entrypoints=websecure" - - "traefik.http.routers.picpeak-uploads.tls.certresolver=letsencrypt" - - "traefik.http.routers.picpeak-uploads.service=picpeak-api" - - "traefik.http.routers.picpeak-uploads.priority=10" - - - "traefik.http.routers.picpeak-images.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/images`)" - - "traefik.http.routers.picpeak-images.entrypoints=websecure" - - "traefik.http.routers.picpeak-images.tls.certresolver=letsencrypt" - - "traefik.http.routers.picpeak-images.service=picpeak-api" - - "traefik.http.routers.picpeak-images.priority=10" - - frontend: - image: picpeak-frontend:latest - build: - context: ./frontend - dockerfile: Dockerfile - args: - - VITE_API_URL=/api - restart: unless-stopped - depends_on: - - backend - networks: - - picpeak - - traefik - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak-frontend.rule=Host(`picpeak.yourdomain.com`)" - - "traefik.http.routers.picpeak-frontend.entrypoints=websecure" - - "traefik.http.routers.picpeak-frontend.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-frontend.loadbalancer.server.port=80" - - "traefik.http.routers.picpeak-frontend.priority=1" - - db: - image: postgres:14-alpine - restart: unless-stopped - environment: - - POSTGRES_USER=${DB_USER:-picpeak} - - POSTGRES_PASSWORD=${DB_PASSWORD} - - POSTGRES_DB=${DB_NAME:-picpeak} - - POSTGRES_HOST_AUTH_METHOD=scram-sha-256 - - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=trust - volumes: - - postgres_data:/var/lib/postgresql/data - networks: - - picpeak - command: postgres -c ssl=off - -volumes: - postgres_data: -``` - -## Troubleshooting - -### 502 Bad Gateway Errors - -1. **Check if backend is running**: - ```bash - docker-compose -f docker-compose.prod.yml ps - docker-compose -f docker-compose.prod.yml logs backend - ``` - -2. **Verify Traefik can reach the backend**: - - Ensure both services are on the same Docker network - - Check Traefik logs: `docker logs traefik` - -3. **Check backend health**: - ```bash - docker-compose -f docker-compose.prod.yml exec backend curl http://localhost:3000/api/health - ``` - -### Frontend Can't Reach API - -1. **Verify API paths don't have double `/api`**: - - Frontend should call `/auth/admin/login`, not `/api/auth/admin/login` - - The base URL in axios should be `/api` - -2. **Check browser console for actual URLs being called** - -3. **Ensure Traefik routing rules are correct**: - - API routes should have higher priority than frontend catch-all - -### CORS Issues - -Should not occur since everything is on the same domain. If you see CORS errors: -1. Check that `FRONTEND_URL` and `ADMIN_URL` match your actual domain -2. Ensure you're not mixing HTTP and HTTPS - -## Testing the Setup - -1. **Test API directly**: - ```bash - curl https://picpeak.yourdomain.com/api/health - ``` - -2. **Test frontend**: - ```bash - curl https://picpeak.yourdomain.com/ - ``` - -3. **Test admin login**: - - Navigate to https://picpeak.yourdomain.com/admin/login - - Check browser console for any errors - -## Important Notes - -1. **SSL/TLS**: Traefik handles SSL termination, so the backend doesn't need SSL -2. **Port Exposure**: Don't expose backend ports directly - let Traefik handle routing -3. **Health Checks**: Configure Traefik health checks for better reliability -4. **Rate Limiting**: Consider adding Traefik rate limiting middleware for API routes \ No newline at end of file diff --git a/TROUBLESHOOTING-TRAEFIK.md b/TROUBLESHOOTING-TRAEFIK.md deleted file mode 100644 index f87ccca..0000000 --- a/TROUBLESHOOTING-TRAEFIK.md +++ /dev/null @@ -1,134 +0,0 @@ -# Traefik Troubleshooting Guide - -## Common Issues and Solutions - -### 1. 404 Errors on API Routes - -**Problem**: Getting 404 errors when accessing `/api/*` routes - -**Causes**: -- Traefik routing rules not properly configured -- Backend container not healthy -- Path stripping not working correctly - -**Solutions**: - -1. **Check container health**: -```bash -docker ps # Check if backend is running -docker logs picpeak-backend # Check for startup errors -``` - -2. **Test backend directly**: -```bash -# Access backend container -docker exec -it picpeak-backend sh - -# Test health endpoint -wget -O- http://localhost:3000/health - -# Test public settings endpoint -wget -O- http://localhost:3000/public/settings -``` - -3. **Check Traefik routing**: -```bash -# Check if routes are registered in Traefik -curl https://traefik.yourdomain.com/api/http/routers | jq '.[] | select(.rule | contains("picpeak"))' -``` - -### 2. Backend Not Accessible Through Traefik - -**Key Configuration Points**: - -1. **Traefik Labels** (in deploy section): - - `traefik.enable=true` - Enable Traefik for this container - - `traefik.docker.network=proxy` - Specify which network Traefik should use - - `traefik.http.routers.picpeak-backend.priority=100` - Higher priority for API routes - -2. **Path Stripping**: - - Frontend expects `/api/*` but backend serves routes without `/api` prefix - - Middleware strips `/api` before forwarding to backend - -3. **Network Configuration**: - - Backend must be in both `picpeak` (internal) and `proxy` (Traefik) networks - -### 3. Environment Variable Issues - -**Critical Variables**: -- `ADMIN_URL` and `FRONTEND_URL` must match your actual domain -- These affect CORS configuration - -**Example .env**: -```env -# URLs -ADMIN_URL=https://picpeak.local.nothaft.cloud -FRONTEND_URL=https://picpeak.local.nothaft.cloud - -# Database -DB_USER=picpeak -DB_PASSWORD=your_secure_password -DB_NAME=picpeak - -# JWT -JWT_SECRET=your_secure_jwt_secret - -# Email (optional) -SMTP_HOST=smtp.example.com -SMTP_PORT=587 -SMTP_SECURE=true -SMTP_USER=noreply@example.com -SMTP_PASS=smtp_password -EMAIL_FROM=noreply@example.com -``` - -### 4. Debugging Steps - -1. **Check if backend is receiving requests**: -```bash -# Watch backend logs -docker logs -f picpeak-backend - -# Look for incoming requests when you try to access the admin page -``` - -2. **Test API routes directly**: -```bash -# From outside -curl -v https://picpeak.local.nothaft.cloud/api/public/settings - -# Should see backend logs if request reaches container -``` - -3. **Verify Traefik middleware**: -```bash -# Check if stripprefix middleware exists -curl https://traefik.yourdomain.com/api/http/middlewares | jq '.[] | select(.name | contains("picpeak"))' -``` - -### 5. Quick Fix Checklist - -- [ ] Backend container is healthy (`docker ps`) -- [ ] Backend is in both networks (`docker inspect picpeak-backend | grep -A 20 Networks`) -- [ ] Traefik labels use correct network (`traefik.docker.network=proxy`) -- [ ] Priority is set correctly (backend: 100, frontend: 10) -- [ ] ADMIN_URL and FRONTEND_URL match your domain -- [ ] Database is accessible from backend -- [ ] Migrations have run successfully - -### 6. Alternative Testing - -If Traefik routing is problematic, test backend directly: - -```bash -# Port forward to test backend directly -docker run --rm -it --network picpeak alpine/curl curl http://backend:3000/health - -# Or expose backend port temporarily -docker run -d --name picpeak-backend-test \ - --network picpeak \ - -p 3001:3000 \ - registry.local.nothaft.cloud/picpeak-backend:latest -``` - -Then access http://localhost:3001/health to verify backend is working. \ No newline at end of file diff --git a/docker-compose.fixed.yml b/docker-compose.fixed.yml deleted file mode 100644 index 511801e..0000000 --- a/docker-compose.fixed.yml +++ /dev/null @@ -1,110 +0,0 @@ -version: '3.8' -services: - backend: - image: 'registry.local.nothaft.cloud/picpeak-backend:latest' - restart: unless-stopped - environment: - - NODE_ENV=production - - DATABASE_CLIENT=pg - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=${DB_USER:-picpeak} - - DB_PASSWORD=${DB_PASSWORD} - - DB_NAME=${DB_NAME:-picpeak} - - PORT=3000 - - JWT_SECRET=${JWT_SECRET} - - ADMIN_URL=https://picpeak.local.nothaft.cloud - - FRONTEND_URL=https://picpeak.local.nothaft.cloud - - SMTP_HOST=${SMTP_HOST} - - SMTP_PORT=${SMTP_PORT} - - SMTP_SECURE=${SMTP_SECURE} - - SMTP_USER=${SMTP_USER} - - SMTP_PASS=${SMTP_PASS} - - EMAIL_FROM=${EMAIL_FROM} - - UMAMI_URL=${UMAMI_URL} - - UMAMI_WEBSITE_ID=${UMAMI_WEBSITE_ID} - - STORAGE_PATH=/app/storage - - EVENTS_PATH=/app/storage/events - - ARCHIVE_PATH=/app/storage/events/archived - volumes: - - '/mnt/DockerMount/picpeak/storage:/app/storage' - - '/mnt/DockerMount/picpeak/data:/app/data' - - '/mnt/DockerMount/picpeak/logs:/app/logs' - networks: - - picpeak - - proxy - deploy: - labels: - - traefik.enable=true - - traefik.docker.network=proxy - # Backend API routing WITHOUT path stripping - - 'traefik.http.routers.picpeak-backend.rule=(Host(`picpeak.nothaft.cloud`) || Host(`picpeak.local.nothaft.cloud`)) && PathPrefix(`/api`)' - - traefik.http.routers.picpeak-backend.entrypoints=https - - traefik.http.routers.picpeak-backend.tls.certresolver=dns - - traefik.http.services.picpeak-backend.loadbalancer.server.port=3000 - # Remove the stripprefix middleware - backend expects /api prefix - # - traefik.http.middlewares.picpeak-stripprefix.stripprefix.prefixes=/api - # - traefik.http.routers.picpeak-backend.middlewares=picpeak-stripprefix - - traefik.http.routers.picpeak-backend.priority=100 - - homepage.group=Public Services - - homepage.name=PicPeak Backend - - homepage.icon=mdi-api - - 'homepage.href=https://picpeak.local.nothaft.cloud/api/health' - - homepage.description=PicPeak API Backend - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - depends_on: - - db - - frontend: - image: 'registry.local.nothaft.cloud/picpeak-frontend:latest' - restart: unless-stopped - depends_on: - - backend - networks: - - picpeak - - proxy - deploy: - labels: - - traefik.enable=true - - traefik.docker.network=proxy - - traefik.http.routers.picpeak.rule=Host(`picpeak.nothaft.cloud`) || Host(`picpeak.local.nothaft.cloud`) - - traefik.http.routers.picpeak.entrypoints=https - - traefik.http.routers.picpeak.tls.certresolver=dns - - traefik.http.services.picpeak.loadbalancer.server.port=80 - - traefik.http.routers.picpeak.priority=10 - - homepage.group=Public Services - - homepage.name=PicPeak - - homepage.icon=mdi-photo - - 'homepage.href=https://picpeak.local.nothaft.cloud/' - - homepage.description=Photo Sharing System - - db: - image: 'postgres:14-alpine' - restart: unless-stopped - environment: - - POSTGRES_USER=${DB_USER:-picpeak} - - POSTGRES_PASSWORD=${DB_PASSWORD} - - POSTGRES_DB=${DB_NAME:-picpeak} - - POSTGRES_HOST_AUTH_METHOD=${PG_AUTH_METHOD:-scram-sha-256} - - POSTGRES_INITDB_ARGS=${PG_INIT_ARGS:---auth-host=scram-sha-256 --auth-local=trust} - volumes: - - '/mnt/DockerMount/picpeak/db:/var/lib/postgresql/data' - networks: - - picpeak - command: ${PG_COMMANDS:-postgres -c ssl=off} - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-picpeak}"] - interval: 10s - timeout: 5s - retries: 5 - -networks: - proxy: - external: true - picpeak: - driver: bridge \ No newline at end of file diff --git a/docker-compose.production.yml b/docker-compose.production.yml deleted file mode 100644 index 6b3ce78..0000000 --- a/docker-compose.production.yml +++ /dev/null @@ -1,131 +0,0 @@ -version: '3.8' -services: - backend: - image: 'registry.local.nothaft.cloud/picpeak-backend:latest' - restart: unless-stopped - environment: - - NODE_ENV=production - - DATABASE_CLIENT=pg - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=${DB_USER:-picpeak} - - DB_PASSWORD=${DB_PASSWORD} - - DB_NAME=${DB_NAME:-picpeak} - - PORT=3000 - - JWT_SECRET=${JWT_SECRET} - - ADMIN_URL=https://picpeak.local.nothaft.cloud - - FRONTEND_URL=https://picpeak.local.nothaft.cloud - - SMTP_HOST=${SMTP_HOST} - - SMTP_PORT=${SMTP_PORT} - - SMTP_SECURE=${SMTP_SECURE} - - SMTP_USER=${SMTP_USER} - - SMTP_PASS=${SMTP_PASS} - - EMAIL_FROM=${EMAIL_FROM} - - UMAMI_URL=${UMAMI_URL} - - UMAMI_WEBSITE_ID=${UMAMI_WEBSITE_ID} - - STORAGE_PATH=/app/storage - - EVENTS_PATH=/app/storage/events - - ARCHIVE_PATH=/app/storage/events/archived - volumes: - - '/mnt/DockerMount/picpeak/storage:/app/storage' - - '/mnt/DockerMount/picpeak/data:/app/data' - - '/mnt/DockerMount/picpeak/logs:/app/logs' - networks: - - picpeak - - proxy - deploy: - labels: - - traefik.enable=true - - traefik.docker.network=proxy - # Backend API routing - - 'traefik.http.routers.picpeak-backend.rule=(Host(`picpeak.nothaft.cloud`) || Host(`picpeak.local.nothaft.cloud`)) && PathPrefix(`/api`)' - - traefik.http.routers.picpeak-backend.entrypoints=https - - traefik.http.routers.picpeak-backend.tls=true - - traefik.http.routers.picpeak-backend.tls.certresolver=dns - - traefik.http.services.picpeak-backend.loadbalancer.server.port=3000 - # Strip /api prefix when forwarding to backend - - traefik.http.middlewares.picpeak-stripprefix.stripprefix.prefixes=/api - - traefik.http.routers.picpeak-backend.middlewares=picpeak-stripprefix - # Higher priority for API routes - - traefik.http.routers.picpeak-backend.priority=100 - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - depends_on: - - db - - frontend: - image: 'registry.local.nothaft.cloud/picpeak-frontend:latest' - restart: unless-stopped - depends_on: - - backend - networks: - - picpeak - - proxy - deploy: - labels: - - traefik.enable=true - - traefik.docker.network=proxy - # Frontend routing (catch-all for non-API routes) - - traefik.http.routers.picpeak.rule=Host(`picpeak.nothaft.cloud`) || Host(`picpeak.local.nothaft.cloud`) - - traefik.http.routers.picpeak.entrypoints=https - - traefik.http.routers.picpeak.tls=true - - traefik.http.routers.picpeak.tls.certresolver=dns - - traefik.http.services.picpeak.loadbalancer.server.port=80 - # Lower priority than backend to ensure /api routes go to backend - - traefik.http.routers.picpeak.priority=10 - - db: - image: 'postgres:14-alpine' - restart: unless-stopped - environment: - - POSTGRES_USER=${DB_USER:-picpeak} - - POSTGRES_PASSWORD=${DB_PASSWORD} - - POSTGRES_DB=${DB_NAME:-picpeak} - - POSTGRES_HOST_AUTH_METHOD=scram-sha-256 - - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=trust - volumes: - - '/mnt/DockerMount/picpeak/db:/var/lib/postgresql/data' - # Mount init script to create umami database - - ./docker/postgres-init:/docker-entrypoint-initdb.d:ro - networks: - - picpeak - command: postgres -c ssl=off - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-picpeak}"] - interval: 10s - timeout: 5s - retries: 5 - - # Optional: Umami analytics - umami: - image: ghcr.io/umami-software/umami:postgresql-latest - restart: unless-stopped - environment: - DATABASE_URL: postgresql://${DB_USER:-picpeak}:${DB_PASSWORD}@db:5432/umami - DATABASE_TYPE: postgresql - HASH_SALT: ${UMAMI_HASH_SALT} - depends_on: - db: - condition: service_healthy - networks: - - picpeak - - proxy - deploy: - labels: - - traefik.enable=true - - traefik.docker.network=proxy - - traefik.http.routers.picpeak-umami.rule=Host(`analytics.picpeak.local.nothaft.cloud`) - - traefik.http.routers.picpeak-umami.entrypoints=https - - traefik.http.routers.picpeak-umami.tls=true - - traefik.http.routers.picpeak-umami.tls.certresolver=dns - - traefik.http.services.picpeak-umami.loadbalancer.server.port=3000 - -networks: - proxy: - external: true - picpeak: - driver: bridge \ No newline at end of file diff --git a/docker-compose.runner.yml b/docker-compose.runner.yml deleted file mode 100644 index 022803d..0000000 --- a/docker-compose.runner.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: '3.8' - -services: - gitea-runner: - image: gitea/act_runner:latest - container_name: gitea-runner-picpeak - restart: unless-stopped - environment: - # IMPORTANT: Replace this with your actual registration token from Gitea - - GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_REGISTRATION_TOKEN_HERE - - GITEA_INSTANCE_URL=https://gitea.nothaft.cloud - - GITEA_RUNNER_NAME=picpeak-docker-runner - # Runner labels - what this runner can handle - - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-bullseye - volumes: - # Mount Docker socket to allow runner to create containers - - /var/run/docker.sock:/var/run/docker.sock - # Persist runner data - - ./runner-data:/data - # Cache directory - - ./runner-cache:/root/.cache - # Optional: Use host network for better performance - # network_mode: host - - # Optional: Watchtower to auto-update the runner - # watchtower: - # image: containrrr/watchtower - # volumes: - # - /var/run/docker.sock:/var/run/docker.sock - # command: --interval 86400 gitea-runner-picpeak \ No newline at end of file diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml deleted file mode 100644 index 47ef1c3..0000000 --- a/docker-compose.traefik.yml +++ /dev/null @@ -1,147 +0,0 @@ -# docker-compose.traefik.yml - Production configuration for external Traefik -version: '3.8' - -services: - backend: - image: picpeak-backend:latest - build: - context: ./backend - dockerfile: Dockerfile - restart: unless-stopped - depends_on: - - db - environment: - - NODE_ENV=production - - PORT=3000 - - JWT_SECRET=${JWT_SECRET} - - ADMIN_URL=https://picpeak.nothaft.cloud - - FRONTEND_URL=https://picpeak.nothaft.cloud - # Database - - DATABASE_CLIENT=pg - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=${DB_USER:-picpeak} - - DB_PASSWORD=${DB_PASSWORD} - - DB_NAME=${DB_NAME:-picpeak} - # Email - - SMTP_HOST=${SMTP_HOST} - - SMTP_PORT=${SMTP_PORT} - - SMTP_SECURE=${SMTP_SECURE} - - SMTP_USER=${SMTP_USER} - - SMTP_PASS=${SMTP_PASS} - - EMAIL_FROM=${EMAIL_FROM} - # Analytics - - UMAMI_URL=${UMAMI_URL} - - UMAMI_WEBSITE_ID=${UMAMI_WEBSITE_ID} - # Storage paths - - STORAGE_PATH=/app/storage - - EVENTS_PATH=/app/storage/events - - ARCHIVE_PATH=/app/storage/events/archived - volumes: - - ./storage:/app/storage - - ./data:/app/data - - ./logs:/app/logs - networks: - - picpeak - - traefik - labels: - - "traefik.enable=true" - - "traefik.docker.network=traefik" - # Backend API routing - - "traefik.http.routers.picpeak-backend.rule=Host(`picpeak.nothaft.cloud`) && PathPrefix(`/api`)" - - "traefik.http.routers.picpeak-backend.entrypoints=websecure" - - "traefik.http.routers.picpeak-backend.tls=true" - - "traefik.http.routers.picpeak-backend.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-backend.loadbalancer.server.port=3000" - # Strip /api prefix when forwarding to backend - - "traefik.http.middlewares.picpeak-stripprefix.stripprefix.prefixes=/api" - - "traefik.http.routers.picpeak-backend.middlewares=picpeak-stripprefix" - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - frontend: - image: picpeak-frontend:latest - build: - context: ./frontend - dockerfile: Dockerfile - args: - - VITE_API_URL=/api - - VITE_UMAMI_URL=${VITE_UMAMI_URL} - - VITE_UMAMI_WEBSITE_ID=${VITE_UMAMI_WEBSITE_ID} - restart: unless-stopped - depends_on: - - backend - networks: - - picpeak - - traefik - labels: - - "traefik.enable=true" - - "traefik.docker.network=traefik" - # Frontend routing (catch-all for non-API routes) - - "traefik.http.routers.picpeak-frontend.rule=Host(`picpeak.nothaft.cloud`)" - - "traefik.http.routers.picpeak-frontend.entrypoints=websecure" - - "traefik.http.routers.picpeak-frontend.tls=true" - - "traefik.http.routers.picpeak-frontend.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-frontend.loadbalancer.server.port=80" - # Lower priority than backend to ensure /api routes go to backend - - "traefik.http.routers.picpeak-frontend.priority=1" - - db: - image: postgres:14-alpine - restart: unless-stopped - environment: - - POSTGRES_USER=${DB_USER:-picpeak} - - POSTGRES_PASSWORD=${DB_PASSWORD} - - POSTGRES_DB=${DB_NAME:-picpeak} - # Allow connections from any host with password authentication - - POSTGRES_HOST_AUTH_METHOD=scram-sha-256 - - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=trust - volumes: - - postgres_data:/var/lib/postgresql/data - # Mount init script to create umami database - - ./docker/postgres-init:/docker-entrypoint-initdb.d - networks: - - picpeak - # Allow connections without SSL requirement from Docker network - command: postgres -c ssl=off - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-picpeak}"] - interval: 10s - timeout: 5s - retries: 5 - - umami: - image: ghcr.io/umami-software/umami:postgresql-latest - restart: unless-stopped - environment: - DATABASE_URL: postgresql://${DB_USER:-picpeak}:${DB_PASSWORD}@db:5432/umami - DATABASE_TYPE: postgresql - HASH_SALT: ${UMAMI_HASH_SALT} - depends_on: - db: - condition: service_healthy - networks: - - picpeak - - traefik - labels: - - "traefik.enable=true" - - "traefik.docker.network=traefik" - # Umami analytics routing - - "traefik.http.routers.picpeak-umami.rule=Host(`analytics.picpeak.nothaft.cloud`)" - - "traefik.http.routers.picpeak-umami.entrypoints=websecure" - - "traefik.http.routers.picpeak-umami.tls=true" - - "traefik.http.routers.picpeak-umami.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak-umami.loadbalancer.server.port=3000" - -networks: - picpeak: - driver: bridge - traefik: - external: true - -volumes: - postgres_data: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0ac1a65 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,92 @@ +version: '3.8' + +services: + # PostgreSQL Database + postgres: + image: postgres:15-alpine + container_name: picpeak-postgres + environment: + POSTGRES_DB: ${DB_NAME:-picpeak} + POSTGRES_USER: ${DB_USER:-picpeak} + POSTGRES_PASSWORD: ${DB_PASSWORD:-picpeak} + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-picpeak}"] + interval: 10s + timeout: 5s + retries: 5 + + # Backend API + backend: + build: ./backend + container_name: picpeak-backend + depends_on: + postgres: + condition: service_healthy + environment: + NODE_ENV: production + DATABASE_CLIENT: pg + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-picpeak} + DB_USER: ${DB_USER:-picpeak} + DB_PASSWORD: ${DB_PASSWORD:-picpeak} + env_file: + - .env + volumes: + - ./storage:/app/storage + - ./data:/app/data + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Frontend + frontend: + build: + context: ./frontend + args: + VITE_API_URL: ${VITE_API_URL:-/api} + VITE_UMAMI_URL: ${VITE_UMAMI_URL} + VITE_UMAMI_WEBSITE_ID: ${VITE_UMAMI_WEBSITE_ID} + container_name: picpeak-frontend + depends_on: + - backend + restart: unless-stopped + + # Nginx Reverse Proxy + nginx: + image: nginx:alpine + container_name: picpeak-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./certbot/conf:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + depends_on: + - frontend + - backend + restart: unless-stopped + command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + + # Certbot for SSL + certbot: + image: certbot/certbot + container_name: picpeak-certbot + volumes: + - ./certbot/conf:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + +volumes: + postgres_data: + +networks: + default: + name: picpeak-network \ No newline at end of file diff --git a/docs/GITEA_ACTIONS_SETUP.md b/docs/GITEA_ACTIONS_SETUP.md deleted file mode 100644 index 2f29f7b..0000000 --- a/docs/GITEA_ACTIONS_SETUP.md +++ /dev/null @@ -1,151 +0,0 @@ -# Gitea Actions Setup Guide - -## Prerequisites - -1. **Gitea Version**: Ensure you're running Gitea 1.19.0 or later -2. **Gitea Actions Enabled**: Check your Gitea configuration - -## Step 1: Enable Gitea Actions in app.ini - -Add or modify these settings in your Gitea `app.ini`: - -```ini -[actions] -ENABLED = true -DEFAULT_ACTIONS_URL = https://gitea.com -``` - -## Step 2: Install Gitea Act Runner - -### Option A: Using Docker - -```bash -docker run -d \ - --name gitea-runner \ - --restart unless-stopped \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v gitea-runner-data:/data \ - -e GITEA_INSTANCE_URL=https://gitea.nothaft.cloud \ - -e GITEA_RUNNER_REGISTRATION_TOKEN= \ - -e GITEA_RUNNER_NAME=docker-runner \ - gitea/act_runner:latest -``` - -### Option B: Using Binary - -1. Download the act_runner: -```bash -wget https://gitea.com/gitea/act_runner/releases/download/v0.2.5/act_runner-0.2.5-linux-amd64 -chmod +x act_runner-0.2.5-linux-amd64 -sudo mv act_runner-0.2.5-linux-amd64 /usr/local/bin/act_runner -``` - -2. Register the runner: -```bash -act_runner register \ - --instance https://gitea.nothaft.cloud \ - --token \ - --name "my-runner" \ - --labels "ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye" -``` - -3. Start the runner: -```bash -act_runner daemon -``` - -## Step 3: Get Registration Token - -1. Go to your Gitea instance admin panel -2. Navigate to Site Administration โ†’ Actions โ†’ Runners -3. Click "Create new Runner" -4. Copy the registration token - -## Step 4: Repository Settings - -1. Go to your repository settings in Gitea -2. Navigate to Settings โ†’ Actions โ†’ General -3. Ensure Actions are enabled for the repository - -## Step 5: Convert GitHub Actions to Gitea Actions - -While Gitea Actions is mostly compatible with GitHub Actions, there are some differences: - -### Workflow Location -- GitHub Actions: `.github/workflows/` -- Gitea Actions: `.gitea/workflows/` (preferred) or `.github/workflows/` - -### Supported Features -โœ… Supported: -- Basic workflow syntax -- Common actions like `actions/checkout` -- Environment variables -- Secrets -- Artifacts -- Matrix builds - -โŒ Not Supported: -- Some GitHub-specific actions -- GitHub Packages -- Some advanced features - -## Step 6: Debug Workflow Issues - -If workflows are stuck in "waiting": - -1. **Check Runner Status**: - ```bash - # If using Docker - docker logs gitea-runner - - # If using binary - journalctl -u act_runner -f - ``` - -2. **Check Gitea Logs**: - ```bash - # Check Gitea logs for action-related errors - tail -f /path/to/gitea/log/gitea.log | grep -i action - ``` - -3. **Verify Runner Labels**: - - Ensure your runner has the labels that match your workflow's `runs-on` - - Common labels: `ubuntu-latest`, `ubuntu-22.04`, `ubuntu-20.04` - -4. **Check Repository Permissions**: - - Ensure the repository has Actions enabled - - Check if there are any branch protection rules blocking Actions - -## Step 7: Alternative - Use Drone CI - -Since you already have Drone CI configured (`.drone.yml`), you might want to use that instead: - -```yaml -# Your existing .drone.yml is already set up for CI/CD -kind: pipeline -type: docker -name: default -# ... rest of your Drone configuration -``` - -## Common Issues and Solutions - -### Issue: Workflows stuck in "waiting" -**Solution**: No runners available. Register and start a runner. - -### Issue: Runner can't connect -**Solution**: Check firewall rules and ensure runner can reach Gitea instance. - -### Issue: Docker-in-Docker errors -**Solution**: Mount Docker socket or use privileged mode for runner. - -### Issue: Actions not showing in UI -**Solution**: Enable Actions in both Gitea config and repository settings. - -## Next Steps - -1. Check your Gitea version and configuration -2. Install and register a runner -3. Enable Actions for your repository -4. Test with the simple workflow created in `.gitea/workflows/test.yml` -5. Once working, migrate your GitHub Actions workflows if needed \ No newline at end of file diff --git a/nginx/nginx.conf.example b/nginx/nginx.conf.example new file mode 100644 index 0000000..d2e1904 --- /dev/null +++ b/nginx/nginx.conf.example @@ -0,0 +1,94 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Gzip compression + gzip on; + gzip_types text/plain text/css text/xml text/javascript application/javascript application/json; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m; + + server { + listen 80; + server_name your-domain.com www.your-domain.com; + + # Let's Encrypt challenge + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # Redirect to HTTPS + location / { + return 301 https://$host$request_uri; + } + } + + server { + listen 443 ssl http2; + server_name your-domain.com www.your-domain.com; + + # SSL configuration + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + # Modern SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # API proxy + location /api { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + + # Rate limiting for API + limit_req zone=general burst=20 nodelay; + } + + # Auth endpoints with stricter rate limiting + location /api/auth { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Strict rate limiting for auth + limit_req zone=auth burst=5 nodelay; + } + + # Static files + location / { + proxy_pass http://frontend:80; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # Security headers for static content + location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { + proxy_pass http://frontend:80; + expires 1y; + add_header Cache-Control "public, immutable"; + } + } +} \ No newline at end of file diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100755 index 0000000..87dd418 --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# PicPeak Backup Script +# Creates backups of database and storage + +set -e + +# Configuration +BACKUP_DIR="./backups" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +BACKUP_NAME="picpeak_backup_${TIMESTAMP}" + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +echo "๐Ÿ”„ Starting PicPeak backup..." + +# Create backup directory +mkdir -p "${BACKUP_DIR}/${BACKUP_NAME}" + +# Backup database +echo "๐Ÿ“Š Backing up database..." +if [ -f "./data/photo_sharing.db" ]; then + cp ./data/photo_sharing.db "${BACKUP_DIR}/${BACKUP_NAME}/" + echo -e "${GREEN}โœ“ SQLite database backed up${NC}" +else + # PostgreSQL backup + docker exec picpeak-postgres pg_dump -U picpeak picpeak > "${BACKUP_DIR}/${BACKUP_NAME}/database.sql" 2>/dev/null || { + echo -e "${RED}โš  Database backup failed - is PostgreSQL running?${NC}" + } +fi + +# Backup storage +echo "๐Ÿ“ธ Backing up photos..." +if [ -d "./storage" ]; then + tar -czf "${BACKUP_DIR}/${BACKUP_NAME}/storage.tar.gz" ./storage 2>/dev/null || { + echo -e "${RED}โš  Storage backup failed${NC}" + exit 1 + } + echo -e "${GREEN}โœ“ Storage backed up${NC}" +fi + +# Backup environment files +echo "โš™๏ธ Backing up configuration..." +cp .env "${BACKUP_DIR}/${BACKUP_NAME}/.env.backup" 2>/dev/null || true + +# Create backup info +echo "๐Ÿ“ Creating backup info..." +cat > "${BACKUP_DIR}/${BACKUP_NAME}/backup_info.txt" << EOF +PicPeak Backup +Created: $(date) +Version: $(grep version backend/package.json | head -1 | awk -F'"' '{print $4}') +Storage Size: $(du -sh ./storage 2>/dev/null | cut -f1 || echo "N/A") +EOF + +# Compress entire backup +echo "๐Ÿ“ฆ Compressing backup..." +cd "${BACKUP_DIR}" +tar -czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}" +rm -rf "${BACKUP_NAME}" + +# Cleanup old backups (keep last 7) +echo "๐Ÿงน Cleaning up old backups..." +ls -t *.tar.gz | tail -n +8 | xargs -r rm + +echo -e "${GREEN}โœ… Backup completed: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz${NC}" +echo "๐Ÿ’ก To restore: tar -xzf ${BACKUP_NAME}.tar.gz && follow restore instructions" \ No newline at end of file