ops: simplify justfile, guard deploy-db, extract scripts, fix .gitignore

This commit is contained in:
Pontoporeia
2026-03-02 15:24:00 +01:00
parent 2110d2b916
commit 52978aa658
10 changed files with 289 additions and 562 deletions

6
.gitignore vendored
View File

@@ -6,11 +6,11 @@ vendor/
compose.lock compose.lock
### Test databases ### ### Test databases ###
database/test.db storage/test.db
### Logs ### ### Logs ###
formulaire/error.log error.log
lib/cache/rate_limit/ src/cache/rate_limit/
# OS files # OS files
.DS_Store .DS_Store

View File

@@ -1,2 +1,3 @@
docs docs
nginx nginx
src/cache/rate_limit

111
README.md
View File

@@ -1,59 +1,96 @@
# PostERG - Monorepo # posterg
PostERG est un projet de l'ERG (École de Recherche Graphique) permettant aux étudiant.e.s sortant en cursus de Master de mettre à disposition leurs mémoires et travaux de fin d'études. Répertoire des travaux de fin d'études de l'[ERG](https://erg.be) (École de Recherche Graphique).
## Structure du projet ## Requirements
Ce monorepo contient deux applications : - PHP 8.4
- SQLite3 (`php8.4-sqlite3`)
- nginx (production)
- **[formulaire/](./formulaire/)** - Formulaire d'ajout de mémoires pour les étudiant.e.s ## Project structure
- **[front-backend/](./front-backend/)** - Site web public affichant les mémoires soumis
## Prérequis ```
posterg/
- PHP 7.4 ou supérieur ├── public/ # DocumentRoot — web-accessible only
- Composer (gestionnaire de dépendances PHP) │ ├── admin/ # Admin panel (session-authenticated)
│ ├── assets/ # CSS, fonts, icons
### Installation de Composer │ ├── media.php # Controlled file serving (covers, PDFs)
│ └── *.php # Public pages (index, search, tfe, apropos)
```shell ├── src/ # PHP classes (not web-accessible)
curl -sS https://getcomposer.org/installer | php │ ├── AdminAuth.php
│ ├── Database.php
│ ├── RateLimit.php
│ └── config.php
├── templates/ # Shared PHP template partials
├── config/ # Bootstrap and credentials (not web-accessible)
├── storage/ # Database and uploaded files (not web-accessible)
│ ├── schema.sql
│ ├── test.db
│ └── fixtures/
├── tests/
├── scripts/ # Dev and server management scripts
│ ├── setup-dev.sh
│ ├── deploy-server.sh # Run on server with sudo to apply nginx config
│ └── manage-admin-users.sh # Run on server with sudo to manage htpasswd
└── nginx/ # nginx config and reference files
└── posterg.conf
``` ```
ou Uploaded files (PDFs, covers) live in `storage/` — outside the webroot — and are
served exclusively through `public/media.php`, which validates paths and MIME types.
```shell ## Development
php -r "readfile('https://getcomposer.org/installer');" | php
```bash
just setup # first-time: installs dev dependencies
just serve # http://localhost:8000 (public) and /admin/
just test # run test suite
``` ```
ou installer le paquet `composer` de votre distribution. Admin credentials in development are set via `config/admin_credentials.php`
(see `config/admin_credentials.example.php`).
## Installation ## Deployment
Chaque sous-projet a ses propres dépendances. Installez-les séparément : Files are pushed to the server with rsync — there is no repo on the remote.
```shell ```bash
cd formulaire && composer install just deploy # rsync app files → posterg:/var/www/posterg/
cd ../front-backend && composer install just deploy-db # push local test.db → remote (only if remote DB is absent)
``` ```
## Lancement en local `deploy-db` refuses to run if a database already exists on the server, to avoid
accidental overwrites of production data.
Pour chaque application, lancez un serveur PHP : ### First-time server setup
```shell ```bash
# Pour le formulaire (port 3000) ssh posterg
cd formulaire && php -S 127.0.0.1:3000 sudo mkdir -p /var/www/posterg
sudo chown www-data:posterg /var/www/posterg
# Pour le site web (port 3001) sudo chmod 775 /var/www/posterg
cd front-backend && php -S 127.0.0.1:3001 exit
``` ```
## Documentation Then deploy once, copy nginx config, and apply:
- [Documentation du formulaire](./formulaire/README.md) ```bash
- [Documentation du site web](./front-backend/README.md) just deploy
rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf
ssh posterg "sudo bash /var/www/posterg/scripts/deploy-server.sh"
ssh posterg "sudo systemctl reload nginx"
```
## Liens ### Admin users (htpasswd)
- [Site web PostERG](https://codeberg.org/PostERG/posterg-website) ```bash
ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh"
```
## Security notes
- Admin panel protected by nginx `auth_basic` + PHP session (`AdminAuth`)
- Uploads stored outside webroot, served via controlled `media.php`
- Rate limiting on public search (`src/RateLimit.php`)
- See `docs/TODO.SECURITY.md` for outstanding items

15
TODO.md
View File

@@ -25,7 +25,22 @@
- [x] Rewrite `public/admin/thanks.php` (dark info cards) - [x] Rewrite `public/admin/thanks.php` (dark info cards)
- [x] Rewrite `public/admin/import.php` (clean dark form) - [x] Rewrite `public/admin/import.php` (clean dark form)
## Justfile / Ops
- [x] Simplify `serve` and `deploy` to one recipe each
- [x] Remove sysadmin recipes (server-logs, server-status, deploy-nginx, deploy-admin-tools)
- [x] Extract server scripts to `scripts/` (deploy-server.sh, manage-admin-users.sh)
- [x] Guard `deploy-db` against overwriting existing remote database
- [x] Update README.md and docs/SERVER_SETUP.md to reflect current structure
## Pending ## Pending
- [ ] Add pagination to répertoire student index (currently capped at 100) - [ ] Add pagination to répertoire student index (currently capped at 100)
- [ ] Thumbnail generation / cover image support for home grid cards - [ ] Thumbnail generation / cover image support for home grid cards
## Admin / Server
- [ ] Add server status view in admin panel (nginx + php-fpm health, site HTTP check)
- [ ] Add server log viewer in admin panel (tail nginx error/access logs via SSH or log endpoint)
- [ ] Add nginx config deploy flow to admin panel (upload `scripts/deploy-server.sh`, run remotely)
- [ ] Add admin user management UI (wraps `scripts/manage-admin-users.sh` on server)

View File

@@ -1,116 +1,62 @@
# Server Setup (Manual) # Server Setup
Since sudo prompts don't work over SSH in justfile, do the initial setup manually. ## One-time setup on server
## One-Time Setup on Server
```bash ```bash
# 1. SSH to server
ssh posterg 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 sudo mkdir -p /var/www/posterg
# 4. Set ownership (www-data is the web server user)
sudo chown www-data:posterg /var/www/posterg sudo chown www-data:posterg /var/www/posterg
# 5. Set permissions (775 = rwxrwxr-x)
sudo chmod 775 /var/www/posterg 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 exit
``` ```
## Deploy from Local Machine ## Deploying the application
Files are pushed via rsync — there is no repo on the server.
```bash ```bash
# Push all app files
just deploy just deploy
# Push initial database (aborts if remote DB already exists)
just deploy-db
``` ```
## Complete Deployment Process ## Applying the nginx config
The config is in `nginx/posterg.conf`. Upload it and run the deploy script on the server:
```bash ```bash
# On server (one time) rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf
ssh posterg ssh posterg "sudo bash /var/www/posterg/scripts/deploy-server.sh"
sudo mkdir -p /var/www/posterg ssh posterg "sudo systemctl reload nginx"
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 `scripts/deploy-server.sh` fixes ownership/permissions and installs the nginx config
from `/tmp/posterg.conf`. It must be run as root.
- **Don't delete `/var/www/html/` yet!** Keep it as backup until you confirm the new structure works ## Managing admin users
- 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 ```bash
ssh posterg ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh"
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 This is an interactive menu for adding, changing, and deleting htpasswd entries
at `/etc/nginx/.htpasswd-posterg`.
```
/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 ## Troubleshooting
### Permission denied during deploy
**Cause:** Directory doesn't exist or has wrong ownership
**Fix:** Run the setup commands above
### Nginx 403 Forbidden ### Nginx 403 Forbidden
**Cause:** Wrong permissions on files
**Fix:**
```bash ```bash
ssh posterg ssh posterg
cd /var/www/posterg sudo chown -R www-data:posterg /var/www/posterg
sudo chown -R www-data:posterg . sudo find /var/www/posterg -type d -exec chmod 755 {} \;
sudo find . -type d -exec chmod 755 {} \; sudo find /var/www/posterg -type f -exec chmod 644 {} \;
sudo find . -type f -exec chmod 644 {} \; sudo chmod 775 /var/www/posterg/storage
sudo chmod 775 database/ sudo chmod 660 /var/www/posterg/storage/*.db
sudo chmod 660 database/*.db
``` ```
### Database connection errors ### Database permission error
**Cause:** Database file permissions
**Fix:**
```bash ```bash
ssh posterg ssh posterg
sudo chown www-data:posterg /var/www/posterg/storage/test.db sudo chown www-data:posterg /var/www/posterg/storage/test.db

233
justfile
View File

@@ -1,72 +1,34 @@
# Post-ERG Justfile # Post-ERG Justfile
# Unified recipes for the complete site (public + admin)
# Default recipe - show available commands
default: default:
@just --list @just --list
# ============================================================================ # ============================================================================
# Development Setup # Development
# ============================================================================ # ============================================================================
[group('dev')] [group('dev')]
setup: setup:
@echo "🛠️ Setting up development environment..." @bash scripts/setup-dev.sh
@bash setup-dev.sh
# ============================================================================
# Development Server
# ============================================================================
[group('dev')] [group('dev')]
serve: serve:
@echo "🚀 Starting Post-ERG development server"
@echo "========================================"
@echo ""
@echo "📍 Public site: http://localhost:8000"
@echo "📍 Admin panel: http://localhost:8000/admin/"
@echo "🔒 Serving from public/ directory (matches production)"
@echo ""
@echo "✨ Live reload enabled - browser auto-refreshes on file save!"
@echo ""
@echo "Press Ctrl+C to stop"
@echo ""
@php -S 127.0.0.1:8000 -t public/ @php -S 127.0.0.1:8000 -t public/
[group('dev')] [group('dev')]
stop: stop:
@echo "🛑 Stopping development server..." @pkill -f "php -S 127.0.0.1:8000" 2>/dev/null && echo "stopped" || echo "no server running"
@pkill -f "php -S 127.0.0.1:8000" 2>/dev/null && echo "✓ Server stopped" || echo "No server running"
[group('dev')] [group('dev')]
logs: logs:
@echo "📋 Development logs" @tail -n 20 error.log 2>/dev/null || echo "no error log"
@echo "==================="
@echo ""
@if [ -f error.log ]; then \
echo "Application errors:"; \
echo "------------------"; \
tail -n 20 error.log; \
else \
echo "No error log found"; \
fi
# ============================================================================ # ============================================================================
# Deploy Group # Deploy
# ============================================================================ # ============================================================================
[group('deploy')] [group('deploy')]
deploy: deploy:
@echo "📤 Deploying Post-ERG complete site"
@echo "===================================="
@echo ""
@echo "⚠️ First time? Ensure /var/www/posterg/ exists on server with:"
@echo " ssh posterg"
@echo " sudo mkdir -p /var/www/posterg"
@echo " sudo chown www-data:posterg /var/www/posterg"
@echo " sudo chmod 775 /var/www/posterg"
@echo ""
@echo "Step 1: Deploying application to /var/www/posterg/..."
rsync -vur --progress \ rsync -vur --progress \
--chown="www-data:posterg" \ --chown="www-data:posterg" \
--exclude 'vendor' \ --exclude 'vendor' \
@@ -82,132 +44,64 @@ deploy:
--exclude 'nginx' \ --exclude 'nginx' \
--exclude 'docs' \ --exclude 'docs' \
--exclude 'justfile*' \ --exclude 'justfile*' \
--exclude 'migrate-structure.sh' \ --exclude 'scripts' \
--exclude 'setup-dev.sh' \
--exclude 'var/cache/*' \ --exclude 'var/cache/*' \
--exclude 'var/logs/*' \ --exclude 'var/logs/*' \
./ posterg:/var/www/posterg/ ./ posterg:/var/www/posterg/
@echo ""
@echo "Step 2: Setting up directories and permissions..."
ssh posterg "cd /var/www/posterg && \ ssh posterg "cd /var/www/posterg && \
mkdir -p var/{cache,logs,tmp} && \ mkdir -p var/{cache,logs,tmp} && \
chown -R www-data:posterg . && \ chown -R www-data:posterg . && \
chmod -R 755 . && \ chmod -R 755 . && \
chmod -R 775 var/ storage/ && \ chmod -R 775 var/ storage/ && \
chmod 660 storage/*.db 2>/dev/null || true" chmod 660 storage/*.db 2>/dev/null || true"
@echo ""
@echo "✅ Deployment complete!"
@echo ""
@echo "📁 Server structure:"
@echo " • App root: /var/www/posterg/"
@echo " • DocumentRoot: /var/www/posterg/public/"
@echo ""
@echo "🔍 Verify deployment:"
@echo " • Public: https://posterg.erg.be/"
@echo " • Admin: https://posterg.erg.be/admin/"
@echo ""
@echo "⚠️ IMPORTANT: Update nginx config to point to /var/www/posterg/public/"
@echo " Run: just deploy-nginx"
[group('deploy')] [group('deploy')]
deploy-database: deploy-db:
@echo "⚠️ Deploying test database (will overwrite remote test.db)" @ssh posterg '[ ! -f /var/www/posterg/storage/test.db ]' || (echo "ERROR: remote database already exists. Remove it manually if you intend to overwrite." && exit 1)
@echo "Creating database directory if needed..." rsync -v --progress ./storage/test.db posterg:/var/www/posterg/storage/test.db
ssh posterg "mkdir -p /var/www/posterg/storage" ssh posterg "chown www-data:posterg /var/www/posterg/storage/test.db && chmod 660 /var/www/posterg/storage/test.db"
rsync -vur --progress ./storage/test.db posterg:/var/www/posterg/storage/test.db
@echo "Setting correct permissions..."
ssh posterg "chown www-data:posterg /var/www/posterg/storage /var/www/posterg/storage/test.db && chmod 775 /var/www/posterg/storage && chmod 660 /var/www/posterg/storage/test.db"
@echo "✅ Test database deployed and configured"
# Legacy alias
[group('deploy')]
test-deploy:
@just deploy-database
# ============================================================================ # ============================================================================
# Testing # Testing
# ============================================================================ # ============================================================================
[group('test')] [group('test')]
test: test:
@echo "🧪 Running Post-ERG Test Suite"
@echo "==============================="
@echo ""
@php tests/run-tests.php @php tests/run-tests.php
[group('test')] [group('test')]
test-unit: test-unit:
@echo "🧪 Unit Tests"
@echo "============="
@php tests/Unit/DatabaseTest.php @php tests/Unit/DatabaseTest.php
@echo ""
@php tests/Unit/RateLimitTest.php @php tests/Unit/RateLimitTest.php
[group('test')] [group('test')]
test-integration: test-integration:
@echo "🧪 Integration Tests"
@echo "===================="
@php tests/Integration/SearchTest.php @php tests/Integration/SearchTest.php
[group('test')] [group('test')]
test-security: test-security:
@echo "🧪 Security Tests"
@echo "================="
@php tests/Security/SecurityTest.php @php tests/Security/SecurityTest.php
[group('test')] [group('test')]
syntax: syntax:
@echo "🔍 Checking PHP Syntax"
@echo "======================"
@find . -maxdepth 1 -name "*.php" -not -path "./vendor/*" -exec php -l {} \; | grep -v "No syntax errors" @find . -maxdepth 1 -name "*.php" -not -path "./vendor/*" -exec php -l {} \; | grep -v "No syntax errors"
@find admin/ -name "*.php" -exec php -l {} \; 2>/dev/null | grep -v "No syntax errors" || true @find admin/ -name "*.php" -exec php -l {} \; 2>/dev/null | grep -v "No syntax errors" || true
@find src/ -name "*.php" -exec php -l {} \; | grep -v "No syntax errors" @find src/ -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"
@echo "✅ All PHP files have valid syntax" @echo "✅ Syntax OK"
# ============================================================================ # ============================================================================
# Database Management # Database
# ============================================================================
# ============================================================================
# Database Statistics
# ============================================================================
[group('stats')]
stats:
@echo "📊 Database Statistics"
@echo "======================"
@echo ""
@sqlite3 storage/test.db "SELECT COUNT(*) || ' total theses' FROM theses;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' published theses' FROM theses WHERE is_published = 1;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' authors' FROM authors;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' supervisors' FROM supervisors;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' keywords' FROM keywords;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' files uploaded' FROM thesis_files;"
[group('stats')]
recent:
@echo "📅 Recent Theses"
@echo "================"
@sqlite3 -column -header storage/test.db "SELECT id, title, year, authors FROM v_theses_public ORDER BY year DESC, title LIMIT 10;"
# ============================================================================
# Database Management
# ============================================================================ # ============================================================================
[group('database')] [group('database')]
init-db: init-db:
@echo "📊 Creating test database from schema..."
@sqlite3 storage/test.db < storage/schema.sql @sqlite3 storage/test.db < storage/schema.sql
@echo "✓ Test database created" @sqlite3 storage/test.db "SELECT COUNT(*) || ' tables' FROM sqlite_master WHERE type='table';"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' tables created' FROM sqlite_master WHERE type='table';"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' orientations loaded' FROM orientations;"
@sqlite3 storage/test.db "SELECT COUNT(*) || ' AP programs loaded' FROM ap_programs;"
[group('database')] [group('database')]
reset-db: reset-db:
@echo "⚠️ Resetting database (will delete all data)..."
@rm -f storage/test.db @rm -f storage/test.db
@just init-db @just init-db
@echo "✓ Database reset complete"
[group('database')] [group('database')]
query: query:
@@ -215,118 +109,27 @@ query:
[group('database')] [group('database')]
show id: show id:
@echo "Thesis #{{id}}"
@echo "=============="
@sqlite3 -column -header storage/test.db "SELECT * FROM v_theses_full WHERE id = {{id}};" @sqlite3 -column -header storage/test.db "SELECT * FROM v_theses_full WHERE id = {{id}};"
[group('database')] [group('database')]
backup: backup:
@echo "💾 Backing up database..."
@sqlite3 storage/test.db .dump > storage/backup_$(date +%Y%m%d_%H%M%S).sql @sqlite3 storage/test.db .dump > storage/backup_$(date +%Y%m%d_%H%M%S).sql
@echo "✓ Database dumped to storage/backup_$(date +%Y%m%d_%H%M%S).sql"
[group('database')] [group('database')]
fixtures: fixtures:
@echo "🎭 Creating test database with fixtures..."
@php storage/fixtures/CreateTestDatabase.php @php storage/fixtures/CreateTestDatabase.php
[group('database')]
deploy-test-db:
@echo "⚠️ Deploying test database to server (will overwrite remote test.db)"
@echo "Creating database directory if needed..."
ssh posterg "mkdir -p /var/www/html/database"
rsync -vur --progress ./storage/test.db posterg:/var/www/html/storage/test.db
@echo "Setting correct permissions..."
ssh posterg "chgrp posterg /var/www/html/database /var/www/html/storage/test.db && \
chmod 775 /var/www/html/database && \
chmod 660 /var/www/html/storage/test.db"
@echo "✅ Test database deployed"
# ============================================================================ # ============================================================================
# Server Tools # Utils
# ============================================================================
[group('server')]
deploy-nginx:
@echo "🔧 Deploying nginx configuration..."
@echo ""
@echo "⚠️ IMPORTANT: Checking nginx config has correct DocumentRoot..."
@if ! grep -q "/var/www/posterg/public" nginx/posterg.conf 2>/dev/null; then \
echo "❌ ERROR: nginx/posterg.conf must contain '/var/www/posterg/public'"; \
echo " Current DocumentRoot needs updating!"; \
echo ""; \
echo " Edit nginx/posterg.conf and change:"; \
echo " root /var/www/html;"; \
echo " To:"; \
echo " root /var/www/posterg/public;"; \
exit 1; \
fi
@echo "✅ nginx config looks correct"
@echo ""
rsync -vur --progress ./nginx/posterg.conf posterg:/tmp/posterg.conf
rsync -vur --progress ./nginx/deploy-production-new.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"
@echo " (This will apply config and show reload command)"
[group('server')]
deploy-admin-tools:
@echo "🔑 Uploading admin user management tools..."
rsync -vur --progress ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh
@echo "✅ Script uploaded"
@echo ""
@echo "To manage admin users:"
@echo " ssh posterg"
@echo " sudo bash /tmp/manage-admin-users.sh"
[group('server')]
server-logs:
@echo "📋 Server logs (last 50 lines)"
@echo "=============================="
@echo ""
@echo "Nginx error log:"
@echo "----------------"
ssh posterg "sudo tail -50 /var/log/nginx/posterg_error.log" || echo "Cannot read logs (permission denied)"
@echo ""
@echo "Nginx access log:"
@echo "-----------------"
ssh posterg "sudo tail -20 /var/log/nginx/posterg_access.log" || echo "Cannot read logs (permission denied)"
[group('server')]
server-status:
@echo "🔍 Server Status"
@echo "================"
@ssh posterg "systemctl is-active nginx && echo '✓ Nginx running' || echo '✗ Nginx stopped'"
@ssh posterg "systemctl is-active php8.4-fpm && echo '✓ PHP-FPM running' || echo '✗ PHP-FPM stopped'"
@echo ""
@echo "Site check:"
@curl -s -o /dev/null -w " • Public: %{http_code}\n" https://posterg.erg.be/ || echo " • Public: offline"
@curl -s -o /dev/null -w " • Admin: %{http_code}\n" https://posterg.erg.be/admin/ || echo " • Admin: offline"
# ============================================================================
# Utility Commands
# ============================================================================ # ============================================================================
[group('utils')] [group('utils')]
clean: clean:
@echo "🧹 Cleaning up development files..." @rm -f error.log admin/error.log
@rm -f error.log
@rm -f admin/error.log
@rm -rf src/cache/rate_limit/* @rm -rf src/cache/rate_limit/*
@rm -f /tmp/posterg-*.log @rm -f /tmp/posterg-*.log /tmp/posterg-*.pid
@rm -f /tmp/posterg-*.pid
@echo "✓ Cleanup complete"
[group('utils')] [group('utils')]
setup-dirs: setup-dirs:
@echo "📁 Creating data directories..." @mkdir -p admin/data/{theses,covers,yaml} src/cache/rate_limit
@mkdir -p admin/data/theses @touch admin/data/theses/.gitkeep admin/data/covers/.gitkeep
@mkdir -p admin/data/covers
@mkdir -p admin/data/yaml
@mkdir -p src/cache/rate_limit
@touch admin/data/theses/.gitkeep
@touch admin/data/covers/.gitkeep
@echo "✓ Directories created"

View File

@@ -1,180 +0,0 @@
#!/bin/bash
# Deploy production nginx configuration and fix permissions for Post-ERG
set -e
echo "🚀 Post-ERG Production Deployment"
echo "=================================="
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
exit 1
fi
echo "📋 Step 1: Fixing file permissions..."
echo "--------------------------------------"
# Change group to posterg (www-data is member of this group)
chown -R theophile:posterg /var/www/html/
echo "✓ Changed group to posterg"
# Set directory permissions (755 - readable/executable by everyone)
find /var/www/html -type d -exec chmod 755 {} \;
echo "✓ Set directory permissions to 755"
# Set file permissions (640 - owner read/write, group read)
find /var/www/html -type f -exec chmod 640 {} \;
echo "✓ Set file permissions to 640"
# Make upload directories writable by group (for www-data to write)
if [ -d "/var/www/html/formulaire/data/theses" ]; then
chmod 775 /var/www/html/formulaire/data/theses
chmod 775 /var/www/html/formulaire/data/covers
echo "✓ Set upload directories to 775"
fi
# Protect database if it exists
if [ -f "/var/www/html/storage/posterg.db" ]; then
chmod 660 /var/www/html/storage/posterg.db
chown www-data:posterg /var/www/html/storage/posterg.db
echo "✓ Protected database file"
fi
echo ""
echo "📋 Step 2: Checking prerequisites..."
echo "--------------------------------------"
# Check if htpasswd is available
if ! command -v htpasswd &>/dev/null; then
echo -e "${YELLOW}⚠️ htpasswd not found, installing apache2-utils...${NC}"
apt-get update -qq
apt-get install -y apache2-utils
echo -e "${GREEN}✓ apache2-utils installed${NC}"
fi
# Check if htpasswd file exists
if [ ! -f "/etc/nginx/.htpasswd-posterg" ]; then
echo -e "${YELLOW}⚠️ Warning: /etc/nginx/.htpasswd-posterg not found${NC}"
echo " Creating it now..."
echo ""
echo "Please enter admin username:"
read -r ADMIN_USER
htpasswd -c /etc/nginx/.htpasswd-posterg "$ADMIN_USER"
echo -e "${GREEN}✓ Password file created${NC}"
echo ""
else
echo "✓ Password file exists"
fi
# Check if config file was uploaded
if [ ! -f "/tmp/posterg.conf" ]; then
echo -e "${RED}✗ Error: /tmp/posterg.conf not found${NC}"
echo "Please upload it first: rsync -vur ./nginx/posterg-production.conf posterg:/tmp/posterg.conf"
exit 1
fi
echo ""
echo "📋 Step 3: Installing nginx configuration..."
echo "--------------------------------------"
# Backup existing config if it exists
if [ -f "/etc/nginx/sites-available/posterg" ]; then
cp /etc/nginx/sites-available/posterg /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S)
echo "✓ Backed up existing config"
fi
# Copy new configuration
cp /tmp/posterg.conf /etc/nginx/sites-available/posterg
echo "✓ Installed configuration to /etc/nginx/sites-available/posterg"
# Create symlink
if [ ! -L "/etc/nginx/sites-enabled/posterg" ]; then
ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/posterg
echo "✓ Created symlink in sites-enabled"
else
echo "✓ Symlink already exists"
fi
# Remove default site
if [ -L "/etc/nginx/sites-enabled/default" ]; then
rm /etc/nginx/sites-enabled/default
echo "✓ Disabled default site"
fi
echo ""
echo "📋 Step 4: Testing nginx configuration..."
echo "--------------------------------------"
if nginx -t; then
echo -e "${GREEN}✓ Nginx configuration is valid${NC}"
else
echo -e "${RED}✗ Nginx configuration has errors!${NC}"
echo "Restoring backup..."
if ls /etc/nginx/sites-available/posterg.backup* 1>/dev/null 2>&1; then
BACKUP=$(ls -t /etc/nginx/sites-available/posterg.backup* | head -1)
cp "$BACKUP" /etc/nginx/sites-available/posterg
echo "Configuration restored from backup"
fi
exit 1
fi
echo ""
echo "📋 Step 5: Reloading nginx..."
echo "--------------------------------------"
if systemctl reload nginx; then
echo -e "${GREEN}✓ Nginx reloaded successfully${NC}"
else
echo -e "${RED}✗ Failed to reload nginx${NC}"
exit 1
fi
echo ""
echo "📋 Step 6: Verifying services..."
echo "--------------------------------------"
# Check PHP-FPM
if systemctl is-active --quiet php8.4-fpm; then
echo -e "${GREEN}✓ PHP 8.4-FPM is running${NC}"
else
echo -e "${YELLOW}⚠️ PHP-FPM is not running, starting it...${NC}"
systemctl start php8.4-fpm
systemctl enable php8.4-fpm
echo -e "${GREEN}✓ PHP-FPM started${NC}"
fi
# Check nginx
if systemctl is-active --quiet nginx; then
echo -e "${GREEN}✓ Nginx is running${NC}"
else
echo -e "${RED}✗ Nginx is not running!${NC}"
exit 1
fi
echo ""
echo "═══════════════════════════════════════"
echo -e "${GREEN}✅ Deployment Complete!${NC}"
echo "═══════════════════════════════════════"
echo ""
echo "🧪 Quick Tests:"
echo " • Test public site: curl -I http://localhost/"
echo " • Test admin panel: curl -I http://localhost/formulaire/"
echo " • Test PHP: curl http://localhost/index.php"
echo ""
echo "📊 View logs:"
echo " • Access log: tail -f /var/log/nginx/posterg_access.log"
echo " • Error log: tail -f /var/log/nginx/posterg_error.log"
echo ""
echo "🔒 Security Checks:"
echo " • Database blocked: curl -I http://localhost/storage/posterg.db"
echo " • MD files blocked: curl -I http://localhost/README.md"
echo " • Shared blocked: curl -I http://localhost/shared/Database.php"
echo ""

105
scripts/deploy-server.sh Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# Deploy production nginx configuration for Post-ERG (NEW STRUCTURE)
# This script applies the nginx config for /var/www/posterg/public/ structure
set -e
echo "🚀 Post-ERG Production Deployment (NEW STRUCTURE)"
echo "=================================================="
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
exit 1
fi
echo "📋 Step 1: Fixing file permissions..."
echo "--------------------------------------"
# Change ownership to www-data:posterg
chown -R www-data:posterg /var/www/posterg/
echo "✓ Changed ownership to www-data:posterg"
# Set directory permissions (755)
find /var/www/posterg -type d -exec chmod 755 {} \;
echo "✓ Set directory permissions to 755"
# Set file permissions (644)
find /var/www/posterg -type f -exec chmod 644 {} \;
echo "✓ Set file permissions to 644"
# Make storage directory writable by group
if [ -d "/var/www/posterg/storage" ]; then
chmod 775 /var/www/posterg/storage
echo "✓ Made storage directory group-writable (775)"
fi
# Fix database file permissions
if [ -f "/var/www/posterg/storage/test.db" ]; then
chmod 660 /var/www/posterg/storage/test.db
chown www-data:posterg /var/www/posterg/storage/test.db
echo "✓ Fixed database file permissions (660)"
fi
# Make admin upload directories writable by group
if [ -d "/var/www/posterg/public/admin/data" ]; then
find /var/www/posterg/public/admin/data -type d -exec chmod 775 {} \;
echo "✓ Made admin upload directories group-writable"
fi
echo ""
echo "📋 Step 2: Deploying nginx configuration..."
echo "--------------------------------------"
# Backup existing config
if [ -f "/etc/nginx/sites-available/posterg" ]; then
cp /etc/nginx/sites-available/posterg /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S)
echo "✓ Backed up existing config"
fi
# Copy new config
if [ -f "/tmp/posterg.conf" ]; then
cp /tmp/posterg.conf /etc/nginx/sites-available/posterg
echo "✓ Installed new nginx config"
else
echo -e "${RED}Error: /tmp/posterg.conf not found${NC}"
echo "Run 'just deploy-nginx' first"
exit 1
fi
# Test nginx configuration
echo ""
echo "📋 Step 3: Testing nginx configuration..."
echo "--------------------------------------"
if nginx -t; then
echo -e "${GREEN}✓ Nginx configuration is valid${NC}"
else
echo -e "${RED}✗ Nginx configuration has errors!${NC}"
echo "Restoring backup..."
cp /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S | tail -1) /etc/nginx/sites-available/posterg
exit 1
fi
echo ""
echo "📋 Step 4: Summary..."
echo "--------------------------------------"
echo -e "${GREEN}✓ Permissions fixed${NC}"
echo -e "${GREEN}✓ Nginx config installed${NC}"
echo -e "${GREEN}✓ Configuration validated${NC}"
echo ""
echo -e "${YELLOW}Ready to reload nginx!${NC}"
echo ""
echo "Run: ${GREEN}sudo systemctl reload nginx${NC}"
echo ""
echo "After reload, verify:"
echo " • https://posterg.erg.be/"
echo " • https://posterg.erg.be/admin/"
echo " • https://posterg.erg.be/storage/test.db (should 404)"

View File

@@ -13,13 +13,13 @@ NC='\033[0m'
PASSWORD_FILE="/etc/nginx/.htpasswd-posterg" PASSWORD_FILE="/etc/nginx/.htpasswd-posterg"
# Check if running as root # Check if running as root
if [ "$EUID" -ne 0 ]; then if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}" echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
exit 1 exit 1
fi fi
# Check if htpasswd is available # Check if htpasswd is available
if ! command -v htpasswd &>/dev/null; then if ! command -v htpasswd &> /dev/null; then
echo -e "${YELLOW}Installing apache2-utils...${NC}" echo -e "${YELLOW}Installing apache2-utils...${NC}"
apt-get update -qq apt-get update -qq
apt-get install -y apache2-utils apt-get install -y apache2-utils
@@ -47,7 +47,7 @@ list_users() {
echo -e "${YELLOW}No password file found.${NC}" echo -e "${YELLOW}No password file found.${NC}"
return return
fi fi
echo -e "${GREEN}Current admin users:${NC}" echo -e "${GREEN}Current admin users:${NC}"
echo "────────────────────────" echo "────────────────────────"
cut -d: -f1 "$PASSWORD_FILE" | nl cut -d: -f1 "$PASSWORD_FILE" | nl
@@ -58,25 +58,25 @@ add_user() {
echo "" echo ""
echo -n "Enter new username: " echo -n "Enter new username: "
read -r USERNAME read -r USERNAME
if [ -z "$USERNAME" ]; then if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}" echo -e "${RED}Username cannot be empty${NC}"
return return
fi fi
# Check if user already exists # Check if user already exists
if [ -f "$PASSWORD_FILE" ] && grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then if [ -f "$PASSWORD_FILE" ] && grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${YELLOW}User '$USERNAME' already exists. Use option 3 to change password.${NC}" echo -e "${YELLOW}User '$USERNAME' already exists. Use option 3 to change password.${NC}"
return return
fi fi
# Add user (use -c only if file doesn't exist) # Add user (use -c only if file doesn't exist)
if [ ! -f "$PASSWORD_FILE" ]; then if [ ! -f "$PASSWORD_FILE" ]; then
htpasswd -c "$PASSWORD_FILE" "$USERNAME" htpasswd -c "$PASSWORD_FILE" "$USERNAME"
else else
htpasswd "$PASSWORD_FILE" "$USERNAME" htpasswd "$PASSWORD_FILE" "$USERNAME"
fi fi
echo -e "${GREEN}✓ User '$USERNAME' added successfully${NC}" echo -e "${GREEN}✓ User '$USERNAME' added successfully${NC}"
} }
@@ -84,22 +84,22 @@ change_password() {
list_users list_users
echo -n "Enter username to change password: " echo -n "Enter username to change password: "
read -r USERNAME read -r USERNAME
if [ -z "$USERNAME" ]; then if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}" echo -e "${RED}Username cannot be empty${NC}"
return return
fi fi
if [ ! -f "$PASSWORD_FILE" ]; then if [ ! -f "$PASSWORD_FILE" ]; then
echo -e "${RED}Password file not found${NC}" echo -e "${RED}Password file not found${NC}"
return return
fi fi
if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${RED}User '$USERNAME' not found${NC}" echo -e "${RED}User '$USERNAME' not found${NC}"
return return
fi fi
htpasswd "$PASSWORD_FILE" "$USERNAME" htpasswd "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ Password changed for user '$USERNAME'${NC}" echo -e "${GREEN}✓ Password changed for user '$USERNAME'${NC}"
} }
@@ -108,25 +108,25 @@ delete_user() {
list_users list_users
echo -n "Enter username to delete: " echo -n "Enter username to delete: "
read -r USERNAME read -r USERNAME
if [ -z "$USERNAME" ]; then if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}" echo -e "${RED}Username cannot be empty${NC}"
return return
fi fi
if [ ! -f "$PASSWORD_FILE" ]; then if [ ! -f "$PASSWORD_FILE" ]; then
echo -e "${RED}Password file not found${NC}" echo -e "${RED}Password file not found${NC}"
return return
fi fi
if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${RED}User '$USERNAME' not found${NC}" echo -e "${RED}User '$USERNAME' not found${NC}"
return return
fi fi
echo -n "Are you sure you want to delete user '$USERNAME'? [y/N] " echo -n "Are you sure you want to delete user '$USERNAME'? [y/N] "
read -r CONFIRM read -r CONFIRM
if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then
htpasswd -D "$PASSWORD_FILE" "$USERNAME" htpasswd -D "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ User '$USERNAME' deleted${NC}" echo -e "${GREEN}✓ User '$USERNAME' deleted${NC}"
@@ -140,28 +140,28 @@ reset_all() {
echo -e "${YELLOW}WARNING: This will delete ALL existing users!${NC}" echo -e "${YELLOW}WARNING: This will delete ALL existing users!${NC}"
echo -n "Are you sure? [y/N] " echo -n "Are you sure? [y/N] "
read -r CONFIRM read -r CONFIRM
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
echo "Cancelled" echo "Cancelled"
return return
fi fi
# Backup existing file # Backup existing file
if [ -f "$PASSWORD_FILE" ]; then if [ -f "$PASSWORD_FILE" ]; then
BACKUP="${PASSWORD_FILE}.backup.$(date +%Y%m%d_%H%M%S)" BACKUP="${PASSWORD_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$PASSWORD_FILE" "$BACKUP" cp "$PASSWORD_FILE" "$BACKUP"
echo -e "${GREEN}✓ Backed up to: $BACKUP${NC}" echo -e "${GREEN}✓ Backed up to: $BACKUP${NC}"
fi fi
echo "" echo ""
echo -n "Enter new username: " echo -n "Enter new username: "
read -r USERNAME read -r USERNAME
if [ -z "$USERNAME" ]; then if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}" echo -e "${RED}Username cannot be empty${NC}"
return return
fi fi
htpasswd -c "$PASSWORD_FILE" "$USERNAME" htpasswd -c "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ Password file reset with user '$USERNAME'${NC}" echo -e "${GREEN}✓ Password file reset with user '$USERNAME'${NC}"
} }
@@ -170,30 +170,30 @@ reset_all() {
while true; do while true; do
show_menu show_menu
read -r CHOICE read -r CHOICE
case $CHOICE in case $CHOICE in
1) 1)
list_users list_users
;; ;;
2) 2)
add_user add_user
;; ;;
3) 3)
change_password change_password
;; ;;
4) 4)
delete_user delete_user
;; ;;
5) 5)
reset_all reset_all
;; ;;
6) 6)
echo "" echo ""
echo "Goodbye!" echo "Goodbye!"
exit 0 exit 0
;; ;;
*) *)
echo -e "${RED}Invalid option${NC}" echo -e "${RED}Invalid option${NC}"
;; ;;
esac esac
done done

View File

@@ -1 +1 @@
[1771972436,1771972448] [1772461679,1772461686]