# Installation — ZF MDT (version web)

MDT web pour serveur GMod DarkRP, thème New York. Le site est le terminal ;
le jeu s'y branche par une API REST, et l'affiche en jeu dans un panneau
DHTML.

---

## Prérequis

| Élément | Version |
|---|---|
| PHP | **8.0 ou supérieur**, avec `pdo_mysql`, `mbstring`, `json` |
| MySQL / MariaDB | 5.7+ / 10.3+ |
| Serveur web | Apache avec `mod_rewrite`, ou nginx (config plus bas) |

Pas de Composer, pas de Node : l'application n'a aucune dépendance externe.
Le déploiement se résume à **envoyer les fichiers, remplir un fichier de
configuration, importer un `.sql`**.

---

## 1. Envoyer les fichiers

Copiez le contenu de `zf_mdt_web/` sur l'hébergement.

**La racine web doit pointer sur `public/`**, pas sur le dossier parent.
C'est ce qui garde `config/`, `app/` et `database/` hors de portée du
navigateur.

Si votre hébergement ne permet pas de changer la racine (mutualisé
classique), placez le contenu de `public/` à la racine et le reste dans un
dossier au-dessus, puis adaptez le chemin dans `public/index.php` :

```php
require dirname(__DIR__) . '/zf_mdt_app/app/bootstrap.php';
```

### nginx

```nginx
root /var/www/zf_mdt/public;
index index.php;

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# Rien d'autre que public/ ne doit être servi.
location ~ /\.(env|git) { deny all; }
```

---

## 2. Créer la base

Créez une base vide (`zf_mdt`) et un utilisateur dédié, puis importez le
schéma :

```bash
mysql -u zf_mdt -p zf_mdt < database/schema.sql
```

Via phpMyAdmin : onglet **Importer**, sélectionnez `database/schema.sql`.

Le schéma crée 13 tables préfixées `mdt_`. Il est ré-exécutable sans risque
(`CREATE TABLE IF NOT EXISTS`).

---

## 3. Configurer

```bash
cp config/config.example.php config/config.php
```

Puis éditez `config/config.php`. Les quatre réglages qui comptent :

```php
'app' => [
    // SANS slash final, et exactement ce que voit le navigateur.
    // Steam refuse le retour de connexion si l'URL diffère.
    'url'   => 'https://mdt.mon-serveur.fr',
    'debug' => false,          // true seulement le temps de l'installation
],

'db' => [
    'host' => '127.0.0.1',
    'name' => 'zf_mdt',
    'user' => 'zf_mdt',
    'pass' => 'votre-mot-de-passe',
],

'security' => [
    // Longue chaîne aléatoire. Changez-la.
    'app_secret'    => '...',
    // Passez à true dès que le site est en https.
    'cookie_secure' => true,
],

'game' => [
    // Doit être identique côté addon GMod. Elle donne accès au fichier.
    'api_key' => 'une-autre-longue-chaine-aleatoire',
],
```

### Clé Steam (facultative)

Sans elle, la connexion fonctionne quand même, mais les comptes sont créés
avec un nom générique et sans avatar. Obtenez-la sur
<https://steamcommunity.com/dev/apikey> et renseignez `steam.api_key`.

---

## 4. Première connexion

Ouvrez le site et connectez-vous avec Steam.

**Le tout premier compte créé devient automatiquement chef de département et
administrateur du site.** Sans ce mécanisme, personne ne pourrait attribuer
le moindre grade — le système serait verrouillé dès le départ.

Tous les comptes suivants sont créés **sans aucun droit**. Ils voient un
écran « en attente d'affectation » jusqu'à ce qu'un gradé leur attribue un
service et un grade depuis **Personnel**.

---

## 5. Caler la grille d'adresses

Chaque appel affiche une adresse new-yorkaise. Deux niveaux se combinent.

### Zones nommées

Pour les lieux qui ont un nom propre (hôpital, banque, commissariat), en jeu
en tant que staff :

```
!mdtzone "Bellevue Hospital Center" 500
```

La commande répond avec l'adresse que la grille calcule à cet endroit — ce
qui en fait aussi l'outil de réglage.

### Grille de repli

Manhattan est un plan en damier : le système convertit une position monde en
croisement rue / avenue, avec un numéro d'immeuble plausible et
l'orientation Est/Ouest par rapport à la 5e Avenue.

```php
'grid' => [
    'origin_x'     => 0,      // point correspondant à 34e rue / 1re avenue
    'origin_y'     => 0,
    'block_size'   => 900,    // unités Source par bloc
    'street_axis'  => 'y',    // axe qui fait varier le numéro de rue
    'first_street' => 34,
],
```

**Méthode de réglage :** placez-vous au coin sud-est de la map, relevez la
position (`getpos` en console), reportez-la dans `origin_x` / `origin_y`.
Marchez ensuite d'un bloc et ajustez `block_size` jusqu'à ce que le numéro de
rue avance exactement de 1.

