diff --git a/setup-remaining-files.sh b/setup-remaining-files.sh new file mode 100644 index 0000000..45f0bae --- /dev/null +++ b/setup-remaining-files.sh @@ -0,0 +1,380 @@ +#!/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"