Compare commits

..

9 Commits

Author SHA1 Message Date
gitea-actions
3d69346d24 chore: bump version to v0.1.70
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 18s
2026-03-31 07:11:36 +00:00
ea849a4fdd Merge remote-tracking branch 'origin/develop' into develop
All checks were successful
Auto Tag Develop / tag (push) Successful in 7s
2026-03-31 09:11:28 +02:00
7b3dcc3c54 fix : déploiement docker 2026-03-31 09:11:00 +02:00
gitea-actions
c6ab8e3624 chore: bump version to v0.1.69
Some checks failed
Auto Tag Develop / tag (push) Successful in 5s
Build & Push Docker Image / build (push) Failing after 6s
2026-03-31 06:57:47 +00:00
f3b65c0617 fix(deploy) : use REGISTRY_TOKEN for Docker registry authentication
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
2026-03-31 08:57:27 +02:00
gitea-actions
15ce234737 chore: bump version to v0.1.68
Some checks failed
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Failing after 6s
2026-03-31 06:38:47 +00:00
caffb74cbf [#INFRA-142] Revoir le système de déploiement (#11)
All checks were successful
Auto Tag Develop / tag (push) Successful in 7s
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [ ] Pas de régression
- [ ] TU/TI/TF rédigée
- [ ] TU/TI/TF OK
- [ ] CHANGELOG modifié

Co-authored-by: gitea-actions <gitea-actions@local>
Reviewed-on: #11
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-31 06:38:38 +00:00
gitea-actions
54354c4435 chore: bump version to v0.1.67
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
Build Release Artefact / build (push) Successful in 1m33s
2026-03-30 13:06:35 +00:00
3dcdf0fb81 [#SIRH-18] Fix connexion conducteur (#10)
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [ ] Pas de régression
- [ ] TU/TI/TF rédigée
- [ ] TU/TI/TF OK
- [ ] CHANGELOG modifié

Reviewed-on: #10
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-30 13:06:27 +00:00
19 changed files with 443 additions and 78 deletions

View File

@@ -24,7 +24,9 @@
"Bash(npx xlsx-cli:*)",
"Bash(cat /home/m-tristan/.claude/projects/-home-m-tristan-workspace-SIRH/4b53d9d7-d8ae-451f-a5cc-5d4fd55f2eef/tool-results/toolu_019hng9Cu2m9wiNACuC2Wm3F.json | python3 -c \"import json,sys; data=json.load\\(sys.stdin\\); print\\(data[0]['text']\\)\" 2>/dev/null | head -2000)",
"Bash(pip3 install:*)",
"Bash(find:*)"
"Bash(find:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
]
}
}

23
.dockerignore Normal file
View File

@@ -0,0 +1,23 @@
.git
.gitea
.env.local
.env.test
docker/
deploy/docker/docker-compose.prod.yml
deploy/docker/deploy.sh
deploy/docker/.env.example
frontend/node_modules
frontend/.nuxt
frontend/.output
var/
LOG/
docs/
doc/
tests/
*.sql
*.xlsx
*.png
*.md
!composer.lock
!symfony.lock
!frontend/package-lock.json

View File

@@ -0,0 +1,30 @@
name: Build & Push Docker Image
on:
push:
tags:
- "v*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Gitea Registry
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.malio.fr -u "${{ gitea.repository_owner }}" --password-stdin
- name: Build Docker image
run: |
docker build \
-f deploy/docker/Dockerfile.prod \
-t gitea.malio.fr/malio-dev/sirh:${{ github.ref_name }} \
-t gitea.malio.fr/malio-dev/sirh:latest \
.
- name: Push Docker image
run: |
docker push gitea.malio.fr/malio-dev/sirh:${{ github.ref_name }}
docker push gitea.malio.fr/malio-dev/sirh:latest

View File

@@ -1,65 +0,0 @@
name: Build Release Artefact
on:
push:
tags:
- "v*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: mbstring, intl, pdo_pgsql, xml, curl, zip, gd
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Install backend deps (prod)
env:
APP_ENV: prod
APP_DEBUG: "0"
run: composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
- name: Build frontend (static)
run: |
cd frontend
npm ci
CI=1 NUXT_TELEMETRY_DISABLED=1 NUXT_PUBLIC_API_BASE=/api NUXT_PUBLIC_APP_BASE=/ npm run generate
test -f .output/public/index.html
- name: Build artefact
shell: bash
run: |
set -euo pipefail
mkdir -p release
tar -czf "release/sirh-${GITHUB_REF_NAME}.tar.gz" \
bin \
config \
migrations \
public \
src \
templates \
vendor \
composer.json \
composer.lock \
symfony.lock \
frontend/.output
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: release/sirh-${{ github.ref_name }}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/9cad43df-2147-4989-b7a4-443067034884/console_3.sql" value="9cad43df-2147-4989-b7a4-443067034884" />
</component>
</project>

