Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f6634bec7 | ||
| b0b05970c1 | |||
|
|
57be0bbf85 | ||
| d85d1cc1d6 | |||
| 7a3e010e88 | |||
| 9529f4cf63 | |||
| 30038255da | |||
| 2844fea802 | |||
| 4234efdb50 |
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{compose.yaml,compose.*.yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
29
.env
29
.env
@@ -21,3 +21,32 @@ JWT_COOKIE_TTL=86400
|
||||
DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}?serverVersion=16&charset=utf8"
|
||||
|
||||
ENCRYPTION_KEY=change_me_in_env_local
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_ENV=dev
|
||||
APP_SECRET=
|
||||
APP_SHARE_DIR=var/share
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> symfony/routing ###
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
DEFAULT_URI=http://localhost
|
||||
###< symfony/routing ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
||||
#
|
||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
###> malio/maintenance ###
|
||||
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
||||
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/maintenance.on
|
||||
INVENTORY_MAINTENANCE_PATH=/var/www/maintenance/inventory/maintenance.on
|
||||
FERME_MAINTENANCE_PATH=/var/www/maintenance/ferme/maintenance.on
|
||||
###< malio/maintenance ###
|
||||
|
||||
4
.env.dev
Normal file
4
.env.dev
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_SECRET=51b43bf17ac894e44bd113e75892c8c5
|
||||
###< symfony/framework-bundle ###
|
||||
3
.env.test
Normal file
3
.env.test
Normal file
@@ -0,0 +1,3 @@
|
||||
# define your env variables for the test env here
|
||||
KERNEL_CLASS='App\Kernel'
|
||||
APP_SECRET='$ecretf0rt3st'
|
||||
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
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -44,3 +44,8 @@ LOG/
|
||||
###> claude ###
|
||||
.claude/
|
||||
###< claude ###
|
||||
|
||||
###> phpunit/phpunit ###
|
||||
/phpunit.xml
|
||||
/.phpunit.cache/
|
||||
###< phpunit/phpunit ###
|
||||
|
||||
@@ -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
|
||||
- **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`
|
||||
- **Docker** : PHP-FPM + Node 24, Nginx (port 8083), PostgreSQL (port 5436)
|
||||
- **Docker** : PHP-FPM + Node 24, Nginx (port 8084), PostgreSQL (port 5436)
|
||||
|
||||
## Structure
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ make fixtures
|
||||
|
||||
## Accès
|
||||
|
||||
- Frontend : http://localhost:8083
|
||||
- API : http://localhost:8083/api
|
||||
- Frontend : http://localhost:8084
|
||||
- API : http://localhost:8084/api
|
||||
- Dev Nuxt (hot reload) : http://localhost:3003
|
||||
- Login : `admin` / `admin`
|
||||
|
||||
4
bin/phpunit
Executable file
4
bin/phpunit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
|
||||
@@ -28,6 +28,7 @@
|
||||
"symfony/monolog-bundle": "^4.0",
|
||||
"symfony/property-access": "8.0.*",
|
||||
"symfony/property-info": "8.0.*",
|
||||
"symfony/rate-limiter": "8.0.*",
|
||||
"symfony/runtime": "8.0.*",
|
||||
"symfony/security-bundle": "8.0.*",
|
||||
"symfony/serializer": "8.0.*",
|
||||
|
||||
11149
composer.lock
generated
Normal file
11149
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
config/applications.yaml
Normal file
6
config/applications.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
parameters:
|
||||
app.managed_applications:
|
||||
- { name: 'SIRH', slug: 'sirh', maintenance_path: '%env(SIRH_MAINTENANCE_PATH)%' }
|
||||
- { name: 'Lesstime', slug: 'lesstime', maintenance_path: '%env(LESSTIME_MAINTENANCE_PATH)%' }
|
||||
- { name: 'Inventory', slug: 'inventory', maintenance_path: '%env(INVENTORY_MAINTENANCE_PATH)%' }
|
||||
- { name: 'Ferme', slug: 'ferme', maintenance_path: '%env(FERME_MAINTENANCE_PATH)%' }
|
||||
@@ -1,25 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use ApiPlatform\Symfony\Bundle\ApiPlatformBundle;
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle;
|
||||
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
||||
use Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle;
|
||||
use Nelmio\CorsBundle\NelmioCorsBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use Symfony\Bundle\MonologBundle\MonologBundle;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
|
||||
return [
|
||||
FrameworkBundle::class => ['all' => true],
|
||||
SecurityBundle::class => ['all' => true],
|
||||
DoctrineBundle::class => ['all' => true],
|
||||
DoctrineMigrationsBundle::class => ['all' => true],
|
||||
NelmioCorsBundle::class => ['all' => true],
|
||||
ApiPlatformBundle::class => ['all' => true],
|
||||
DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
LexikJWTAuthenticationBundle::class => ['all' => true],
|
||||
MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
19
config/packages/cache.yaml
Normal file
19
config/packages/cache.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
framework:
|
||||
cache:
|
||||
# Unique name of your app: used to compute stable namespaces for cache keys.
|
||||
#prefix_seed: your_vendor_name/app_name
|
||||
|
||||
# The "app" cache stores to the filesystem by default.
|
||||
# The data in this cache should persist between deploys.
|
||||
# Other options include:
|
||||
|
||||
# Redis
|
||||
#app: cache.adapter.redis
|
||||
#default_redis_provider: redis://localhost
|
||||
|
||||
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
|
||||
#app: cache.adapter.apcu
|
||||
|
||||
# Namespaced pools use the above "app" backend by default
|
||||
#pools:
|
||||
#my.dedicated.cache: null
|
||||
6
config/packages/doctrine_migrations.yaml
Normal file
6
config/packages/doctrine_migrations.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
doctrine_migrations:
|
||||
migrations_paths:
|
||||
# namespace is arbitrary but should be different from App\Migrations
|
||||
# as migrations classes should NOT be autoloaded
|
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations'
|
||||
enable_profiler: false
|
||||
55
config/packages/monolog.yaml
Normal file
55
config/packages/monolog.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
monolog:
|
||||
channels:
|
||||
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||
|
||||
when@dev:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
channels: ["!event"]
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine", "!console"]
|
||||
|
||||
when@test:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
channels: ["!event"]
|
||||
nested:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
|
||||
when@prod:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
channels: ["!deprecation"]
|
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||
nested:
|
||||
type: stream
|
||||
path: php://stderr
|
||||
level: debug
|
||||
formatter: monolog.formatter.json
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine"]
|
||||
deprecation:
|
||||
type: stream
|
||||
channels: [deprecation]
|
||||
path: php://stderr
|
||||
formatter: monolog.formatter.json
|
||||
11
config/packages/nyholm_psr7.yaml
Normal file
11
config/packages/nyholm_psr7.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
# Register nyholm/psr7 services for autowiring with PSR-17 (HTTP factories)
|
||||
Psr\Http\Message\RequestFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
Psr\Http\Message\ResponseFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
Psr\Http\Message\ServerRequestFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
Psr\Http\Message\StreamFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
Psr\Http\Message\UploadedFileFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
Psr\Http\Message\UriFactoryInterface: '@nyholm.psr7.psr17_factory'
|
||||
|
||||
nyholm.psr7.psr17_factory:
|
||||
class: Nyholm\Psr7\Factory\Psr17Factory
|
||||
3
config/packages/property_info.yaml
Normal file
3
config/packages/property_info.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
framework:
|
||||
property_info:
|
||||
with_constructor_extractor: true
|
||||
10
config/packages/routing.yaml
Normal file
10
config/packages/routing.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
framework:
|
||||
router:
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
default_uri: '%env(DEFAULT_URI)%'
|
||||
|
||||
when@prod:
|
||||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
||||
11
config/packages/validator.yaml
Normal file
11
config/packages/validator.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
framework:
|
||||
validation:
|
||||
# Enables validator auto-mapping support.
|
||||
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||
#auto_mapping:
|
||||
# App\Entity\: []
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
||||
1911
config/reference.php
Normal file
1911
config/reference.php
Normal file
File diff suppressed because it is too large
Load Diff
4
config/routes/framework.yaml
Normal file
4
config/routes/framework.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
when@dev:
|
||||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.php'
|
||||
prefix: /_error
|
||||
@@ -4,6 +4,7 @@ parameters:
|
||||
|
||||
imports:
|
||||
- { resource: version.yaml }
|
||||
- { resource: applications.yaml }
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '0.1.0'
|
||||
app.version: '0.1.2'
|
||||
|
||||
265
doc/deployment-docker.md
Normal file
265
doc/deployment-docker.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Deploiement Docker — Central
|
||||
|
||||
## Pre-requis
|
||||
|
||||
### Docker
|
||||
|
||||
```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.
|
||||
|
||||
### Nginx
|
||||
|
||||
```bash
|
||||
sudo apt install -y nginx
|
||||
sudo systemctl enable nginx
|
||||
sudo systemctl start nginx
|
||||
```
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
PostgreSQL tourne sur l'hote ou dans un conteneur separe, accessible depuis Central via `host.docker.internal`.
|
||||
|
||||
Creer la base de donnees de production :
|
||||
|
||||
```bash
|
||||
cd /var/www/postgres
|
||||
docker compose exec postgres psql -U admin
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Si le user n'existe pas encore
|
||||
CREATE USER malio WITH PASSWORD 'motdepasse';
|
||||
|
||||
-- Creer la base
|
||||
CREATE DATABASE central_prod OWNER malio;
|
||||
\q
|
||||
```
|
||||
|
||||
## Premiere installation
|
||||
|
||||
### 1. Creer le dossier de deploiement
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/central
|
||||
sudo chown -R $(whoami):$(whoami) /var/www/central
|
||||
cd /var/www/central
|
||||
```
|
||||
|
||||
### 2. Se connecter au registry Docker Gitea
|
||||
|
||||
```bash
|
||||
docker login gitea.malio.fr
|
||||
```
|
||||
|
||||
- Username : le compte Gitea autorise au registry
|
||||
- Password : le token registry
|
||||
|
||||
### 3. Creer les fichiers de deploiement
|
||||
|
||||
Copier les fichiers du repo :
|
||||
|
||||
- `infra/prod/docker-compose.yml`
|
||||
- `infra/prod/deploy.sh`
|
||||
|
||||
Option equivalent ecrite en clair pour `docker-compose.yml` :
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: gitea.malio.fr/malio-dev/central:${CENTRAL_IMAGE_TAG:-latest}
|
||||
container_name: central-app
|
||||
env_file: .env
|
||||
ports:
|
||||
- "8084:80"
|
||||
volumes:
|
||||
- ./config/jwt:/var/www/html/config/jwt:ro
|
||||
- ./uploads:/var/www/html/var/uploads
|
||||
- /var/www/sirh:/var/www/maintenance/sirh
|
||||
- /var/www/lesstime:/var/www/maintenance/lesstime
|
||||
- /var/www/inventory:/var/www/maintenance/inventory
|
||||
- /var/www/ferme:/var/www/maintenance/ferme
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Rendre le script executable :
|
||||
|
||||
```bash
|
||||
cp /chemin/vers/le/repo/Central/infra/prod/deploy.sh ./deploy.sh
|
||||
chmod +x deploy.sh
|
||||
```
|
||||
|
||||
### 4. Creer le fichier `.env`
|
||||
|
||||
Exemple minimal de production :
|
||||
|
||||
```env
|
||||
APP_ENV=prod
|
||||
APP_DEBUG=0
|
||||
APP_SECRET=<generer avec: openssl rand -hex 32>
|
||||
|
||||
DATABASE_URL="postgresql://malio:motdepasse@host.docker.internal:5432/central_prod?serverVersion=16&charset=utf8"
|
||||
|
||||
DEFAULT_URI=http://central.malio-dev.fr
|
||||
APP_SHARE_DIR=var/share
|
||||
|
||||
CORS_ALLOW_ORIGIN='^http://central\.malio-dev\.fr$'
|
||||
|
||||
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
||||
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
||||
JWT_PASSPHRASE=<generer avec: openssl rand -hex 32>
|
||||
JWT_COOKIE_SECURE=0
|
||||
JWT_TOKEN_TTL=86400
|
||||
JWT_COOKIE_TTL=86400
|
||||
|
||||
ENCRYPTION_KEY=<generer avec: openssl rand -hex 32>
|
||||
|
||||
SIRH_MAINTENANCE_PATH=/var/www/maintenance/sirh/maintenance.on
|
||||
LESSTIME_MAINTENANCE_PATH=/var/www/maintenance/lesstime/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
|
||||
|
||||
```bash
|
||||
mkdir -p config/jwt
|
||||
openssl genpkey -algorithm RSA -out config/jwt/private.pem -pkeyopt rsa_keygen_bits:4096
|
||||
openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem
|
||||
sudo chown 33:33 config/jwt/private.pem config/jwt/public.pem
|
||||
sudo chmod 644 config/jwt/private.pem config/jwt/public.pem
|
||||
```
|
||||
|
||||
### 6. Creer les dossiers persistants
|
||||
|
||||
```bash
|
||||
mkdir -p uploads
|
||||
```
|
||||
|
||||
### 7. Verifier l'acces aux apps managées
|
||||
|
||||
Central pilote les fichiers `maintenance.on` des autres projets via des volumes montes en lecture/ecriture.
|
||||
|
||||
Verifier que les dossiers existent :
|
||||
|
||||
```bash
|
||||
ls -ld /var/www/sirh /var/www/lesstime /var/www/inventory /var/www/ferme
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### 8. Configurer Nginx systeme
|
||||
|
||||
Creer `/etc/nginx/sites-available/central.conf` :
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name central.malio-dev.fr;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8084;
|
||||
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;
|
||||
client_max_body_size 55m;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Activer le site :
|
||||
|
||||
```bash
|
||||
sudo ln -sf /etc/nginx/sites-available/central.conf /etc/nginx/sites-enabled/central.conf
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 9. Deployer
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Le script :
|
||||
|
||||
- pull l'image Docker
|
||||
- redemarre le conteneur
|
||||
- lance les migrations Doctrine
|
||||
- vide et rechauffe le cache Symfony
|
||||
|
||||
## Deployer une nouvelle version
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
./deploy.sh # latest
|
||||
./deploy.sh v0.1.0 # version specifique
|
||||
```
|
||||
|
||||
## Verification apres deploiement
|
||||
|
||||
1. Ouvrir `http://central.malio-dev.fr`
|
||||
2. Se connecter avec un compte admin
|
||||
3. Verifier que la page Applications charge
|
||||
4. Activer la maintenance sur SIRH
|
||||
5. Verifier que `https://sirh.malio-dev.fr` renvoie la page de maintenance
|
||||
6. Desactiver la maintenance depuis Central
|
||||
|
||||
## Rollback
|
||||
|
||||
### Image seule
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
./deploy.sh v0.1.0
|
||||
```
|
||||
|
||||
### Avec rollback de migration
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
sudo docker compose exec -T -u www-data app php bin/console doctrine:migrations:migrate prev --no-interaction
|
||||
./deploy.sh v0.1.0
|
||||
```
|
||||
|
||||
## Voir les logs
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
sudo docker compose logs -f
|
||||
sudo docker compose logs -f --tail=100
|
||||
```
|
||||
|
||||
Logs Symfony :
|
||||
|
||||
```bash
|
||||
cd /var/www/central
|
||||
sudo docker compose exec -T app cat var/log/prod.log
|
||||
```
|
||||
|
||||
## Structure finale du dossier
|
||||
|
||||
```text
|
||||
/var/www/central/
|
||||
├── docker-compose.yml
|
||||
├── deploy.sh
|
||||
├── .env
|
||||
├── config/jwt/
|
||||
│ ├── private.pem
|
||||
│ └── public.pem
|
||||
└── uploads/
|
||||
```
|
||||
@@ -38,7 +38,7 @@ services:
|
||||
depends_on:
|
||||
- php
|
||||
ports:
|
||||
- "8083:80"
|
||||
- "8084:80"
|
||||
volumes:
|
||||
- ./:/var/www/html:ro
|
||||
- ./infra/dev/nginx.conf:/etc/nginx/conf.d/central.conf:ro
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
"patch": "Impossible de mettre à jour la ressource.",
|
||||
"delete": "Impossible de supprimer la ressource."
|
||||
},
|
||||
"applications": {
|
||||
"activateMaintenance": "Impossible d'activer le mode maintenance.",
|
||||
"deactivateMaintenance": "Impossible de désactiver le mode maintenance."
|
||||
},
|
||||
"auth": {
|
||||
"login": "Identifiants invalides.",
|
||||
"logout": "Impossible de se déconnecter.",
|
||||
@@ -14,6 +18,10 @@
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"applications": {
|
||||
"activateMaintenance": "Le mode maintenance a été activé.",
|
||||
"deactivateMaintenance": "Le mode maintenance a été désactivé."
|
||||
},
|
||||
"auth": {
|
||||
"login": "Connexion réussie.",
|
||||
"logout": "Déconnexion réussie."
|
||||
@@ -21,5 +29,29 @@
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Tableau de bord"
|
||||
},
|
||||
"applications": {
|
||||
"eyebrow": "Pilotage centralise",
|
||||
"title": "Supervision des applications",
|
||||
"description": "Active ou desactive le mode maintenance sans te connecter a chaque projet. Chaque action pilote le fichier de maintenance de l'application cible.",
|
||||
"listTitle": "Applications managees",
|
||||
"listDescription": "L'etat affiche correspond au trigger de maintenance present sur le serveur de production.",
|
||||
"emptyTitle": "Aucune application disponible",
|
||||
"emptyDescription": "La configuration backend ne retourne encore aucune application geree.",
|
||||
"status": {
|
||||
"active": "Maintenance active",
|
||||
"inactive": "En ligne"
|
||||
},
|
||||
"card": {
|
||||
"activeDescription": "Les utilisateurs voient actuellement la page de maintenance de cette application.",
|
||||
"inactiveDescription": "L'application repond normalement et le trigger de maintenance est absent.",
|
||||
"triggerFile": "Fichier trigger maintenance.on"
|
||||
},
|
||||
"actions": {
|
||||
"refresh": "Rafraichir",
|
||||
"activate": "Activer la maintenance",
|
||||
"deactivate": "Desactiver la maintenance",
|
||||
"pending": "Mise a jour en cours"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14853
frontend/package-lock.json
generated
Normal file
14853
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,176 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-neutral-900">Tableau de bord</h1>
|
||||
<p class="mt-4 text-neutral-600">Bienvenue sur Central.</p>
|
||||
<div class="flex h-full flex-col gap-6 pb-10">
|
||||
<section class="flex flex-col gap-2">
|
||||
<h1 class="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||
{{ t('applications.title') }}
|
||||
</h1>
|
||||
<p class="max-w-3xl text-sm leading-6 text-neutral-500 sm:text-base">
|
||||
{{ t('applications.description') }}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="min-h-0 rounded-3xl border border-primary-500/15 bg-white shadow-sm">
|
||||
<header class="flex flex-col gap-4 border-b border-primary-500/10 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-primary-500">
|
||||
{{ t('applications.listTitle') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-neutral-500">
|
||||
{{ t('applications.listDescription') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="inline-flex items-center justify-center rounded-xl border border-primary-500/20 px-4 py-2 text-sm font-semibold text-primary-500 transition-colors hover:bg-tertiary-500 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
:disabled="loading"
|
||||
@click="loadApplications()"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:refresh"
|
||||
size="18"
|
||||
class="mr-2"
|
||||
:class="loading ? 'animate-spin' : ''"
|
||||
/>
|
||||
{{ t('applications.actions.refresh') }}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div v-if="loading" class="grid gap-4 p-6 xl:grid-cols-2">
|
||||
<div
|
||||
v-for="index in 4"
|
||||
:key="index"
|
||||
class="animate-pulse rounded-2xl border border-primary-500/10 p-5"
|
||||
>
|
||||
<div class="h-4 w-24 rounded bg-neutral-200" />
|
||||
<div class="mt-4 h-7 w-40 rounded bg-neutral-200" />
|
||||
<div class="mt-3 h-4 w-full rounded bg-neutral-100" />
|
||||
<div class="mt-6 h-11 w-44 rounded-xl bg-neutral-200" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="applications.length === 0" class="px-6 py-12 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-tertiary-500 text-primary-500">
|
||||
<Icon name="mdi:server-off" size="28" />
|
||||
</div>
|
||||
<h3 class="mt-4 text-lg font-bold text-primary-500">
|
||||
{{ t('applications.emptyTitle') }}
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-neutral-500">
|
||||
{{ t('applications.emptyDescription') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid gap-4 p-6 xl:grid-cols-2">
|
||||
<article
|
||||
v-for="application in applications"
|
||||
:key="application.slug"
|
||||
class="flex h-full min-w-0 flex-col rounded-2xl border p-5 transition-colors"
|
||||
:class="application.maintenance ? 'border-red-200 bg-red-50/60' : 'border-emerald-200 bg-emerald-50/60'"
|
||||
>
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div class="min-w-0">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-neutral-500">
|
||||
{{ application.slug }}
|
||||
</p>
|
||||
<h3 class="mt-2 break-words text-xl font-bold text-primary-500">
|
||||
{{ application.name }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="inline-flex w-fit items-center rounded-full px-3 py-1 text-xs font-bold"
|
||||
:class="application.maintenance ? 'bg-red-100 text-red-700' : 'bg-emerald-100 text-emerald-700'"
|
||||
>
|
||||
<span
|
||||
class="mr-2 h-2.5 w-2.5 rounded-full"
|
||||
:class="application.maintenance ? 'bg-red-500' : 'bg-emerald-500'"
|
||||
/>
|
||||
{{ application.maintenance ? t('applications.status.active') : t('applications.status.inactive') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 flex-1 text-sm leading-6 text-neutral-600">
|
||||
{{
|
||||
application.maintenance
|
||||
? t('applications.card.activeDescription')
|
||||
: t('applications.card.inactiveDescription')
|
||||
}}
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex flex-col gap-3 border-t border-black/5 pt-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p class="text-xs font-medium uppercase tracking-[0.14em] text-neutral-400">
|
||||
{{ t('applications.card.triggerFile') }}
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="inline-flex w-full items-center justify-center rounded-xl px-4 py-3 text-sm font-semibold text-white transition-opacity disabled:cursor-not-allowed disabled:opacity-60 sm:w-auto sm:min-w-[220px]"
|
||||
:class="application.maintenance ? 'bg-red-600 hover:bg-red-700' : 'bg-primary-500 hover:bg-primary-600'"
|
||||
:disabled="Boolean(pendingBySlug[application.slug])"
|
||||
@click="toggleMaintenance(application)"
|
||||
>
|
||||
<Icon
|
||||
:name="pendingBySlug[application.slug] ? 'mdi:loading' : (application.maintenance ? 'mdi:shield-check-outline' : 'mdi:alert-outline')"
|
||||
size="18"
|
||||
class="mr-2"
|
||||
:class="pendingBySlug[application.slug] ? 'animate-spin' : ''"
|
||||
/>
|
||||
{{
|
||||
pendingBySlug[application.slug]
|
||||
? t('applications.actions.pending')
|
||||
: application.maintenance
|
||||
? t('applications.actions.deactivate')
|
||||
: t('applications.actions.activate')
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ManagedApplication } from '~/services/dto/managed-application'
|
||||
import { getManagedApplications, setApplicationMaintenance } from '~/services/managed-applications'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const applications = ref<ManagedApplication[]>([])
|
||||
const loading = ref(true)
|
||||
const pendingBySlug = ref<Record<string, boolean>>({})
|
||||
|
||||
async function loadApplications() {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
applications.value = await getManagedApplications()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleMaintenance(application: ManagedApplication) {
|
||||
pendingBySlug.value = {
|
||||
...pendingBySlug.value,
|
||||
[application.slug]: true
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedApplication = await setApplicationMaintenance(application.slug, !application.maintenance)
|
||||
applications.value = applications.value.map((item) => item.slug === updatedApplication.slug ? updatedApplication : item)
|
||||
} finally {
|
||||
pendingBySlug.value = {
|
||||
...pendingBySlug.value,
|
||||
[application.slug]: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadApplications()
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Tableau de bord'
|
||||
title: 'Applications'
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<div class="mx-auto w-full max-w-lg">
|
||||
<span
|
||||
class="flex items-center justify-center bg-white text-xl font-bold uppercase text-primary-500 p-4"
|
||||
>
|
||||
<img src="/malio.png" alt="Logo" class="w-[150px]"/>
|
||||
</span>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<span
|
||||
class="flex items-center justify-center bg-white text-xl font-bold uppercase text-primary-500 p-4"
|
||||
>
|
||||
<img src="/malio.png" alt="Logo" class="w-[150px]"/>
|
||||
</span>
|
||||
<h1 class="mt-2 text-3xl font-bold tracking-wide text-neutral-800">Central</h1>
|
||||
</div>
|
||||
<form
|
||||
class="mt-8 space-y-6 rounded-lg border border-neutral-200 bg-white p-6 shadow-sm"
|
||||
@submit.prevent="handleSubmit"
|
||||
|
||||
10
frontend/services/dto/managed-application.ts
Normal file
10
frontend/services/dto/managed-application.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export type ManagedApplication = {
|
||||
slug: string
|
||||
name: string
|
||||
maintenance: boolean
|
||||
}
|
||||
|
||||
export type ManagedApplicationCollection = {
|
||||
'hydra:member'?: ManagedApplication[]
|
||||
member?: ManagedApplication[]
|
||||
}
|
||||
33
frontend/services/managed-applications.ts
Normal file
33
frontend/services/managed-applications.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { ManagedApplication, ManagedApplicationCollection } from './dto/managed-application'
|
||||
|
||||
function normalizeManagedApplications(response: ManagedApplication[] | ManagedApplicationCollection): ManagedApplication[] {
|
||||
if (Array.isArray(response)) {
|
||||
return response
|
||||
}
|
||||
|
||||
return response['hydra:member'] ?? response.member ?? []
|
||||
}
|
||||
|
||||
export async function getManagedApplications(): Promise<ManagedApplication[]> {
|
||||
const api = useApi()
|
||||
const response = await api.get<ManagedApplication[] | ManagedApplicationCollection>('/applications')
|
||||
|
||||
return normalizeManagedApplications(response)
|
||||
}
|
||||
|
||||
export function setApplicationMaintenance(slug: string, maintenance: boolean) {
|
||||
const api = useApi()
|
||||
|
||||
return api.post<ManagedApplication>(
|
||||
`/applications/${slug}/maintenance`,
|
||||
{ maintenance },
|
||||
{
|
||||
toastSuccessKey: maintenance
|
||||
? 'success.applications.activateMaintenance'
|
||||
: 'success.applications.deactivateMaintenance',
|
||||
toastErrorKey: maintenance
|
||||
? 'errors.applications.activateMaintenance'
|
||||
: 'errors.applications.deactivateMaintenance'
|
||||
}
|
||||
)
|
||||
}
|
||||
82
infra/prod/Dockerfile
Normal file
82
infra/prod/Dockerfile
Normal file
@@ -0,0 +1,82 @@
|
||||
# --- 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_IPRESOLVE=4 composer install --no-dev --no-scripts --no-interaction
|
||||
|
||||
COPY bin bin/
|
||||
COPY config config/
|
||||
COPY migrations migrations/
|
||||
COPY public public/
|
||||
COPY src src/
|
||||
|
||||
RUN composer dump-autoload --optimize --no-dev
|
||||
|
||||
# --- Stage 2: Build frontend ---
|
||||
FROM node:lts-alpine AS frontend-build
|
||||
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
ENV NODE_OPTIONS=--dns-result-order=ipv4first
|
||||
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 infra/prod/supervisord.conf /etc/supervisor/conf.d/app.conf
|
||||
COPY infra/prod/nginx.conf /etc/nginx/sites-enabled/central.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
|
||||
|
||||
# Symfony needs a .env file to boot (variables are overridden by env_file in docker-compose)
|
||||
RUN echo "APP_ENV=prod" > /var/www/html/.env
|
||||
|
||||
# Permissions
|
||||
RUN mkdir -p /var/www/html/var /var/www/html/var/uploads \
|
||||
&& 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
infra/prod/deploy.sh
Executable file
28
infra/prod/deploy.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
TAG="${1:-latest}"
|
||||
export CENTRAL_IMAGE_TAG="$TAG"
|
||||
|
||||
echo "==> Deploying central:${TAG}..."
|
||||
|
||||
echo "==> Pulling image..."
|
||||
sudo docker compose pull
|
||||
|
||||
echo "==> Starting container..."
|
||||
sudo docker compose up -d
|
||||
|
||||
echo "==> Waiting for container to be ready..."
|
||||
sleep 3
|
||||
|
||||
echo "==> Running migrations..."
|
||||
sudo docker compose exec -T -u www-data app php bin/console doctrine:migrations:migrate --no-interaction
|
||||
|
||||
echo "==> Clearing cache..."
|
||||
sudo docker compose exec -T -u www-data app php bin/console cache:clear --env=prod
|
||||
sudo docker compose exec -T -u www-data app php bin/console cache:warmup --env=prod
|
||||
|
||||
VERSION=$(sudo docker compose exec -T app cat config/version.yaml | grep 'app.version' | awk -F"'" '{print $2}')
|
||||
echo "==> Deployed v${VERSION}"
|
||||
17
infra/prod/docker-compose.yml
Normal file
17
infra/prod/docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
app:
|
||||
image: gitea.malio.fr/malio-dev/central:${CENTRAL_IMAGE_TAG:-latest}
|
||||
container_name: central-app
|
||||
env_file: .env
|
||||
ports:
|
||||
- "8084:80"
|
||||
volumes:
|
||||
- ./config/jwt:/var/www/html/config/jwt:ro
|
||||
- ./uploads:/var/www/html/var/uploads
|
||||
- /var/www/sirh:/var/www/maintenance/sirh
|
||||
- /var/www/lesstime:/var/www/maintenance/lesstime
|
||||
- /var/www/inventory:/var/www/maintenance/inventory
|
||||
- /var/www/ferme:/var/www/maintenance/ferme
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
14
infra/prod/nginx-proxy.conf
Normal file
14
infra/prod/nginx-proxy.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name central.malio-dev.fr;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8084;
|
||||
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;
|
||||
client_max_body_size 55m;
|
||||
}
|
||||
}
|
||||
48
infra/prod/nginx.conf
Normal file
48
infra/prod/nginx.conf
Normal file
@@ -0,0 +1,48 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name central.malio-dev.fr;
|
||||
|
||||
root /var/www/html/frontend/.output/public;
|
||||
index index.html;
|
||||
|
||||
client_max_body_size 55m;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
28
infra/prod/supervisord.conf
Normal file
28
infra/prod/supervisord.conf
Normal 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
|
||||
0
migrations/.gitignore
vendored
Normal file
0
migrations/.gitignore
vendored
Normal file
32
migrations/Version20260403083313.php
Normal file
32
migrations/Version20260403083313.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20260403083313 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE "user" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, username VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY (id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649F85E0677 ON "user" (username)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE "user"');
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,38 @@
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<server name="APP_ENV" value="test" force="true" />
|
||||
<server name="SHELL_VERBOSITY" value="-1" />
|
||||
|
||||
<!-- ###+ symfony/framework-bundle ### -->
|
||||
<env name="APP_ENV" value="dev"/>
|
||||
<env name="APP_SECRET" value=""/>
|
||||
<env name="APP_SHARE_DIR" value="var/share"/>
|
||||
<!-- ###- symfony/framework-bundle ### -->
|
||||
|
||||
<!-- ###+ symfony/routing ### -->
|
||||
<!-- Configure how to generate URLs in non-HTTP contexts, such as CLI commands. -->
|
||||
<!-- See https://symfony.com/doc/current/routing.html#generating-urls-in-commands -->
|
||||
<env name="DEFAULT_URI" value="http://localhost"/>
|
||||
<!-- ###- symfony/routing ### -->
|
||||
|
||||
<!-- ###+ doctrine/doctrine-bundle ### -->
|
||||
<!-- Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url -->
|
||||
<!-- IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml -->
|
||||
<!-- -->
|
||||
<!-- DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db" -->
|
||||
<!-- DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" -->
|
||||
<!-- DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" -->
|
||||
<env name="DATABASE_URL" value="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"/>
|
||||
<!-- ###- doctrine/doctrine-bundle ### -->
|
||||
|
||||
<!-- ###+ lexik/jwt-authentication-bundle ### -->
|
||||
<env name="JWT_SECRET_KEY" value="%kernel.project_dir%/config/jwt/private.pem"/>
|
||||
<env name="JWT_PUBLIC_KEY" value="%kernel.project_dir%/config/jwt/public.pem"/>
|
||||
<env name="JWT_PASSPHRASE" value="43d0b1e183b53eb04d458f29a1fa8f19badf1e6f0fd0df587402348a7e7e1e37"/>
|
||||
<!-- ###- lexik/jwt-authentication-bundle ### -->
|
||||
|
||||
<!-- ###+ nelmio/cors-bundle ### -->
|
||||
<env name="CORS_ALLOW_ORIGIN" value="'^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'"/>
|
||||
<!-- ###- nelmio/cors-bundle ### -->
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
|
||||
0
src/ApiResource/.gitignore
vendored
Normal file
0
src/ApiResource/.gitignore
vendored
Normal file
51
src/ApiResource/ManagedApplication.php
Normal file
51
src/ApiResource/ManagedApplication.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\State\ManagedApplicationProvider;
|
||||
use App\State\MaintenanceToggleProcessor;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
uriTemplate: '/applications',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
),
|
||||
new Get(
|
||||
uriTemplate: '/applications/{slug}',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
),
|
||||
new Post(
|
||||
uriTemplate: '/applications/{slug}/maintenance',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
denormalizationContext: ['groups' => ['app:write']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
processor: MaintenanceToggleProcessor::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class ManagedApplication
|
||||
{
|
||||
#[ApiProperty(identifier: true)]
|
||||
#[Groups(['app:read'])]
|
||||
public string $slug = '';
|
||||
|
||||
#[Groups(['app:read'])]
|
||||
public string $name = '';
|
||||
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
public bool $maintenance = false;
|
||||
}
|
||||
0
src/Controller/.gitignore
vendored
Normal file
0
src/Controller/.gitignore
vendored
Normal file
0
src/Entity/.gitignore
vendored
Normal file
0
src/Entity/.gitignore
vendored
Normal file
0
src/Repository/.gitignore
vendored
Normal file
0
src/Repository/.gitignore
vendored
Normal file
63
src/State/MaintenanceToggleProcessor.php
Normal file
63
src/State/MaintenanceToggleProcessor.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\ManagedApplication;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class MaintenanceToggleProcessor implements ProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @param list<array{name: string, slug: string, maintenance_path: string}> $managedApplications
|
||||
*/
|
||||
public function __construct(
|
||||
#[Autowire('%app.managed_applications%')]
|
||||
private array $managedApplications,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param ManagedApplication $data
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): ManagedApplication
|
||||
{
|
||||
$slug = $uriVariables['slug'] ?? '';
|
||||
$appConfig = null;
|
||||
|
||||
foreach ($this->managedApplications as $app) {
|
||||
if ($app['slug'] === $slug) {
|
||||
$appConfig = $app;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $appConfig) {
|
||||
throw new NotFoundHttpException(sprintf('Application "%s" not found.', $slug));
|
||||
}
|
||||
|
||||
$maintenancePath = $appConfig['maintenance_path'];
|
||||
|
||||
if ($data->maintenance) {
|
||||
$directory = dirname($maintenancePath);
|
||||
|
||||
if (!is_dir($directory)) {
|
||||
mkdir($directory, 0755, true);
|
||||
}
|
||||
|
||||
touch($maintenancePath);
|
||||
} elseif (file_exists($maintenancePath)) {
|
||||
unlink($maintenancePath);
|
||||
}
|
||||
|
||||
$dto = new ManagedApplication();
|
||||
$dto->slug = $appConfig['slug'];
|
||||
$dto->name = $appConfig['name'];
|
||||
$dto->maintenance = file_exists($maintenancePath);
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
56
src/State/ManagedApplicationProvider.php
Normal file
56
src/State/ManagedApplicationProvider.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\ManagedApplication;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class ManagedApplicationProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param list<array{name: string, slug: string, maintenance_path: string}> $managedApplications
|
||||
*/
|
||||
public function __construct(
|
||||
#[Autowire('%app.managed_applications%')]
|
||||
private array $managedApplications,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ManagedApplication|array
|
||||
{
|
||||
if ($operation instanceof GetCollection) {
|
||||
return array_map(
|
||||
fn (array $app) => $this->buildDto($app),
|
||||
$this->managedApplications,
|
||||
);
|
||||
}
|
||||
|
||||
$slug = $uriVariables['slug'] ?? '';
|
||||
|
||||
foreach ($this->managedApplications as $app) {
|
||||
if ($app['slug'] === $slug) {
|
||||
return $this->buildDto($app);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException(sprintf('Application "%s" not found.', $slug));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name: string, slug: string, maintenance_path: string} $app
|
||||
*/
|
||||
private function buildDto(array $app): ManagedApplication
|
||||
{
|
||||
$dto = new ManagedApplication();
|
||||
$dto->slug = $app['slug'];
|
||||
$dto->name = $app['name'];
|
||||
$dto->maintenance = file_exists($app['maintenance_path']);
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
243
symfony.lock
Normal file
243
symfony.lock
Normal file
@@ -0,0 +1,243 @@
|
||||
{
|
||||
"api-platform/symfony": {
|
||||
"version": "4.3",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "4.0",
|
||||
"ref": "e9952e9f393c2d048f10a78f272cd35e807d972b"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/api_platform.yaml",
|
||||
"config/routes/api_platform.yaml",
|
||||
"src/ApiResource/.gitignore"
|
||||
]
|
||||
},
|
||||
"doctrine/deprecations": {
|
||||
"version": "1.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "fdd756167454623e21f1d769c5b814b243782a67"
|
||||
}
|
||||
},
|
||||
"doctrine/doctrine-bundle": {
|
||||
"version": "3.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.0",
|
||||
"ref": "d39a3bd844edfe90c20ae520b804a3bf4f82b4ad"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/doctrine.yaml",
|
||||
"src/Entity/.gitignore",
|
||||
"src/Repository/.gitignore"
|
||||
]
|
||||
},
|
||||
"doctrine/doctrine-fixtures-bundle": {
|
||||
"version": "4.3",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.0",
|
||||
"ref": "1f5514cfa15b947298df4d771e694e578d4c204d"
|
||||
},
|
||||
"files": [
|
||||
"src/DataFixtures/AppFixtures.php"
|
||||
]
|
||||
},
|
||||
"doctrine/doctrine-migrations-bundle": {
|
||||
"version": "4.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.1",
|
||||
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/doctrine_migrations.yaml",
|
||||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"friendsofphp/php-cs-fixer": {
|
||||
"version": "3.94",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.39",
|
||||
"ref": "97aaf9026490db73b86c23d49e5774bc89d2b232"
|
||||
},
|
||||
"files": [
|
||||
".php-cs-fixer.dist.php"
|
||||
]
|
||||
},
|
||||
"lexik/jwt-authentication-bundle": {
|
||||
"version": "3.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "2.5",
|
||||
"ref": "e9481b233a11ef7e15fe055a2b21fd3ac1aa2bb7"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/lexik_jwt_authentication.yaml"
|
||||
]
|
||||
},
|
||||
"nelmio/cors-bundle": {
|
||||
"version": "2.6",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.5",
|
||||
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/nelmio_cors.yaml"
|
||||
]
|
||||
},
|
||||
"nyholm/psr7": {
|
||||
"version": "1.8",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "4a8c0345442dcca1d8a2c65633dcf0285dd5a5a2"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/nyholm_psr7.yaml"
|
||||
]
|
||||
},
|
||||
"phpunit/phpunit": {
|
||||
"version": "13.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "11.1",
|
||||
"ref": "ca0bc067abfb40a8de1b2561b96cbfc2b833c314"
|
||||
},
|
||||
"files": [
|
||||
".env.test",
|
||||
"phpunit.dist.xml",
|
||||
"tests/bootstrap.php",
|
||||
"bin/phpunit"
|
||||
]
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.3",
|
||||
"ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461"
|
||||
},
|
||||
"files": [
|
||||
"bin/console"
|
||||
]
|
||||
},
|
||||
"symfony/flex": {
|
||||
"version": "2.10",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "2.4",
|
||||
"ref": "52e9754527a15e2b79d9a610f98185a1fe46622a"
|
||||
},
|
||||
"files": [
|
||||
".env",
|
||||
".env.dev"
|
||||
]
|
||||
},
|
||||
"symfony/framework-bundle": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.4",
|
||||
"ref": "09f6e081c763a206802674ce0cb34a022f0ffc6d"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/cache.yaml",
|
||||
"config/packages/framework.yaml",
|
||||
"config/preload.php",
|
||||
"config/routes/framework.yaml",
|
||||
"config/services.yaml",
|
||||
"public/index.php",
|
||||
"src/Controller/.gitignore",
|
||||
"src/Kernel.php",
|
||||
".editorconfig"
|
||||
]
|
||||
},
|
||||
"symfony/monolog-bundle": {
|
||||
"version": "4.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.7",
|
||||
"ref": "1b9efb10c54cb51c713a9391c9300ff8bceda459"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/monolog.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/property-info": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.3",
|
||||
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/property_info.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.4",
|
||||
"ref": "bc94c4fd86f393f3ab3947c18b830ea343e51ded"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/routing.yaml",
|
||||
"config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/security-bundle": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.4",
|
||||
"ref": "c42fee7802181cdd50f61b8622715829f5d2335c"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/security.yaml",
|
||||
"config/routes/security.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/uid": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.0",
|
||||
"ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
|
||||
}
|
||||
},
|
||||
"symfony/validator": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.0",
|
||||
"ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/validator.yaml"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user