#!/bin/bash # Setup script to create remaining files echo "Creating remaining project files..." # Create directories mkdir -p backend/src/services mkdir -p backend/src/utils mkdir -p backend/src/routes mkdir -p backend/migrations mkdir -p backend/scripts mkdir -p backend/__tests__ mkdir -p frontend/public mkdir -p frontend/src/components mkdir -p frontend/src/contexts mkdir -p frontend/src/hooks mkdir -p frontend/src/pages/admin mkdir -p frontend/src/services mkdir -p frontend/src/config mkdir -p nginx/sites-enabled mkdir -p scripts mkdir -p storage/events/active mkdir -p storage/events/archived mkdir -p storage/thumbnails mkdir -p data mkdir -p logs mkdir -p certbot/conf mkdir -p certbot/www # Create .gitkeep files touch storage/events/active/.gitkeep touch storage/events/archived/.gitkeep touch storage/thumbnails/.gitkeep touch data/.gitkeep touch logs/.gitkeep # Create remaining backend services cat > backend/src/services/imageProcessor.js << 'EOF' const sharp = require('sharp'); const path = require('path'); const fs = require('fs').promises; const THUMBNAIL_WIDTH = 300; const THUMBNAIL_PATH = path.join(__dirname, '../../../storage/thumbnails'); async function generateThumbnail(imagePath) { const filename = path.basename(imagePath); const thumbnailFilename = `thumb_${filename}`; const thumbnailPath = path.join(THUMBNAIL_PATH, thumbnailFilename); // Ensure thumbnail directory exists await fs.mkdir(THUMBNAIL_PATH, { recursive: true }); // Generate thumbnail await sharp(imagePath) .resize(THUMBNAIL_WIDTH, null, { withoutEnlargement: true, fit: 'inside' }) .jpeg({ quality: 80 }) .toFile(thumbnailPath); return path.relative(path.join(__dirname, '../../../storage'), thumbnailPath); } module.exports = { generateThumbnail }; EOF # Create logger utility cat > backend/src/utils/logger.js << 'EOF' const winston = require('winston'); const path = require('path'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.File({ filename: path.join(__dirname, '../../../logs/error.log'), level: 'error' }), new winston.transports.File({ filename: path.join(__dirname, '../../../logs/combined.log') }) ] }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) })); } module.exports = logger; EOF echo "Backend services created." # Create migration init script cat > backend/migrations/init.js << 'EOF' const bcrypt = require('bcrypt'); const { db, initializeDatabase } = require('../src/database/db'); async function runMigrations() { console.log('Running database migrations...'); try { // Initialize tables await initializeDatabase(); // Create default admin user if none exists const adminExists = await db('admin_users').first(); if (!adminExists) { const defaultPassword = 'admin123'; // Change this! const passwordHash = await bcrypt.hash(defaultPassword, 10); await db('admin_users').insert({ username: 'admin', email: 'admin@example.com', password_hash: passwordHash }); console.log('Default admin user created:'); console.log('Username: admin'); console.log('Password: admin123'); console.log('⚠️ Please change this password immediately!'); } console.log('Migrations completed successfully'); process.exit(0); } catch (error) { console.error('Migration failed:', error); process.exit(1); } } runMigrations(); EOF echo "Migration script created." # Create README cat > README.md << 'EOF' # Photo Sharing Platform A secure, self-hosted photo sharing platform designed for weddings and events. Features automatic expiration, email notifications, and simple file-based management. ## Features - 🔒 Password Protected Galleries - ⏰ Automatic Expiration - 📧 Email Notifications - 📁 Simple File Management - 📊 Analytics Integration - 🎨 Customizable Themes - 📱 Mobile Responsive - ⚡ Docker Ready ## Quick Start 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` Default credentials: admin / admin123 (change immediately!) ## Documentation See DEPLOYMENT.md for detailed deployment instructions. ## License MIT License EOF echo "README created." # Create main installation script cat > scripts/install.sh << 'EOF' #!/bin/bash set -e echo "Photo Sharing Platform - Docker Installation" echo "===========================================" # Check if running as root if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1 fi # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Check prerequisites echo "Checking prerequisites..." # Install Docker if not present if ! command_exists docker; then echo "Installing Docker..." curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh rm get-docker.sh fi # Install Docker Compose if not present if ! command_exists docker-compose; then echo "Installing Docker Compose..." curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose fi # Create necessary directories echo "Creating directory structure..." mkdir -p storage/events/{active,archived} mkdir -p storage/thumbnails mkdir -p data mkdir -p logs mkdir -p nginx/sites-enabled mkdir -p certbot/{conf,www} # Set permissions chmod -R 755 storage chmod -R 755 data chmod -R 755 logs # Copy environment file if [ ! -f .env ]; then cp .env.example .env echo "Created .env file. Please edit it with your configuration." fi # Generate secure passwords echo "Generating secure passwords..." JWT_SECRET=$(openssl rand -base64 32) DB_PASSWORD=$(openssl rand -base64 32) UMAMI_HASH_SALT=$(openssl rand -base64 32) # Update .env file with generated values sed -i "s/JWT_SECRET=.*/JWT_SECRET=$JWT_SECRET/" .env sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$DB_PASSWORD/" .env sed -i "s/UMAMI_HASH_SALT=.*/UMAMI_HASH_SALT=$UMAMI_HASH_SALT/" .env echo "" echo "Installation complete!" echo "Next steps:" echo "1. Edit .env file with your domain names and SMTP settings" echo "2. Run: ./scripts/setup-ssl.sh to configure SSL certificates" echo "3. Run: docker-compose -f docker-compose.prod.yml up -d" echo "4. Run: docker-compose -f docker-compose.prod.yml exec backend npm run migrate" EOF chmod +x scripts/install.sh echo "Installation script created." # Create nginx config cat > nginx/nginx.conf << 'EOF' user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; # 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; add_header Referrer-Policy "no-referrer-when-downgrade" always; # 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; include /etc/nginx/sites-enabled/*.conf; } EOF echo "Nginx config created." # Create frontend package.json cat > frontend/package.json << 'EOF' { "name": "photo-sharing-frontend", "version": "1.0.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.8.0", "axios": "^1.3.2", "react-query": "^3.39.3", "date-fns": "^2.29.3", "react-toastify": "^9.1.1", "react-dropzone": "^14.2.3", "react-image-gallery": "^1.2.11", "react-countdown": "^2.3.5", "tailwindcss": "^3.2.4", "autoprefixer": "^10.4.13", "postcss": "^8.4.21", "web-vitals": "^2.1.4" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "react-scripts": "5.0.1" }, "proxy": "http://localhost:3000" } EOF echo "Frontend package.json created." echo "" echo "Setup script complete!" echo "Most important files have been created." echo "" echo "To complete the setup:" echo "1. Run this script: chmod +x setup-remaining-files.sh && ./setup-remaining-files.sh" echo "2. Review and update the created files as needed" echo "3. Install dependencies: cd backend && npm install && cd ../frontend && npm install" echo "4. Follow the deployment instructions in the README"