mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
- Add comprehensive migration guides (DEPLOYMENT_MIGRATION.md, DIRECTORY_STRUCTURE.md, MIGRATION_CHECKLIST.md) - Refactor admin panel: split add.php, create reusable header/footer - Update styles: admin.css, common.css, main.css - Improve public pages: index.php, memoire.php - Reorganize database documentation into database/docs/ - Update .gitignore and justfile This prepares for migration to public/ directory structure
572 lines
15 KiB
Markdown
572 lines
15 KiB
Markdown
# Deployment & Dev Server Migration Guide
|
|
|
|
Analysis of current `justfile` and required changes for the new directory structure.
|
|
|
|
---
|
|
|
|
## 🔍 Current Setup Analysis
|
|
|
|
### Current Dev Server (❌ INCORRECT)
|
|
```bash
|
|
# From justfile line 33
|
|
php -S 127.0.0.1:8000
|
|
```
|
|
|
|
**Problems:**
|
|
- Serves from project root (all files accessible via web)
|
|
- Exposes sensitive files: `database/`, `tests/`, `vendor/`, config files
|
|
- Doesn't match production DocumentRoot configuration
|
|
- Security risk: `.env`, database files, source code all accessible
|
|
|
|
### Current Deployment (❌ INCORRECT)
|
|
```bash
|
|
# From justfile lines 56-75
|
|
rsync -vur --progress \
|
|
--exclude 'vendor' --exclude 'tests' --exclude '*.db' ... \
|
|
./ posterg:/var/www/html/
|
|
```
|
|
|
|
**Problems:**
|
|
- Deploys entire project to DocumentRoot
|
|
- Relies on exclusions to hide sensitive files (error-prone)
|
|
- Wrong structure: `/var/www/html/` should only contain public files
|
|
- Private files (`src/`, `config/`) are still in DocumentRoot
|
|
- Nginx serves from `/var/www/html/` exposing everything not excluded
|
|
|
|
---
|
|
|
|
## ✅ Required Changes
|
|
|
|
### 1. New Directory Structure on Server
|
|
|
|
**Before (current):**
|
|
```
|
|
/var/www/html/ # DocumentRoot ❌
|
|
├── index.php # Public
|
|
├── search.php # Public
|
|
├── admin/ # Public (protected by nginx)
|
|
├── assets/ # Public
|
|
├── inc/ # ❌ EXPOSED (config files!)
|
|
├── lib/ # ❌ EXPOSED (source code!)
|
|
├── database/ # ❌ EXPOSED (database files!)
|
|
└── vendor/ # ❌ (excluded but in wrong place)
|
|
```
|
|
|
|
**After (recommended):**
|
|
```
|
|
/var/www/posterg/ # Application root (private)
|
|
├── public/ # DocumentRoot ✅ (only this exposed)
|
|
│ ├── index.php
|
|
│ ├── search.php
|
|
│ ├── memoire.php
|
|
│ ├── admin/
|
|
│ └── assets/
|
|
├── src/ # ✅ PRIVATE
|
|
├── config/ # ✅ PRIVATE
|
|
├── database/ # ✅ PRIVATE
|
|
├── vendor/ # ✅ PRIVATE
|
|
├── var/ # ✅ PRIVATE (cache, logs)
|
|
└── lib/ # ✅ PRIVATE
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 Updated Justfile
|
|
|
|
### Change 1: Dev Server
|
|
|
|
**Current:**
|
|
```just
|
|
[group('dev')]
|
|
serve:
|
|
@echo "🚀 Starting Post-ERG development server"
|
|
@echo "📍 Public site: http://localhost:8000"
|
|
@echo "📍 Admin panel: http://localhost:8000/admin/"
|
|
@php -S 127.0.0.1:8000
|
|
```
|
|
|
|
**New:**
|
|
```just
|
|
[group('dev')]
|
|
serve:
|
|
@echo "🚀 Starting Post-ERG development server"
|
|
@echo "========================================"
|
|
@echo ""
|
|
@echo "📍 Public site: http://localhost:8000"
|
|
@echo "📍 Admin panel: http://localhost:8000/admin/"
|
|
@echo ""
|
|
@echo "🔒 Serving from public/ directory (matches production)"
|
|
@echo ""
|
|
@if [ -d "vendor/php-live-reload" ]; then \
|
|
echo "✨ Live reload enabled - browser auto-refreshes on file save!"; \
|
|
else \
|
|
echo "💡 Tip: Run 'just setup' to enable live reload"; \
|
|
fi
|
|
@echo ""
|
|
@echo "Press Ctrl+C to stop"
|
|
@echo ""
|
|
@php -S 127.0.0.1:8000 -t public/
|
|
|
|
# Alternative: If you need router script for URL rewriting
|
|
[group('dev')]
|
|
serve-router:
|
|
@echo "🚀 Starting with router script (for clean URLs)"
|
|
@php -S 127.0.0.1:8000 -t public/ public/router.php
|
|
```
|
|
|
|
**Key change:** `-t public/` flag tells PHP server to use `public/` as DocumentRoot
|
|
|
|
---
|
|
|
|
### Change 2: Deployment Strategy
|
|
|
|
**Current (❌):**
|
|
```just
|
|
deploy:
|
|
rsync -vur --progress \
|
|
--exclude 'vendor' --exclude 'tests' ... \
|
|
./ posterg:/var/www/html/
|
|
```
|
|
|
|
**New (✅) - Two-Step Deployment:**
|
|
|
|
```just
|
|
[group('deploy')]
|
|
deploy:
|
|
@echo "📤 Deploying Post-ERG site"
|
|
@echo "=========================="
|
|
@echo ""
|
|
@echo "Step 1: Deploying application files..."
|
|
|
|
# Deploy entire application to private directory
|
|
rsync -vur --progress \
|
|
--exclude '.git*' \
|
|
--exclude '.jj' \
|
|
--exclude 'tests/' \
|
|
--exclude 'docs/' \
|
|
--exclude '*.md' \
|
|
--exclude 'justfile*' \
|
|
--exclude 'setup-dev.sh' \
|
|
--exclude 'migrate-structure.sh' \
|
|
--exclude 'database/test.db' \
|
|
--exclude 'database/backup_*' \
|
|
--exclude 'database/fixtures/' \
|
|
--exclude 'var/cache/*' \
|
|
--exclude 'var/logs/*' \
|
|
--exclude '.DS_Store' \
|
|
./ posterg:/var/www/posterg/
|
|
|
|
@echo ""
|
|
@echo "Step 2: Setting up directory structure..."
|
|
|
|
# Create necessary directories on server
|
|
ssh posterg "mkdir -p /var/www/posterg/var/{cache,logs,tmp} && \
|
|
chown -R www-data:posterg /var/www/posterg/var && \
|
|
chmod -R 775 /var/www/posterg/var"
|
|
|
|
@echo ""
|
|
@echo "Step 3: Setting permissions..."
|
|
|
|
# Set correct ownership and permissions
|
|
ssh posterg "cd /var/www/posterg && \
|
|
chown -R www-data:posterg . && \
|
|
find . -type d -exec chmod 755 {} \; && \
|
|
find . -type f -exec chmod 644 {} \; && \
|
|
chmod -R 775 var/ && \
|
|
chmod -R 775 database/ && \
|
|
chmod 660 database/*.db"
|
|
|
|
@echo ""
|
|
@echo "✅ Deployment complete!"
|
|
@echo ""
|
|
@echo "🔍 Verify deployment:"
|
|
@echo " • Public: https://posterg.erg.be/"
|
|
@echo " • Admin: https://posterg.erg.be/admin/"
|
|
@echo ""
|
|
@echo "📁 Server structure:"
|
|
@echo " • App root: /var/www/posterg/"
|
|
@echo " • DocumentRoot: /var/www/posterg/public/"
|
|
|
|
# Quick deploy - only public files (faster for frontend changes)
|
|
[group('deploy')]
|
|
deploy-public:
|
|
@echo "⚡ Quick deploy: public files only"
|
|
rsync -vur --progress \
|
|
--delete \
|
|
./public/ posterg:/var/www/posterg/public/
|
|
@echo "✅ Public files updated"
|
|
|
|
# Deploy only code (no assets)
|
|
[group('deploy')]
|
|
deploy-code:
|
|
@echo "⚡ Deploying PHP code only"
|
|
rsync -vur --progress \
|
|
--include='**.php' \
|
|
--include='**/' \
|
|
--exclude='*' \
|
|
./ posterg:/var/www/posterg/
|
|
@echo "✅ Code updated"
|
|
```
|
|
|
|
---
|
|
|
|
### Change 3: Database Deployment
|
|
|
|
**Current:**
|
|
```just
|
|
test-deploy:
|
|
ssh posterg "mkdir -p /var/www/html/database"
|
|
rsync -vur --progress ./database/test.db posterg:/var/www/html/database/test.db
|
|
```
|
|
|
|
**New:**
|
|
```just
|
|
[group('deploy')]
|
|
deploy-database:
|
|
@echo "📊 Deploying database..."
|
|
@echo "⚠️ This will overwrite the remote database!"
|
|
@read -p "Continue? [y/N] " -n 1 -r; \
|
|
echo; \
|
|
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \
|
|
ssh posterg "mkdir -p /var/www/posterg/database" && \
|
|
rsync -vur --progress ./database/test.db posterg:/var/www/posterg/database/ && \
|
|
ssh posterg "chown www-data:posterg /var/www/posterg/database/test.db && \
|
|
chmod 660 /var/www/posterg/database/test.db" && \
|
|
echo "✅ Database deployed"; \
|
|
else \
|
|
echo "❌ Cancelled"; \
|
|
fi
|
|
|
|
# Backup remote database before deploying
|
|
[group('deploy')]
|
|
backup-remote-db:
|
|
@echo "💾 Backing up remote database..."
|
|
@ssh posterg "sqlite3 /var/www/posterg/database/test.db .dump" > database/remote_backup_$(date +%Y%m%d_%H%M%S).sql
|
|
@echo "✅ Remote database backed up locally"
|
|
```
|
|
|
|
---
|
|
|
|
### Change 4: Updated Nginx Deployment
|
|
|
|
**Current nginx configuration probably points to:**
|
|
```nginx
|
|
root /var/www/html;
|
|
```
|
|
|
|
**Should change to:**
|
|
```nginx
|
|
root /var/www/posterg/public;
|
|
```
|
|
|
|
**Updated recipe:**
|
|
```just
|
|
[group('server')]
|
|
deploy-nginx:
|
|
@echo "🔧 Deploying nginx configuration..."
|
|
@echo ""
|
|
@echo "⚠️ IMPORTANT: Nginx config must point to /var/www/posterg/public"
|
|
@echo ""
|
|
|
|
# Check if nginx config has correct DocumentRoot
|
|
@if ! grep -q "/var/www/posterg/public" nginx/posterg.conf 2>/dev/null; then \
|
|
echo "❌ ERROR: nginx/posterg.conf doesn't contain '/var/www/posterg/public'"; \
|
|
echo " Update DocumentRoot before deploying!"; \
|
|
exit 1; \
|
|
fi
|
|
|
|
rsync -vur --progress ./nginx/posterg.conf posterg:/tmp/posterg.conf
|
|
rsync -vur --progress ./nginx/deploy-production.sh posterg:/tmp/deploy-production.sh
|
|
|
|
@echo "✅ Files uploaded to /tmp/ on server"
|
|
@echo ""
|
|
@echo "Next steps on the server:"
|
|
@echo " ssh posterg"
|
|
@echo " sudo bash /tmp/deploy-production.sh"
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Nginx Configuration Changes
|
|
|
|
### Update `nginx/posterg.conf`
|
|
|
|
**Find and replace:**
|
|
```nginx
|
|
# OLD
|
|
root /var/www/html;
|
|
|
|
# NEW
|
|
root /var/www/posterg/public;
|
|
```
|
|
|
|
**Complete example:**
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name posterg.erg.be;
|
|
|
|
# Redirect to HTTPS
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name posterg.erg.be;
|
|
|
|
# NEW DocumentRoot - only public directory
|
|
root /var/www/posterg/public;
|
|
index index.php index.html;
|
|
|
|
# SSL configuration
|
|
ssl_certificate /etc/letsencrypt/live/posterg.erg.be/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/posterg.erg.be/privkey.pem;
|
|
|
|
# Logs
|
|
access_log /var/log/nginx/posterg_access.log;
|
|
error_log /var/log/nginx/posterg_error.log;
|
|
|
|
# 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;
|
|
|
|
# Deny access to sensitive files (should already be outside public/)
|
|
location ~ /\. {
|
|
deny all;
|
|
}
|
|
|
|
location ~ /database/ {
|
|
deny all;
|
|
}
|
|
|
|
# Admin area - basic auth
|
|
location /admin/ {
|
|
auth_basic "Admin Access";
|
|
auth_basic_user_file /etc/nginx/.htpasswd;
|
|
|
|
location ~ \.php$ {
|
|
include snippets/fastcgi-php.conf;
|
|
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
|
|
# IMPORTANT: Set correct SCRIPT_FILENAME
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
}
|
|
}
|
|
|
|
# PHP files
|
|
location ~ \.php$ {
|
|
include snippets/fastcgi-php.conf;
|
|
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
}
|
|
|
|
# Static files
|
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Deny access to uploaded files execution
|
|
location ~ ^/uploads/.*\.php$ {
|
|
deny all;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Migration Steps
|
|
|
|
### Step 1: Local Development
|
|
```bash
|
|
# 1. Create new structure
|
|
mkdir -p public/{admin,assets}
|
|
mkdir -p src config var/{cache,logs,tmp}
|
|
|
|
# 2. Move public files
|
|
mv index.php search.php memoire.php public/
|
|
mv assets/* public/assets/
|
|
mv admin/* public/admin/
|
|
|
|
# 3. Move private files
|
|
mv inc/* config/ # or src/ depending on content
|
|
mv lib src/lib
|
|
|
|
# 4. Update paths in PHP files (see PATHS_UPDATE.md below)
|
|
|
|
# 5. Test local dev server
|
|
just serve
|
|
# Opens http://localhost:8000
|
|
# Verify that sensitive files return 404:
|
|
# http://localhost:8000/database/test.db → 404
|
|
# http://localhost:8000/config/ → 404
|
|
# http://localhost:8000/src/ → 404
|
|
```
|
|
|
|
### Step 2: Update Nginx Config
|
|
```bash
|
|
# Edit nginx/posterg.conf
|
|
# Change: root /var/www/html;
|
|
# To: root /var/www/posterg/public;
|
|
|
|
# Validate locally
|
|
nginx -t -c nginx/posterg.conf
|
|
```
|
|
|
|
### Step 3: Deploy
|
|
```bash
|
|
# 1. Backup current production
|
|
ssh posterg "tar -czf /tmp/posterg-backup-$(date +%Y%m%d).tar.gz /var/www/html"
|
|
|
|
# 2. Create new directory structure
|
|
ssh posterg "mkdir -p /var/www/posterg"
|
|
|
|
# 3. Deploy application
|
|
just deploy
|
|
|
|
# 4. Deploy nginx config
|
|
just deploy-nginx
|
|
|
|
# 5. On server: activate new config
|
|
ssh posterg
|
|
sudo bash /tmp/deploy-production.sh
|
|
|
|
# 6. Reload nginx
|
|
sudo systemctl reload nginx
|
|
|
|
# 7. Test
|
|
just server-status
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Testing Checklist
|
|
|
|
### Local Testing
|
|
- [ ] `just serve` starts on port 8000
|
|
- [ ] Public site loads: http://localhost:8000
|
|
- [ ] Admin loads: http://localhost:8000/admin/
|
|
- [ ] Assets load (CSS, images, JS)
|
|
- [ ] Database files NOT accessible via browser
|
|
- [ ] Config files NOT accessible via browser
|
|
- [ ] Tests still pass: `just test`
|
|
|
|
### Production Testing
|
|
- [ ] HTTPS works: https://posterg.erg.be/
|
|
- [ ] Admin login works
|
|
- [ ] Search functionality works
|
|
- [ ] Database connections work
|
|
- [ ] File uploads work (if applicable)
|
|
- [ ] Logs written to `/var/www/posterg/var/logs/`
|
|
- [ ] Sensitive URLs return 404:
|
|
- https://posterg.erg.be/database/test.db
|
|
- https://posterg.erg.be/config/
|
|
- https://posterg.erg.be/src/
|
|
- https://posterg.erg.be/vendor/
|
|
|
|
---
|
|
|
|
## 📊 Path Changes Summary
|
|
|
|
| File Type | Current Path | New Path |
|
|
|-----------|-------------|----------|
|
|
| Public index | `/index.php` | `/public/index.php` |
|
|
| Admin panel | `/admin/` | `/public/admin/` |
|
|
| Assets | `/assets/` | `/public/assets/` |
|
|
| Config | `/inc/` | `/config/` or `/src/` |
|
|
| Libraries | `/lib/` | `/src/lib/` |
|
|
| Database | `/database/` | `/database/` (stays) |
|
|
| Vendor | `/vendor/` | `/vendor/` (stays) |
|
|
| Tests | `/tests/` | `/tests/` (stays) |
|
|
|
|
---
|
|
|
|
## 🔒 Security Improvements
|
|
|
|
### Before
|
|
- ❌ All files in DocumentRoot
|
|
- ❌ Relies on nginx deny rules
|
|
- ❌ One misconfiguration = full exposure
|
|
- ❌ Database accessible if nginx fails
|
|
|
|
### After
|
|
- ✅ Only `public/` in DocumentRoot
|
|
- ✅ Physical separation of public/private
|
|
- ✅ Nginx misconfiguration = site down (not exposed)
|
|
- ✅ Database physically unreachable via web
|
|
|
|
---
|
|
|
|
## 💡 Tips
|
|
|
|
### Router Script (for clean URLs)
|
|
Create `public/router.php` for development:
|
|
```php
|
|
<?php
|
|
// Development router script
|
|
if (php_sapi_name() === 'cli-server') {
|
|
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
|
|
// Serve static files directly
|
|
if (file_exists(__DIR__ . $path) && is_file(__DIR__ . $path)) {
|
|
return false;
|
|
}
|
|
|
|
// Route everything else to index.php
|
|
require __DIR__ . '/index.php';
|
|
}
|
|
```
|
|
|
|
### Environment Detection
|
|
In your code, detect environment:
|
|
```php
|
|
<?php
|
|
// config/bootstrap.php
|
|
define('IS_DEV', php_sapi_name() === 'cli-server');
|
|
define('APP_ROOT', dirname(__DIR__)); // /var/www/posterg
|
|
define('PUBLIC_ROOT', APP_ROOT . '/public');
|
|
```
|
|
|
|
### Use Relative Requires
|
|
```php
|
|
// Before (brittle)
|
|
require_once '/var/www/html/inc/config.php';
|
|
|
|
// After (portable)
|
|
require_once __DIR__ . '/../config/app.php';
|
|
// or
|
|
require_once APP_ROOT . '/config/app.php';
|
|
```
|
|
|
|
---
|
|
|
|
## 🆘 Troubleshooting
|
|
|
|
### Issue: 404 on all pages after deploy
|
|
**Cause:** Nginx DocumentRoot not updated
|
|
**Fix:** Check nginx config has `root /var/www/posterg/public;`
|
|
|
|
### Issue: PHP includes fail
|
|
**Cause:** Hardcoded paths or wrong APP_ROOT
|
|
**Fix:** Use `__DIR__` or define APP_ROOT constant
|
|
|
|
### Issue: Database connection fails
|
|
**Cause:** Path to database file wrong
|
|
**Fix:** Update path from `database/test.db` to `../database/test.db` (from public/)
|
|
|
|
### Issue: Can't write to cache/logs
|
|
**Cause:** Wrong permissions on var/ directory
|
|
**Fix:** `sudo chown -R www-data:posterg /var/www/posterg/var && chmod -R 775 /var/www/posterg/var`
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. Create `PATHS_UPDATE.md` - document all PHP file path changes needed
|
|
2. Create `public/router.php` - for cleaner dev URLs
|
|
3. Update all `require`/`include` statements to use relative paths
|
|
4. Test migration on staging/local first
|
|
5. Schedule production deployment during low-traffic period
|