View File

@@ -1,4 +1,5 @@
# SIRH
Application de gestion des absences employée
## Importer un dump de prod en dev
@@ -17,3 +18,8 @@ Remplie la base avec le dump :
```shell
docker compose exec -T db psql -U root -d sirh < sirh.sql
```
## Mettre SUPER_ADMIN sur un user
```sql
UPDATE users SET roles = '["ROLE_ADMIN","ROLE_SUPER_ADMIN"]' WHERE username = 'emilie';
```

View File

@@ -1,2 +1,2 @@
parameters:
app.version: '0.1.66'
app.version: '0.1.70'

View File

@@ -0,0 +1,25 @@
# Symfony
APP_ENV=prod
APP_DEBUG=0
APP_SECRET=change-me
# Database (use host.docker.internal to reach bare-metal PostgreSQL)
DATABASE_URL="postgresql://sirh_user:password@host.docker.internal:5432/sirh?serverVersion=16&charset=utf8"
# JWT
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=change-me
JWT_COOKIE_SECURE=1
JWT_COOKIE_SAMESITE=lax
JWT_TOKEN_TTL=86400
JWT_COOKIE_TTL=86400
# CORS
CORS_ALLOW_ORIGIN='^https?://sirh\.malio-dev\.fr$'
# App
DEFAULT_URI=https://sirh.malio-dev.fr
APP_SHARE_DIR=var/share
RTT_START_DATE=2026-02-23
HOLIDAY_URL="https://calendrier.api.gouv.fr/jours-feries/"

View File

