refactor: reorganize to standard PHP structure

- Moved /lib → /src (PHP source code)
- Moved /includes → /public/includes (main site templates)
- Admin section remains self-contained in /public/admin with its own /inc
- Updated all require/include paths across codebase
- Updated config/bootstrap.php, justfile, tests, docs
- All tests passing 

Structure now follows PHP best practices:
  /config      - Configuration files
  /database    - SQLite database + schema
  /docs        - Documentation (intact)
  /nginx       - Server config (intact)
  /public      - Web-accessible files (entry point)
    /admin     - Self-contained admin interface
    /assets    - CSS, fonts, icons
    /includes  - Main site templates (header/footer)
  /scripts     - Deployment scripts (intact)
  /src         - PHP source classes (Database, AdminAuth, RateLimit)
  /tests       - Test suites
This commit is contained in:
Théophile Gervreau-Mercier
2026-02-12 12:11:16 +01:00
parent 0b650cd3e7
commit 0e4921583e
26 changed files with 40 additions and 42 deletions

View File

@@ -25,7 +25,7 @@ if (php_sapi_name() === 'cli-server') {
// Simple helper function for including templates
function include_template($name) {
$path = APP_ROOT . '/includes/' . $name;
$path = APP_ROOT . '/public/includes/' . $name;
if (file_exists($path)) {
include $path;
}

View File

@@ -25,7 +25,7 @@ This deploys all files to `/var/www/posterg/`:
- `includes/``/var/www/posterg/includes/`
- `config/``/var/www/posterg/config/`
- `database/``/var/www/posterg/database/`
- `lib/``/var/www/posterg/lib/`
- `src/``/var/www/posterg/lib/`
### 3. Update Nginx Configuration

View File

@@ -130,7 +130,7 @@ just fixtures
### Create a New Page
1. Create `newpage.php` in root
2. Add `require_once __DIR__ . '/lib/Database.php';`
2. Add `require_once __DIR__ . '/src/Database.php';`
3. Include header: `include 'inc/header.php';`
4. Add your content
5. Include footer: `include 'inc/footer.php';`
@@ -138,7 +138,7 @@ just fixtures
Example:
```php
<?php
require_once __DIR__ . '/lib/Database.php';
require_once __DIR__ . '/src/Database.php';
include 'inc/header.php';
?>
@@ -154,7 +154,7 @@ include 'inc/header.php';
### Add a Database Function
1. Edit `lib/Database.php`
1. Edit `src/Database.php`
2. Add your method to the `Database` class
3. Write a test in `tests/Unit/DatabaseTest.php`
4. Run tests: `just test-unit`
@@ -215,7 +215,7 @@ See `tests/README.md` for complete testing guide.
**Quick example:**
```php
<?php
require_once __DIR__ . '/../../lib/Database.php';
require_once __DIR__ . '/../../src/Database.php';
echo "My Test\n";
echo "=======\n\n";
@@ -243,7 +243,7 @@ just deploy
This deploys:
- Public site (root PHP files)
- Admin panel (`admin/`)
- Shared libraries (`lib/`)
- Shared libraries (`src/`)
**Note:** `vendor/` is automatically excluded from deployment.

View File

@@ -66,7 +66,7 @@ git commit -m "Pre-migration checkpoint"
This will:
- Move `apps/public/*` to root
- Move `apps/admin/` to `admin/`
- Rename `shared/` to `lib/`
- Rename `shared/` to `src/`
- Update all `require` paths automatically
- Remove `apps/` directory
- Update `.gitignore`
@@ -149,9 +149,9 @@ just deploy-admin
| `apps/public/assets/` | `assets/` |
| `apps/public/inc/` | `inc/` |
| `apps/admin/` | `admin/` |
| `shared/Database.php` | `lib/Database.php` |
| `shared/config.php` | `lib/config.php` |
| `shared/cache/` | `lib/cache/` |
| `shared/Database.php` | `src/Database.php` |
| `shared/config.php` | `src/config.php` |
| `shared/cache/` | `src/cache/` |
### Path Updates

View File

@@ -96,9 +96,9 @@ echo "vendor/" >> .gitignore
| `apps/public/inc/header.php` | `inc/header.php` |
| `apps/public/assets/posterg.css` | `assets/posterg.css` |
| `apps/admin/index.php` | `admin/index.php` |
| `shared/Database.php` | `lib/Database.php` |
| `shared/config.php` | `lib/config.php` |
| `shared/cache/` | `lib/cache/` |
| `shared/Database.php` | `src/Database.php` |
| `shared/config.php` | `src/config.php` |
| `shared/cache/` | `src/cache/` |
## Deployment Changes

View File

@@ -138,7 +138,7 @@ and serve files through a dedicated PHP endpoint that validates access rights.
### 5. Rate Limiter Vulnerable to IP Spoofing
**File:** `lib/RateLimit.php` — method `getClientIdentifier()`
**File:** `src/RateLimit.php` — method `getClientIdentifier()`
```php
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
@@ -373,7 +373,7 @@ define('DATABASE_PATH', APP_ROOT . '/database/test.db');
```
This constant is never used anywhere. `Database.php` uses `getDatabasePath()` from
`lib/config.php`. The duplicate creates confusion about which configuration is
`src/config.php`. The duplicate creates confusion about which configuration is
authoritative and could lead to future bugs if someone uses the wrong one.
**Fix:** Remove the `DATABASE_PATH` define from `bootstrap.php`.

View File

@@ -111,7 +111,7 @@ include APP_ROOT . '/includes/header.php';
|---------|-------------|----------------------------|
| Purpose | Reusable library | Single website |
| Structure | Complex (PSR-4, namespaces) | Simple (includes, classes) |
| Directories | `src/`, `resources/`, `var/`, `bin/` | `public/`, `includes/`, `lib/` |
| Directories | `src/`, `resources/`, `var/`, `bin/` | `public/`, `includes/`, `src/` |
| 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 |

View File

@@ -14,7 +14,7 @@
| 1 | No HTTPS — admin credentials exposed in transit | 🔴 CRITICAL | **TLS is terminated upstream by the reverse proxy** (outside nginx). nginx.conf does not need to handle TLS directly. |
| 3 | Uploaded files stored inside the webroot | 🟠 HIGH | Storage moved to `STORAGE_ROOT` (`/var/www/posterg/storage/`), defined in `config/bootstrap.php`. `formulaire.php` updated. |
| 4 | File path mismatch — media files broken and insecure | 🟠 HIGH | DB paths are now storage-relative (`theses/YEAR/ID/file`, `covers/file`). New controller `public/media.php` serves files safely. `memoire.php` and `search.php` updated to use `/media.php?path=…`. Cover recording in `formulaire.php` fixed (was never inserted into DB). |
| 5 | Rate limiter bypassed by IP spoofing (`X-Forwarded-For`) | 🟠 HIGH | `lib/RateLimit.php` `getClientIdentifier()` now uses `REMOTE_ADDR` only. |
| 5 | Rate limiter bypassed by IP spoofing (`X-Forwarded-For`) | 🟠 HIGH | `src/RateLimit.php` `getClientIdentifier()` now uses `REMOTE_ADDR` only. |
| 6 | `.htaccess` security rules silently ignored by nginx | 🟠 HIGH | All rules ported to `nginx/posterg.conf` (CSP to `/admin/` block, `.log` denial, `autoindex off`). See `nginx/HTACCESS_TO_NGINX.md`. |
| 13 | Deprecated `X-XSS-Protection` header | 🔵 LOW | **Removed** from `nginx/posterg.conf`; see `nginx/SECURITY_HEADERS.md` for rationale. |
@@ -49,7 +49,7 @@
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 2 | No PHP-level authentication in admin panel | 🔴 CRITICAL | `lib/AdminAuth.php` implements a session guard with `password_verify` + `session_regenerate_id`. All admin PHP files now call `AdminAuth::requireLogin()` instead of bare `session_start()`. Credentials stored in gitignored `config/admin_credentials.php` (define `ADMIN_PASSWORD_HASH`). No-op when constant is absent (dev / cli-server). Also resolves item #8 (session cookie hardening via `session_set_cookie_params` before `session_start`). See `nginx/PHP_AUTH_LAYER.md`. |
| 2 | No PHP-level authentication in admin panel | 🔴 CRITICAL | `src/AdminAuth.php` implements a session guard with `password_verify` + `session_regenerate_id`. All admin PHP files now call `AdminAuth::requireLogin()` instead of bare `session_start()`. Credentials stored in gitignored `config/admin_credentials.php` (define `ADMIN_PASSWORD_HASH`). No-op when constant is absent (dev / cli-server). Also resolves item #8 (session cookie hardening via `session_set_cookie_params` before `session_start`). See `nginx/PHP_AUTH_LAYER.md`. |
| 8 | Session cookies not hardened (`Secure`, `HttpOnly`, `SameSite` missing) | 🟡 MEDIUM | **Resolved as part of item #2.** `AdminAuth::startSession()` sets `HttpOnly=true`, `SameSite=Strict`, `Secure=true` (off on cli-server), `Path=/admin`, `Lifetime=0` before every `session_start()`. |
---

View File

@@ -160,7 +160,7 @@ syntax:
@echo "======================"
@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 lib/ -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"
# ============================================================================
@@ -315,7 +315,7 @@ clean:
@echo "🧹 Cleaning up development files..."
@rm -f error.log
@rm -f admin/error.log
@rm -rf lib/cache/rate_limit/*
@rm -rf src/cache/rate_limit/*
@rm -f /tmp/posterg-*.log
@rm -f /tmp/posterg-*.pid
@echo "✓ Cleanup complete"
@@ -326,7 +326,7 @@ setup-dirs:
@mkdir -p admin/data/theses
@mkdir -p admin/data/covers
@mkdir -p admin/data/yaml
@mkdir -p lib/cache/rate_limit
@mkdir -p src/cache/rate_limit
@touch admin/data/theses/.gitkeep
@touch admin/data/covers/.gitkeep
@echo "✓ Directories created"

View File

@@ -1 +0,0 @@
[1770319036]

View File

@@ -1 +0,0 @@
[1770377487]

View File

@@ -1,7 +1,7 @@
<?php
// Load configuration
require_once __DIR__ . '/../config/bootstrap.php';
require_once APP_ROOT . '/lib/Database.php';
require_once APP_ROOT . '/src/Database.php';
$pageTitle = "Liste des TFE";
$page = isset($_GET["page"]) ? intval($_GET["page"]) : 1;
@@ -19,7 +19,7 @@ try {
$totalPages = 0;
}
include APP_ROOT . '/includes/header.php';
include APP_ROOT . '/public/includes/header.php';
?>
<main>
@@ -54,4 +54,4 @@ include APP_ROOT . '/includes/header.php';
</nav>
<?php endif; ?>
<?php include APP_ROOT . '/includes/footer.php'; ?>
<?php include APP_ROOT . '/public/includes/footer.php'; ?>

View File

@@ -18,8 +18,7 @@ $root = dirname(__DIR__);
$watchDirs = [
$root . '/public',
$root . '/includes',
$root . '/lib',
$root . '/src',
$root . '/config',
];

View File

@@ -3,7 +3,7 @@
require_once __DIR__ . '/../config/bootstrap.php';
// Load required libraries and classes
require_once APP_ROOT . '/lib/Database.php';
require_once APP_ROOT . '/src/Database.php';
// Check if an id parameter is provided in the URL
if (isset($_GET['id'])) {
@@ -29,7 +29,7 @@ if (isset($_GET['id'])) {
}
// Include the header template
include APP_ROOT . '/includes/header.php'; ?>
include APP_ROOT . '/public/includes/header.php'; ?>
<main>
<div class="item">
<div class="card-content">
@@ -151,4 +151,4 @@ include APP_ROOT . '/includes/header.php'; ?>
</main>
<!-- Include the footer template -->
<?php include APP_ROOT . '/includes/footer.php'; ?>
<?php include APP_ROOT . '/public/includes/footer.php'; ?>

View File

@@ -1,8 +1,8 @@
<?php
// Bootstrap application
require_once __DIR__ . '/../config/bootstrap.php';
require_once APP_ROOT . '/lib/Database.php';
require_once APP_ROOT . '/lib/RateLimit.php';
require_once APP_ROOT . '/src/Database.php';
require_once APP_ROOT . '/src/RateLimit.php';
// Rate limiting: 30 requests per minute
@@ -16,7 +16,7 @@ if (!$rateLimit->check()) {
$rateLimit->sendHeaders();
// Display error page
include APP_ROOT . '/includes/header.php';
include APP_ROOT . '/public/includes/header.php';
echo '<section class="section">';
echo ' <div class="container">';
echo ' <div class="notification is-danger">';
@@ -26,7 +26,7 @@ if (!$rateLimit->check()) {
echo ' </div>';
echo ' </div>';
echo '</section>';
include APP_ROOT . '/includes/footer.php';
include APP_ROOT . '/public/includes/footer.php';
exit;
}
@@ -122,7 +122,7 @@ try {
$languages = [];
}
include APP_ROOT . '/includes/header.php'; ?>
include APP_ROOT . '/public/includes/header.php'; ?>
<section class="section">
<div class="container">
@@ -415,4 +415,4 @@ include APP_ROOT . '/includes/header.php'; ?>
</div>
</section>
<?php include APP_ROOT . '/includes/footer.php'; ?>
<?php include APP_ROOT . '/public/includes/footer.php'; ?>

View File

@@ -0,0 +1 @@
[1770894664]

View File

@@ -4,7 +4,7 @@
* Tests search queries and results
*/
require_once __DIR__ . '/../../lib/Database.php';
require_once __DIR__ . '/../../src/Database.php';
echo "Search Functionality Test\n";
echo "=========================\n\n";

View File

@@ -4,7 +4,7 @@
* Tests SQL injection protection and input sanitization
*/
require_once __DIR__ . '/../../lib/Database.php';
require_once __DIR__ . '/../../src/Database.php';
echo "Security Test Suite\n";
echo "===================\n\n";

View File

@@ -4,7 +4,7 @@
* Tests basic database connectivity and query functionality
*/
require_once __DIR__ . '/../../lib/Database.php';
require_once __DIR__ . '/../../src/Database.php';
echo "Database Connection Test\n";
echo "========================\n\n";

View File

@@ -4,7 +4,7 @@
* Tests rate limiting functionality
*/
require_once __DIR__ . '/../../lib/RateLimit.php';
require_once __DIR__ . '/../../src/RateLimit.php';
echo "Rate Limit Test\n";
echo "===============\n\n";