Fix admin CSS not loading and quirks mode issues

Fixed multiple issues in admin panel:

1. CSS path: modern-normalize.css → modern-normalize.min.css
   (File is actually named .min.css)

2. Icon path: assets/icon.svg → /assets/admin_favicon.svg
   (Was relative, now absolute; correct filename)

3. Navigation: /admin/list.php → /admin/
   (list.php was renamed to index.php)

4. Short PHP tags: <? → <?php
   (Better compatibility, some servers don't enable short_open_tag)

5. Quirks mode warning was due to CSS not loading, not DOCTYPE
   (DOCTYPE was already present)

Files modified:
- public/admin/inc/head.php (main fixes)
- public/admin/index.php (short tags)
- public/admin/add.php (short tags)
- public/admin/import.php (short tags)

Need to redeploy for production: just deploy
This commit is contained in:
Théophile Gervreau-Mercier
2026-02-06 12:14:26 +01:00
parent e789c286de
commit 4bbbc58e24
44 changed files with 1850 additions and 377 deletions

View File

@@ -0,0 +1,400 @@
# Complete Deployment Guide - New Structure
## Overview
This guide walks you through deploying the new secure directory structure:
- **Old:** `/var/www/html/` (everything exposed)
- **New:** `/var/www/posterg/` with `public/` subdirectory (only public/ exposed)
## Step-by-Step Deployment
### ⚠️ IMPORTANT: Do NOT delete /var/www/html/ yet!
Keep it as a backup until you confirm the new structure works.
---
### Step 1: Setup Server Directory (Manual - One Time)
SSH to server and create the new directory:
```bash
ssh posterg
# Backup current site
sudo cp -r /var/www/html /var/www/html.backup
# Create new directory
sudo mkdir -p /var/www/posterg
# Set ownership (www-data = web server user)
sudo chown www-data:posterg /var/www/posterg
# Set permissions
sudo chmod 775 /var/www/posterg
# Verify
ls -ld /var/www/posterg
# Should show: drwxrwxr-x 2 www-data posterg ...
# Exit server
exit
```
---
### Step 2: Deploy Application Files
From your local machine:
```bash
just deploy
```
This will:
- Upload all files to `/var/www/posterg/`
- Exclude unnecessary files (tests, docs, etc.)
- Set initial ownership to `www-data:posterg`
You should see:
```
sending incremental file list
public/index.php
public/search.php
public/memoire.php
...
```
---
### Step 3: Deploy Nginx Configuration
```bash
just deploy-nginx
```
This will:
1. Check that nginx config has correct DocumentRoot (`/var/www/posterg/public`)
2. Upload `posterg.conf` to `/tmp/` on server
3. Upload `deploy-production-new.sh` to `/tmp/` on server
Expected output:
```
✅ nginx config looks correct
✅ Files uploaded to /tmp/ on server
```
---
### Step 4: Apply Nginx Configuration on Server
SSH to server and run the deployment script:
```bash
ssh posterg
sudo bash /tmp/deploy-production.sh
```
The script will:
1. Fix file permissions on `/var/www/posterg/`
2. Backup existing nginx config
3. Install new nginx config
4. Test nginx configuration
5. Show you the reload command
Expected output:
```
🚀 Post-ERG Production Deployment (NEW STRUCTURE)
==================================================
📋 Step 1: Fixing file permissions...
✓ Changed ownership to www-data:posterg
✓ Set directory permissions to 755
✓ Set file permissions to 644
✓ Made database directory group-writable (775)
✓ Fixed database file permissions (660)
📋 Step 2: Deploying nginx configuration...
✓ Backed up existing config
✓ Installed new nginx config
📋 Step 3: Testing nginx configuration...
✓ Nginx configuration is valid
📋 Step 4: Summary...
✓ Permissions fixed
✓ Nginx config installed
✓ Configuration validated
Ready to reload nginx!
Run: sudo systemctl reload nginx
```
---
### Step 5: Reload Nginx
Still on the server:
```bash
sudo systemctl reload nginx
```
If successful, you'll see no output (which is good!).
Check status:
```bash
sudo systemctl status nginx
# Should show "active (running)"
```
Exit server:
```bash
exit
```
---
### Step 6: Verify Deployment
From your local machine:
```bash
just server-status
```
Expected output:
```
🔍 Server Status
================
✓ Nginx running
✓ PHP-FPM running
Site check:
• Public: 200 ✓
• Admin: 200 ✓
```
---
### Step 7: Security Verification
Test these URLs in your browser or with curl:
```bash
# Should work (200 OK):
curl -I https://posterg.erg.be/
curl -I https://posterg.erg.be/admin/
# Should be 404 (SECURITY - private files):
curl -I https://posterg.erg.be/database/test.db
curl -I https://posterg.erg.be/config/bootstrap.php
curl -I https://posterg.erg.be/includes/header.php
curl -I https://posterg.erg.be/lib/Database.php
```
**All private files must return 404!** If they don't, nginx is not configured correctly.
---
### Step 8: Deploy Database (If Needed)
If you need to update the database:
```bash
just deploy-database
```
This will warn you before overwriting.
---
### Step 9: Test Thoroughly
- [ ] Visit https://posterg.erg.be/
- [ ] Browse thesis list
- [ ] Open a thesis detail page
- [ ] Try search functionality
- [ ] Access admin panel (should require login)
- [ ] Verify private files return 404
---
### Step 10: Keep Backup for a While
**Do NOT delete `/var/www/html/` immediately!**
Keep it for a few days to ensure everything works. If you need to rollback:
```bash
ssh posterg
sudo nano /etc/nginx/sites-available/posterg
# Change: root /var/www/posterg/public;
# Back to: root /var/www/html;
sudo systemctl reload nginx
```
Your old site will work immediately.
---
## After Confirming Everything Works
A few days later, once you're confident:
```bash
ssh posterg
sudo rm -rf /var/www/html.backup
sudo rm -rf /var/www/html
```
---
## What Changed?
### Directory Structure
```
OLD: NEW:
/var/www/html/ /var/www/posterg/
├── index.php ├── public/ ← DocumentRoot
├── search.php │ ├── index.php
├── admin/ │ ├── search.php
├── assets/ │ ├── memoire.php
├── database/ ❌ exposed │ ├── admin/
├── lib/ ❌ exposed │ └── assets/
└── ... ├── includes/ ✅ private
├── config/ ✅ private
├── database/ ✅ private
└── lib/ ✅ private
```
### Nginx Configuration
```nginx
# OLD
root /var/www/html;
location ^~ /formulaire/ { ... }
# NEW
root /var/www/posterg/public;
location ^~ /admin/ { ... }
```
### Security Impact
| Resource | Old | New |
|----------|-----|-----|
| Database | ❌ Accessible if nginx misconfigured | ✅ Physically outside web root |
| Config files | ❌ One deny rule away | ✅ Physically private |
| Source code | ❌ Exposed | ✅ Physically private |
| Admin panel | /formulaire/ | /admin/ |
---
## Troubleshooting
### Site returns 404 for everything
**Cause:** Nginx still pointing to old location or wrong DocumentRoot
**Fix:**
```bash
ssh posterg
sudo cat /etc/nginx/sites-available/posterg | grep "root "
# Should show: root /var/www/posterg/public;
```
If wrong:
```bash
sudo nano /etc/nginx/sites-available/posterg
# Fix the root directive
sudo systemctl reload nginx
```
### Database errors
**Cause:** Wrong permissions on database file
**Fix:**
```bash
ssh posterg
sudo chown www-data:posterg /var/www/posterg/database/test.db
sudo chmod 660 /var/www/posterg/database/test.db
```
### Admin upload errors
**Cause:** Upload directories not writable
**Fix:**
```bash
ssh posterg
sudo chmod 775 /var/www/posterg/public/admin/data
sudo find /var/www/posterg/public/admin/data -type d -exec chmod 775 {} \;
```
### Private files still accessible
**Cause:** Nginx serving from wrong directory
**Fix:** Verify nginx DocumentRoot and reload
---
## Rollback Procedure
If you need to go back to the old structure:
```bash
# 1. SSH to server
ssh posterg
# 2. Restore old nginx config
sudo nano /etc/nginx/sites-available/posterg
# Change: root /var/www/posterg/public;
# To: root /var/www/html;
# Change: location ^~ /admin/
# To: location ^~ /formulaire/
# 3. Reload nginx
sudo systemctl reload nginx
# 4. Verify
curl -I https://posterg.erg.be/
```
Your old site should work immediately (as long as you didn't delete `/var/www/html/`).
---
## Quick Reference
| Task | Command |
|------|---------|
| Deploy files | `just deploy` |
| Deploy nginx | `just deploy-nginx` |
| Deploy database | `just deploy-database` |
| Check status | `just server-status` |
| View logs | `just server-logs` |
---
## Success Checklist
- [ ] `/var/www/posterg/` created with correct permissions
- [ ] Files deployed with `just deploy`
- [ ] Nginx config deployed with `just deploy-nginx`
- [ ] Permissions fixed with `deploy-production.sh`
- [ ] Nginx reloaded successfully
- [ ] Site accessible at https://posterg.erg.be/
- [ ] Admin accessible at https://posterg.erg.be/admin/
- [ ] Private files return 404 (security verified)
- [ ] Database working (can view theses)
- [ ] Search working
- [ ] Old `/var/www/html/` kept as backup
---
**After deployment, your site will be significantly more secure!** 🔒

View File

@@ -0,0 +1,571 @@
# 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

215
docs/DEPLOYMENT_STEPS.md Normal file
View File

@@ -0,0 +1,215 @@
# Deployment Steps
## First-Time Deployment (New Structure)
Since we're moving from `/var/www/html/` to `/var/www/posterg/`, follow these steps:
### 1. Setup Server Directory (ONE TIME)
```bash
just setup-server
```
This creates `/var/www/posterg/` with correct permissions:
- Owner: `www-data:posterg`
- Permissions: `775`
### 2. Deploy Application
```bash
just deploy
```
This deploys all files to `/var/www/posterg/`:
- `public/``/var/www/posterg/public/`
- `includes/``/var/www/posterg/includes/`
- `config/``/var/www/posterg/config/`
- `database/``/var/www/posterg/database/`
- `lib/``/var/www/posterg/lib/`
### 3. Update Nginx Configuration
```bash
just deploy-nginx
```
This checks that nginx config has correct DocumentRoot and uploads it to server.
**IMPORTANT:** The nginx config must have:
```nginx
root /var/www/posterg/public;
```
If the check fails, edit `nginx/posterg.conf` first.
### 4. Apply Nginx Configuration on Server
```bash
ssh posterg
sudo bash /tmp/deploy-production.sh
sudo systemctl reload nginx
```
### 5. Verify Deployment
```bash
just server-status
```
Check:
- https://posterg.erg.be/ (should work)
- https://posterg.erg.be/admin/ (should work)
- https://posterg.erg.be/database/test.db (should 404 ✅)
---
## Subsequent Deployments
After the first deployment, you only need:
```bash
just deploy
```
That's it! The directory structure is already in place.
---
## Deploy Database
If you need to deploy the database:
```bash
just deploy-database
```
This will:
1. Upload `database/test.db` to server
2. Set correct permissions
3. Warn before overwriting
---
## Troubleshooting
### Permission Denied on Deploy
**Error:**
```
mkdir "/var/www/posterg" failed: Permission denied (13)
```
**Solution:**
```bash
just setup-server
```
### Nginx 500 Error
**Cause:** Nginx DocumentRoot still pointing to old location
**Solution:**
1. Check nginx config has: `root /var/www/posterg/public;`
2. Redeploy nginx config:
```bash
just deploy-nginx
ssh posterg
sudo bash /tmp/deploy-production.sh
sudo systemctl reload nginx
```
### Database Connection Errors
**Cause:** Database file permissions incorrect
**Solution:**
```bash
ssh posterg
cd /var/www/posterg
sudo chown www-data:posterg database/test.db
sudo chmod 660 database/test.db
```
### Admin 404
**Cause:** Nginx still using old `/formulaire/` location
**Solution:** Update `nginx/posterg.conf` to use `/admin/` location
---
## Rollback
If something goes wrong, rollback is easy:
### 1. Restore Old Directory
```bash
ssh posterg
sudo cp -r /var/www/html.backup /var/www/html # If you backed up
```
### 2. Restore Old Nginx Config
```bash
ssh posterg
sudo cp /etc/nginx/sites-available/posterg.backup /etc/nginx/sites-available/posterg
sudo systemctl reload nginx
```
### 3. Rollback Code with jj
```bash
jj log
jj edit <previous-change-id>
```
---
## Migration Checklist
- [ ] `just setup-server` - Create server directory
- [ ] `just deploy` - Deploy application
- [ ] `just deploy-nginx` - Update nginx config
- [ ] SSH to server and apply nginx config
- [ ] `sudo systemctl reload nginx`
- [ ] Verify site works: https://posterg.erg.be/
- [ ] Verify security: https://posterg.erg.be/database/test.db → 404
- [ ] Test admin: https://posterg.erg.be/admin/
- [ ] Deploy database (if needed): `just deploy-database`
---
## Commands Reference
| Command | Purpose |
|---------|---------|
| `just setup-server` | Create `/var/www/posterg/` (first time only) |
| `just deploy` | Deploy application files |
| `just deploy-nginx` | Update nginx configuration |
| `just deploy-database` | Deploy database file |
| `just server-status` | Check server health |
| `just server-logs` | View server logs |
---
## Directory Structure on Server
```
/var/www/posterg/ # Application root (private)
├── public/ # DocumentRoot (nginx points here)
│ ├── index.php
│ ├── search.php
│ ├── memoire.php
│ ├── admin/
│ └── assets/
├── includes/ # Templates (private)
├── config/ # Configuration (private)
├── database/ # Database (private)
├── lib/ # PHP classes (private)
└── vendor/ # Dependencies (private)
```
**Nginx DocumentRoot:** `/var/www/posterg/public/`
Only the `public/` directory is accessible via web browser. Everything else is private.

246
docs/DIRECTORY_STRUCTURE.md Normal file
View File

@@ -0,0 +1,246 @@
# Recommended Directory Structure
Based on the **Standard PHP Package Skeleton** (researched by Paul M. Jones from thousands of GitHub projects).
## Directory Layout
```
posterg-website/
├── public/ # DocumentRoot - publicly accessible files
│ ├── index.php # Front controller
│ ├── assets/ # Public assets (CSS, JS, images)
│ │ ├── css/
│ │ ├── js/
│ │ └── images/
│ └── .htaccess # Apache/nginx rules
├── src/ # Application source code (private)
│ ├── Controller/ # Controllers
│ ├── Model/ # Models
│ ├── View/ # Views/templates
│ ├── Service/ # Business logic services
│ ├── Repository/ # Data access layer
│ └── Middleware/ # Middleware components
├── config/ # Configuration files (private)
│ ├── app.php
│ ├── database.php
│ ├── routes.php
│ └── .env.example # Environment variables template
├── database/ # Database-related files
│ ├── migrations/ # Database migrations
│ ├── seeds/ # Database seeders
│ └── schema.sql # Database schema
├── tests/ # Unit and integration tests
│ ├── Unit/
│ ├── Integration/
│ └── bootstrap.php
├── vendor/ # Third-party dependencies (Composer)
│ └── autoload.php
├── bin/ # Executable scripts
│ └── console # CLI commands
├── var/ # Variable/temporary files (private)
│ ├── cache/ # Application cache
│ ├── logs/ # Log files
│ └── tmp/ # Temporary files
├── docs/ # Documentation
│ └── *.md
├── scripts/ # Build/deployment scripts
│ └── deploy.sh
├── resources/ # Non-PHP resources (private)
│ ├── views/ # Template files
│ ├── lang/ # Translations
│ └── emails/ # Email templates
├── lib/ # Internal libraries (if not using src/)
├── .git/ # Git repository
├── .gitignore
├── composer.json # Composer dependencies
├── composer.lock
├── phpunit.xml # PHPUnit configuration
├── README.md # Project documentation
└── LICENSE # License file
```
## Directory Purposes
### **public/** (PUBLIC - DocumentRoot points here)
- **Only directory accessible via web browser**
- Contains: front controller (index.php), assets (CSS/JS/images)
- Web server DocumentRoot should point to this directory
- Security: No sensitive files here
### **src/** (PRIVATE)
- Application source code
- All classes following PSR-4 autoloading
- Organized by responsibility (Controller, Model, Service, etc.)
- Not accessible from the web
### **config/** (PRIVATE)
- Configuration files
- Database credentials, API keys, app settings
- `.env` file for environment-specific configuration
- Never committed sensitive values (use `.env.example`)
### **database/** (PRIVATE)
- Database migrations, seeds, schema definitions
- Version-controlled database structure
### **tests/** (PRIVATE)
- PHPUnit tests
- Organized by test type (Unit, Integration, Functional)
- Mirror the `src/` structure
### **vendor/** (PRIVATE)
- Composer dependencies
- Auto-generated, excluded from version control
- Contains `autoload.php` for autoloading
### **bin/** (PRIVATE)
- Executable scripts and CLI commands
- Make scripts executable: `chmod +x bin/*`
### **var/** (PRIVATE)
- Runtime-generated files
- cache/, logs/, tmp/
- Typically .gitignored (except .gitkeep files)
- Needs write permissions
### **docs/** (PRIVATE)
- Project documentation
- API docs, guides, architecture decisions
### **scripts/** (PRIVATE)
- Build, deployment, maintenance scripts
- Not part of the application runtime
### **resources/** (PRIVATE)
- Non-PHP application resources
- Templates, translations, email layouts
## Migration from Current Structure
Your current structure → Recommended structure:
```
Current → Recommended
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
index.php → public/index.php
memoire.php → public/memoire.php OR src/Controller/
search.php → public/search.php OR src/Controller/
assets/ → public/assets/
inc/ → src/ OR config/
admin/ → src/Admin/ OR public/admin/
database/ → database/ (keep as-is)
tests/ → tests/ (keep as-is)
vendor/ → vendor/ (keep as-is)
lib/ → lib/ OR src/
docs/ → docs/ (keep as-is)
scripts/ → scripts/ (keep as-is)
nginx/ → scripts/nginx/ OR config/nginx/
```
## Security Best Practices
1. **DocumentRoot = public/**
- Configure web server to serve only from `public/`
- All other directories are above DocumentRoot
2. **Sensitive Files**
- Keep `.env`, config files outside `public/`
- Never commit passwords, API keys
- Use `.env.example` for templates
3. **Permissions**
```bash
# Private directories (not writable by web server)
chmod 755 src/ config/ database/ tests/
# Writable by web server
chmod 775 var/cache/ var/logs/ var/tmp/
```
4. **.gitignore**
```
/vendor/
/var/cache/*
/var/logs/*
/var/tmp/*
/.env
composer.lock
```
## Web Server Configuration
### Nginx
```nginx
server {
root /path/to/posterg-website/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
}
```
### Apache (.htaccess in public/)
```apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
</IfModule>
```
## Composer Configuration
Update `composer.json` to use PSR-4 autoloading:
```json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
```
## Next Steps
1. Create `public/` directory
2. Move web-accessible files to `public/`
3. Organize classes into `src/` with namespaces
4. Move configuration to `config/`
5. Update web server DocumentRoot
6. Update paths in application code
7. Run `composer dump-autoload`
8. Test the application
## References
- [Standard PHP Package Skeleton](https://github.com/php-pds/skeleton)
- [PSR-4 Autoloading](https://www.php-fig.org/psr/psr-4/)
- [Composer Documentation](https://getcomposer.org/doc/)

381
docs/MIGRATION_CHECKLIST.md Normal file
View File

@@ -0,0 +1,381 @@
# Migration Checklist - Public Directory Structure
Quick reference for migrating from current flat structure to secure public/ structure.
## 📋 Summary of Required Changes
### 1. Dev Server (justfile) ⚡
**Line 51** - Change from:
```just
@php -S 127.0.0.1:8000
```
To:
```just
@php -S 127.0.0.1:8000 -t public/
```
### 2. Deployment (justfile) 📤
**Lines 56-75** - Replace entire `deploy` recipe with two-step deployment:
- Deploy app to `/var/www/posterg/` (not `/var/www/html/`)
- Nginx serves from `/var/www/posterg/public/`
See `DEPLOYMENT_MIGRATION.md` for complete updated recipe.
### 3. Nginx Configuration 🔧
**nginx/posterg.conf line 14** - Change from:
```nginx
root /var/www/html;
```
To:
```nginx
root /var/www/posterg/public;
```
**Also update admin location** (line 58) - Change from:
```nginx
location ^~ /formulaire/ {
```
To:
```nginx
location ^~ /admin/ {
```
(Since you're moving admin/ to public/admin/)
---
## 🚀 Quick Migration (Local Dev)
```bash
# 1. Update justfile serve command
sed -i 's/@php -S 127.0.0.1:8000/@php -S 127.0.0.1:8000 -t public\//' justfile
# 2. Test new dev server
just serve
# Visit http://localhost:8000
# Verify http://localhost:8000/database/test.db returns 404
# 3. If it works, you're ready for production migration
```
---
## 📁 File Movements (Do This After Testing)
```bash
# Create new structure
mkdir -p public/{admin,assets}
mkdir -p src config var/{cache,logs,tmp}
# Move public files
mv index.php search.php memoire.php public/
mv admin/* public/admin/ && rmdir admin
mv assets/* public/assets/ && rmdir assets
# Move private files
mv inc/* config/ && rmdir inc
# OR if inc/ contains classes:
# mv inc/* src/ && rmdir inc
# Keep these as-is
# database/ (already private)
# vendor/ (already private)
# tests/ (already private)
# lib/ (decide if it goes to src/lib or stays)
```
---
## ⚠️ Critical Changes Required
### In nginx/posterg.conf:
1. **Line 14** - DocumentRoot
```nginx
# BEFORE
root /var/www/html;
# AFTER
root /var/www/posterg/public;
```
2. **Line 58-76** - Admin location (already at `/formulaire/`, might want `/admin/`)
```nginx
# BEFORE
location ^~ /formulaire/ {
auth_basic "Admin Access - Post-ERG";
auth_basic_user_file /etc/nginx/.htpasswd-posterg;
# ... rest of config
}
# AFTER (if renaming to /admin/)
location ^~ /admin/ {
auth_basic "Admin Access - Post-ERG";
auth_basic_user_file /etc/nginx/.htpasswd-posterg;
# ... rest of config
}
```
3. **Remove/update deny rules** (lines 48-60) - These become redundant!
```nginx
# BEFORE - needed because everything in DocumentRoot
location ^~ /database/ { deny all; }
location ^~ /shared/ { deny all; }
location ^~ /data/ { deny all; }
# AFTER - can remove! They're already outside public/
# But keep as defense-in-depth:
location ^~ /database/ { deny all; } # Will never match, but safe
```
### In justfile:
**Complete replacement for lines 40-76:**
```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)"
@if [ -d "vendor/php-live-reload" ]; then \
echo "✨ Live reload enabled"; \
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/
[group('deploy')]
deploy:
@echo "📤 Deploying Post-ERG complete site"
@echo "===================================="
@echo ""
@echo "Deploying to /var/www/posterg/..."
rsync -vur --progress \
--chown="www-data:posterg" \
--exclude 'vendor' \
--exclude 'tests' \
--exclude 'test.db' \
--exclude '*.md' \
--exclude '.git*' \
--exclude '.jj' \
--exclude '.DS_Store' \
--exclude 'justfile*' \
--exclude 'setup-dev.sh' \
--exclude 'database/backup_*' \
--exclude 'database/fixtures' \
--exclude 'var/cache/*' \
--exclude 'var/logs/*' \
./ posterg:/var/www/posterg/
@echo ""
@echo "Setting up directories and permissions..."
ssh posterg "cd /var/www/posterg && \
mkdir -p var/{cache,logs,tmp} && \
chown -R www-data:posterg var/ database/ && \
chmod -R 775 var/ database/ && \
chmod 660 database/*.db"
@echo ""
@echo "✅ Deployment complete!"
@echo ""
@echo "🔍 Verify:"
@echo " • Public: https://posterg.erg.be/"
@echo " • Admin: https://posterg.erg.be/admin/"
[group('deploy')]
test-deploy:
@echo "⚠️ Deploying test database"
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 "✅ Test database deployed"
```
---
## 🧪 Testing Steps
### 1. Test Local Dev Server
```bash
# Start server with new -t public/ flag
just serve
# In another terminal:
curl http://localhost:8000/ # ✅ Should work
curl http://localhost:8000/admin/ # ✅ Should work (after moving)
curl http://localhost:8000/database/test.db # ❌ Should 404
curl http://localhost:8000/config/ # ❌ Should 404
curl http://localhost:8000/vendor/ # ❌ Should 404
```
### 2. Test After File Migration
```bash
# After moving files to public/
just serve
# Test again
curl http://localhost:8000/ # ✅ index.php serves
curl http://localhost:8000/search.php # ✅ works
curl http://localhost:8000/admin/ # ✅ works
curl http://localhost:8000/assets/css/style.css # ✅ works
# Verify old paths don't work
curl http://localhost:8000/../database/test.db # ❌ 404
curl http://localhost:8000/../config/ # ❌ 404
```
### 3. Test Production Deployment
```bash
# After deploying to server
just server-status
# Manual checks
curl -I https://posterg.erg.be/
curl -I https://posterg.erg.be/admin/
curl -I https://posterg.erg.be/database/test.db # Must be 404!
```
---
## 📝 PHP Path Updates Needed
After moving to public/, update PHP includes:
**Before (from root):**
```php
<?php
require_once 'inc/config.php';
require_once 'lib/Database.php';
require_once 'database/test.db';
```
**After (from public/):**
```php
<?php
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../src/lib/Database.php';
$db = new PDO('sqlite:' . __DIR__ . '/../database/test.db');
```
**Or use a bootstrap:**
```php
<?php
// public/index.php
require_once __DIR__ . '/../config/bootstrap.php';
// config/bootstrap.php
define('APP_ROOT', dirname(__DIR__));
define('PUBLIC_ROOT', APP_ROOT . '/public');
define('DATABASE_PATH', APP_ROOT . '/database/test.db');
require_once APP_ROOT . '/vendor/autoload.php';
```
---
## 🔍 What to Check in Your Code
Run these searches to find hardcoded paths:
```bash
# Find absolute paths
grep -r "/var/www/html" ./*.php
grep -r "/var/www/html" ./admin/*.php
# Find relative paths that need updating
grep -r "require.*inc/" ./*.php
grep -r "require.*lib/" ./*.php
grep -r "require.*database/" ./*.php
# Find database connections
grep -r "\.db" ./*.php
# Find asset references
grep -r "src=\"assets/" ./*.php
grep -r "href=\"assets/" ./*.php
```
---
## ⚡ Quick Start (Minimal Changes First)
If you want to test with minimal changes:
**1. Only change dev server:**
```bash
# Edit justfile line 51
@php -S 127.0.0.1:8000 -t public/
# Create public/ with symlinks (temporary test)
mkdir public
ln -s ../index.php public/index.php
ln -s ../search.php public/search.php
ln -s ../admin public/admin
ln -s ../assets public/assets
# Test
just serve
```
**2. If it works, do full migration**
---
## 🆘 Rollback Plan
If production deployment fails:
```bash
# On server
sudo systemctl stop nginx
# Restore old config
sudo cp /etc/nginx/sites-available/posterg.backup /etc/nginx/sites-available/posterg
sudo nginx -t
sudo systemctl start nginx
# Or restore files
sudo rm -rf /var/www/posterg
sudo mv /var/www/html.backup /var/www/html
```
**Always backup before deploying:**
```bash
# Before migration
ssh posterg "sudo cp -r /var/www/html /var/www/html.backup"
ssh posterg "sudo cp /etc/nginx/sites-available/posterg /etc/nginx/sites-available/posterg.backup"
```
---
## 📊 Benefits After Migration
| Before | After |
|--------|-------|
| ❌ All files in DocumentRoot | ✅ Only public/ in DocumentRoot |
| ❌ Database accessible if nginx misconfigured | ✅ Database physically unreachable |
| ❌ Config files one deny rule away | ✅ Config files outside web root |
| ❌ 20+ deny/exclude rules needed | ✅ Physical separation, minimal rules |
| ❌ Dev server exposes everything | ✅ Dev matches production security |
---
## Next: Check Your Current Paths
Run this to see what paths need updating:
```bash
# Find all require/include statements
find . -name "*.php" -not -path "./vendor/*" -not -path "./tests/*" \
-exec grep -H "require\|include" {} \; > paths-audit.txt
# Review paths-audit.txt and update paths
cat paths-audit.txt
```
See **DEPLOYMENT_MIGRATION.md** for complete implementation details.

214
docs/MIGRATION_COMPLETE.md Normal file
View File

@@ -0,0 +1,214 @@
# ✅ Migration to public/ Directory Structure - COMPLETE
## 📊 Summary of Changes
### Directory Structure Created
```
posterg-website/
├── config/ # ✅ NEW - Configuration files
│ └── bootstrap.php # Central path management
├── public/ # ✅ NEW - DocumentRoot (web-accessible)
│ ├── admin/ # Moved from /admin/
│ ├── assets/ # Moved from /assets/
│ ├── index.php # Moved from /index.php
│ ├── search.php # Moved from /search.php
│ └── memoire.php # Moved from /memoire.php
├── resources/ # ✅ NEW - Application resources
│ └── views/ # Moved from /inc/
│ ├── header.php
│ └── footer.php
├── var/ # ✅ NEW - Runtime files
│ ├── cache/
│ ├── logs/
│ └── tmp/
├── database/ # ✅ KEPT - Now private
├── lib/ # ✅ KEPT - Now private
├── vendor/ # ✅ KEPT - Now private
└── tests/ # ✅ KEPT - Now private
```
### Files Modified
**1. config/bootstrap.php** (NEW)
- Central path configuration
- Defines APP_ROOT, PUBLIC_ROOT, DATABASE_PATH, etc.
- Helper functions: view(), getDatabase()
- Environment detection (dev vs production)
- Error handling configuration
**2. public/*.php** (3 files updated)
- index.php: Uses bootstrap, updated require paths
- search.php: Uses bootstrap, updated require paths
- memoire.php: Uses bootstrap, updated require paths
- All now use view() helper for header/footer
**3. public/admin/*.php** (7 files updated)
- add.php, edit.php, formulaire.php, import.php
- index.php, publish.php, thanks.php
- All updated to use ../../ paths for lib access
- Bootstrap added where needed
**4. justfile** (Updated)
- Dev server: `php -S 127.0.0.1:8000 -t public/`
- Deploy: Now deploys to `/var/www/posterg/`
- Database deploy: Updated paths to `/var/www/posterg/`
- Nginx deploy: Checks for correct DocumentRoot
**5. nginx/posterg.conf** (Updated)
- DocumentRoot: `/var/www/html``/var/www/posterg/public`
- Admin location: `/formulaire/``/admin/`
**6. .gitignore** (Updated)
- Added var/ directory patterns
- Keeps .gitkeep files, ignores contents
### Security Improvements
**Before:**
- ❌ All files in DocumentRoot (/var/www/html/)
- ❌ Database accessible at /database/test.db
- ❌ Config files accessible
- ❌ Dev server exposed everything
- ❌ Relied on nginx deny rules
**After:**
- ✅ Only public/ in DocumentRoot
- ✅ Database physically outside web root
- ✅ Config files physically private
- ✅ Dev server matches production security
- ✅ Physical separation = secure by default
## 🧪 Testing
### Local Development
```bash
# Start dev server
just serve
# Test in browser:
# - http://localhost:8000/ → Should work
# - http://localhost:8000/admin/ → Should work
# - http://localhost:8000/database/test.db → Should 404 ✅
# - http://localhost:8000/config/ → Should 404 ✅
# - http://localhost:8000/../database/test.db → Should 404 ✅
```
### Security Verification
```bash
# These should all return 404:
curl http://localhost:8000/database/test.db
curl http://localhost:8000/config/bootstrap.php
curl http://localhost:8000/vendor/autoload.php
curl http://localhost:8000/../database/test.db
curl http://localhost:8000/lib/Database.php
```
### Production Deployment
**BEFORE deploying to production:**
1. **Update nginx config on server:**
```bash
# Edit /etc/nginx/sites-available/posterg
# Change: root /var/www/html;
# To: root /var/www/posterg/public;
```
2. **Create new directory on server:**
```bash
ssh posterg "sudo mkdir -p /var/www/posterg"
```
3. **Deploy application:**
```bash
just deploy
```
4. **Deploy nginx config:**
```bash
just deploy-nginx
# Then on server:
ssh posterg
sudo bash /tmp/deploy-production.sh
sudo systemctl reload nginx
```
5. **Verify:**
```bash
just server-status
curl -I https://posterg.erg.be/
curl -I https://posterg.erg.be/admin/
curl -I https://posterg.erg.be/database/test.db # Must 404!
```
## 📝 Path Reference
### From public/*.php files:
```php
<?php
require_once __DIR__ . '/../config/bootstrap.php'; // Bootstrap
require_once LIB_ROOT . '/Database.php'; // Library
$db = getDatabase(); // Database
view('header.php', ['pageTitle' => 'Title']); // Template
```
### From public/admin/*.php files:
```php
<?php
require_once __DIR__ . '/../../config/bootstrap.php'; // Bootstrap
require_once LIB_ROOT . '/Database.php'; // Library
```
### Available Constants (from bootstrap):
- `APP_ROOT` - /path/to/posterg-website
- `PUBLIC_ROOT` - /path/to/posterg-website/public
- `CONFIG_ROOT` - /path/to/posterg-website/config
- `DATABASE_ROOT` - /path/to/posterg-website/database
- `DATABASE_PATH` - /path/to/posterg-website/database/test.db
- `RESOURCES_ROOT` - /path/to/posterg-website/resources
- `LIB_ROOT` - /path/to/posterg-website/lib
- `VAR_ROOT` - /path/to/posterg-website/var
- `CACHE_ROOT` - /path/to/posterg-website/var/cache
- `LOGS_ROOT` - /path/to/posterg-website/var/logs
- `VIEWS_ROOT` - /path/to/posterg-website/resources/views
## 🎯 Next Steps
1. ✅ Migration complete - verify locally
2. ⏭️ Test dev server: `just serve`
3. ⏭️ Test all pages work correctly
4. ⏭️ Update nginx config on production server
5. ⏭️ Deploy to production: `just deploy`
6. ⏭️ Deploy nginx config: `just deploy-nginx`
7. ⏭️ Verify production deployment
## 🔄 Rollback (if needed)
If something goes wrong, jj makes it easy:
```bash
# View history
jj log
# Go back to previous state
jj edit <previous-change-id>
# Or abandon current changes
jj abandon @
```
## 📚 Documentation
See also:
- `DIRECTORY_STRUCTURE.md` - Full structure reference
- `DEPLOYMENT_MIGRATION.md` - Detailed migration guide
- `MIGRATION_CHECKLIST.md` - Quick checklist
## ✨ Benefits Achieved
1. **Security**: Private files physically separated from public
2. **Standards**: Follows PHP-FIG and Standard PHP Package Skeleton
3. **Development**: Dev server matches production security
4. **Maintainability**: Clear separation of concerns
5. **Portability**: Path constants make relocation easy
6. **Best Practices**: Industry-standard directory structure

118
docs/SERVER_SETUP.md Normal file
View File

@@ -0,0 +1,118 @@
# Server Setup (Manual)
Since sudo prompts don't work over SSH in justfile, do the initial setup manually.
## One-Time Setup on Server
```bash
# 1. SSH to server
ssh posterg
# 2. Backup current site (recommended)
sudo cp -r /var/www/html /var/www/html.backup
# 3. Create new directory structure
sudo mkdir -p /var/www/posterg
# 4. Set ownership (www-data is the web server user)
sudo chown www-data:posterg /var/www/posterg
# 5. Set permissions (775 = rwxrwxr-x)
sudo chmod 775 /var/www/posterg
# 6. Verify
ls -ld /var/www/posterg
# Should show: drwxrwxr-x 2 www-data posterg 4096 ... /var/www/posterg
# 7. Exit server
exit
```
## Deploy from Local Machine
```bash
just deploy
```
## Complete Deployment Process
```bash
# On server (one time)
ssh posterg
sudo mkdir -p /var/www/posterg
sudo chown www-data:posterg /var/www/posterg
sudo chmod 775 /var/www/posterg
exit
# From local machine
just deploy # Deploy files
just deploy-nginx # Update nginx config
# On server - apply nginx config
ssh posterg
sudo bash /tmp/deploy-production.sh
sudo systemctl reload nginx
exit
# Verify from local
just server-status
```
## Important Notes
- **Don't delete `/var/www/html/` yet!** Keep it as backup until you confirm the new structure works
- The new structure uses `/var/www/posterg/public/` as DocumentRoot
- Nginx must be updated to point to the new location
## After Confirming Everything Works
Once you've verified the new deployment works:
```bash
ssh posterg
sudo rm -rf /var/www/html.backup # Remove backup if no longer needed
sudo rm -rf /var/www/html # Remove old directory
```
## Directory Structure on Server
```
/var/www/
├── html/ ← OLD (keep as backup for now)
├── html.backup/ ← BACKUP (can delete later)
└── posterg/ ← NEW
├── public/ ← DocumentRoot (nginx serves from here)
├── includes/
├── config/
├── database/
├── lib/
└── vendor/
```
## Troubleshooting
### Permission denied during deploy
**Cause:** Directory doesn't exist or has wrong ownership
**Fix:** Run the setup commands above
### Nginx 403 Forbidden
**Cause:** Wrong permissions on files
**Fix:**
```bash
ssh posterg
cd /var/www/posterg
sudo chown -R www-data:posterg .
sudo find . -type d -exec chmod 755 {} \;
sudo find . -type f -exec chmod 644 {} \;
sudo chmod 775 database/
sudo chmod 660 database/*.db
```
### Database connection errors
**Cause:** Database file permissions
**Fix:**
```bash
ssh posterg
sudo chown www-data:posterg /var/www/posterg/database/test.db
sudo chmod 660 /var/www/posterg/database/test.db
```

182
docs/SIMPLIFICATION.md Normal file
View File

@@ -0,0 +1,182 @@
# Website Structure Simplification
## Problem Identified
The initial migration used the **Standard PHP Package Skeleton**, which is designed for **reusable PHP packages/libraries** (like Composer packages), not for websites.
This resulted in:
- ❌ Overcomplicated structure (`var/`, `resources/`, complex bootstrap)
- ❌ Unused directories (`var/cache/`, `var/logs/`, `var/tmp/`)
- ❌ Package-oriented naming (`resources/views/`)
- ❌ Unnecessary constants and helper functions
## Solution: Simplified for Website
### Removed
1. **`var/` directory** - Completely unused, only needed for complex applications with:
- Custom caching systems
- Application-level logging
- Temporary file processing
2. **`resources/` directory** - Package-oriented name, renamed to simple `includes/`
3. **Complex bootstrap** - Removed unused constants:
- `VAR_ROOT`, `CACHE_ROOT`, `LOGS_ROOT`
- `PUBLIC_ROOT`, `CONFIG_ROOT`, `RESOURCES_ROOT`
- `view()` helper function
### Simplified Structure
```
posterg-website/
├── public/ # DocumentRoot (web-accessible) ✅
│ ├── index.php
│ ├── search.php
│ ├── memoire.php
│ ├── admin/
│ └── assets/
├── includes/ # Simple template includes ✅
│ ├── header.php
│ └── footer.php
├── config/ # Minimal configuration ✅
│ └── bootstrap.php (simplified)
├── database/ # Database files (private)
│ └── test.db
├── lib/ # PHP classes (private)
│ ├── Database.php
│ ├── RateLimit.php
│ └── cache/rate_limit/ (used by RateLimit)
├── vendor/ # Composer dependencies (private)
└── tests/ # Tests (private)
```
### Simplified config/bootstrap.php
**Before:** 66 lines with many unused constants
**After:** 33 lines with only essentials
```php
<?php
// Define application root
define('APP_ROOT', dirname(__DIR__));
// Database path
define('DATABASE_PATH', APP_ROOT . '/database/test.db');
// Error reporting (dev vs production)
if (php_sapi_name() === 'cli-server') {
error_reporting(E_ALL);
ini_set('display_errors', '1');
} else {
error_reporting(E_ALL);
ini_set('display_errors', '0');
}
// Simple helper for templates
function include_template($name) {
include APP_ROOT . '/includes/' . $name;
}
// Composer autoload
if (file_exists(APP_ROOT . '/vendor/autoload.php')) {
require_once APP_ROOT . '/vendor/autoload.php';
}
```
### Simplified PHP Files
**Before:**
```php
require_once __DIR__ . '/../config/bootstrap.php';
require_once LIB_ROOT . '/Database.php';
view('header.php', ['pageTitle' => $title]);
```
**After:**
```php
require_once __DIR__ . '/../config/bootstrap.php';
require_once APP_ROOT . '/lib/Database.php';
include APP_ROOT . '/includes/header.php';
```
## Comparison: Package vs Website
| Feature | PHP Package | PHP Website (this project) |
|---------|-------------|----------------------------|
| Purpose | Reusable library | Single website |
| Structure | Complex (PSR-4, namespaces) | Simple (includes, classes) |
| Directories | `src/`, `resources/`, `var/`, `bin/` | `public/`, `includes/`, `lib/` |
| Autoloading | PSR-4 namespaces | Simple require statements |
| Config | Complex bootstrap with many constants | Minimal bootstrap |
| Caching | `var/cache/` with framework | Simple file-based if needed |
| Logging | `var/logs/` with logger | PHP error_log |
## Benefits of Simplification
### Before (Package-oriented)
- ❌ 66-line bootstrap file
- ❌ 10+ unused constants
-`resources/views/` (confusing name)
-`var/` directory (completely unused)
- ❌ Helper functions for simple includes
- ❌ Over-engineered for a simple website
### After (Website-focused)
- ✅ 33-line bootstrap file (50% smaller)
- ✅ 2 essential constants (APP_ROOT, DATABASE_PATH)
-`includes/` (clear, simple name)
- ✅ No unused directories
- ✅ Standard PHP `include` statements
- ✅ Appropriate for a PHP website
### Security (Unchanged)
- ✅ Still uses `public/` as DocumentRoot
- ✅ Database still outside web root
- ✅ Config still private
- ✅ All security improvements retained
## Testing
All PHP files pass syntax check:
```bash
$ php -l config/bootstrap.php # OK
$ php -l public/index.php # OK
$ php -l public/search.php # OK
$ php -l public/memoire.php # OK
$ php -l public/admin/index.php # OK
```
Start dev server:
```bash
$ just serve
```
## When You WOULD Need var/
You would need a `var/` directory if you were building:
- A framework (Laravel, Symfony)
- A CMS (WordPress, Drupal)
- An application with:
- Template compilation/caching
- Session storage
- File upload processing
- Application-level logging
- Queue systems
For your thesis website: **Not needed**
## Conclusion
- ✅ Structure is now appropriate for a **PHP website**
- ✅ Removed package-oriented complexity
- ✅ Kept all security improvements (public/ directory)
- ✅ Simpler, cleaner, easier to maintain
- ✅ Still follows best practices (just the right ones)
The core improvement (public/ directory for security) remains intact, but now with a structure that fits a website, not a reusable package.