@@ -0,0 +1,77 @@
# --- Stage 1: Build backend ---
FROM php:8.4-cli AS backend-build
RUN apt-get update && apt-get install -y \
libicu-dev libpq-dev libpng-dev libzip-dev libxml2-dev \
unzip curl git \
&& docker-php-ext-install -j$(nproc) intl pdo_pgsql zip gd opcache \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock symfony.lock ./
RUN APP_ENV=prod APP_DEBUG=0 composer install --no-dev --optimize-autoloader --no-scripts --no-interaction
COPY bin bin/
COPY config config/
COPY migrations migrations/
COPY public public/
COPY src src/
COPY templates templates/
# --- Stage 2: Build frontend ---
FROM node:lts-alpine AS frontend-build
WORKDIR /app/frontend
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
ENV CI=1 \
NUXT_TELEMETRY_DISABLED=1 \
NUXT_PUBLIC_API_BASE=/api \
NUXT_PUBLIC_APP_BASE=/
RUN npm run generate
# --- Stage 3: Production image ---
FROM php:8.4-fpm AS production
RUN apt-get update && apt-get install -y \
libicu-dev libpq-dev libpng-dev libzip-dev libxml2-dev \
nginx supervisor \
&& docker-php-ext-install -j$(nproc) intl pdo_pgsql zip gd opcache \
&& rm -rf /var/lib/apt/lists/*
# PHP production config
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# PHP-FPM: forward worker output to stderr for docker logs
RUN echo "catch_workers_output = yes" >> /usr/local/etc/php-fpm.d/www.conf \
&& echo "decorate_workers_output = no" >> /usr/local/etc/php-fpm.d/www.conf
# Nginx: log to stdout/stderr
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# Remove default nginx site
RUN rm -f /etc/nginx/sites-enabled/default
# Configs
COPY deploy/docker/supervisord.conf /etc/supervisor/conf.d/app.conf
COPY deploy/docker/nginx.conf /etc/nginx/sites-enabled/sirh.conf
# Backend from stage 1
COPY --from=backend-build /app /var/www/html
# Frontend from stage 2
COPY --from=frontend-build /app/frontend/.output/public /var/www/html/frontend/.output/public
# Permissions
RUN mkdir -p /var/www/html/var \
&& chown -R www-data:www-data /var/www/html/var
WORKDIR /var/www/html
EXPOSE 80
CMD ["supervisord", "-n", "-c", "/etc/supervisor/conf.d/app.conf"]

28
deploy/docker/deploy.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
TAG="${1:-latest}"
export SIRH_IMAGE_TAG="$TAG"
echo "==> Deploying sirh:${TAG}..."
echo "==> Pulling image..."
docker compose pull
echo "==> Starting container..."
docker compose up -d
echo "==> Waiting for container to be ready..."
sleep 3
echo "==> Running migrations..."
docker compose exec -T app php bin/console doctrine:migrations:migrate --no-interaction
echo "==> Clearing cache..."
docker compose exec -T app php bin/console cache:clear --env=prod
docker compose exec -T app php bin/console cache:warmup --env=prod
VERSION=$(docker compose exec -T app cat config/version.yaml | grep 'app.version' | awk -F"'" '{print $2}')
echo "==> Deployed v${VERSION}"

View File

@@ -0,0 +1,13 @@
services:
app:
image: gitea.malio.fr/malio-dev/sirh:${SIRH_IMAGE_TAG:-latest}
container_name: sirh-app
env_file: .env
ports:
- "8080:80"
volumes:
- ./config/jwt:/var/www/html/config/jwt:ro
- ./uploads:/var/www/html/var/uploads
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped

46
deploy/docker/nginx.conf Normal file
View File

@@ -0,0 +1,46 @@
server {
listen 80;
server_name _;
root /var/www/html/frontend/.output/public;
index index.html;
access_log /dev/stdout;
error_log /dev/stderr;
location ^~ /api/ {
root /var/www/html/public;
try_files $uri /index.php?$query_string;
}
location ^~ /bundles/ {
root /var/www/html/public;
try_files $uri =404;
}
location = /api/login_check {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
fastcgi_param DOCUMENT_ROOT /var/www/html/public;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param PATH_INFO /login_check;
fastcgi_param REQUEST_URI /login_check;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/index\.php(/|$) {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
fastcgi_param DOCUMENT_ROOT /var/www/html/public;
fastcgi_pass 127.0.0.1:9000;
internal;
}
location ~ \.php$ {
return 404;
}
location / {
try_files $uri $uri/ /index.html;
}
}

View File

@@ -0,0 +1,28 @@
[supervisord]
nodaemon=true
user=root
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
[program:php-fpm]
command=php-fpm -F
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
stopasgroup=true
stopsignal=QUIT
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
stopasgroup=true
stopsignal=QUIT

View File

@@ -0,0 +1,12 @@
server {
listen 80;
server_name sirh.malio-dev.fr;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

135
doc/deployment-docker.md Normal file
View File

@@ -0,0 +1,135 @@
# Deploiement Docker — SIRH
## Pre-requis
Installer Docker et Docker Compose sur la machine :
```bash
# Ubuntu
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
```
Se deconnecter/reconnecter pour que le groupe `docker` prenne effet.
## Premier deploiement
### 1. Creer le dossier de deploiement
```bash
sudo mkdir -p /var/www/sirh
sudo chown -R $(whoami):$(whoami) /var/www/sirh
```
### 2. Copier les fichiers depuis le repo
```bash
cp deploy/docker/docker-compose.prod.yml /var/www/sirh/docker-compose.yml
cp deploy/docker/deploy.sh /var/www/sirh/deploy.sh
cp deploy/docker/.env.example /var/www/sirh/.env
chmod +x /var/www/sirh/deploy.sh
```
### 3. Configurer l'environnement
Editer `/var/www/sirh/.env` avec les vraies valeurs :
- `APP_SECRET` : generer avec `openssl rand -hex 32`
- `DATABASE_URL` : `postgresql://user:pass@host.docker.internal:5432/sirh?serverVersion=16&charset=utf8`
- `JWT_PASSPHRASE` : generer avec `openssl rand -hex 32`
### 4. Generer les cles JWT
```bash
cd /var/www/sirh
mkdir -p config/jwt
docker run --rm -v $(pwd)/config/jwt:/jwt php:8.4-cli bash -c \
"apt-get update -qq && apt-get install -y -qq openssl > /dev/null && \
openssl genpkey -algorithm RSA -out /jwt/private.pem -pkeyopt rsa_keygen_bits:4096 && \
openssl rsa -pubout -in /jwt/private.pem -out /jwt/public.pem"
```
### 5. Creer le dossier uploads
```bash
mkdir -p /var/www/sirh/uploads
```
### 6. Configurer le login au registry Gitea
```bash
docker login gitea.malio.fr
# Username: ton user Gitea
# Password: ton token Gitea
```
### 7. Configurer Nginx systeme
```bash
sudo cp deploy/nginx/sirh-docker.conf /etc/nginx/sites-available/sirh.conf
sudo ln -sf /etc/nginx/sites-available/sirh.conf /etc/nginx/sites-enabled/sirh.conf
sudo nginx -t && sudo systemctl reload nginx
```
### 8. Deployer
```bash
cd /var/www/sirh
./deploy.sh
```
## Deployer une release
```bash
cd /var/www/sirh
./deploy.sh # deploie la derniere version (latest)
./deploy.sh v0.1.61 # deploie une version specifique
```
## Rollback
### Image seule (pas de changement de schema BDD)
```bash
./deploy.sh v0.1.60
```
### Avec rollback de migration
```bash
# 1. Rollback schema (pendant que la version actuelle tourne encore)
docker compose exec -T app php bin/console doctrine:migrations:migrate prev --no-interaction
# 2. Deployer l'ancienne version
./deploy.sh v0.1.60
```
## Voir les logs
```bash
cd /var/www/sirh
docker compose logs -f # tous les logs
docker compose logs -f --tail=100 # 100 dernieres lignes
```
## Migration depuis l'ancien deploiement (tar.gz)
Si l'application tourne deja en bare metal :
1. Installer Docker (voir pre-requis)
2. Creer le dossier `/var/www/sirh-docker/` (ne pas ecraser l'ancien)
3. Copier les fichiers :
```bash
cp /var/www/sirh/.env /var/www/sirh-docker/.env
cp -a /var/www/sirh/config/jwt /var/www/sirh-docker/config/jwt
cp -a /var/www/sirh/var/uploads /var/www/sirh-docker/uploads
```
4. Editer `/var/www/sirh-docker/.env` : changer `DATABASE_URL` pour utiliser `host.docker.internal` au lieu de `127.0.0.1`
5. Mettre a jour Nginx systeme : remplacer la conf par `deploy/nginx/sirh-docker.conf`
6. Arreter l'ancien PHP-FPM : `sudo systemctl stop php8.4-fpm`
7. Deployer : `cd /var/www/sirh-docker && ./deploy.sh`
8. Verifier que tout marche, puis supprimer l'ancien dossier

View File

@@ -19,6 +19,7 @@
</NuxtLink>
</template>
<NuxtLink
v-if="isAdmin || !isDriver"
to="/hours"
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
:class="[
@@ -30,12 +31,13 @@
<p>Heures</p>
</NuxtLink>
<NuxtLink
v-if="isAdmin"
v-if="isAdmin || isDriver"
to="/driver-hours"
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
:class="route.path.startsWith('/driver-hours')
? 'bg-tertiary-500 text-primary-500 font-bold'
: ''"
:class="[
route.path.startsWith('/driver-hours') ? 'bg-tertiary-500 text-primary-500 font-bold' : '',
!isAdmin && isDriver ? 'border-t border-secondary-500 pt-3' : ''
]"
>
<Icon name="mdi:truck-outline" size="24"/>
<p>Heures Conducteurs</p>
@@ -115,5 +117,6 @@ const auth = useAuthStore()
const {version} = useAppVersion()
const isAdmin = computed(() => auth.user?.roles?.includes('ROLE_ADMIN') ?? false)
const isSuperAdmin = computed(() => auth.user?.roles?.includes('ROLE_SUPER_ADMIN') ?? false)
const isDriver = computed(() => auth.user?.isDriver ?? false)
const route = useRoute()
</script>

View File

@@ -69,7 +69,8 @@ const handleSubmit = async () => {
await auth.login(username.value, password.value)
const isAdmin = auth.user?.roles?.includes('ROLE_ADMIN')
await router.push(isAdmin ? '/calendar' : '/hours')
const isDriver = auth.user?.isDriver
await router.push(isAdmin ? '/calendar' : isDriver ? '/driver-hours' : '/hours')
} finally {
isSubmitting.value = false
}

View File

@@ -2,4 +2,5 @@ export type UserData = {
id: number
username: string
roles: string[]
isDriver: boolean
}

View File

@@ -224,5 +224,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this;
}
#[Groups(['user:read'])]
public function getIsDriver(): bool
{
return $this->employee?->getIsDriver() ?? false;
}
public function eraseCredentials(): void {}
}