Compare commits
12 Commits
4234efdb50
...
v0.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be12175e17 | ||
| e8fc85c173 | |||
|
|
b39e6f81d8 | ||
| 28690be509 | |||
|
|
7f6634bec7 | ||
| b0b05970c1 | |||
|
|
57be0bbf85 | ||
| d85d1cc1d6 | |||
| 7a3e010e88 | |||
| 9529f4cf63 | |||
| 30038255da | |||
| 2844fea802 |
65
.gitea/workflows/auto-tag-develop.yml
Normal file
65
.gitea/workflows/auto-tag-develop.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Auto Tag Develop
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
persist-credentials: true
|
||||||
|
|
||||||
|
- name: Create next tag from config/version.yaml
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Skip if current commit already has a vX.Y.Z tag
|
||||||
|
if git tag --points-at HEAD | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||||
|
echo "Tag already exists on this commit. Skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
changed_version=false
|
||||||
|
if git diff --name-only "${{ gitea.event.before }}" "${{ gitea.event.after }}" | grep -q '^config/version\.yaml$'; then
|
||||||
|
changed_version=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
read_version() {
|
||||||
|
awk -F': *' '/app\.version:/{print $2}' config/version.yaml | tr -d '[:space:]' | tr -d "'\""
|
||||||
|
}
|
||||||
|
|
||||||
|
if $changed_version; then
|
||||||
|
version="$(read_version)"
|
||||||
|
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Invalid version in version.yaml: $version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
last_tag="$(git tag -l 'v*' --sort=-v:refname | head -n1 || true)"
|
||||||
|
if [ -z "$last_tag" ]; then
|
||||||
|
version="0.1.0"
|
||||||
|
else
|
||||||
|
base="${last_tag#v}"
|
||||||
|
IFS='.' read -r major minor patch <<< "$base"
|
||||||
|
version="${major}.${minor}.$((patch + 1))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "parameters:\\n app.version: '%s'\\n" "$version" > config/version.yaml
|
||||||
|
git config user.name "gitea-actions"
|
||||||
|
git config user.email "gitea-actions@local"
|
||||||
|
git add config/version.yaml
|
||||||
|
git commit -m "chore: bump version to v$version" || true
|
||||||
|
git push origin develop || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="v$version"
|
||||||
|
git tag "$tag"
|
||||||
|
git push origin "$tag"
|
||||||
30
.gitea/workflows/build-docker.yml
Normal file
30
.gitea/workflows/build-docker.yml
Normal 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 infra/prod/Dockerfile \
|
||||||
|
-t gitea.malio.fr/malio-dev/central:${{ github.ref_name }} \
|
||||||
|
-t gitea.malio.fr/malio-dev/central:latest \
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: Push Docker image
|
||||||
|
run: |
|
||||||
|
docker push gitea.malio.fr/malio-dev/central:${{ github.ref_name }}
|
||||||
|
docker push gitea.malio.fr/malio-dev/central:latest
|
||||||
@@ -7,7 +7,7 @@ Application de gestion du SI Malio. Monorepo Symfony 8 (API Platform 4) + Nuxt 4
|
|||||||
- **Backend** : PHP 8.4, Symfony 8.0, API Platform 4, Doctrine ORM, PostgreSQL 16
|
- **Backend** : PHP 8.4, Symfony 8.0, API Platform 4, Doctrine ORM, PostgreSQL 16
|
||||||
- **Frontend** : Nuxt 4 (SSR off / SPA), Vue 3, Pinia, Tailwind CSS, @malio/layer-ui, nuxt-toast, @nuxtjs/i18n, @nuxt/icon
|
- **Frontend** : Nuxt 4 (SSR off / SPA), Vue 3, Pinia, Tailwind CSS, @malio/layer-ui, nuxt-toast, @nuxtjs/i18n, @nuxt/icon
|
||||||
- **Auth** : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login à `/login_check`, cookie `BEARER`
|
- **Auth** : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login à `/login_check`, cookie `BEARER`
|
||||||
- **Docker** : PHP-FPM + Node 24, Nginx (port 8083), PostgreSQL (port 5436)
|
- **Docker** : PHP-FPM + Node 24, Nginx (port 8084), PostgreSQL (port 5436)
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ make fixtures
|
|||||||
|
|
||||||
## Accès
|
## Accès
|
||||||
|
|
||||||
- Frontend : http://localhost:8083
|
- Frontend : http://localhost:8084
|
||||||
- API : http://localhost:8083/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`
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.1.0'
|
app.version: '0.1.4'
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ services:
|
|||||||
container_name: central-app
|
container_name: central-app
|
||||||
env_file: .env
|
env_file: .env
|
||||||
ports:
|
ports:
|
||||||
- "8083:80"
|
- "8084:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/jwt:/var/www/html/config/jwt:ro
|
- ./config/jwt:/var/www/html/config/jwt:ro
|
||||||
- ./uploads:/var/www/html/var/uploads
|
- ./uploads:/var/www/html/var/uploads
|
||||||
@@ -112,15 +112,15 @@ APP_SECRET=<generer avec: openssl rand -hex 32>
|
|||||||
|
|
||||||
DATABASE_URL="postgresql://malio:motdepasse@host.docker.internal:5432/central_prod?serverVersion=16&charset=utf8"
|
DATABASE_URL="postgresql://malio:motdepasse@host.docker.internal:5432/central_prod?serverVersion=16&charset=utf8"
|
||||||
|
|
||||||
DEFAULT_URI=https://central.malio-dev.fr
|
DEFAULT_URI=http://central.malio-dev.fr
|
||||||
APP_SHARE_DIR=var/share
|
APP_SHARE_DIR=var/share
|
||||||
|
|
||||||
CORS_ALLOW_ORIGIN='^https?://central\.malio-dev\.fr$'
|
CORS_ALLOW_ORIGIN='^http://central\.malio-dev\.fr$'
|
||||||
|
|
||||||
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
||||||
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
||||||
JWT_PASSPHRASE=<generer avec: openssl rand -hex 32>
|
JWT_PASSPHRASE=<generer avec: openssl rand -hex 32>
|
||||||
JWT_COOKIE_SECURE=1
|
JWT_COOKIE_SECURE=0
|
||||||
JWT_TOKEN_TTL=86400
|
JWT_TOKEN_TTL=86400
|
||||||
JWT_COOKIE_TTL=86400
|
JWT_COOKIE_TTL=86400
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ server {
|
|||||||
server_name central.malio-dev.fr;
|
server_name central.malio-dev.fr;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:8083;
|
proxy_pass http://127.0.0.1:8084;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@@ -212,7 +212,7 @@ cd /var/www/central
|
|||||||
|
|
||||||
## Verification apres deploiement
|
## Verification apres deploiement
|
||||||
|
|
||||||
1. Ouvrir `https://central.malio-dev.fr`
|
1. Ouvrir `http://central.malio-dev.fr`
|
||||||
2. Se connecter avec un compte admin
|
2. Se connecter avec un compte admin
|
||||||
3. Verifier que la page Applications charge
|
3. Verifier que la page Applications charge
|
||||||
4. Activer la maintenance sur SIRH
|
4. Activer la maintenance sur SIRH
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- php
|
- php
|
||||||
ports:
|
ports:
|
||||||
- "8083:80"
|
- "8084:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/var/www/html:ro
|
- ./:/var/www/html:ro
|
||||||
- ./infra/dev/nginx.conf:/etc/nginx/conf.d/central.conf:ro
|
- ./infra/dev/nginx.conf:/etc/nginx/conf.d/central.conf:ro
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
43
frontend/package-lock.json
generated
43
frontend/package-lock.json
generated
@@ -570,6 +570,29 @@
|
|||||||
"integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==",
|
"integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@emnapi/core": {
|
||||||
|
"version": "1.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
|
||||||
|
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/wasi-threads": "1.2.1",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
|
||||||
|
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
@@ -5692,6 +5715,16 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "11.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||||
|
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
@@ -12650,15 +12683,6 @@
|
|||||||
"url": "https://opencollective.com/svgo"
|
"url": "https://opencollective.com/svgo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svgo/node_modules/commander": {
|
|
||||||
"version": "11.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
|
||||||
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tagged-tag": {
|
"node_modules/tagged-tag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
|
||||||
@@ -12764,7 +12788,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
container_name: central-app
|
container_name: central-app
|
||||||
env_file: .env
|
env_file: .env
|
||||||
ports:
|
ports:
|
||||||
- "8083:80"
|
- "8084:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/jwt:/var/www/html/config/jwt:ro
|
- ./config/jwt:/var/www/html/config/jwt:ro
|
||||||
- ./uploads:/var/www/html/var/uploads
|
- ./uploads:/var/www/html/var/uploads
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ server {
|
|||||||
server_name central.malio-dev.fr;
|
server_name central.malio-dev.fr;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:8083;
|
proxy_pass http://127.0.0.1:8084;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|||||||
@@ -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