### Carte du CAD

Déposez une vue de dessus de la map dans `public/assets/img/map.jpg`, puis
renseignez les bornes du monde qu'elle couvre :

```php
'map' => [
    'image' => '/assets/img/map.jpg',
    'min_x' => -8000, 'max_x' => 8000,
    'min_y' => -8000, 'max_y' => 8000,
],
```

---

## 6. Brancher le serveur de jeu

Copiez `gmod/zf_mdt_bridge/` dans `garrysmod/addons/` du serveur, puis
éditez `lua/zf_mdt_bridge/sh_config.lua` :

```lua
C.URL = "https://mdt.mon-serveur.fr"   -- sans slash final
C.Key = "la-meme-cle-que-game.api_key"
```

Au démarrage, la console serveur affiche :

```
[ZF MDT Bridge] site joignable (0 unite(s) connue(s)).
```

Si vous voyez `site injoignable`, vérifiez dans l'ordre : l'URL, la clé, et
que le serveur GMod peut sortir en HTTP (certains hébergeurs le bloquent).

### Restreindre l'API à votre serveur

Une fois l'IP du serveur GMod connue, ajoutez-la :

```php
'game' => [
    'allowed_ips' => ['203.0.113.42'],
],
```

La clé seule protège déjà, mais deux barrières valent mieux qu'une.

---

## 7. Rattacher vos jobs

Le pont détecte le service d'un joueur par son job DarkRP. Deux méthodes,
cumulables, dans `sh_config.lua` :

```lua
-- Correspondance exacte (le plus sûr)
C.Jobs = {
    ["Officier de Police"] = { dept = "police", rank = "officer" },
    ["Paramedic"]          = { dept = "ems",    rank = "medic" },
    ["Pompier"]            = { dept = "fire",   rank = "firefighter" },
}

-- Filet de sécurité par mot-clé
C.JobPatterns = {
    { match = "police", dept = "police", rank = "officer" },
    { match = "medic",  dept = "ems",    rank = "emt" },
    { match = "fire",   dept = "fire",   rank = "firefighter" },
}
```

Si l'ancien addon `zf_mdt` est encore installé, **sa configuration fait
autorité** et ces tables sont ignorées : inutile de tenir deux listes.

---

## Utilisation en jeu

| Commande | Qui | Effet |
|---|---|---|
| `/mdt` | Secours | Ouvre le terminal en panneau DHTML |
| `/911 <texte>` | Tous | Appel d'urgence — le service est deviné aux mots-clés |
| `!mdtzone "Nom" [rayon]` | Staff | Enregistre une zone d'adresse |
| `zf_mdt_callsign 47 ADAM` | Secours | Définit son indicatif |
| `zf_mdt_status onscene` | Secours | Change de statut sans ouvrir le terminal |

Le terminal ne s'ouvre que dans un véhicule de service ou devant une entité
`zf_mdt_terminal`. Pour lever cette contrainte le temps des tests :

```lua
C.RequireVehicleOrTerminal = false
```

---

## Vérifications après installation

1. **Le site répond** — la page de connexion s'affiche.
2. **La base est installée** — sinon un bandeau le signale sur la connexion.
3. **Steam fonctionne** — vous revenez connecté après le passage par Steam.
4. **Le pont est branché** — `[ZF MDT Bridge] site joignable` en console.
5. **Les unités remontent** — prenez un job de police, attendez 10 s, la
   page Répartition doit lister votre unité.
6. **Les appels remontent** — tapez `/911 il y a le feu` en civil : un appel
   FDNY doit apparaître.

---

## Sécurité — ce qui est déjà en place

- Requêtes préparées partout, aucune concaténation SQL
- Routes **protégées par défaut** : il faut `->open()` pour en exposer une
- CSRF sur toute écriture, cookie `HttpOnly` + `SameSite=Lax`
- Session régénérée à chaque changement de privilège, expiration sur
  inactivité (2 h par défaut)
- Réponse Steam **revalidée auprès de Steam** — sans cette étape, une URL de
  retour forgée permettrait de se connecter en tant que n'importe qui
- Jetons de connexion à usage unique, consommés avant traitement
- Journal d'audit de toute consultation, avec détection des consultations de
  sa propre fiche et des rafales de recherches

**À faire de votre côté :** passer le site en HTTPS, mettre `debug` à
`false`, et changer `app_secret` et `api_key`.

---

## Entretien

Aucune tâche planifiée n'est nécessaire : la purge des jetons expirés, des
mandats périmés et des appels jamais traités est déclenchée par les appels
du serveur de jeu.

Si vous préférez un cron, ce n'est pas interdit :

```bash
*/15 * * * * curl -s -H "X-ZF-Key: VOTRE_CLE" https://mdt.exemple.fr/api/game/queue > /dev/null
```
