Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f80578c26a | ||
| 6dd3b7c701 | |||
|
|
fb2691251a | ||
| 94115a80f6 | |||
|
|
99d161921e | ||
| bf9f4aaa29 | |||
|
|
7f797e307d | ||
| 89465f5cd5 | |||
|
|
0e68d9dbe7 | ||
| 19ac37fb3e | |||
|
|
7e967a1649 | ||
| f5ab0335f9 | |||
|
|
44e1e4a293 | ||
| ad92a4c434 | |||
|
|
be12175e17 | ||
| e8fc85c173 | |||
|
|
b39e6f81d8 | ||
| 28690be509 |
1
.env
1
.env
@@ -48,5 +48,4 @@ DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&ch
|
|||||||
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
||||||
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/maintenance.on
|
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/maintenance.on
|
||||||
INVENTORY_MAINTENANCE_PATH=/var/www/maintenance/inventory/maintenance.on
|
INVENTORY_MAINTENANCE_PATH=/var/www/maintenance/inventory/maintenance.on
|
||||||
FERME_MAINTENANCE_PATH=/var/www/maintenance/ferme/maintenance.on
|
|
||||||
###< malio/maintenance ###
|
###< malio/maintenance ###
|
||||||
|
|||||||
102
README.md
102
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Central
|
# Central
|
||||||
|
|
||||||
Application de gestion du SI Malio — gestion des applications, bases de données, mode maintenance, déploiements.
|
Application de supervision du SI Malio. Permet de piloter le mode maintenance de toutes les applications sans se connecter a chaque projet.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -10,9 +10,107 @@ make install
|
|||||||
make fixtures
|
make fixtures
|
||||||
```
|
```
|
||||||
|
|
||||||
## Accès
|
## Acces
|
||||||
|
|
||||||
- Frontend : http://localhost:8084
|
- Frontend : http://localhost:8084
|
||||||
- API : http://localhost:8084/api
|
- API : http://localhost:8084/api
|
||||||
- Dev Nuxt (hot reload) : http://localhost:3003
|
- Dev Nuxt (hot reload) : http://localhost:3003
|
||||||
- Login : `admin` / `admin`
|
- Login : `admin` / `admin`
|
||||||
|
|
||||||
|
## Applications gerees
|
||||||
|
|
||||||
|
| Application | Slug | Port prod | Fichier maintenance |
|
||||||
|
|-------------|------|-----------|---------------------|
|
||||||
|
| SIRH | `sirh` | 8080 | `/var/www/sirh/maintenance.on` |
|
||||||
|
| Lesstime | `lesstime` | 8081 | `/var/www/lesstime/maintenance.on` |
|
||||||
|
| Inventory | `inventory` | 8082 | `/var/www/inventory/maintenance.on` |
|
||||||
|
|
||||||
|
La configuration est dans `config/applications.yaml`.
|
||||||
|
|
||||||
|
## Comment fonctionne la maintenance
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Central (container Docker, port 8084)
|
||||||
|
|
|
||||||
|
| Volumes Docker :
|
||||||
|
| /var/www/sirh (hote) --> /var/www/maintenance/sirh (container)
|
||||||
|
| /var/www/lesstime (hote) --> /var/www/maintenance/lesstime (container)
|
||||||
|
| /var/www/inventory (hote) --> /var/www/maintenance/inventory (container)
|
||||||
|
|
|
||||||
|
| Quand l'admin clique "Activer maintenance" sur Lesstime :
|
||||||
|
|
|
||||||
|
v
|
||||||
|
API : POST /api/applications/lesstime/maintenance { "maintenance": true }
|
||||||
|
|
|
||||||
|
v
|
||||||
|
MaintenanceToggleProcessor
|
||||||
|
--> touch /var/www/maintenance/lesstime/maintenance.on (dans le container)
|
||||||
|
--> via le volume Docker, cree /var/www/lesstime/maintenance.on sur l'hote
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Nginx de l'hote (reverse proxy de Lesstime) :
|
||||||
|
if (-f /var/www/lesstime/maintenance.on) --> return 503
|
||||||
|
--> sert /var/www/lesstime/public/maintenance.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cote Central (ce projet)
|
||||||
|
|
||||||
|
1. `config/applications.yaml` definit les apps et leur `maintenance_path` (variable d'env)
|
||||||
|
2. Les variables d'env (`SIRH_MAINTENANCE_PATH`, etc.) pointent vers `/var/www/maintenance/{slug}/maintenance.on`
|
||||||
|
3. Le `docker-compose.yml` prod monte les dossiers de deploy des apps vers `/var/www/maintenance/`
|
||||||
|
4. `MaintenanceToggleProcessor` cree ou supprime le fichier `maintenance.on`
|
||||||
|
5. `ManagedApplicationProvider` lit `file_exists()` pour afficher l'etat actuel
|
||||||
|
|
||||||
|
### Cote application cible (SIRH, Lesstime, Inventory)
|
||||||
|
|
||||||
|
Chaque application doit avoir :
|
||||||
|
|
||||||
|
1. **Un `maintenance.html`** dans `/var/www/{app}/public/` sur le serveur (extrait automatiquement par `deploy.sh`)
|
||||||
|
2. **Un nginx reverse proxy sur l'hote** qui verifie l'existence du fichier `maintenance.on` :
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
server_name app.malio-dev.fr;
|
||||||
|
root /var/www/{app}/public;
|
||||||
|
|
||||||
|
if (-f /var/www/{app}/maintenance.on) {
|
||||||
|
return 503;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 503 @maintenance;
|
||||||
|
location @maintenance { rewrite ^(.*)$ /maintenance.html break; }
|
||||||
|
location = /maintenance.html { internal; }
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:{port};
|
||||||
|
# headers...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ajouter une nouvelle application
|
||||||
|
|
||||||
|
1. Ajouter l'entree dans `config/applications.yaml`
|
||||||
|
2. Ajouter la variable d'env `{APP}_MAINTENANCE_PATH` dans `.env` et `.env` prod
|
||||||
|
3. Ajouter le volume dans `docker-compose.yml` prod : `/var/www/{app}:/var/www/maintenance/{app}`
|
||||||
|
4. Configurer le nginx reverse proxy de l'hote pour la nouvelle app (voir ci-dessus)
|
||||||
|
5. S'assurer que `maintenance.html` existe dans `/var/www/{app}/public/`
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **Backend** : PHP 8.4, Symfony 8.0, API Platform 4
|
||||||
|
- **Frontend** : Nuxt 4 (SPA), Vue 3, Tailwind CSS
|
||||||
|
- **Auth** : JWT HTTP-only cookie
|
||||||
|
- **Docker** : PHP-FPM + Nginx + Supervisor
|
||||||
|
|
||||||
|
## Deploiement
|
||||||
|
|
||||||
|
Voir `doc/deployment-docker.md` pour le guide complet.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/central
|
||||||
|
./deploy.sh # latest
|
||||||
|
./deploy.sh v0.1.5 # version specifique
|
||||||
|
```
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ parameters:
|
|||||||
- { name: 'SIRH', slug: 'sirh', maintenance_path: '%env(SIRH_MAINTENANCE_PATH)%' }
|
- { name: 'SIRH', slug: 'sirh', maintenance_path: '%env(SIRH_MAINTENANCE_PATH)%' }
|
||||||
- { name: 'Lesstime', slug: 'lesstime', maintenance_path: '%env(LESSTIME_MAINTENANCE_PATH)%' }
|
- { name: 'Lesstime', slug: 'lesstime', maintenance_path: '%env(LESSTIME_MAINTENANCE_PATH)%' }
|
||||||
- { name: 'Inventory', slug: 'inventory', maintenance_path: '%env(INVENTORY_MAINTENANCE_PATH)%' }
|
- { name: 'Inventory', slug: 'inventory', maintenance_path: '%env(INVENTORY_MAINTENANCE_PATH)%' }
|
||||||
- { name: 'Ferme', slug: 'ferme', maintenance_path: '%env(FERME_MAINTENANCE_PATH)%' }
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.1.2'
|
app.version: '0.1.11'
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ services:
|
|||||||
- /var/www/sirh:/var/www/maintenance/sirh
|
- /var/www/sirh:/var/www/maintenance/sirh
|
||||||
- /var/www/lesstime:/var/www/maintenance/lesstime
|
- /var/www/lesstime:/var/www/maintenance/lesstime
|
||||||
- /var/www/inventory:/var/www/maintenance/inventory
|
- /var/www/inventory:/var/www/maintenance/inventory
|
||||||
- /var/www/ferme:/var/www/maintenance/ferme
|
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -129,7 +128,6 @@ ENCRYPTION_KEY=<generer avec: openssl rand -hex 32>
|
|||||||
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
||||||
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/maintenance.on
|
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/maintenance.on
|
||||||
INVENTORY_MAINTENANCE_PATH=/var/www/maintenance/inventory/maintenance.on
|
INVENTORY_MAINTENANCE_PATH=/var/www/maintenance/inventory/maintenance.on
|
||||||
FERME_MAINTENANCE_PATH=/var/www/maintenance/ferme/maintenance.on
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Generer les cles JWT
|
### 5. Generer les cles JWT
|
||||||
@@ -155,7 +153,7 @@ Central pilote les fichiers `maintenance.on` des autres projets via des volumes
|
|||||||
Verifier que les dossiers existent :
|
Verifier que les dossiers existent :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ls -ld /var/www/sirh /var/www/lesstime /var/www/inventory /var/www/ferme
|
ls -ld /var/www/sirh /var/www/lesstime /var/www/inventory
|
||||||
```
|
```
|
||||||
|
|
||||||
Si Central ne peut pas ecrire `maintenance.on`, il faudra ajuster les permissions sur ces dossiers pour que le processus du conteneur puisse creer/supprimer ce fichier.
|
Si Central ne peut pas ecrire `maintenance.on`, il faudra ajuster les permissions sur ces dossiers pour que le processus du conteneur puisse creer/supprimer ce fichier.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export function useAppVersion() {
|
|||||||
if (version.value) {
|
if (version.value) {
|
||||||
return version.value
|
return version.value
|
||||||
}
|
}
|
||||||
const response = await api.get<{ version: string }>('version', {}, {
|
const response = await api.get<{ version: string }>('/version', {}, {
|
||||||
toast: false
|
toast: false
|
||||||
})
|
})
|
||||||
version.value = response.version
|
version.value = response.version
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY composer.json composer.lock symfony.lock ./
|
COPY composer.json composer.lock symfony.lock ./
|
||||||
RUN APP_ENV=prod APP_DEBUG=0 COMPOSER_IPRESOLVE=4 composer install --no-dev --no-scripts --no-interaction
|
RUN APP_ENV=prod APP_DEBUG=0 composer install --no-dev --no-scripts --no-interaction
|
||||||
|
|
||||||
COPY bin bin/
|
COPY bin bin/
|
||||||
COPY config config/
|
COPY config config/
|
||||||
@@ -26,7 +26,6 @@ FROM node:lts-alpine AS frontend-build
|
|||||||
|
|
||||||
WORKDIR /app/frontend
|
WORKDIR /app/frontend
|
||||||
COPY frontend/package.json frontend/package-lock.json ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
ENV NODE_OPTIONS=--dns-result-order=ipv4first
|
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
COPY frontend/ ./
|
COPY frontend/ ./
|
||||||
@@ -74,7 +73,8 @@ RUN echo "APP_ENV=prod" > /var/www/html/.env
|
|||||||
|
|
||||||
# Permissions
|
# Permissions
|
||||||
RUN mkdir -p /var/www/html/var /var/www/html/var/uploads \
|
RUN mkdir -p /var/www/html/var /var/www/html/var/uploads \
|
||||||
&& chown -R www-data:www-data /var/www/html/var
|
/var/www/maintenance/sirh /var/www/maintenance/lesstime /var/www/maintenance/inventory \
|
||||||
|
&& chown -R www-data:www-data /var/www/html/var /var/www/maintenance
|
||||||
|
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ services:
|
|||||||
- /var/www/sirh:/var/www/maintenance/sirh
|
- /var/www/sirh:/var/www/maintenance/sirh
|
||||||
- /var/www/lesstime:/var/www/maintenance/lesstime
|
- /var/www/lesstime:/var/www/maintenance/lesstime
|
||||||
- /var/www/inventory:/var/www/maintenance/inventory
|
- /var/www/inventory:/var/www/maintenance/inventory
|
||||||
- /var/www/ferme:/var/www/maintenance/ferme
|
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
normalizationContext: ['groups' => ['me:read']],
|
normalizationContext: ['groups' => ['me:read']],
|
||||||
),
|
),
|
||||||
new Get(
|
new Get(
|
||||||
|
security: "is_granted('ROLE_ADMIN')",
|
||||||
normalizationContext: ['groups' => ['user:list']],
|
normalizationContext: ['groups' => ['user:list']],
|
||||||
),
|
),
|
||||||
new GetCollection(
|
new GetCollection(
|
||||||
|
security: "is_granted('ROLE_ADMIN')",
|
||||||
normalizationContext: ['groups' => ['user:list']],
|
normalizationContext: ['groups' => ['user:list']],
|
||||||
),
|
),
|
||||||
new Post(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
new Post(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
||||||
|
|||||||
@@ -44,13 +44,17 @@ final readonly class MaintenanceToggleProcessor implements ProcessorInterface
|
|||||||
if ($data->maintenance) {
|
if ($data->maintenance) {
|
||||||
$directory = dirname($maintenancePath);
|
$directory = dirname($maintenancePath);
|
||||||
|
|
||||||
if (!is_dir($directory)) {
|
if (!is_dir($directory) && !mkdir($directory, 0755, true)) {
|
||||||
mkdir($directory, 0755, true);
|
throw new \RuntimeException(sprintf('Cannot create directory "%s".', $directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
touch($maintenancePath);
|
if (!touch($maintenancePath)) {
|
||||||
|
throw new \RuntimeException(sprintf('Cannot create maintenance file at "%s".', $maintenancePath));
|
||||||
|
}
|
||||||
} elseif (file_exists($maintenancePath)) {
|
} elseif (file_exists($maintenancePath)) {
|
||||||
unlink($maintenancePath);
|
if (!unlink($maintenancePath)) {
|
||||||
|
throw new \RuntimeException(sprintf('Cannot remove maintenance file at "%s".', $maintenancePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$dto = new ManagedApplication();
|
$dto = new ManagedApplication();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use ApiPlatform\Metadata\Operation;
|
|||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @implements ProviderInterface<User>
|
* @implements ProviderInterface<User>
|
||||||
@@ -20,7 +21,12 @@ final readonly class MeProvider implements ProviderInterface
|
|||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): User
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): User
|
||||||
{
|
{
|
||||||
// @var User $user
|
$user = $this->security->getUser();
|
||||||
return $this->security->getUser();
|
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
throw new AccessDeniedHttpException('User not authenticated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user