Compare commits
252 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8565534e6 | |||
| 43fcbb5ef6 | |||
| fefc5d651b | |||
| 163d9b23e9 | |||
| 53509820c6 | |||
| a8ad3a7558 | |||
| f46e268d39 | |||
| abad3194a4 | |||
| 6e7eec0587 | |||
| 8c83668364 | |||
| 771c06ccd8 | |||
| b479b992cf | |||
| f018e52afa | |||
| be2f01c070 | |||
| 9ab50238b9 | |||
| cf5d93630f | |||
| e5e17d3862 | |||
| 7410569ced | |||
| b107f443aa | |||
| 8e2d68ebd5 | |||
| f050127fd7 | |||
| d02fd63834 | |||
| b143cc759f | |||
| cc5534ef00 | |||
| 26f56006f6 | |||
| f8dacee3e7 | |||
| ed1f30f2b6 | |||
| 8517e9824c | |||
| 6cec8ff31d | |||
| e7deba52e9 | |||
| d95c173fa8 | |||
| 8a0e4423f2 | |||
| b956236934 | |||
| 7767c98304 | |||
| 7e380ccb69 | |||
| 94d1148eb1 | |||
| 8a14f58ce5 | |||
| 79dbbdaaec | |||
| bf5704db54 | |||
| dec98f9508 | |||
| d3a62e980d | |||
| b89ed62795 | |||
| 5ffc9fb495 | |||
| 5e48bc4623 | |||
| 81b953fb05 | |||
| c6659f8d85 | |||
| e86fe42b3d | |||
| ef528aa524 | |||
| e6cc4cbc96 | |||
| 587173c79f | |||
| 0fc871bd46 | |||
| 2a1bdfbdcb | |||
| 11dca5630c | |||
| c58dedf80a | |||
| dedae02676 | |||
| 9e9391465d | |||
| 1908de681e | |||
| 18ea38f85a | |||
| d740ad255f | |||
| 76381c75bd | |||
| 49bb471b60 | |||
| 7d281196c1 | |||
| fee96e7900 | |||
| 1e37c43dcd | |||
| 15f65b1014 | |||
| 0a56f8c180 | |||
| 0563c7c8de | |||
| 09f6cd7463 | |||
| 7bd5e7baa5 | |||
| f75e805cc0 | |||
| 1cd4a1ff97 | |||
| 41a88ea914 | |||
| c85c28fee2 | |||
| 580104de00 | |||
| d145fc1b2e | |||
| 3a788ba9b3 | |||
| c032ae81cd | |||
| cd2cc5e345 | |||
| 169beb9637 | |||
| 337b1ab0fd | |||
| a138af013e | |||
| f1d7be2a4c | |||
| f51a00f62a | |||
| cda2c397c4 | |||
| 824bedb99f | |||
| c8511db01c | |||
| bd59a17212 | |||
| 34be19fbed | |||
| ee6d41c155 | |||
| 280a8fa3f3 | |||
| 9705182547 | |||
| 3fd5ae3741 | |||
| 1e260caddb | |||
| 520d00adc7 | |||
| dea91714fa | |||
| 97ca8766af | |||
| 54e72cc705 | |||
| ad58958f49 | |||
| 0c4b88b5cd | |||
| 5ce5df950f | |||
| 17fea2272f | |||
| de62510f45 | |||
| e18ed652f0 | |||
| 6f24d7d5bf | |||
| 7497cc399c | |||
| ee77975c9c | |||
| ae0c0041c9 | |||
| 2cdc7e4aac | |||
| 7a574f4531 | |||
| 2db82d19f3 | |||
| 678cf4f76a | |||
| fa17d0a037 | |||
| c7b5f3ef61 | |||
| bda1a6ef2a | |||
| fbbfef37ba | |||
| 15c88c4943 | |||
| df58e93886 | |||
| 42711cde5d | |||
| 5d80f20999 | |||
| 9e9e8525d6 | |||
| 9f4adbe4a4 | |||
| 26e79126f5 | |||
| 8504508396 | |||
| f3fe80fa46 | |||
| 743a6911f5 | |||
| 3f1fddccd1 | |||
| bf39a53aeb | |||
| df33d49cff | |||
| ccdb9e17fe | |||
| e2e50ae3db | |||
| 0769e5ae9d | |||
| 2cdb495402 | |||
| e3fd3878b2 | |||
| 0ff2c399b3 | |||
| f5db6b7178 | |||
| 2411ebced7 | |||
| 85fa81e3d9 | |||
| 4bcb72ebec | |||
| 7269a8f3ab | |||
| d5a0b8f554 | |||
| b8cf1c76c1 | |||
| dac1fd77c5 | |||
| 677cfb955d | |||
| d613439a82 | |||
| 1b39c438bf | |||
| 4e9686d6a7 | |||
| b983f4e3ec | |||
| 4b430e241f | |||
| a21356239f | |||
| 1585bee150 | |||
| 9ecd5c4939 | |||
| 4c83b80b6d | |||
| 53566db2a3 | |||
| 9f8d9dd0ca | |||
| d7263ae3f2 | |||
| ed23aca9c8 | |||
| 2be70722db | |||
| 840c005fcc | |||
| ac5b68dff1 | |||
| da80fc5efa | |||
| 5e67820f7c | |||
| f2cda5aa34 | |||
| b79455fb03 | |||
| 7085473c00 | |||
| 3869ad50e9 | |||
| 972d599642 | |||
| 7d35472ea5 | |||
| 87f7a717f3 | |||
| f5dbe0b6af | |||
| a8182d4cf9 | |||
| 1ccbd92c34 | |||
| c598f4fe21 | |||
| 34348293b4 | |||
| 6b86a4f30d | |||
| 071b35f2d4 | |||
| eb68f84f7c | |||
| 254fd0582f | |||
| c2d602b9bf | |||
| 098e98b719 | |||
| 893c5ef456 | |||
| 22d742ab63 | |||
| e291e5b050 | |||
| ec09fdc2cf | |||
| d59038fbc1 | |||
| 5cb36cb3c0 | |||
| da33a221e9 | |||
| fb8396b588 | |||
| 67d2fa633f | |||
| c344299cdb | |||
| 24effa1f78 | |||
| ccf287f482 | |||
| b3540e84db | |||
| c20ea7cedc | |||
| a489ddedc3 | |||
| 81f256109a | |||
| a0d31688ac | |||
| 6e7dd94d03 | |||
| 8ecb9d714f | |||
| 476f2b5233 | |||
| 52973d189f | |||
| 0f96554ce2 | |||
| b4cb29c258 | |||
| dd90a829a9 | |||
| 520c3ad89b | |||
| 8623275854 | |||
| f637300e87 | |||
| fefd5c5080 | |||
| 3258a8a12c | |||
| 173f7ececa | |||
| a73c5d0ecb | |||
| 58b84c2451 | |||
| 3c8721d706 | |||
| 4519f5437f | |||
| b17abd776e | |||
| aac126cb87 | |||
| 6b38405e94 | |||
| bd386d1b0a | |||
| 7f80c876d3 | |||
| 4be7b28efd | |||
| 27dd22080c | |||
| b766525000 | |||
| a57492ac19 | |||
| cc00da4e5e | |||
| b3bfb7e267 | |||
| fa54a58394 | |||
| 957fcfff8b | |||
| 3ccf880057 | |||
| bc73c40be1 | |||
| 47daa459e2 | |||
| a58dcdd08e | |||
| 8ba3f45215 | |||
| 53d59a5b56 | |||
| fe1af4d78f | |||
| b93a08da71 | |||
| c7a2133eed | |||
| 43f7a54b15 | |||
| 5ba4ee1f90 | |||
| f84ad91dc8 | |||
| d268461bfd | |||
| edcda185db | |||
| f297ae557b | |||
| 6407bf44bc | |||
| 2737e53de5 | |||
| dae32e3607 | |||
| d03ec054d2 | |||
| 682894f326 | |||
| b2438ec3d8 | |||
| 6a21bd4735 | |||
| e1a8c90f3e | |||
| 1b74c0a3bd | |||
| 6c4b01590d | |||
| 76d7a28678 |
@ -1,5 +1,5 @@
|
|||||||
*
|
postgres-data/
|
||||||
!target/*-runner
|
ffsaf-media/
|
||||||
!target/*-runner.jar
|
docker-compose.yml
|
||||||
!target/lib/*
|
ffsaf_cle_prive.jks
|
||||||
!target/quarkus-app/*
|
prod.env
|
||||||
97
.gitea/workflows/deploy_in_prod.yml
Normal file
97
.gitea/workflows/deploy_in_prod.yml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
name: Deploy Production Server
|
||||||
|
|
||||||
|
# Only run the workflow when a PR is merged on main and closed
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
|
||||||
|
# Here we check that the PR was correctly merged to main
|
||||||
|
jobs:
|
||||||
|
if_merged:
|
||||||
|
if: github.event.pull_request.merged == true
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '17.0.12'
|
||||||
|
distribution: 'graalvm'
|
||||||
|
cache: 'maven'
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: src/main/webapp/package-lock.json
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: |
|
||||||
|
echo "${{ vars.VITE_ENV }}" > src/main/webapp/.env
|
||||||
|
cd src/main/webapp
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
- name: Inject frontend in backend
|
||||||
|
run: |
|
||||||
|
rm -rf src/main/resources/META-INF/resources
|
||||||
|
mkdir -p src/main/resources/META-INF/
|
||||||
|
mv src/main/webapp/dist src/main/resources/META-INF/resources
|
||||||
|
|
||||||
|
- name: Build backend make_pdf tool
|
||||||
|
run: |
|
||||||
|
chmod 740 mvnw
|
||||||
|
cd src/main/pdf_gen
|
||||||
|
../../../mvnw clean compile assembly:single
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
- name: Build backend
|
||||||
|
run: |
|
||||||
|
chmod 740 mvnw
|
||||||
|
./mvnw package -Pnative -DskipTests
|
||||||
|
|
||||||
|
- name: Copy runner to vps via scp
|
||||||
|
uses: appleboy/scp-action@v0.1.7 # Latest in date when creating the workflow
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.SSH_USER }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
source: "target/*-runner,src/main/resources/cacerts,src/main/docker/Dockerfile.native,docker-compose.yml,.dockerignore,src/main/pdf_gen/target/pdf_gen-*.jar"
|
||||||
|
target: ${{ secrets.TARGET_DIR }} # Need to create it first on the VPS
|
||||||
|
|
||||||
|
- name: Re-start ffsaf container
|
||||||
|
uses: appleboy/ssh-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.SSH_USER }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
cd ${{ secrets.TARGET_DIR }}
|
||||||
|
docker logs ffsaf > "log/ffsaf_logs_$(date +"%Y-%m-%d_%H-%M-%S").log" 2>&1
|
||||||
|
docker stop ffsaf
|
||||||
|
docker rm ffsaf
|
||||||
|
docker compose up --build -d ffsaf
|
||||||
|
|
||||||
|
- name: Check ffsaf container
|
||||||
|
uses: appleboy/ssh-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.SSH_USER }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
if docker ps | grep ffsaf; then
|
||||||
|
echo 'Container is running'
|
||||||
|
else
|
||||||
|
echo 'Container is not running'
|
||||||
|
exit 1 # This mark the pipeline as failed
|
||||||
|
fi
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -45,6 +45,8 @@ nb-configuration.xml
|
|||||||
# Custom
|
# Custom
|
||||||
/config/application.properties
|
/config/application.properties
|
||||||
/cle_prive.jks
|
/cle_prive.jks
|
||||||
|
/mail-truststore.p12
|
||||||
/src/main/resources/META-INF/resources/
|
/src/main/resources/META-INF/resources/
|
||||||
/media/
|
/media/
|
||||||
/media-ext/
|
/media-ext/
|
||||||
|
/sign.jpg
|
||||||
|
|||||||
53
README.md
53
README.md
@ -1,8 +1,22 @@
|
|||||||
# ffsaf-site
|
# FFSAF - Intranet
|
||||||
|
|
||||||
This project uses Quarkus, the Supersonic Subatomic Java Framework.
|
## Introduction et context
|
||||||
|
|
||||||
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
|
L’intranet de la Fédération française de Soft Armored Fighting a pour but centralise la prise de licences des adhérents
|
||||||
|
et l’affiliation des clubs. Il permet aussi de recueillir les résultats des compétitions organisées par les clubs et,
|
||||||
|
plus récemment, d’en faciliter l’organisation grâce à un outil intégré.
|
||||||
|
|
||||||
|
Le système de prise de licence se divise en trois parties :
|
||||||
|
|
||||||
|
* Un formulaire public pour la première demande d’affiliation ;
|
||||||
|
* Un espace club, accessible après validation, pour la saisie des informations relatives aux demandes de licence des
|
||||||
|
adhérents ;
|
||||||
|
* Un espace fédération pour accepter les demandes de licences et d’affiliation.
|
||||||
|
Un espace membre permet enfin à chaque adhérent de télécharger son attestation de licence.
|
||||||
|
|
||||||
|
Pour les compétitions, le système permet la création de catégories, de poules et de matchs, avec génération automatique
|
||||||
|
des matchs au sein d’une poule. Il supporte plusieurs lices, assure la synchronisation en temps réel des modifications
|
||||||
|
entre toutes les instances de l’application web et publie automatiquement les résultats sur le site de l’organisateur.
|
||||||
|
|
||||||
## Running the application in dev mode
|
## Running the application in dev mode
|
||||||
|
|
||||||
@ -51,35 +65,4 @@ Or, if you don't have GraalVM installed, you can run the native executable build
|
|||||||
|
|
||||||
You can then execute your native executable with: `./target/ffsaf-site-1.0-SNAPSHOT-runner`
|
You can then execute your native executable with: `./target/ffsaf-site-1.0-SNAPSHOT-runner`
|
||||||
|
|
||||||
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
|
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
|
||||||
|
|
||||||
## Related Guides
|
|
||||||
|
|
||||||
- Reactive MySQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the MySQL database using the reactive pattern
|
|
||||||
- RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A Jakarta REST implementation utilizing build time processing and Vert.x.
|
|
||||||
This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it.
|
|
||||||
- Hibernate ORM with Panache ([guide](https://quarkus.io/guides/hibernate-orm-panache)): Simplify your persistence code for Hibernate ORM via the
|
|
||||||
active record or the repository pattern
|
|
||||||
- Reactive PostgreSQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the PostgreSQL database using the reactive pattern
|
|
||||||
|
|
||||||
## Provided Code
|
|
||||||
|
|
||||||
### Hibernate ORM
|
|
||||||
|
|
||||||
Create your first JPA entity
|
|
||||||
|
|
||||||
[Related guide section...](https://quarkus.io/guides/hibernate-orm)
|
|
||||||
|
|
||||||
[Related Hibernate with Panache section...](https://quarkus.io/guides/hibernate-orm-panache)
|
|
||||||
|
|
||||||
### RESTEasy Reactive
|
|
||||||
|
|
||||||
Easily start your Reactive RESTful Web Services
|
|
||||||
|
|
||||||
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
|
|
||||||
|
|
||||||
### RESTEasy Reactive Qute
|
|
||||||
|
|
||||||
Create your web page using Quarkus RESTEasy Reactive & Qute
|
|
||||||
|
|
||||||
[Related guide section...](https://quarkus.io/guides/qute#type-safe-templates)
|
|
||||||
51
docker-compose.yml
Normal file
51
docker-compose.yml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
services:
|
||||||
|
ffsaf:
|
||||||
|
container_name: ffsaf
|
||||||
|
hostname: ffsaf
|
||||||
|
restart: always
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: src/main/docker/Dockerfile.native
|
||||||
|
volumes:
|
||||||
|
- ${PWD}/ffsaf.properties:/work/config/application.properties
|
||||||
|
- ${PWD}/ffsaf_cle_prive.jks:/work/cle_prive.jks
|
||||||
|
- ${PWD}/mail-truststore.p12:/work/mail-truststore.p12
|
||||||
|
- ${PWD}/sign.jpg:/work/sign.jpg
|
||||||
|
- ${PWD}/ffsaf-media:/work/media
|
||||||
|
depends_on:
|
||||||
|
ffsaf-db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: true
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- intra
|
||||||
|
- nginx
|
||||||
|
|
||||||
|
ffsaf-db:
|
||||||
|
image: public.ecr.aws/docker/library/postgres:17.2
|
||||||
|
hostname: ffsaf-db
|
||||||
|
container_name: ffsaf-db
|
||||||
|
user: postgres
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- pgadmin
|
||||||
|
- default
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
volumes:
|
||||||
|
- ${PWD}/postgres-data:/var/lib/postgresql/data
|
||||||
|
env_file: prod.env
|
||||||
|
|
||||||
|
networks:
|
||||||
|
intra:
|
||||||
|
name: intra
|
||||||
|
driver: bridge
|
||||||
|
pgadmin:
|
||||||
|
name: pgadmin
|
||||||
|
external: true
|
||||||
|
nginx:
|
||||||
|
name: ${NETWORK_NAME:-gateway}
|
||||||
|
external: true
|
||||||
65
pom.xml
65
pom.xml
@ -1,20 +1,19 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>fr.titionfire</groupId>
|
<groupId>fr.titionfire</groupId>
|
||||||
<artifactId>ffsaf-site</artifactId>
|
<artifactId>ffsaf-site</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<properties>
|
<properties>
|
||||||
<compiler-plugin.version>3.11.0</compiler-plugin.version>
|
<compiler-plugin.version>3.12.1</compiler-plugin.version>
|
||||||
<maven.compiler.release>17</maven.compiler.release>
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
<quarkus.platform.version>3.6.5</quarkus.platform.version>
|
<quarkus.platform.version>3.16.4</quarkus.platform.version>
|
||||||
<skipITs>true</skipITs>
|
<skipITs>true</skipITs>
|
||||||
<surefire-plugin.version>3.1.2</surefire-plugin.version>
|
<surefire-plugin.version>3.2.3</surefire-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -34,31 +33,27 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
|
<artifactId>quarkus-rest-jackson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-resteasy-reactive-qute</artifactId>
|
<artifactId>quarkus-rest-qute</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
|
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-reactive-mysql-client</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-reactive-pg-client</artifactId>
|
<artifactId>quarkus-reactive-pg-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-resteasy-reactive</artifactId>
|
<artifactId>quarkus-rest</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
|
<artifactId>quarkus-rest-client-jackson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -70,17 +65,24 @@
|
|||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-arc</artifactId>
|
<artifactId>quarkus-arc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkiverse.tika</groupId>
|
||||||
|
<artifactId>quarkus-tika</artifactId>
|
||||||
|
<version>2.0.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-junit5</artifactId>
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-oidc</artifactId>
|
<artifactId>quarkus-oidc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-keycloak-authorization</artifactId>
|
<artifactId>quarkus-keycloak-authorization</artifactId>
|
||||||
@ -88,10 +90,9 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-keycloak-admin-client-reactive</artifactId>
|
<artifactId>quarkus-keycloak-admin-rest-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
@ -104,11 +105,34 @@
|
|||||||
<artifactId>jodd-util</artifactId>
|
<artifactId>jodd-util</artifactId>
|
||||||
<version>6.2.1</version>
|
<version>6.2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-websockets</artifactId>
|
<artifactId>quarkus-websockets</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-smallrye-openapi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-swagger-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.xmlgraphics</groupId>
|
||||||
|
<artifactId>fop</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-mailer</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
@ -177,10 +201,11 @@
|
|||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<skipITs>false</skipITs>
|
<skipITs>false</skipITs>
|
||||||
<quarkus.package.type>native</quarkus.package.type>
|
|
||||||
<quarkus.native.additional-build-args>
|
<quarkus.native.additional-build-args>
|
||||||
-H:+UnlockExperimentalVMOptions
|
--initialize-at-run-time=com.fasterxml.jackson.databind.ext.DOMDeserializer
|
||||||
</quarkus.native.additional-build-args>
|
</quarkus.native.additional-build-args>
|
||||||
|
<quarkus.native.enabled>true</quarkus.native.enabled>
|
||||||
|
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|||||||
@ -14,13 +14,17 @@
|
|||||||
# docker run -i --rm -p 8080:8080 quarkus/ffsaf-site
|
# docker run -i --rm -p 8080:8080 quarkus/ffsaf-site
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
|
# replace FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 for jvm need sub application (ie. make_pdf)
|
||||||
|
FROM registry.access.redhat.com/ubi8/openjdk-17:1.18
|
||||||
|
|
||||||
|
USER 0
|
||||||
WORKDIR /work/
|
WORKDIR /work/
|
||||||
RUN chown 1001 /work \
|
RUN chown 1001 /work \
|
||||||
&& chmod "g+rwX" /work \
|
&& chmod "g+rwX" /work \
|
||||||
&& chown 1001:root /work
|
&& chown 1001:root /work
|
||||||
COPY --chown=1001:root ffsaf/target/*-runner /work/application
|
COPY --chown=1001:root target/*-runner /work/application
|
||||||
COPY --chown=1001:root ffsaf/src/main/resources/cacerts /work/cacerts
|
COPY --chown=1001:root src/main/resources/cacerts /work/cacerts
|
||||||
|
COPY --chown=1001:root src/main/pdf_gen/target/pdf_gen-*.jar /work/make_pdf.jar
|
||||||
RUN mkdir /work/media && chown -R 1001:root /work/media
|
RUN mkdir /work/media && chown -R 1001:root /work/media
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
@ -2,12 +2,14 @@ package fr.titionfire;
|
|||||||
|
|
||||||
import io.quarkus.oidc.IdToken;
|
import io.quarkus.oidc.IdToken;
|
||||||
import io.quarkus.oidc.RefreshToken;
|
import io.quarkus.oidc.RefreshToken;
|
||||||
|
import io.quarkus.oidc.UserInfo;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
import org.jboss.resteasy.reactive.NoCache;
|
import org.jboss.resteasy.reactive.NoCache;
|
||||||
|
|
||||||
@Path("/hello")
|
@Path("/hello")
|
||||||
@ -30,6 +32,9 @@ public class ExampleResource {
|
|||||||
@IdToken
|
@IdToken
|
||||||
JsonWebToken idToken;
|
JsonWebToken idToken;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
UserInfo userInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point for the Access Token issued by the OpenID Connect Provider
|
* Injection point for the Access Token issued by the OpenID Connect Provider
|
||||||
*/
|
*/
|
||||||
@ -54,12 +59,14 @@ public class ExampleResource {
|
|||||||
@GET
|
@GET
|
||||||
@Produces("text/html")
|
@Produces("text/html")
|
||||||
@NoCache
|
@NoCache
|
||||||
|
@Operation(hidden = true)
|
||||||
public String getTokens() {
|
public String getTokens() {
|
||||||
StringBuilder response = new StringBuilder().append("<html>")
|
StringBuilder response = new StringBuilder().append("<html>")
|
||||||
.append("<body>")
|
.append("<body>")
|
||||||
.append("<ul>");
|
.append("<ul>");
|
||||||
|
|
||||||
|
System.out.println(idToken);
|
||||||
|
System.out.println(accessToken);
|
||||||
Object userName = this.idToken.getClaim("preferred_username");
|
Object userName = this.idToken.getClaim("preferred_username");
|
||||||
|
|
||||||
if (userName != null) {
|
if (userName != null) {
|
||||||
@ -69,25 +76,17 @@ public class ExampleResource {
|
|||||||
response.append("<li>username: ").append(this.idToken.toString()).append("</li>");
|
response.append("<li>username: ").append(this.idToken.toString()).append("</li>");
|
||||||
}
|
}
|
||||||
|
|
||||||
Object scopes = this.accessToken.getClaim("scope");
|
/*Object scopes = this.accessToken.getClaim("scope");
|
||||||
|
|
||||||
if (scopes != null) {
|
if (scopes != null) {
|
||||||
response.append("<li>scopes: ").append(scopes.toString()).append("</li>");
|
response.append("<li>scopes: ").append(scopes.toString()).append("</li>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scopes != null) {
|
response.append("<li>scopes: ").append(this.accessToken.toString()).append("</li>");
|
||||||
response.append("<li>scopes: ").append(this.accessToken.toString()).append("</li>");
|
response.append("<li>scopes: ").append(this.accessToken.getClaim("user_groups").toString()).append("</li>");*/
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (scopes != null) {
|
response.append("<li>getRoles: ").append(this.securityIdentity.getRoles()).append("</li>");
|
||||||
response.append("<li>scopes: ").append(this.accessToken.getClaim("user_groups").toString()).append("</li>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scopes != null) {
|
|
||||||
response.append("<li>getRoles: ").append(this.securityIdentity.getRoles()).append("</li>");
|
|
||||||
}
|
|
||||||
|
|
||||||
response.append("<li>refresh_token: ").append(refreshToken.getToken() != null).append("</li>");
|
response.append("<li>refresh_token: ").append(refreshToken.getToken() != null).append("</li>");
|
||||||
|
|
||||||
return response.append("</ul>").append("</body>").append("</html>").toString();
|
return response.append("</ul>").append("</body>").append("</html>").toString();
|
||||||
|
|||||||
27
src/main/java/fr/titionfire/PingPage.java
Normal file
27
src/main/java/fr/titionfire/PingPage.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package fr.titionfire;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
@Tag(name = "Ping API", description = "API pour tester la connectivité")
|
||||||
|
@Path("/api")
|
||||||
|
public class PingPage {
|
||||||
|
|
||||||
|
@Operation(summary = "Renvoie un message de réussite", description = "Cette méthode renvoie un message de réussite si la connexion est établie avec succès.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite")
|
||||||
|
})
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public Response get() {
|
||||||
|
return Response.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,17 +1,16 @@
|
|||||||
package fr.titionfire;
|
package fr.titionfire;
|
||||||
|
|
||||||
import io.quarkus.qute.Template;
|
import io.quarkus.qute.Template;
|
||||||
import io.quarkus.qute.TemplateInstance;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.QueryParam;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@Path("/some-page")
|
@Path("api/some-page")
|
||||||
public class SomePage {
|
public class SomePage {
|
||||||
|
|
||||||
private final Template page;
|
private final Template page;
|
||||||
@ -22,8 +21,12 @@ public class SomePage {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
public TemplateInstance get(@QueryParam("name") String name) {
|
@Operation(hidden = true)
|
||||||
return page.data("name", name);
|
public Uni<String> get() {
|
||||||
|
return Uni.createFrom()
|
||||||
|
.completionStage(() -> page
|
||||||
|
.data("name", "test")
|
||||||
|
.renderAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,68 @@
|
|||||||
|
package fr.titionfire.ffsaf;
|
||||||
|
|
||||||
|
import io.vertx.core.http.HttpServerRequest;
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import jakarta.ws.rs.Priorities;
|
||||||
|
import jakarta.ws.rs.container.ResourceInfo;
|
||||||
|
import jakarta.ws.rs.core.Context;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
|
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||||
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
@Priority(Priorities.USER)
|
||||||
|
public class FrontendForwardingFilterREST implements ExceptionMapper<NotFoundException> {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(FrontendForwardingFilterREST.class);
|
||||||
|
|
||||||
|
@Context
|
||||||
|
UriInfo info;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
HttpServerRequest request;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
ResourceInfo resourceInfo;
|
||||||
|
|
||||||
|
private static String text = null;
|
||||||
|
|
||||||
|
private static final String API_NAMESPACE_REGEX = "^/(api/.*|api|openapi|q/.*)";
|
||||||
|
private static final String FILENAME_REGEX = "^/.*\\.[^.]+$";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response toResponse(NotFoundException exception) {
|
||||||
|
final String path = info.getPath();
|
||||||
|
final String address = request.remoteAddress().toString();
|
||||||
|
|
||||||
|
boolean isApiNamespace = path.matches(API_NAMESPACE_REGEX);
|
||||||
|
if (isApiNamespace) {
|
||||||
|
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
|
||||||
|
return exception.getResponse();
|
||||||
|
}
|
||||||
|
boolean isFilename = path.matches(FILENAME_REGEX);
|
||||||
|
if (isFilename) {
|
||||||
|
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
|
||||||
|
return exception.getResponse();
|
||||||
|
}
|
||||||
|
boolean actualErrorResponse = resourceInfo != null && resourceInfo.getResourceMethod() != null;
|
||||||
|
if (actualErrorResponse) {
|
||||||
|
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
|
||||||
|
return exception.getResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text == null)
|
||||||
|
text = new Scanner(
|
||||||
|
Objects.requireNonNull(this.getClass().getResourceAsStream("/META-INF/resources/index.html")),
|
||||||
|
StandardCharsets.UTF_8).useDelimiter("\\A").next();
|
||||||
|
LOG.infof("Request %s from IP %s => redirect", "method", path, address);
|
||||||
|
return Response.status(200).entity(text).type(MediaType.TEXT_HTML_TYPE).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package fr.titionfire.ffsaf.data;
|
||||||
|
|
||||||
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
|
import org.hibernate.dialect.PostgreSQLDialect;
|
||||||
|
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||||
|
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.BasicTypeRegistry;
|
||||||
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
public class CustomPostgreSQLDialect extends PostgreSQLDialect {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(PostgreSQLDialect.class);
|
||||||
|
|
||||||
|
public CustomPostgreSQLDialect() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
|
||||||
|
super.initializeFunctionRegistry(functionContributions);
|
||||||
|
|
||||||
|
LOGGER.info("Initializing custom function registry");
|
||||||
|
|
||||||
|
SqmFunctionRegistry functionRegistry = functionContributions.getFunctionRegistry();
|
||||||
|
TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration();
|
||||||
|
BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
||||||
|
BasicType<String> stringType = basicTypeRegistry.resolve(StandardBasicTypes.STRING);
|
||||||
|
|
||||||
|
functionRegistry.namedDescriptorBuilder("unaccent").setInvariantType(stringType).setExactArgumentCount(1)
|
||||||
|
.setParameterTypes(new FunctionParameterType[]{FunctionParameterType.STRING}).register();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/fr/titionfire/ffsaf/data/id/RegisterId.java
Normal file
17
src/main/java/fr/titionfire/ffsaf/data/id/RegisterId.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.id;
|
||||||
|
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@Embeddable
|
||||||
|
public class RegisterId implements Serializable {
|
||||||
|
private Long competitionId;
|
||||||
|
private Long membreId;
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.data.model;
|
|||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@ -16,11 +17,13 @@ import lombok.*;
|
|||||||
public class AffiliationModel {
|
public class AffiliationModel {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Schema(description = "Identifiant de l'affiliation", example = "42")
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "club", referencedColumnName = "id")
|
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||||
ClubModel club;
|
ClubModel club;
|
||||||
|
|
||||||
|
@Schema(description = "Saison de l'affiliation", example = "2021")
|
||||||
int saison;
|
int saison;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package fr.titionfire.ffsaf.data.model;
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
@ -20,24 +21,27 @@ public class AffiliationRequestModel {
|
|||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
String siren;
|
String state_id;
|
||||||
String RNA;
|
|
||||||
String address;
|
String address;
|
||||||
|
String contact;
|
||||||
|
|
||||||
String president_lname;
|
String m1_lname;
|
||||||
String president_fname;
|
String m1_fname;
|
||||||
String president_email;
|
String m1_email;
|
||||||
int president_lincence;
|
int m1_lincence;
|
||||||
|
RoleAsso m1_role;
|
||||||
|
|
||||||
String tresorier_lname;
|
String m2_lname;
|
||||||
String tresorier_fname;
|
String m2_fname;
|
||||||
String tresorier_email;
|
String m2_email;
|
||||||
int tresorier_lincence;
|
int m2_lincence;
|
||||||
|
RoleAsso m2_role;
|
||||||
|
|
||||||
String secretaire_lname;
|
String m3_lname;
|
||||||
String secretaire_fname;
|
String m3_fname;
|
||||||
String secretaire_email;
|
String m3_email;
|
||||||
int secretaire_lincence;
|
int m3_lincence;
|
||||||
|
RoleAsso m3_role;
|
||||||
|
|
||||||
int saison;
|
int saison;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "category")
|
||||||
|
public class CategoryModel {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@Column(name = "system_type")
|
||||||
|
CompetitionSystem system;
|
||||||
|
Long systemId;
|
||||||
|
|
||||||
|
String name = "";
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "id_compet", referencedColumnName = "id")
|
||||||
|
CompetitionModel compet;
|
||||||
|
|
||||||
|
@OneToMany(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "id_category", referencedColumnName = "id")
|
||||||
|
List<MatchModel> matchs;
|
||||||
|
|
||||||
|
@OneToMany(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "id_category", referencedColumnName = "id")
|
||||||
|
List<TreeModel> tree;
|
||||||
|
|
||||||
|
Integer type;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "checkout")
|
||||||
|
public class CheckoutModel {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Schema(description = "Identifiant du checkout", example = "42")
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "membre", referencedColumnName = "id")
|
||||||
|
MembreModel membre;
|
||||||
|
|
||||||
|
Date creationDate = new Date();
|
||||||
|
|
||||||
|
List<Long> licenseIds;
|
||||||
|
|
||||||
|
Integer checkoutId;
|
||||||
|
|
||||||
|
PaymentStatus paymentStatus;
|
||||||
|
|
||||||
|
public enum PaymentStatus {
|
||||||
|
PENDING, AUTHORIZED, REFUSED, UNKNOW, REGISTERED, REFUNDING, REFUNDED, CONTESTED
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import fr.titionfire.ffsaf.utils.Contact;
|
|||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -17,40 +18,63 @@ import java.util.Map;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "club")
|
@Table(name = "club")
|
||||||
public class ClubModel {
|
public class ClubModel implements LoggableModel {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Schema(description = "Identifiant du club", example = "1")
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
|
@Schema(description = "Identifiant long du club (UUID)", example = "b94f3167-3f6a-449c-a73b-ec84202bf07e")
|
||||||
String clubId;
|
String clubId;
|
||||||
|
|
||||||
|
@Schema(description = "Nom du club", example = "Association sportive")
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
@Schema(description = "Pays du club", example = "FR")
|
||||||
String country;
|
String country;
|
||||||
|
|
||||||
String shieldURL;
|
|
||||||
|
|
||||||
//@Enumerated(EnumType.STRING)
|
//@Enumerated(EnumType.STRING)
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@CollectionTable(name = "club_contact_mapping",
|
@CollectionTable(name = "club_contact_mapping",
|
||||||
joinColumns = {@JoinColumn(name = "club_id", referencedColumnName = "id")})
|
joinColumns = {@JoinColumn(name = "club_id", referencedColumnName = "id")})
|
||||||
@MapKeyColumn(name = "contact_type")
|
@MapKeyColumn(name = "contact_type")
|
||||||
|
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}")
|
||||||
Map<Contact, String> contact;
|
Map<Contact, String> contact;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]")
|
||||||
String training_location;
|
String training_location;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]")
|
||||||
String training_day_time;
|
String training_day_time;
|
||||||
|
|
||||||
|
@Schema(description = "Contact interne du club", example = "john.doe@test.com")
|
||||||
String contact_intern;
|
String contact_intern;
|
||||||
|
|
||||||
String RNA;
|
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris")
|
||||||
|
String address;
|
||||||
|
|
||||||
String SIRET;
|
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234")
|
||||||
|
String StateId;
|
||||||
|
|
||||||
String no_affiliation;
|
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||||
|
Long no_affiliation;
|
||||||
|
|
||||||
|
@Schema(description = "Club international", example = "false")
|
||||||
boolean international;
|
boolean international;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "club", fetch = FetchType.EAGER)
|
@OneToMany(mappedBy = "club", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||||
|
@Schema(description = "Liste des affiliations du club (optionnel)")
|
||||||
List<AffiliationModel> affiliations;
|
List<AffiliationModel> affiliations;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getObjectName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogModel.ObjectType getObjectType() {
|
||||||
|
return LogModel.ObjectType.Club;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.RegisterMode;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "compet")
|
||||||
|
public class CompetitionModel {
|
||||||
|
@Id
|
||||||
|
@Access(AccessType.PROPERTY)
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@Column(name = "system_type")
|
||||||
|
CompetitionSystem system;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||||
|
ClubModel club;
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
String uuid;
|
||||||
|
|
||||||
|
Date date;
|
||||||
|
Date todate;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
String description;
|
||||||
|
String adresse;
|
||||||
|
|
||||||
|
Date startRegister;
|
||||||
|
Date endRegister;
|
||||||
|
|
||||||
|
RegisterMode registerMode;
|
||||||
|
|
||||||
|
boolean publicVisible;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||||
|
List<RegisterModel> insc;
|
||||||
|
|
||||||
|
List<Long> banMembre = new ArrayList<>();
|
||||||
|
|
||||||
|
String owner;
|
||||||
|
|
||||||
|
String data1;
|
||||||
|
String data2;
|
||||||
|
String data3;
|
||||||
|
String data4;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.id.RegisterId;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "helloasso_register")
|
||||||
|
public class HelloAssoRegisterModel {
|
||||||
|
@EmbeddedId
|
||||||
|
RegisterId id;
|
||||||
|
|
||||||
|
@MapsId("competitionId")
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn(name = "id_competition")
|
||||||
|
CompetitionModel competition;
|
||||||
|
|
||||||
|
@MapsId("membreId")
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn(name = "id_membre")
|
||||||
|
MembreModel membre;
|
||||||
|
|
||||||
|
Integer orderId;
|
||||||
|
|
||||||
|
public HelloAssoRegisterModel(CompetitionModel competition, MembreModel membre, Integer orderId) {
|
||||||
|
this.id = new RegisterId(competition.getId(), membre.getId());
|
||||||
|
this.competition = competition;
|
||||||
|
this.membre = membre;
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.data.model;
|
|||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@ -13,18 +14,39 @@ import lombok.*;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "licence")
|
@Table(name = "licence")
|
||||||
public class LicenceModel {
|
public class LicenceModel implements LoggableModel {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Schema(description = "L'identifiant de la licence.")
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "membre", referencedColumnName = "id")
|
@JoinColumn(name = "membre", referencedColumnName = "id")
|
||||||
|
@Schema(description = "Le membre de la licence. (optionnel)")
|
||||||
MembreModel membre;
|
MembreModel membre;
|
||||||
|
|
||||||
|
Long club_id;
|
||||||
|
|
||||||
|
@Schema(description = "La saison de la licence.", example = "2025")
|
||||||
int saison;
|
int saison;
|
||||||
|
|
||||||
boolean certificate;
|
@Schema(description = "Nom et date du médecin sur certificat médical.", example = "M. Jean¤2025-02-03", format = "<Nom>¤<yyyy-mm-dd>")
|
||||||
|
String certificate;
|
||||||
|
|
||||||
|
@Schema(description = "Licence validée", example = "true")
|
||||||
boolean validate;
|
boolean validate;
|
||||||
|
|
||||||
|
@Schema(description = "Licence payer", example = "true")
|
||||||
|
@Column(nullable = false, columnDefinition = "boolean default false")
|
||||||
|
boolean pay = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getObjectName() {
|
||||||
|
return "licence " + id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogModel.ObjectType getObjectType() {
|
||||||
|
return LogModel.ObjectType.Licence;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/main/java/fr/titionfire/ffsaf/data/model/LogModel.java
Normal file
47
src/main/java/fr/titionfire/ffsaf/data/model/LogModel.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "log")
|
||||||
|
public class LogModel {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
String subject;
|
||||||
|
|
||||||
|
Date dateTime;
|
||||||
|
|
||||||
|
ActionType action;
|
||||||
|
|
||||||
|
ObjectType object;
|
||||||
|
|
||||||
|
Long target_id;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
String target_name;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
String message;
|
||||||
|
|
||||||
|
public enum ActionType {
|
||||||
|
ADD, REMOVE, UPDATE
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ObjectType {
|
||||||
|
Membre, Affiliation, Licence, Club, Competition, Register
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
public interface LoggableModel {
|
||||||
|
Long getId();
|
||||||
|
String getObjectName();
|
||||||
|
LogModel.ObjectType getObjectType();
|
||||||
|
}
|
||||||
56
src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java
Normal file
56
src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@ToString
|
||||||
|
@Table(name = "match")
|
||||||
|
public class MatchModel {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@Column(name = "system_type")
|
||||||
|
CompetitionSystem system;
|
||||||
|
Long systemId;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "c1", referencedColumnName = "id")
|
||||||
|
MembreModel c1_id = null;
|
||||||
|
|
||||||
|
String c1_str = null;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "c2", referencedColumnName = "id")
|
||||||
|
MembreModel c2_id = null;
|
||||||
|
|
||||||
|
String c2_str = null;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "id_category", referencedColumnName = "id")
|
||||||
|
CategoryModel category = null;
|
||||||
|
|
||||||
|
long category_ord = 0;
|
||||||
|
|
||||||
|
boolean isEnd = true;
|
||||||
|
|
||||||
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match"))
|
||||||
|
List<ScoreEmbeddable> scores = new ArrayList<>();
|
||||||
|
|
||||||
|
char poule = 'A';
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import fr.titionfire.ffsaf.utils.RoleAsso;
|
|||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -20,39 +21,83 @@ import java.util.List;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "membre")
|
@Table(name = "membre")
|
||||||
public class MembreModel {
|
public class MembreModel implements LoggableModel {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Access(AccessType.PROPERTY)
|
||||||
|
@Schema(description = "L'identifiant du membre.", example = "1")
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
|
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
|
||||||
String userId;
|
String userId;
|
||||||
|
|
||||||
|
@Schema(description = "Le nom du membre.", example = "Dupont")
|
||||||
String lname;
|
String lname;
|
||||||
|
@Schema(description = "Le prénom du membre.", example = "Jean")
|
||||||
String fname;
|
String fname;
|
||||||
|
|
||||||
|
@Schema(description = "La catégorie du membre.", example = "SENIOR")
|
||||||
Categorie categorie;
|
Categorie categorie;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "club", referencedColumnName = "id")
|
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||||
|
@Schema(description = "Le club du membre.")
|
||||||
ClubModel club;
|
ClubModel club;
|
||||||
|
|
||||||
|
@Schema(description = "Le genre du membre.", example = "H")
|
||||||
Genre genre;
|
Genre genre;
|
||||||
|
|
||||||
int licence;
|
@Schema(description = "Le numéro de licence du membre.", example = "12345")
|
||||||
|
Integer licence;
|
||||||
|
|
||||||
|
@Schema(description = "Le pays du membre.", example = "FR")
|
||||||
String country;
|
String country;
|
||||||
|
|
||||||
|
@Schema(description = "La date de naissance du membre.")
|
||||||
Date birth_date;
|
Date birth_date;
|
||||||
|
|
||||||
|
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
|
||||||
String email;
|
String email;
|
||||||
|
|
||||||
|
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
|
||||||
RoleAsso role;
|
RoleAsso role;
|
||||||
|
|
||||||
|
@Schema(description = "Le grade d'arbitrage du membre.", example = "NA")
|
||||||
GradeArbitrage grade_arbitrage;
|
GradeArbitrage grade_arbitrage;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
String url_photo;
|
String url_photo;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY)
|
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||||
|
@Schema(description = "Les licences du membre. (optionnel)")
|
||||||
List<LicenceModel> licences;
|
List<LicenceModel> licences;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getObjectName() {
|
||||||
|
return fname + " " + lname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogModel.ObjectType getObjectType() {
|
||||||
|
return LogModel.ObjectType.Membre;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MembreModel{" +
|
||||||
|
"id=" + id +
|
||||||
|
", userId='" + userId + '\'' +
|
||||||
|
", lname='" + lname + '\'' +
|
||||||
|
", fname='" + fname + '\'' +
|
||||||
|
", categorie=" + categorie +
|
||||||
|
", genre=" + genre +
|
||||||
|
", licence=" + licence +
|
||||||
|
", country='" + country + '\'' +
|
||||||
|
", birth_date=" + birth_date +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", role=" + role +
|
||||||
|
", grade_arbitrage=" + grade_arbitrage +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.id.RegisterId;
|
||||||
|
import fr.titionfire.ffsaf.utils.Categorie;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.hibernate.annotations.OnDelete;
|
||||||
|
import org.hibernate.annotations.OnDeleteAction;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "register")
|
||||||
|
public class RegisterModel {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
RegisterId id;
|
||||||
|
|
||||||
|
@MapsId("competitionId")
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "id_competition")
|
||||||
|
CompetitionModel competition;
|
||||||
|
|
||||||
|
@MapsId("membreId")
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "id_membre")
|
||||||
|
MembreModel membre;
|
||||||
|
|
||||||
|
Integer weight;
|
||||||
|
int overCategory = 0;
|
||||||
|
Categorie categorie;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "club")
|
||||||
|
@OnDelete(action = OnDeleteAction.SET_NULL)
|
||||||
|
ClubModel club = null;
|
||||||
|
|
||||||
|
@Column(nullable = false, columnDefinition = "boolean default false")
|
||||||
|
boolean lockEdit = false;
|
||||||
|
|
||||||
|
public RegisterModel(CompetitionModel competition, MembreModel membre, Integer weight, int overCategory,
|
||||||
|
Categorie categorie, ClubModel club) {
|
||||||
|
this.id = new RegisterId(competition.getId(), membre.getId());
|
||||||
|
this.competition = competition;
|
||||||
|
this.membre = membre;
|
||||||
|
this.weight = weight;
|
||||||
|
this.overCategory = overCategory;
|
||||||
|
this.categorie = categorie;
|
||||||
|
this.club = club;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "sequence")
|
||||||
|
public class SequenceModel {
|
||||||
|
@Id
|
||||||
|
SequenceType type;
|
||||||
|
|
||||||
|
long value;
|
||||||
|
}
|
||||||
39
src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java
Normal file
39
src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "tree")
|
||||||
|
public class TreeModel {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@Column(name = "id_category")
|
||||||
|
Long category;
|
||||||
|
|
||||||
|
Integer level;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "match_id", referencedColumnName = "id")
|
||||||
|
MatchModel match;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
|
||||||
|
@JoinColumn(referencedColumnName = "id")
|
||||||
|
TreeModel left;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
|
||||||
|
@JoinColumn(referencedColumnName = "id")
|
||||||
|
TreeModel right;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.AffiliationModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AffiliationRepository implements PanacheRepositoryBase<AffiliationModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CategoryModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CategoryRepository implements PanacheRepositoryBase<CategoryModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CheckoutModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CheckoutRepository implements PanacheRepositoryBase<CheckoutModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CompetitionRepository implements PanacheRepositoryBase<CompetitionModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.HelloAssoRegisterModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class HelloAssoRegisterRepository implements PanacheRepositoryBase<HelloAssoRegisterModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class LogRepository implements PanacheRepositoryBase<LogModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class MatchRepository implements PanacheRepositoryBase<MatchModel, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.id.RegisterId;
|
||||||
|
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class RegisterRepository implements PanacheRepositoryBase<RegisterModel, RegisterId> {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.SequenceModel;
|
||||||
|
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class SequenceRepository implements PanacheRepositoryBase<SequenceModel, SequenceType> {
|
||||||
|
|
||||||
|
public Uni<Long> getNextValueInTransaction(SequenceType type) {
|
||||||
|
return this.findById(type).onItem().ifNull()
|
||||||
|
.switchTo(() -> this.persist(new SequenceModel(type, 1L)))
|
||||||
|
.chain(v -> {
|
||||||
|
v.setValue(v.getValue() + 1);
|
||||||
|
return this.persistAndFlush(v);
|
||||||
|
})
|
||||||
|
.map(SequenceModel::getValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class TreeRepository implements PanacheRepositoryBase<TreeModel, Long> {
|
||||||
|
}
|
||||||
@ -18,14 +18,12 @@ public class ClubEntity {
|
|||||||
private String name;
|
private String name;
|
||||||
private String clubId;
|
private String clubId;
|
||||||
private String country;
|
private String country;
|
||||||
private String shieldURL;
|
|
||||||
private Map<Contact, String> contact;
|
private Map<Contact, String> contact;
|
||||||
private String training_location;
|
private String training_location;
|
||||||
private String training_day_time;
|
private String training_day_time;
|
||||||
private String contact_intern;
|
private String contact_intern;
|
||||||
private String RNA;
|
private String StateId;
|
||||||
private String SIRET;
|
private Long no_affiliation;
|
||||||
private String no_affiliation;
|
|
||||||
private boolean international;
|
private boolean international;
|
||||||
|
|
||||||
public static ClubEntity fromModel (ClubModel model) {
|
public static ClubEntity fromModel (ClubModel model) {
|
||||||
@ -38,13 +36,11 @@ public class ClubEntity {
|
|||||||
.name(model.getName())
|
.name(model.getName())
|
||||||
.clubId(model.getClubId())
|
.clubId(model.getClubId())
|
||||||
.country(model.getCountry())
|
.country(model.getCountry())
|
||||||
.shieldURL(model.getShieldURL())
|
|
||||||
.contact(model.getContact())
|
.contact(model.getContact())
|
||||||
.training_location(model.getTraining_location())
|
.training_location(model.getTraining_location())
|
||||||
.training_day_time(model.getTraining_day_time())
|
.training_day_time(model.getTraining_day_time())
|
||||||
.contact_intern(model.getContact_intern())
|
.contact_intern(model.getContact_intern())
|
||||||
.RNA(model.getRNA())
|
.StateId(model.getStateId())
|
||||||
.SIRET(model.getSIRET())
|
|
||||||
.no_affiliation(model.getNo_affiliation())
|
.no_affiliation(model.getNo_affiliation())
|
||||||
.international(model.isInternational())
|
.international(model.isInternational())
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public class MembreEntity {
|
|||||||
private Categorie categorie;
|
private Categorie categorie;
|
||||||
private ClubEntity club;
|
private ClubEntity club;
|
||||||
private Genre genre;
|
private Genre genre;
|
||||||
private int licence;
|
private Integer licence;
|
||||||
private String country;
|
private String country;
|
||||||
private Date birth_date;
|
private Date birth_date;
|
||||||
private String email;
|
private String email;
|
||||||
|
|||||||
@ -1,60 +1,482 @@
|
|||||||
package fr.titionfire.ffsaf.domain.service;
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
|
import fr.titionfire.ffsaf.data.model.*;
|
||||||
import fr.titionfire.ffsaf.data.repository.AffiliationRequestRepository;
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
import fr.titionfire.ffsaf.rest.client.SirenService;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.StateIdService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.Genre;
|
||||||
|
import fr.titionfire.ffsaf.utils.GradeArbitrage;
|
||||||
|
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||||
import fr.titionfire.ffsaf.utils.Utils;
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.quarkus.mailer.Mail;
|
||||||
|
import io.quarkus.mailer.reactive.ReactiveMailer;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class AffiliationService {
|
public class AffiliationService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AffiliationService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CombRepository combRepository;
|
CombRepository combRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AffiliationRequestRepository repository;
|
ClubRepository clubRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AffiliationRequestRepository repositoryRequest;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AffiliationRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeycloakService keycloakService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SequenceRepository sequenceRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LicenceRepository licenceRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ReactiveMailer reactiveMailer;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerService ls;
|
||||||
|
|
||||||
|
@RestClient
|
||||||
|
StateIdService stateIdService;
|
||||||
|
|
||||||
|
@RestClient
|
||||||
|
SirenService sirenService;
|
||||||
|
|
||||||
@ConfigProperty(name = "upload_dir")
|
@ConfigProperty(name = "upload_dir")
|
||||||
String media;
|
String media;
|
||||||
|
|
||||||
public Uni<String> save(AffiliationRequestForm form) {
|
@ConfigProperty(name = "notif.affRequest.mail")
|
||||||
|
List<String> mails;
|
||||||
|
|
||||||
|
public Uni<List<AffiliationRequestModel>> getAllReq() {
|
||||||
|
return repositoryRequest.listAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<AffiliationRequestModel> pre_save(AffiliationRequestForm form, boolean unique) {
|
||||||
AffiliationRequestModel affModel = form.toModel();
|
AffiliationRequestModel affModel = form.toModel();
|
||||||
affModel.setSaison(Utils.getSaison());
|
int currentSaison = Utils.getSaison();
|
||||||
|
List<String> out = new ArrayList<>();
|
||||||
|
out.add(affModel.getState_id());
|
||||||
|
|
||||||
return Uni.createFrom().item(affModel)
|
return Uni.createFrom().item(affModel)
|
||||||
.call(model -> ((model.getPresident_lincence() != 0) ? combRepository.find("licence",
|
.invoke(Unchecked.consumer(model -> {
|
||||||
model.getPresident_lincence()).count().invoke(count -> {
|
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
|
||||||
if (count == 0) {
|
throw new DBadRequestException("Saison non valid");
|
||||||
throw new IllegalArgumentException("Licence président inconnue");
|
}
|
||||||
}
|
}))
|
||||||
}) : Uni.createFrom().nullItem())
|
.chain(() -> ((affModel.getState_id().charAt(0) == 'W') ? stateIdService.get_rna(
|
||||||
|
affModel.getState_id()) : sirenService.get_unite(affModel.getState_id())
|
||||||
|
.chain(stateIdService::getAssoDataFromUnit)).onItem().transform(o -> {
|
||||||
|
if (o.getRna() != null && !o.getRna().isBlank())
|
||||||
|
out.add(o.getRna());
|
||||||
|
if (o.getSiren() != null && !o.getSiren().isBlank())
|
||||||
|
out.add(o.getSiren());
|
||||||
|
if (o.getIdentite().getSiret_siege() != null && !o.getIdentite().getSiret_siege().isBlank())
|
||||||
|
out.add(o.getIdentite().getSiret_siege());
|
||||||
|
return out;
|
||||||
|
}).onFailure().recoverWithItem(out)
|
||||||
|
.chain(a -> repositoryRequest.count("state_id IN ?1 and saison = ?2",
|
||||||
|
out, affModel.getSaison()))
|
||||||
|
.onItem().invoke(Unchecked.consumer(count -> {
|
||||||
|
if (count != 0 && unique) {
|
||||||
|
throw new DBadRequestException("Demande d'affiliation déjà existante");
|
||||||
|
}
|
||||||
|
}))
|
||||||
)
|
)
|
||||||
.call(model -> ((model.getTresorier_lincence() != 0) ? combRepository.find("licence",
|
.chain(() -> clubRepository.find("StateId IN ?1", out).firstResult().chain(club ->
|
||||||
model.getTresorier_lincence()).count().invoke(count -> {
|
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
|
||||||
|
.onItem().invoke(Unchecked.consumer(count -> {
|
||||||
|
if (count != 0) {
|
||||||
|
throw new DBadRequestException("Affiliation déjà existante");
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.map(o -> affModel)
|
||||||
|
.call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence",
|
||||||
|
model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
throw new IllegalArgumentException("Licence trésorier inconnue");
|
throw new DBadRequestException("Licence membre n°1 inconnue");
|
||||||
}
|
}
|
||||||
}) : Uni.createFrom().nullItem())
|
})) : Uni.createFrom().nullItem())
|
||||||
)
|
)
|
||||||
.call(model -> ((model.getSecretaire_lincence() != 0) ? combRepository.find("licence",
|
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
|
||||||
model.getSecretaire_lincence()).count().invoke(count -> {
|
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
throw new IllegalArgumentException("Licence secrétaire inconnue");
|
throw new DBadRequestException("Licence membre n°2 inconnue");
|
||||||
}
|
}
|
||||||
}) : Uni.createFrom().nullItem())
|
})) : Uni.createFrom().nullItem())
|
||||||
).chain(model -> Panache.withTransaction(() -> repository.persist(model)))
|
)
|
||||||
.call(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
|
||||||
|
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||||
|
if (count == 0) {
|
||||||
|
throw new DBadRequestException("Licence membre n°3 inconnue");
|
||||||
|
}
|
||||||
|
})) : Uni.createFrom().nullItem())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> saveEdit(AffiliationRequestForm form) {
|
||||||
|
return pre_save(form, false)
|
||||||
|
.chain(model -> repositoryRequest.findById(form.getId())
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||||
|
.chain(origine -> {
|
||||||
|
origine.setName(model.getName());
|
||||||
|
origine.setAddress(model.getAddress());
|
||||||
|
origine.setContact(model.getContact());
|
||||||
|
origine.setM1_lname(model.getM1_lname());
|
||||||
|
origine.setM1_fname(model.getM1_fname());
|
||||||
|
origine.setM1_lincence(model.getM1_lincence());
|
||||||
|
origine.setM1_role(model.getM1_role());
|
||||||
|
origine.setM1_email(model.getM1_email());
|
||||||
|
origine.setM2_lname(model.getM2_lname());
|
||||||
|
origine.setM2_fname(model.getM2_fname());
|
||||||
|
origine.setM2_lincence(model.getM2_lincence());
|
||||||
|
origine.setM2_role(model.getM2_role());
|
||||||
|
origine.setM2_email(model.getM2_email());
|
||||||
|
origine.setM3_lname(model.getM3_lname());
|
||||||
|
origine.setM3_fname(model.getM3_fname());
|
||||||
|
origine.setM3_lincence(model.getM3_lincence());
|
||||||
|
origine.setM3_role(model.getM3_role());
|
||||||
|
origine.setM3_email(model.getM3_email());
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repositoryRequest.persist(origine));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> save(AffiliationRequestForm form) {
|
||||||
|
LOGGER.debug("Affiliation Request Created");
|
||||||
|
LOGGER.debug(form.toString());
|
||||||
|
|
||||||
|
// noinspection ResultOfMethodCallIgnored,ReactiveStreamsUnusedPublisher
|
||||||
|
return pre_save(form, true)
|
||||||
|
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
|
||||||
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
||||||
"aff_request/logo")))
|
"aff_request/logo")))
|
||||||
.call(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
||||||
|
"aff_request/status")))
|
||||||
|
.call(model -> reactiveMailer.send(
|
||||||
|
Mail.withText("no-reply@ffsaf.fr",
|
||||||
|
"[NOTIF] FFSAF - Nouvelle demande d'affiliation",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Une nouvelle demande d'affiliation a été déposée sur l'intranet pour le club: %s.
|
||||||
|
""", model.getName())
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>")
|
||||||
|
.addBcc(mails.toArray(String[]::new))
|
||||||
|
))
|
||||||
|
.map(__ -> "Ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> saveAdmin(AffiliationRequestSaveForm form) {
|
||||||
|
LOGGER.debug("Affiliation Request Saved");
|
||||||
|
LOGGER.debug(form.toString());
|
||||||
|
|
||||||
|
return repositoryRequest.findById(form.getId())
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||||
|
.map(model -> {
|
||||||
|
model.setName(form.getName());
|
||||||
|
model.setState_id(form.getState_id());
|
||||||
|
model.setAddress(form.getAddress());
|
||||||
|
model.setContact(form.getContact());
|
||||||
|
|
||||||
|
if (form.getM1_mode() == 2) {
|
||||||
|
model.setM1_lname(form.getM1_lname());
|
||||||
|
model.setM1_fname(form.getM1_fname());
|
||||||
|
} else {
|
||||||
|
model.setM1_lincence(
|
||||||
|
form.getM1_lincence() == null ? 0 : Integer.parseInt(form.getM1_lincence()));
|
||||||
|
}
|
||||||
|
model.setM1_role(form.getM1_role());
|
||||||
|
if (form.getM1_email_mode() == 0)
|
||||||
|
model.setM1_email(form.getM1_email());
|
||||||
|
|
||||||
|
if (form.getM2_mode() == 2) {
|
||||||
|
model.setM2_lname(form.getM2_lname());
|
||||||
|
model.setM2_fname(form.getM2_fname());
|
||||||
|
} else {
|
||||||
|
model.setM2_lincence(
|
||||||
|
form.getM2_lincence() == null ? 0 : Integer.parseInt(form.getM2_lincence()));
|
||||||
|
}
|
||||||
|
model.setM2_role(form.getM2_role());
|
||||||
|
if (form.getM2_email_mode() == 0)
|
||||||
|
model.setM2_email(form.getM2_email());
|
||||||
|
|
||||||
|
if (form.getM3_mode() == 2) {
|
||||||
|
model.setM3_lname(form.getM3_lname());
|
||||||
|
model.setM3_fname(form.getM3_fname());
|
||||||
|
} else {
|
||||||
|
model.setM3_lincence(
|
||||||
|
form.getM3_lincence() == null ? 0 : Integer.parseInt(form.getM3_lincence()));
|
||||||
|
}
|
||||||
|
model.setM3_role(form.getM3_role());
|
||||||
|
if (form.getM3_email_mode() == 0)
|
||||||
|
model.setM3_email(form.getM3_email());
|
||||||
|
|
||||||
|
return model;
|
||||||
|
})
|
||||||
|
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
|
||||||
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
||||||
|
"aff_request/logo")))
|
||||||
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
||||||
"aff_request/status")))
|
"aff_request/status")))
|
||||||
.map(__ -> "Ok");
|
.map(__ -> "Ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Uni<?> setMembre(AffiliationRequestSaveForm.Member member, ClubModel club, int saison) {
|
||||||
|
return Uni.createFrom().nullItem().chain(__ -> {
|
||||||
|
if (member.getMode() == 2) {
|
||||||
|
MembreModel membreModel = new MembreModel();
|
||||||
|
membreModel.setFname(member.getFname());
|
||||||
|
membreModel.setLname(member.getLname().toUpperCase());
|
||||||
|
membreModel.setClub(club);
|
||||||
|
membreModel.setRole(member.getRole());
|
||||||
|
membreModel.setEmail(member.getEmail());
|
||||||
|
membreModel.setGrade_arbitrage(GradeArbitrage.NA);
|
||||||
|
membreModel.setGenre(Genre.NA);
|
||||||
|
membreModel.setCountry("FR");
|
||||||
|
return Panache.withTransaction(() ->
|
||||||
|
combRepository.persist(membreModel)
|
||||||
|
.chain(m -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||||
|
.invoke(l -> m.setLicence(Math.toIntExact(l)))
|
||||||
|
.chain(() -> combRepository.persist(m))));
|
||||||
|
} else {
|
||||||
|
return combRepository.find("licence", Integer.parseInt(member.getLicence())).firstResult()
|
||||||
|
.onItem().ifNull().switchTo(() -> {
|
||||||
|
MembreModel membreModel = new MembreModel();
|
||||||
|
membreModel.setFname(member.getFname());
|
||||||
|
membreModel.setLname(member.getLname().toUpperCase());
|
||||||
|
return Panache.withTransaction(
|
||||||
|
() -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||||
|
.invoke(l -> membreModel.setLicence(Math.toIntExact(l)))
|
||||||
|
.chain(() -> combRepository.persist(membreModel)));
|
||||||
|
})
|
||||||
|
.map(m -> {
|
||||||
|
m.setClub(club);
|
||||||
|
m.setRole(member.getRole());
|
||||||
|
m.setEmail(member.getEmail());
|
||||||
|
return m;
|
||||||
|
}).call(m -> Panache.withTransaction(() -> combRepository.persist(m)));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.call(m -> ((m.getUserId() == null) ? keycloakService.initCompte(m.getId())
|
||||||
|
.onFailure().invoke(t -> LOGGER.warnf("Failed to init account: %s", t.getMessage())).onFailure()
|
||||||
|
.recoverWithNull() :
|
||||||
|
keycloakService.setClubGroupMembre(m, club).map(__ -> m.getUserId()))
|
||||||
|
.call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage()))
|
||||||
|
.call(userId -> keycloakService.setEmail(userId, m.getEmail())))
|
||||||
|
.call(m -> Mutiny.fetch(m.getLicences())
|
||||||
|
.call(l1 -> l1 != null && l1.stream().anyMatch(l -> l.getSaison() == saison) ?
|
||||||
|
Uni.createFrom().nullItem() :
|
||||||
|
Panache.withTransaction(() -> licenceRepository.persist(
|
||||||
|
new LicenceModel(null, m, club.getId(), saison, null, true, false)))
|
||||||
|
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, m.getObjectName(),
|
||||||
|
licenceModel))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> accept(AffiliationRequestSaveForm form) {
|
||||||
|
LOGGER.debug("Affiliation Request Accepted");
|
||||||
|
LOGGER.debug(form.toString());
|
||||||
|
|
||||||
|
return repositoryRequest.findById(form.getId())
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||||
|
.chain(req ->
|
||||||
|
clubRepository.find("StateId = ?1", form.getState_id()).firstResult()
|
||||||
|
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
|
||||||
|
.call(club -> setMembre(form.new Member(1), club, req.getSaison()).onFailure()
|
||||||
|
.recoverWithNull()
|
||||||
|
.call(__ -> setMembre(form.new Member(2), club, req.getSaison()).onFailure()
|
||||||
|
.recoverWithNull()
|
||||||
|
.call(___ -> setMembre(form.new Member(3), club, req.getSaison()))))
|
||||||
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom()
|
||||||
|
.future(Utils.replacePhoto(form.getId(), form.getLogo(), media,
|
||||||
|
"aff_request/logo")))
|
||||||
|
.onItem()
|
||||||
|
.invoke(model -> Uni.createFrom()
|
||||||
|
.future(Utils.replacePhoto(form.getId(), form.getStatus(), media,
|
||||||
|
"aff_request/status")))
|
||||||
|
.call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/logo",
|
||||||
|
"ppClub"))
|
||||||
|
.call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/status",
|
||||||
|
"clubStatus"))
|
||||||
|
)
|
||||||
|
.map(__ -> "Ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<ClubModel> acceptNew(AffiliationRequestSaveForm form, AffiliationRequestModel model) {
|
||||||
|
LOGGER.debug("New Club Accepted");
|
||||||
|
return Uni.createFrom().nullItem()
|
||||||
|
.chain(() -> {
|
||||||
|
ClubModel club = new ClubModel();
|
||||||
|
club.setName(form.getName());
|
||||||
|
club.setCountry("FR");
|
||||||
|
club.setStateId(form.getState_id());
|
||||||
|
club.setAddress(form.getAddress());
|
||||||
|
club.setContact_intern(form.getContact());
|
||||||
|
club.setAffiliations(new ArrayList<>());
|
||||||
|
return Panache.withTransaction(() -> clubRepository.persist(club)
|
||||||
|
.chain(c -> sequenceRepository.getNextValueInTransaction(SequenceType.Affiliation)
|
||||||
|
.invoke(c::setNo_affiliation)
|
||||||
|
.chain(() -> clubRepository.persist(c))
|
||||||
|
.chain(() -> repositoryRequest.delete(model))
|
||||||
|
)
|
||||||
|
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||||
|
.map(c -> club));
|
||||||
|
})
|
||||||
|
.call(club -> reactiveMailer.send(
|
||||||
|
Mail.withText(form.getM1_email(),
|
||||||
|
"FFSAF - Acceptation de votre demande d'affiliation",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Votre demande d'affiliation pour le club %s a été acceptée.
|
||||||
|
Le numéro d'affiliation de votre club est le %d.
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe de la FFSAF
|
||||||
|
""", club.getName(), club.getNo_affiliation())
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
|
||||||
|
.addTo(form.getM2_email(), form.getM3_email())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<ClubModel> acceptOld(AffiliationRequestSaveForm form, AffiliationRequestModel model, ClubModel club) {
|
||||||
|
AtomicBoolean nameChange = new AtomicBoolean(false);
|
||||||
|
LOGGER.debug("Old Club Accepted");
|
||||||
|
return Uni.createFrom().nullItem()
|
||||||
|
.chain(() -> {
|
||||||
|
if (!form.getName().equals(club.getName())) {
|
||||||
|
club.setName(form.getName());
|
||||||
|
nameChange.set(true);
|
||||||
|
}
|
||||||
|
club.setCountry("FR");
|
||||||
|
club.setStateId(form.getState_id());
|
||||||
|
club.setAddress(form.getAddress());
|
||||||
|
club.setContact_intern(form.getContact());
|
||||||
|
return Panache.withTransaction(() -> clubRepository.persist(club)
|
||||||
|
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||||
|
.chain(() -> repositoryRequest.delete(model)))
|
||||||
|
.call(() -> nameChange.get() ? keycloakService.updateGroupFromClub(
|
||||||
|
club) // update group in keycloak
|
||||||
|
: Uni.createFrom().nullItem());
|
||||||
|
})
|
||||||
|
.map(__ -> club);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<SimpleReqAffiliation> getRequest(long id) {
|
||||||
|
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||||
|
.call(out -> clubRepository.find("StateId = ?1", out.getStateId()).firstResult().invoke(c -> {
|
||||||
|
if (c != null) {
|
||||||
|
out.setClub(c.getId());
|
||||||
|
out.setClub_name(c.getName());
|
||||||
|
out.setClub_no_aff(c.getNo_affiliation());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<SimpleAffiliation>> getCurrentSaisonAffiliation() {
|
||||||
|
return repositoryRequest.list("saison = ?1 or saison = ?1 + 1", Utils.getSaison())
|
||||||
|
.map(models -> models.stream()
|
||||||
|
.map(model -> new SimpleAffiliation(model.getId() * -1, model.getState_id(), model.getSaison(),
|
||||||
|
false)).toList())
|
||||||
|
.chain(aff -> repository.list("saison = ?1", Utils.getSaison())
|
||||||
|
.map(models -> models.stream().map(SimpleAffiliation::fromModel).toList())
|
||||||
|
.map(aff2 -> Stream.concat(aff2.stream(), aff.stream()).toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<SimpleAffiliation>> getAffiliation(long id) {
|
||||||
|
return clubRepository.findById(id)
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
|
||||||
|
.call(model -> Mutiny.fetch(model.getAffiliations()))
|
||||||
|
.chain(model -> repositoryRequest.list("state_id = ?1", model.getStateId())
|
||||||
|
.map(reqs -> reqs.stream().map(req ->
|
||||||
|
new SimpleAffiliation(req.getId() * -1, model.getStateId(), req.getSaison(), false)))
|
||||||
|
.map(aff2 -> Stream.concat(aff2,
|
||||||
|
model.getAffiliations().stream().map(SimpleAffiliation::fromModel)).toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<SimpleAffiliation> setAffiliation(long id, int saison) {
|
||||||
|
return clubRepository.findById(id)
|
||||||
|
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
|
||||||
|
.call(model -> Mutiny.fetch(model.getAffiliations()))
|
||||||
|
.invoke(Unchecked.consumer(club -> {
|
||||||
|
if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) {
|
||||||
|
throw new DBadRequestException("Affiliation déjà existante");
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.chain(club ->
|
||||||
|
Panache.withTransaction(() -> repository.persist(new AffiliationModel(null, club, saison))
|
||||||
|
.chain(c -> (club.getNo_affiliation() != null) ? Uni.createFrom().item(c) :
|
||||||
|
sequenceRepository.getNextValueInTransaction(SequenceType.Affiliation)
|
||||||
|
.invoke(club::setNo_affiliation)
|
||||||
|
.chain(() -> clubRepository.persist(club))
|
||||||
|
.map(o -> c)
|
||||||
|
)))
|
||||||
|
.map(SimpleAffiliation::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> deleteAffiliation(long id) {
|
||||||
|
return Panache.withTransaction(() -> repository.deleteById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> deleteReqAffiliation(long id, String reason, boolean federationAdmin) {
|
||||||
|
return repositoryRequest.findById(id)
|
||||||
|
.call(aff -> federationAdmin ? reactiveMailer.send(
|
||||||
|
Mail.withText(aff.getM1_email(),
|
||||||
|
"FFSAF - Votre demande d'affiliation a été rejetée.",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):
|
||||||
|
%s
|
||||||
|
|
||||||
|
Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse contact@ffsaf.fr.
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe de la FFSAF
|
||||||
|
""", aff.getName(), reason)
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
|
||||||
|
.addTo(aff.getM2_email(), aff.getM3_email())
|
||||||
|
) : Uni.createFrom().nullItem())
|
||||||
|
.chain(aff -> Panache.withTransaction(() -> repositoryRequest.delete(aff)))
|
||||||
|
.call(__ -> Utils.deleteMedia(id, media, "aff_request/logo"))
|
||||||
|
.call(__ -> Utils.deleteMedia(id, media, "aff_request/status"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,266 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.CategoryModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CategoryData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CategoryFullData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.TreeData;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CategoryService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CategoryRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionRepository competRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MatchRepository matchRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TreeRepository treeRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetPermService permService;
|
||||||
|
|
||||||
|
public Uni<CategoryData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", id, system)
|
||||||
|
.firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||||
|
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet()))
|
||||||
|
.map(CategoryData::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<CategoryData>> getAllAdmin(SecurityCtx securityCtx, CompetitionSystem system) {
|
||||||
|
return permService.getAllHaveAdminAccess(securityCtx)
|
||||||
|
.chain(ids -> repository.list("system = ?1 AND compet.id IN ?2", system, ids))
|
||||||
|
.map(pouleModels -> pouleModels.stream().map(CategoryData::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<CategoryData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, CategoryData data) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
|
||||||
|
.chain(o -> {
|
||||||
|
if (o == null) {
|
||||||
|
return competRepository.findById(data.getCompet())
|
||||||
|
.onItem().ifNull().failWith(() -> new RuntimeException("Competition not found"))
|
||||||
|
.call(o2 -> permService.hasEditPerm(securityCtx, o2))
|
||||||
|
.chain(competitionModel -> {
|
||||||
|
CategoryModel model = new CategoryModel();
|
||||||
|
|
||||||
|
model.setId(null);
|
||||||
|
model.setSystem(system);
|
||||||
|
model.setSystemId(data.getId());
|
||||||
|
model.setCompet(competitionModel);
|
||||||
|
model.setName(data.getName());
|
||||||
|
model.setMatchs(new ArrayList<>());
|
||||||
|
model.setTree(new ArrayList<>());
|
||||||
|
model.setType(data.getType());
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
o.setName(data.getName());
|
||||||
|
o.setType(data.getType());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(o));
|
||||||
|
}
|
||||||
|
}).map(CategoryData::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatchModel findMatch(List<MatchModel> matchModelList, Long id) {
|
||||||
|
return matchModelList.stream().filter(m -> m.getSystemId().equals(id))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeModel findNode(List<TreeModel> node, Long match_id) {
|
||||||
|
return node.stream().filter(m -> m.getMatch().getSystemId().equals(match_id))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flatTreeChild(TreeModel current, ArrayList<TreeModel> node) {
|
||||||
|
if (current != null) {
|
||||||
|
node.add(current);
|
||||||
|
flatTreeChild(current.getLeft(), node);
|
||||||
|
flatTreeChild(current.getRight(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flatTreeChild(TreeData current, ArrayList<TreeData> node) {
|
||||||
|
if (current != null) {
|
||||||
|
node.add(current);
|
||||||
|
flatTreeChild(current.getLeft(), node);
|
||||||
|
flatTreeChild(current.getRight(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<TreeModel> persisteTree(TreeData data, List<TreeModel> node, CategoryModel poule,
|
||||||
|
List<MatchModel> matchModelList) {
|
||||||
|
TreeModel mm = findNode(node, data.getMatch());
|
||||||
|
if (mm == null) {
|
||||||
|
mm = new TreeModel();
|
||||||
|
mm.setId(null);
|
||||||
|
}
|
||||||
|
mm.setLevel(data.getLevel());
|
||||||
|
mm.setCategory(poule.getId());
|
||||||
|
mm.setMatch(findMatch(matchModelList, data.getMatch()));
|
||||||
|
|
||||||
|
return Uni.createFrom().item(mm)
|
||||||
|
.call(o -> (data.getLeft() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setLeft(null)) :
|
||||||
|
persisteTree(data.getLeft(), node, poule, matchModelList).invoke(o::setLeft)))
|
||||||
|
.call(o -> (data.getRight() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setRight(null)) :
|
||||||
|
persisteTree(data.getRight(), node, poule, matchModelList).invoke(o::setRight)))
|
||||||
|
.chain(o -> Panache.withTransaction(() -> treeRepository.persist(o)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> syncCategory(SecurityCtx securityCtx, CompetitionSystem system, CategoryFullData data) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system)
|
||||||
|
.firstResult()
|
||||||
|
.onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||||
|
.onItem().ifNull().switchTo(
|
||||||
|
() -> competRepository.findById(data.getCompet())
|
||||||
|
.onItem().ifNull().failWith(() -> new RuntimeException("Compet not found"))
|
||||||
|
.call(o -> permService.hasEditPerm(securityCtx, o))
|
||||||
|
.map(o -> {
|
||||||
|
CategoryModel model = new CategoryModel();
|
||||||
|
model.setId(null);
|
||||||
|
model.setSystem(system);
|
||||||
|
model.setSystemId(data.getId());
|
||||||
|
model.setMatchs(new ArrayList<>());
|
||||||
|
model.setTree(new ArrayList<>());
|
||||||
|
model.setCompet(o);
|
||||||
|
return model;
|
||||||
|
}))
|
||||||
|
.call(o -> Mutiny.fetch(o.getMatchs()))
|
||||||
|
.call(o -> Mutiny.fetch(o.getTree()))
|
||||||
|
.map(o -> {
|
||||||
|
o.setName(data.getName());
|
||||||
|
o.setType(data.getType());
|
||||||
|
|
||||||
|
WorkData workData = new WorkData();
|
||||||
|
workData.category = o;
|
||||||
|
return workData;
|
||||||
|
})
|
||||||
|
.call(o -> Panache.withTransaction(() -> repository.persist(o.category)))
|
||||||
|
.call(o -> (data.getMatches() == null || data.getMatches().isEmpty()) ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.createFrom()
|
||||||
|
.item(data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id())
|
||||||
|
.filter(Objects::nonNull)).distinct().toList())
|
||||||
|
.chain(ids -> ids.isEmpty() ? Uni.createFrom().nullItem()
|
||||||
|
: combRepository.list("id IN ?1", ids)
|
||||||
|
.invoke(o2 -> o2.forEach(m -> o.membres.put(m.getId(), m)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.invoke(in -> {
|
||||||
|
ArrayList<TreeModel> node = new ArrayList<>();
|
||||||
|
for (TreeModel treeModel : in.category.getTree())
|
||||||
|
flatTreeChild(treeModel, node);
|
||||||
|
|
||||||
|
ArrayList<TreeData> new_node = new ArrayList<>();
|
||||||
|
for (TreeData treeModel : data.getTrees())
|
||||||
|
flatTreeChild(treeModel, new_node);
|
||||||
|
|
||||||
|
in.toRmNode = node.stream().filter(m -> new_node.stream()
|
||||||
|
.noneMatch(m2 -> m2.getMatch().equals(m.getMatch().getSystemId())))
|
||||||
|
.map(TreeModel::getId).toList();
|
||||||
|
|
||||||
|
in.unlinkNode = node;
|
||||||
|
in.unlinkNode.forEach(n -> {
|
||||||
|
n.setRight(null);
|
||||||
|
n.setLeft(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
in.toRmMatch = in.category.getMatchs().stream()
|
||||||
|
.filter(m -> data.getMatches().stream().noneMatch(m2 -> m2.getId().equals(m.getSystemId())))
|
||||||
|
.map(MatchModel::getId).toList();
|
||||||
|
})
|
||||||
|
.call(in -> in.unlinkNode.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Panache.withTransaction(() -> treeRepository.persist(in.unlinkNode)))
|
||||||
|
.call(in -> in.toRmNode.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in.toRmNode)))
|
||||||
|
.call(in -> in.toRmMatch.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Panache.withTransaction(() -> Uni.join().all(
|
||||||
|
in.toRmMatch.stream().map(l -> matchRepository.deleteById(l)).toList())
|
||||||
|
.andCollectFailures()))
|
||||||
|
.call(in -> data.getMatches().isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.join().all(
|
||||||
|
data.getMatches().stream().map(m -> {
|
||||||
|
MatchModel mm = findMatch(in.category.getMatchs(), m.getId());
|
||||||
|
if (mm == null) {
|
||||||
|
mm = new MatchModel();
|
||||||
|
mm.setId(null);
|
||||||
|
mm.setSystem(system);
|
||||||
|
mm.setSystemId(m.getId());
|
||||||
|
}
|
||||||
|
mm.setCategory(in.category);
|
||||||
|
mm.setCategory_ord(m.getCategory_ord());
|
||||||
|
mm.setC1_str(m.getC1_str());
|
||||||
|
mm.setC2_str(m.getC2_str());
|
||||||
|
mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null));
|
||||||
|
mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null));
|
||||||
|
mm.setEnd(m.isEnd());
|
||||||
|
mm.setPoule(m.getPoule());
|
||||||
|
mm.getScores().clear();
|
||||||
|
mm.getScores().addAll(m.getScores());
|
||||||
|
|
||||||
|
MatchModel finalMm = mm;
|
||||||
|
return Panache.withTransaction(() -> matchRepository.persist(finalMm)
|
||||||
|
.invoke(o -> in.match.add(o)));
|
||||||
|
}).toList())
|
||||||
|
.andCollectFailures())
|
||||||
|
.call(in -> data.getTrees().isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.join().all(data.getTrees().stream()
|
||||||
|
.map(m -> persisteTree(m, in.category.getTree(), in.category, in.match)).toList())
|
||||||
|
.andCollectFailures())
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WorkData {
|
||||||
|
CategoryModel category;
|
||||||
|
HashMap<Long, MembreModel> membres = new HashMap<>();
|
||||||
|
List<MatchModel> match = new ArrayList<>();
|
||||||
|
List<Long> toRmMatch;
|
||||||
|
List<TreeModel> unlinkNode;
|
||||||
|
List<Long> toRmNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||||
|
.call(o -> permService.hasEditPerm(securityCtx, o.getCompet()))
|
||||||
|
.call(o -> Mutiny.fetch(o.getTree())
|
||||||
|
.call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.createFrom().item(o2.stream().peek(m -> {
|
||||||
|
m.setRight(null);
|
||||||
|
m.setLeft(null);
|
||||||
|
}).toList())
|
||||||
|
.call(in -> Panache.withTransaction(() -> treeRepository.persist(in)))
|
||||||
|
.map(in -> in.stream().map(TreeModel::getId).toList())
|
||||||
|
.call(in -> in.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.call(o -> matchRepository.delete("poule.id = ?1", o.getId()))
|
||||||
|
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,175 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CheckoutModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CheckoutRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.HelloAssoService;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.CheckoutMetadata;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.quarkus.scheduler.Scheduled;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CheckoutService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CheckoutRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LicenceRepository licenceRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MembreService membreService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LicenceService licenceService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerService ls;
|
||||||
|
|
||||||
|
@RestClient
|
||||||
|
HelloAssoService helloAssoService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "frontRootUrl")
|
||||||
|
String frontRootUrl;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "unitLicencePrice")
|
||||||
|
int unitLicencePrice;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "helloasso.organizationSlug")
|
||||||
|
String organizationSlug;
|
||||||
|
|
||||||
|
public Uni<Boolean> canDeleteLicence(long id) {
|
||||||
|
return repository.find("?1 IN licenseIds", id).count().map(count -> count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) {
|
||||||
|
return membreService.getByAccountId(securityCtx.getSubject())
|
||||||
|
.call(membreModel -> Mutiny.fetch(membreModel.getClub()))
|
||||||
|
.chain(membreModel -> {
|
||||||
|
CheckoutModel model = new CheckoutModel();
|
||||||
|
model.setMembre(membreModel);
|
||||||
|
model.setLicenseIds(ids);
|
||||||
|
model.setPaymentStatus(CheckoutModel.PaymentStatus.UNKNOW);
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
|
})
|
||||||
|
.chain(checkoutModel -> {
|
||||||
|
CheckoutIntentsRequest request = new CheckoutIntentsRequest();
|
||||||
|
request.setTotalAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
|
||||||
|
request.setInitialAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
|
||||||
|
request.setItemName("%d licences %d-%d pour %s".formatted(checkoutModel.getLicenseIds().size(),
|
||||||
|
Utils.getSaison(), Utils.getSaison() + 1, checkoutModel.getMembre().getClub().getName()));
|
||||||
|
request.setBackUrl(frontRootUrl + "/club/member/pay");
|
||||||
|
request.setErrorUrl(frontRootUrl + "/club/member/pay/error");
|
||||||
|
request.setReturnUrl(frontRootUrl + "/club/member/pay/return");
|
||||||
|
request.setContainsDonation(false);
|
||||||
|
request.setPayer(new CheckoutIntentsRequest.Payer(checkoutModel.getMembre().getFname(),
|
||||||
|
checkoutModel.getMembre().getLname(), checkoutModel.getMembre().getEmail()));
|
||||||
|
request.setMetadata(new CheckoutMetadata(checkoutModel.getId()));
|
||||||
|
|
||||||
|
return helloAssoService.checkout(organizationSlug, request)
|
||||||
|
.call(response -> {
|
||||||
|
checkoutModel.setCheckoutId(response.getId());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(checkoutModel));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.onFailure().transform(t -> new DInternalError(t.getMessage()))
|
||||||
|
.map(CheckoutIntentsResponse::getRedirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> paymentStatusChange(String state, CheckoutMetadata metadata) {
|
||||||
|
return repository.findById(metadata.getCheckoutDBId())
|
||||||
|
.chain(checkoutModel -> {
|
||||||
|
CheckoutModel.PaymentStatus newStatus = CheckoutModel.PaymentStatus.valueOf(state.toUpperCase());
|
||||||
|
|
||||||
|
Uni<?> uni = Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
if (checkoutModel.getPaymentStatus().equals(newStatus))
|
||||||
|
return uni;
|
||||||
|
|
||||||
|
if (newStatus.equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
|
||||||
|
for (Long id : checkoutModel.getLicenseIds()) {
|
||||||
|
uni = uni.chain(__ -> licenceRepository.findById(id)
|
||||||
|
.onFailure().recoverWithNull()
|
||||||
|
.call(licenceModel -> {
|
||||||
|
if (licenceModel == null) {
|
||||||
|
ls.logAnonymous(LogModel.ActionType.UPDATE, LogModel.ObjectType.Licence,
|
||||||
|
"Fail to save payment for licence (checkout n°" + checkoutModel.getCheckoutId() + ")",
|
||||||
|
"", id);
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.logUpdateAnonymous("Paiement de la licence", licenceModel);
|
||||||
|
licenceModel.setPay(true);
|
||||||
|
|
||||||
|
if (licenceModel.getCertificate() != null && licenceModel.getCertificate()
|
||||||
|
.length() > 3) {
|
||||||
|
if (!licenceModel.isValidate())
|
||||||
|
ls.logUpdateAnonymous("Validation automatique de la licence",
|
||||||
|
licenceModel);
|
||||||
|
return licenceService.validateLicences(licenceModel);
|
||||||
|
} else {
|
||||||
|
return Panache.withTransaction(
|
||||||
|
() -> licenceRepository.persist(licenceModel));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else if (checkoutModel.getPaymentStatus().equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
|
||||||
|
for (Long id : checkoutModel.getLicenseIds()) {
|
||||||
|
uni = uni.chain(__ -> licenceRepository.findById(id)
|
||||||
|
.onFailure().recoverWithNull()
|
||||||
|
.call(licenceModel -> {
|
||||||
|
if (licenceModel == null)
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
ls.logUpdateAnonymous("Annulation automatique du paiement de la licence",
|
||||||
|
licenceModel);
|
||||||
|
licenceModel.setPay(false);
|
||||||
|
if (licenceModel.isValidate())
|
||||||
|
ls.logUpdateAnonymous(
|
||||||
|
"Annulation automatique de la validation de la licence",
|
||||||
|
licenceModel);
|
||||||
|
licenceModel.setValidate(false);
|
||||||
|
return Panache.withTransaction(() -> licenceRepository.persist(licenceModel));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uni = uni.call(__ -> ls.append());
|
||||||
|
|
||||||
|
checkoutModel.setPaymentStatus(newStatus);
|
||||||
|
return uni.chain(__ -> Panache.withTransaction(() -> repository.persist(checkoutModel)));
|
||||||
|
})
|
||||||
|
.onFailure().invoke(Throwable::printStackTrace)
|
||||||
|
.map(__ -> Response.ok().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 * * * ?")
|
||||||
|
Uni<Void> everyHours() {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.add(Calendar.HOUR, -1);
|
||||||
|
Date dateLimit = calendar.getTime();
|
||||||
|
|
||||||
|
return repository.delete("creationDate < ?1 AND (checkoutId IS NULL OR paymentStatus = ?2)", dateLimit,
|
||||||
|
CheckoutModel.PaymentStatus.UNKNOW)
|
||||||
|
.map(__ -> null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,41 @@
|
|||||||
package fr.titionfire.ffsaf.domain.service;
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import fr.titionfire.ffsaf.data.model.AffiliationModel;
|
||||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||||
|
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||||
|
import fr.titionfire.ffsaf.net2.request.SReqClub;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.*;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.PartClubForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.*;
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.PanacheQuery;
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.quarkus.panache.common.Page;
|
||||||
|
import io.quarkus.panache.common.Sort;
|
||||||
import io.quarkus.vertx.VertxContextSupport;
|
import io.quarkus.vertx.VertxContextSupport;
|
||||||
|
import io.smallrye.mutiny.Multi;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
@ -20,13 +44,30 @@ public class ClubService {
|
|||||||
@Inject
|
@Inject
|
||||||
ClubRepository repository;
|
ClubRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ServerCustom serverCustom;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeycloakService keycloakService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerService ls;
|
||||||
|
|
||||||
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
|
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
|
||||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
return VertxContextSupport.subscribeAndAwait(
|
||||||
|
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<SimpleClubModel> findAllClub() throws Throwable {
|
public Collection<SimpleClubModel> findAllClub() throws Throwable {
|
||||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(
|
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(
|
||||||
() -> repository.findAll().list().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList())));
|
() -> repository.findAll().list()
|
||||||
|
.map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<List<ClubModel>> getAll() {
|
public Uni<List<ClubModel>> getAll() {
|
||||||
@ -35,8 +76,276 @@ public class ClubService {
|
|||||||
|
|
||||||
public Uni<?> setClubId(Long id, String id1) {
|
public Uni<?> setClubId(Long id, String id1) {
|
||||||
return repository.findById(id).chain(clubModel -> {
|
return repository.findById(id).chain(clubModel -> {
|
||||||
|
ls.logChange("KC UUID", clubModel.getClubId(), id1, clubModel);
|
||||||
clubModel.setClubId(id1);
|
clubModel.setClubId(id1);
|
||||||
return Panache.withTransaction(() -> repository.persist(clubModel));
|
return Panache.withTransaction(() -> repository.persist(clubModel))
|
||||||
|
.call(() -> ls.append());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<PageResult<SimpleClubList>> search(Integer limit, int page, String search, String country) {
|
||||||
|
if (search == null)
|
||||||
|
search = "";
|
||||||
|
search = search + "%";
|
||||||
|
|
||||||
|
PanacheQuery<ClubModel> query;
|
||||||
|
|
||||||
|
if (country == null || country.isBlank())
|
||||||
|
query = repository.find("LOWER(name) LIKE LOWER(?1)",
|
||||||
|
Sort.ascending("name"), search).page(Page.ofSize(limit));
|
||||||
|
else
|
||||||
|
query = repository.find("LOWER(name) LIKE LOWER(?1) AND country LIKE ?2",
|
||||||
|
Sort.ascending("name"), search, country + "%").page(Page.ofSize(limit));
|
||||||
|
return getPageResult(query, limit, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<PageResult<SimpleClubList>> getPageResult(PanacheQuery<ClubModel> query, int limit, int page) {
|
||||||
|
return Uni.createFrom().item(new PageResult<SimpleClubList>())
|
||||||
|
.invoke(result -> result.setPage(page))
|
||||||
|
.invoke(result -> result.setPage_size(limit))
|
||||||
|
.call(result -> query.count().invoke(result::setResult_count))
|
||||||
|
.call(result -> query.pageCount()
|
||||||
|
.invoke(Unchecked.consumer(pages -> {
|
||||||
|
if (page > pages) throw new DBadRequestException("Page out of range");
|
||||||
|
}))
|
||||||
|
.invoke(result::setPage_count))
|
||||||
|
.call(result -> query.page(Page.of(page, limit)).list()
|
||||||
|
.map(membreModels -> membreModels.stream().map(SimpleClubList::fromModel).toList())
|
||||||
|
.invoke(result::setResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<ClubModel> getById(long id) {
|
||||||
|
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<ClubModel> getByClubId(String clubId) {
|
||||||
|
return repository.find("clubId", clubId).firstResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<ClubModel> getOfUser(SecurityCtx securityCtx) {
|
||||||
|
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null || m.getClub() == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
}))
|
||||||
|
.map(MembreModel::getClub)
|
||||||
|
.call(club -> Mutiny.fetch(club.getContact()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<DeskMember>> getClubDesk(Consumer<ClubModel> consumer, long id) {
|
||||||
|
return repository.findById(id).invoke(consumer)
|
||||||
|
.chain(club -> combRepository.list("club = ?1", club))
|
||||||
|
.map(combs -> combs.stream()
|
||||||
|
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||||
|
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||||
|
.map(DeskMember::fromModel)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<VerySimpleMembre>> getMembers(SecurityCtx securityCtx) {
|
||||||
|
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null || m.getClub() == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
if (!securityCtx.isInClubGroup(m.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
}))
|
||||||
|
.chain(m -> combRepository.list("club = ?1", m.getClub()))
|
||||||
|
.map(membreModels -> membreModels.stream()
|
||||||
|
.map(m -> new VerySimpleMembre(m.getLname(), m.getFname(), m.getLicence())).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> updateOfUser(SecurityCtx securityCtx, PartClubForm form) {
|
||||||
|
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null || m.getClub() == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
if (!securityCtx.isInClubGroup(m.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
}))
|
||||||
|
.map(MembreModel::getClub)
|
||||||
|
.call(club -> Mutiny.fetch(club.getContact()))
|
||||||
|
.chain(Unchecked.function(club -> {
|
||||||
|
ls.logChange("Contact interne", club.getContact_intern(), form.getContact_intern(), club);
|
||||||
|
club.setContact_intern(form.getContact_intern());
|
||||||
|
ls.logChange("Adresse administrative", club.getAddress(), form.getAddress(), club);
|
||||||
|
club.setAddress(form.getAddress());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!Objects.equals(club.getContact(), MAPPER.readValue(form.getContact(), typeRef)))
|
||||||
|
ls.logUpdate("Contact(s)...", club);
|
||||||
|
club.setContact(MAPPER.readValue(form.getContact(), typeRef));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new DBadRequestException("Erreur de format des contacts");
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.logChange("Lieux d'entrainements", club.getTraining_location(), form.getTraining_location(),
|
||||||
|
club);
|
||||||
|
club.setTraining_location(form.getTraining_location());
|
||||||
|
ls.logChange("Horaires d'entrainements", club.getTraining_day_time(), form.getTraining_day_time(),
|
||||||
|
club);
|
||||||
|
club.setTraining_day_time(form.getTraining_day_time());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(club)).call(() -> ls.append());
|
||||||
|
}))
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> update(long id, FullClubForm input) {
|
||||||
|
AtomicBoolean nameChange = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()))
|
||||||
|
.onItem().transformToUni(Unchecked.function(m -> {
|
||||||
|
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!input.getName().equals(m.getName())) {
|
||||||
|
m.setName(input.getName());
|
||||||
|
nameChange.set(true);
|
||||||
|
}
|
||||||
|
m.setCountry(input.getCountry());
|
||||||
|
m.setInternational(input.isInternational());
|
||||||
|
|
||||||
|
if (!input.isInternational()) {
|
||||||
|
ls.logChange("Lieux d'entrainements", m.getTraining_location(), input.getTraining_location(),
|
||||||
|
m);
|
||||||
|
m.setTraining_location(input.getTraining_location());
|
||||||
|
ls.logChange("Horaires d'entrainements", m.getTraining_day_time(), input.getTraining_day_time(),
|
||||||
|
m);
|
||||||
|
m.setTraining_day_time(input.getTraining_day_time());
|
||||||
|
ls.logChange("Contact interne", m.getContact_intern(), input.getContact_intern(), m);
|
||||||
|
m.setContact_intern(input.getContact_intern());
|
||||||
|
if (input.getState_id() != null && !input.getState_id().isBlank()) {
|
||||||
|
ls.logChange("N° SIRET", m.getClubId(), input.getState_id(), m);
|
||||||
|
m.setStateId(input.getState_id());
|
||||||
|
}
|
||||||
|
ls.logChange("Adresse administrative", m.getAddress(), input.getAddress(), m);
|
||||||
|
m.setAddress(input.getAddress());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!Objects.equals(m.getContact(), MAPPER.readValue(input.getContact(), typeRef)))
|
||||||
|
ls.logUpdate("Contact(s)...", m);
|
||||||
|
m.setContact(MAPPER.readValue(input.getContact(), typeRef));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new DBadRequestException("Erreur de format des contacts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append());
|
||||||
|
}))
|
||||||
|
.call(clubModel -> nameChange.get() ? keycloakService.updateGroupFromClub(clubModel) // update group in keycloak
|
||||||
|
: Uni.createFrom().nullItem())
|
||||||
|
.invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients,
|
||||||
|
SimpleClubModel.fromModel(membreModel)))
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Long> add(FullClubForm input) {
|
||||||
|
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
return Uni.createFrom().nullItem()
|
||||||
|
.chain(() -> {
|
||||||
|
ClubModel clubModel = new ClubModel();
|
||||||
|
|
||||||
|
clubModel.setName(input.getName());
|
||||||
|
clubModel.setCountry(input.getCountry());
|
||||||
|
clubModel.setInternational(input.isInternational());
|
||||||
|
clubModel.setNo_affiliation(null);
|
||||||
|
if (!input.isInternational()) {
|
||||||
|
clubModel.setTraining_location(input.getTraining_location());
|
||||||
|
clubModel.setTraining_day_time(input.getTraining_day_time());
|
||||||
|
clubModel.setContact_intern(input.getContact_intern());
|
||||||
|
if (input.getState_id() != null && !input.getState_id().isBlank())
|
||||||
|
clubModel.setStateId(input.getState_id());
|
||||||
|
clubModel.setAddress(input.getAddress());
|
||||||
|
|
||||||
|
try {
|
||||||
|
clubModel.setContact(MAPPER.readValue(input.getContact(), typeRef));
|
||||||
|
} catch (JsonProcessingException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repository.persist(clubModel));
|
||||||
|
})
|
||||||
|
.call(clubModel -> ls.logAAdd(clubModel))
|
||||||
|
.call(clubModel -> keycloakService.getGroupFromClub(clubModel)) // create group in keycloak
|
||||||
|
.invoke(clubModel -> SReqClub.sendAddIfNeed(serverCustom.clients, SimpleClubModel.fromModel(clubModel)))
|
||||||
|
.map(ClubModel::getId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> delete(long id) {
|
||||||
|
return repository.findById(id)
|
||||||
|
.chain(club -> combRepository.list("club = ?1", club)
|
||||||
|
.map(combModels -> combModels.stream().peek(combModel -> {
|
||||||
|
combModel.setClub(null);
|
||||||
|
combModel.setRole(RoleAsso.MEMBRE);
|
||||||
|
}).toList())
|
||||||
|
.call(list -> (list.isEmpty()) ? Uni.createFrom().voidItem() :
|
||||||
|
Uni.join().all(list.stream().filter(m -> m.getUserId() != null)
|
||||||
|
.map(m -> keycloakService.clearUser(m.getUserId())).toList())
|
||||||
|
.andCollectFailures())
|
||||||
|
.chain(list -> Panache.withTransaction(() -> combRepository.persist(list)))
|
||||||
|
.map(o -> club)
|
||||||
|
)
|
||||||
|
.call(clubModel -> (clubModel.getClubId() == null) ? Uni.createFrom()
|
||||||
|
.voidItem() : keycloakService.removeClubGroup(clubModel.getClubId()))
|
||||||
|
.invoke(membreModel -> SReqClub.sendRmIfNeed(serverCustom.clients, id))
|
||||||
|
.call(clubModel -> ls.logADelete(clubModel))
|
||||||
|
.chain(clubModel -> Panache.withTransaction(() -> repository.delete(clubModel)))
|
||||||
|
.call(__ -> Utils.deleteMedia(id, media, "ppClub"))
|
||||||
|
.call(__ -> Utils.deleteMedia(id, media, "clubStatus"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<RenewAffData> getRenewData(long id, List<Long> mIds) {
|
||||||
|
RenewAffData data = new RenewAffData();
|
||||||
|
|
||||||
|
return repository.findById(id)
|
||||||
|
.call(clubModel -> Mutiny.fetch(clubModel.getAffiliations()))
|
||||||
|
.invoke(clubModel -> {
|
||||||
|
data.setName(clubModel.getName());
|
||||||
|
data.setState_id(clubModel.getStateId());
|
||||||
|
data.setAddress(clubModel.getAddress());
|
||||||
|
data.setContact(clubModel.getContact_intern());
|
||||||
|
data.setSaison(
|
||||||
|
clubModel.getAffiliations().stream().max(Comparator.comparing(AffiliationModel::getSaison))
|
||||||
|
.map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1))
|
||||||
|
.orElse(Utils.getSaison()));
|
||||||
|
})
|
||||||
|
.chain(club -> combRepository.list("id IN ?1", mIds))
|
||||||
|
.invoke(combs -> data.setMembers(combs.stream()
|
||||||
|
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||||
|
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||||
|
.map(RenewAffData.RenewMember::new)
|
||||||
|
.toList()))
|
||||||
|
.map(o -> data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<ClubMapData>> getMapData() {
|
||||||
|
return repository.list("international", false).toMulti().flatMap(list -> Multi.createFrom().iterable(list))
|
||||||
|
.call(clubModel -> Mutiny.fetch(clubModel.getContact()))
|
||||||
|
.map(clubModel -> {
|
||||||
|
ClubMapData data = new ClubMapData();
|
||||||
|
|
||||||
|
data.setName(clubModel.getName());
|
||||||
|
data.setUuid(clubModel.getClubId());
|
||||||
|
if (clubModel.getTraining_location() != null) {
|
||||||
|
try {
|
||||||
|
MAPPER.readTree(clubModel.getTraining_location()).forEach(l -> {
|
||||||
|
ClubMapData.Location loc = new ClubMapData.Location();
|
||||||
|
loc.setLat(l.get("lat").asDouble());
|
||||||
|
loc.setLng(l.get("lng").asDouble());
|
||||||
|
loc.setAddr(l.get("text").asText());
|
||||||
|
data.training_location.add(loc);
|
||||||
|
});
|
||||||
|
} catch (JsonProcessingException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.setTraining_day_time(clubModel.getTraining_day_time());
|
||||||
|
data.setContact(clubModel.getContact());
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}).collect().asList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,256 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.RegisterRepository;
|
||||||
|
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||||
|
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||||
|
import fr.titionfire.ffsaf.net2.request.SReqCompet;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.RegisterMode;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.cache.Cache;
|
||||||
|
import io.quarkus.cache.CacheName;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CompetPermService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ServerCustom serverCustom;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionRepository competitionRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("safca-config")
|
||||||
|
Cache cache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("safca-have-access")
|
||||||
|
Cache cacheAccess;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("have-access")
|
||||||
|
Cache cacheNoneAccess;
|
||||||
|
@Inject
|
||||||
|
RegisterRepository registerRepository;
|
||||||
|
|
||||||
|
|
||||||
|
public Uni<SimpleCompet> getSafcaConfig(long id) {
|
||||||
|
return cache.get(id, k -> {
|
||||||
|
CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
|
||||||
|
SReqCompet.getConfig(serverCustom.clients, id, f);
|
||||||
|
try {
|
||||||
|
return f.get(1500, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<Long>> getAllHaveAdminAccess(SecurityCtx securityCtx) {
|
||||||
|
ArrayList<Long> out = new ArrayList<>();
|
||||||
|
|
||||||
|
Uni<HashMap<Long, String>> safca = cacheAccess.getAsync(securityCtx.getSubject(),
|
||||||
|
k -> competitionRepository.list("system = ?1", CompetitionSystem.SAFCA)
|
||||||
|
.chain(competitionModels -> {
|
||||||
|
CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>();
|
||||||
|
SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f);
|
||||||
|
return Uni.createFrom().future(f, Duration.ofMillis(1500))
|
||||||
|
.map(map_ -> {
|
||||||
|
HashMap<Long, String> map = new HashMap<>();
|
||||||
|
map_.forEach((key, value) -> map.put(Long.parseLong(key), value));
|
||||||
|
|
||||||
|
for (CompetitionModel model : competitionModels) {
|
||||||
|
if (model.getOwner().equals(securityCtx.getSubject()))
|
||||||
|
map.putIfAbsent(model.getId(), "owner");
|
||||||
|
else if (securityCtx.roleHas("federation_admin")
|
||||||
|
|| securityCtx.roleHas("safca_super_admin"))
|
||||||
|
map.putIfAbsent(model.getId(), "admin");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
.onFailure().call(throwable -> cacheAccess.invalidate(securityCtx.getSubject()));
|
||||||
|
|
||||||
|
Uni<HashMap<Long, String>> none = cacheNoneAccess.getAsync(securityCtx.getSubject(),
|
||||||
|
k -> competitionRepository.list("system = ?1", CompetitionSystem.NONE)
|
||||||
|
.map(competitionModels -> {
|
||||||
|
HashMap<Long, String> map = new HashMap<>();
|
||||||
|
for (CompetitionModel model : competitionModels) {
|
||||||
|
if (model.getOwner().equals(securityCtx.getSubject()))
|
||||||
|
map.putIfAbsent(model.getId(), "owner");
|
||||||
|
else if (securityCtx.roleHas("federation_admin"))
|
||||||
|
map.putIfAbsent(model.getId(), "admin");
|
||||||
|
else if (securityCtx.isInClubGroup(model.getClub().getId()) && (securityCtx.roleHas(
|
||||||
|
"club_president")
|
||||||
|
|| securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas(
|
||||||
|
"club_secretaire")
|
||||||
|
|| securityCtx.roleHas("club_tresorier")))
|
||||||
|
map.putIfAbsent(model.getId(), "admin");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return safca.invoke(map ->
|
||||||
|
map.forEach((k, v) -> {
|
||||||
|
if (v.equals("owner") || v.equals("admin"))
|
||||||
|
out.add(k);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.call(__ -> none.invoke(map ->
|
||||||
|
map.forEach((k, v) -> {
|
||||||
|
if (v.equals("owner") || v.equals("admin"))
|
||||||
|
out.add(k);
|
||||||
|
})
|
||||||
|
))
|
||||||
|
.map(__ -> out.stream().distinct().toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
|
||||||
|
return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, long id) {
|
||||||
|
return hasViewPerm(securityCtx, competitionRepository.findById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
|
||||||
|
return in.call(cm -> (cm.isPublicVisible() || cm.getRegisterMode() == RegisterMode.FREE
|
||||||
|
|| cm.getRegisterMode() == RegisterMode.HELLOASSO
|
||||||
|
|| (cm.getRegisterMode() == RegisterMode.CLUB_ADMIN && securityCtx.isClubAdmin())) ?
|
||||||
|
Uni.createFrom().nullItem() :
|
||||||
|
hasAdminViewPerm(securityCtx, cm).onFailure()
|
||||||
|
.recoverWithUni(__ ->
|
||||||
|
registerRepository.count("membre.userId = ?1 AND competition = ?2",
|
||||||
|
securityCtx.getSubject(), cm).map(Unchecked.function(c -> {
|
||||||
|
if (c == 0)
|
||||||
|
throw new DForbiddenException();
|
||||||
|
return cm;
|
||||||
|
}))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
|
||||||
|
return hasAdminViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, long id) {
|
||||||
|
return hasAdminViewPerm(securityCtx, competitionRepository.findById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
|
||||||
|
return in.call(Unchecked.function(o -> {
|
||||||
|
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
if (o.getSystem() == CompetitionSystem.SAFCA)
|
||||||
|
return hasSafcaViewPerm(securityCtx, o.getId());
|
||||||
|
|
||||||
|
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here
|
||||||
|
throw new DForbiddenException();
|
||||||
|
|
||||||
|
if (o.getSystem() == CompetitionSystem.NONE)
|
||||||
|
if (securityCtx.roleHas("club_president") || securityCtx.roleHas("club_respo_intra")
|
||||||
|
|| securityCtx.roleHas("club_secretaire") || securityCtx.roleHas("club_tresorier"))
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
throw new DForbiddenException();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
|
||||||
|
return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, long id) {
|
||||||
|
return hasEditPerm(securityCtx, competitionRepository.findById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
|
||||||
|
*/
|
||||||
|
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
|
||||||
|
return in.call(Unchecked.function(o -> {
|
||||||
|
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
if (o.getSystem() == CompetitionSystem.SAFCA)
|
||||||
|
return hasSafcaEditPerm(securityCtx, o.getId());
|
||||||
|
|
||||||
|
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here
|
||||||
|
throw new DForbiddenException();
|
||||||
|
|
||||||
|
if (o.getSystem() == CompetitionSystem.NONE)
|
||||||
|
if (securityCtx.isClubAdmin())
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
throw new DForbiddenException();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<?> hasSafcaViewPerm(SecurityCtx securityCtx, long id) {
|
||||||
|
return securityCtx.roleHas("safca_super_admin") ?
|
||||||
|
Uni.createFrom().nullItem()
|
||||||
|
:
|
||||||
|
getSafcaConfig(id).chain(Unchecked.function(o -> {
|
||||||
|
if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table()
|
||||||
|
.contains(UUID.fromString(securityCtx.getSubject())))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<?> hasSafcaEditPerm(SecurityCtx securityCtx, long id) {
|
||||||
|
return securityCtx.roleHas("safca_super_admin") ?
|
||||||
|
Uni.createFrom().nullItem()
|
||||||
|
:
|
||||||
|
getSafcaConfig(id).chain(Unchecked.function(o -> {
|
||||||
|
if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,611 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.HelloAssoRegisterModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
|
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||||
|
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||||
|
import fr.titionfire.ffsaf.net2.request.SReqCompet;
|
||||||
|
import fr.titionfire.ffsaf.net2.request.SReqRegister;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.NotificationData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.RegisterRequestData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.RegisterMode;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.quarkus.cache.Cache;
|
||||||
|
import io.quarkus.cache.CacheName;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.quarkus.mailer.Mail;
|
||||||
|
import io.quarkus.mailer.reactive.ReactiveMailer;
|
||||||
|
import io.smallrye.mutiny.Multi;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import io.vertx.mutiny.core.Vertx;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CompetitionService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(CompetitionService.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MatchRepository matchRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RegisterRepository registerRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeycloakService keycloakService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ServerCustom serverCustom;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MembreService membreService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetPermService permService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HelloAssoRegisterRepository helloAssoRepository;
|
||||||
|
|
||||||
|
@SuppressWarnings("CdiInjectionPointsInspection")
|
||||||
|
@Inject
|
||||||
|
ReactiveMailer reactiveMailer;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Vertx vertx;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("safca-config")
|
||||||
|
Cache cache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("safca-have-access")
|
||||||
|
Cache cacheAccess;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@CacheName("have-access")
|
||||||
|
Cache cacheNoneAccess;
|
||||||
|
|
||||||
|
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) {
|
||||||
|
return permService.hasViewPerm(securityCtx, id).map(CompetitionData::fromModelLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<CompetitionData> getByIdAdmin(SecurityCtx securityCtx, Long id) {
|
||||||
|
if (id == 0) {
|
||||||
|
return Uni.createFrom()
|
||||||
|
.item(new CompetitionData(null, "", "", "", "", new Date(), new Date(),
|
||||||
|
CompetitionSystem.NONE, RegisterMode.FREE, new Date(), new Date(), true,
|
||||||
|
null, "", "", null, true, "", "", "", ""));
|
||||||
|
}
|
||||||
|
return permService.hasAdminViewPerm(securityCtx, id)
|
||||||
|
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
|
||||||
|
.map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc)))
|
||||||
|
.chain(data ->
|
||||||
|
vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
keycloakService.getUser(UUID.fromString(data.getOwner()))
|
||||||
|
.ifPresent(user -> data.setOwner(user.getUsername()));
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<CompetitionData>> getAll(SecurityCtx securityCtx) {
|
||||||
|
List<CompetitionData> out = new ArrayList<>();
|
||||||
|
return permService.getAllHaveAdminAccess(securityCtx)
|
||||||
|
.call(ids -> repository.list("id IN ?1", ids)
|
||||||
|
.invoke(cm -> {
|
||||||
|
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
|
||||||
|
out.forEach(competition -> competition.setCanEdit(true));
|
||||||
|
}))
|
||||||
|
.call(ids ->
|
||||||
|
repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids,
|
||||||
|
securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO,
|
||||||
|
RegisterMode.CLUB_ADMIN) : List.of(RegisterMode.FREE, RegisterMode.HELLOASSO))
|
||||||
|
.invoke(cm -> out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList()))
|
||||||
|
.call(cm -> registerRepository.list(
|
||||||
|
"membre.userId = ?1 AND competition.id NOT IN ?2 AND competition NOT IN ?3",
|
||||||
|
securityCtx.getSubject(), ids, cm)
|
||||||
|
.chain(registerModels -> {
|
||||||
|
Uni<Void> uni = Uni.createFrom().nullItem();
|
||||||
|
for (RegisterModel registerModel : registerModels) {
|
||||||
|
uni = uni.call(__ -> Mutiny.fetch(registerModel.getCompetition())
|
||||||
|
.invoke(cm2 -> out.add(CompetitionData.fromModelLight(cm2))));
|
||||||
|
}
|
||||||
|
return uni;
|
||||||
|
})
|
||||||
|
))
|
||||||
|
.map(__ -> out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<CompetitionData>> getAllAdmin(SecurityCtx securityCtx) {
|
||||||
|
return permService.getAllHaveAdminAccess(securityCtx)
|
||||||
|
.chain(ids -> repository.list("id IN ?1", ids))
|
||||||
|
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<CompetitionData>> getAllSystemAdmin(SecurityCtx securityCtx,
|
||||||
|
CompetitionSystem system) {
|
||||||
|
return permService.getAllHaveAdminAccess(securityCtx)
|
||||||
|
.chain(ids -> repository.list("system = ?1 AND id IN ?2", system, ids))
|
||||||
|
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
|
||||||
|
if (data.getId() == null) {
|
||||||
|
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(combModel -> {
|
||||||
|
if (!securityCtx.getRoles().contains("create_compet") && !securityCtx.getRoles()
|
||||||
|
.contains("federation_admin"))
|
||||||
|
throw new DForbiddenException("Vous ne pouvez pas créer de compétition");
|
||||||
|
}))
|
||||||
|
.map(MembreModel::getClub)
|
||||||
|
.chain(clubModel -> {
|
||||||
|
CompetitionModel model = new CompetitionModel();
|
||||||
|
|
||||||
|
model.setId(null);
|
||||||
|
model.setSystem(data.getSystem());
|
||||||
|
model.setClub(clubModel);
|
||||||
|
model.setInsc(new ArrayList<>());
|
||||||
|
model.setUuid(UUID.randomUUID().toString());
|
||||||
|
model.setOwner(securityCtx.getSubject());
|
||||||
|
|
||||||
|
copyData(data, model);
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
|
}).map(CompetitionData::fromModel)
|
||||||
|
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
|
||||||
|
securityCtx.getSubject()) : Uni.createFrom().nullItem())
|
||||||
|
.call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate(
|
||||||
|
securityCtx.getSubject()) : Uni.createFrom().nullItem());
|
||||||
|
} else {
|
||||||
|
return permService.hasEditPerm(securityCtx, data.getId())
|
||||||
|
.chain(model -> {
|
||||||
|
copyData(data, model);
|
||||||
|
|
||||||
|
return vertx.getOrCreateContext().executeBlocking(() -> // Update owner
|
||||||
|
keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null))
|
||||||
|
.invoke(Unchecked.consumer(newOwner -> {
|
||||||
|
if (newOwner == null)
|
||||||
|
throw new DBadRequestException("User " + data.getOwner() + " not found");
|
||||||
|
if (!newOwner.equals(model.getOwner())) {
|
||||||
|
if (!securityCtx.roleHas("federation_admin")
|
||||||
|
&& !securityCtx.roleHas("safca_super_admin")
|
||||||
|
&& !securityCtx.getSubject().equals(model.getOwner()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
model.setOwner(newOwner);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.chain(__ -> Panache.withTransaction(() -> repository.persist(model)));
|
||||||
|
}).map(CompetitionData::fromModel)
|
||||||
|
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
|
||||||
|
securityCtx.getSubject()) : Uni.createFrom().nullItem())
|
||||||
|
.call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate(
|
||||||
|
securityCtx.getSubject()) : Uni.createFrom().nullItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyData(CompetitionData data, CompetitionModel model) {
|
||||||
|
if (model.getBanMembre() == null)
|
||||||
|
model.setBanMembre(new ArrayList<>());
|
||||||
|
|
||||||
|
model.setName(data.getName());
|
||||||
|
model.setAdresse(data.getAdresse());
|
||||||
|
model.setDescription(data.getDescription());
|
||||||
|
model.setDate(data.getDate());
|
||||||
|
model.setTodate(data.getDate());
|
||||||
|
model.setPublicVisible(data.isPublicVisible());
|
||||||
|
model.setStartRegister(data.getStartRegister());
|
||||||
|
model.setEndRegister(data.getEndRegister());
|
||||||
|
model.setRegisterMode(data.getRegisterMode());
|
||||||
|
model.setData1(data.getData1());
|
||||||
|
model.setData2(data.getData2());
|
||||||
|
model.setData3(data.getData3());
|
||||||
|
model.setData4(data.getData4());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<SimpleRegisterComb>> getRegister(SecurityCtx securityCtx, Long id, String source) {
|
||||||
|
if ("admin".equals(source))
|
||||||
|
return permService.hasEditPerm(securityCtx, id)
|
||||||
|
.chain(c -> Mutiny.fetch(c.getInsc()))
|
||||||
|
.onItem().transformToMulti(Multi.createFrom()::iterable)
|
||||||
|
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
|
||||||
|
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences()))
|
||||||
|
.collect().asList();
|
||||||
|
if ("club".equals(source))
|
||||||
|
return Uni.createFrom().nullItem()
|
||||||
|
.invoke(Unchecked.consumer(__ -> {
|
||||||
|
if (!securityCtx.isClubAdmin())
|
||||||
|
throw new DForbiddenException();
|
||||||
|
}))
|
||||||
|
.chain(__ -> membreService.getByAccountId(securityCtx.getSubject()))
|
||||||
|
.chain(model -> registerRepository.list("competition.id = ?1 AND membre.club = ?2", id,
|
||||||
|
model.getClub()))
|
||||||
|
.onItem().transformToMulti(Multi.createFrom()::iterable)
|
||||||
|
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
|
||||||
|
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences()))
|
||||||
|
.collect().asList();
|
||||||
|
|
||||||
|
return membreService.getByAccountId(securityCtx.getSubject())
|
||||||
|
.chain(model -> registerRepository.find("competition.id = ?1 AND membre = ?2", id, model).firstResult()
|
||||||
|
.map(rm -> rm == null ? List.of() : List.of(SimpleRegisterComb.fromModel(rm, List.of()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<SimpleRegisterComb> addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data,
|
||||||
|
String source) {
|
||||||
|
if ("admin".equals(source))
|
||||||
|
return permService.hasEditPerm(securityCtx, id)
|
||||||
|
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
|
||||||
|
.call(combModel -> {
|
||||||
|
if (c.getBanMembre() == null)
|
||||||
|
c.setBanMembre(new ArrayList<>());
|
||||||
|
c.getBanMembre().remove(combModel.getId());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(c));
|
||||||
|
})
|
||||||
|
.chain(combModel -> updateRegister(data, c, combModel, true)))
|
||||||
|
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
|
||||||
|
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
|
||||||
|
if ("club".equals(source))
|
||||||
|
return repository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(cm -> {
|
||||||
|
if (!(cm.getRegisterMode() == RegisterMode.CLUB_ADMIN || cm.getRegisterMode() == RegisterMode.FREE)
|
||||||
|
|| !securityCtx.isClubAdmin())
|
||||||
|
throw new DForbiddenException();
|
||||||
|
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
|
||||||
|
throw new DBadRequestException("Inscription fermée");
|
||||||
|
}))
|
||||||
|
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
|
||||||
|
.invoke(Unchecked.consumer(model -> {
|
||||||
|
if (!securityCtx.isInClubGroup(model.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
if (c.getBanMembre().contains(model.getId()))
|
||||||
|
throw new DForbiddenException(
|
||||||
|
"Vous n'avez pas le droit d'inscrire ce membre (par décision de l'administrateur de la compétition)");
|
||||||
|
}))
|
||||||
|
.chain(combModel -> updateRegister(data, c, combModel, false)))
|
||||||
|
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
|
||||||
|
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
|
||||||
|
|
||||||
|
return repository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(cm -> {
|
||||||
|
if (cm.getRegisterMode() != RegisterMode.FREE)
|
||||||
|
throw new DForbiddenException();
|
||||||
|
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
|
||||||
|
throw new DBadRequestException("Inscription fermée");
|
||||||
|
}))
|
||||||
|
.chain(c -> membreService.getByAccountId(securityCtx.getSubject())
|
||||||
|
.invoke(Unchecked.consumer(model -> {
|
||||||
|
if (c.getBanMembre().contains(model.getId()))
|
||||||
|
throw new DForbiddenException(
|
||||||
|
"Vous n'avez pas le droit de vous inscrire (par décision de l'administrateur de la compétition)");
|
||||||
|
}))
|
||||||
|
.chain(combModel -> updateRegister(data, c, combModel, false)))
|
||||||
|
.map(r -> SimpleRegisterComb.fromModel(r, List.of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<RegisterModel> updateRegister(RegisterRequestData data, CompetitionModel c,
|
||||||
|
MembreModel combModel, boolean admin) {
|
||||||
|
return registerRepository.find("competition = ?1 AND membre = ?2", c, combModel).firstResult()
|
||||||
|
.onFailure().recoverWithNull()
|
||||||
|
.map(Unchecked.function(r -> {
|
||||||
|
if (r != null) {
|
||||||
|
if (!admin && r.isLockEdit())
|
||||||
|
throw new DForbiddenException(
|
||||||
|
"Modification bloquée par l'administrateur de la compétition");
|
||||||
|
r.setWeight(data.getWeight());
|
||||||
|
r.setOverCategory(data.getOverCategory());
|
||||||
|
r.setCategorie(
|
||||||
|
(combModel.getBirth_date() == null) ? combModel.getCategorie() :
|
||||||
|
Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
|
||||||
|
c.getDate()));
|
||||||
|
int days = Utils.getDaysBeforeCompetition(c.getDate());
|
||||||
|
if (days > -7)
|
||||||
|
r.setClub(combModel.getClub());
|
||||||
|
if (admin)
|
||||||
|
r.setLockEdit(data.isLockEdit());
|
||||||
|
} else {
|
||||||
|
r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(),
|
||||||
|
(combModel.getBirth_date() == null) ? combModel.getCategorie() :
|
||||||
|
Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
|
||||||
|
c.getDate()),
|
||||||
|
(combModel.getClub() == null) ? null : combModel.getClub());
|
||||||
|
if (admin)
|
||||||
|
r.setLockEdit(data.isLockEdit());
|
||||||
|
else
|
||||||
|
r.setLockEdit(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.getSystem() == CompetitionSystem.SAFCA) {
|
||||||
|
SReqRegister.sendIfNeed(serverCustom.clients,
|
||||||
|
new CompetitionData.SimpleRegister(r.getMembre().getId(),
|
||||||
|
r.getOverCategory(), r.getWeight(), r.getCategorie(),
|
||||||
|
(r.getClub() == null) ? null : r.getClub().getId()), c.getId());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}))
|
||||||
|
.chain(r -> Panache.withTransaction(() -> registerRepository.persist(r)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<MembreModel> findComb(Long licence, String fname, String lname) {
|
||||||
|
if (licence != null && licence != 0) {
|
||||||
|
return combRepository.find("licence = ?1", licence).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(combModel -> {
|
||||||
|
if (combModel == null)
|
||||||
|
throw new DForbiddenException("Licence " + licence + " non trouvé");
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if (fname == null || lname == null)
|
||||||
|
return Uni.createFrom().failure(new DBadRequestException("Nom et prénom requis"));
|
||||||
|
return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)",
|
||||||
|
lname,
|
||||||
|
fname).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(combModel -> {
|
||||||
|
if (combModel == null)
|
||||||
|
throw new DForbiddenException("Combattant " + fname + " " + lname + " non trouvé");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Void> removeRegisterComb(SecurityCtx securityCtx, Long id, Long combId, String source, boolean ban) {
|
||||||
|
if ("admin".equals(source))
|
||||||
|
return permService.hasEditPerm(securityCtx, id)
|
||||||
|
.chain(cm -> {
|
||||||
|
if (cm.getBanMembre() == null)
|
||||||
|
cm.setBanMembre(new ArrayList<>());
|
||||||
|
if (ban) {
|
||||||
|
if (!cm.getBanMembre().contains(combId))
|
||||||
|
cm.getBanMembre().add(combId);
|
||||||
|
} else {
|
||||||
|
cm.getBanMembre().remove(combId);
|
||||||
|
}
|
||||||
|
return Panache.withTransaction(() -> repository.persist(cm));
|
||||||
|
})
|
||||||
|
.chain(c -> deleteRegister(combId, c, true));
|
||||||
|
if ("club".equals(source))
|
||||||
|
return repository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(cm -> {
|
||||||
|
if (!(cm.getRegisterMode() == RegisterMode.CLUB_ADMIN || cm.getRegisterMode() == RegisterMode.FREE)
|
||||||
|
|| !securityCtx.isClubAdmin())
|
||||||
|
throw new DForbiddenException();
|
||||||
|
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
|
||||||
|
throw new DBadRequestException("Inscription fermée");
|
||||||
|
}))
|
||||||
|
.call(cm -> membreService.getByAccountId(securityCtx.getSubject())
|
||||||
|
.invoke(Unchecked.consumer(model -> {
|
||||||
|
if (!securityCtx.isInClubGroup(model.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
})))
|
||||||
|
.chain(c -> deleteRegister(combId, c, false));
|
||||||
|
|
||||||
|
return repository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(cm -> {
|
||||||
|
if (cm.getRegisterMode() != RegisterMode.FREE)
|
||||||
|
throw new DForbiddenException();
|
||||||
|
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
|
||||||
|
throw new DBadRequestException("Inscription fermée");
|
||||||
|
}))
|
||||||
|
.chain(c -> deleteRegister(combId, c, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<Void> deleteRegister(Long combId, CompetitionModel c, boolean admin) {
|
||||||
|
return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult()
|
||||||
|
.onFailure().transform(t -> new DBadRequestException("Combattant non inscrit"))
|
||||||
|
.call(Unchecked.function(registerModel -> {
|
||||||
|
if (!admin && registerModel.isLockEdit())
|
||||||
|
throw new DForbiddenException("Modification bloquée par l'administrateur de la compétition");
|
||||||
|
return Panache.withTransaction(() -> registerRepository.delete(registerModel));
|
||||||
|
}))
|
||||||
|
.replaceWithVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> delete(SecurityCtx securityCtx, Long id) {
|
||||||
|
return repository.findById(id).invoke(Unchecked.consumer(c -> {
|
||||||
|
if (!(securityCtx.getSubject().equals(c.getOwner()) || securityCtx.roleHas("federation_admin")))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
}))
|
||||||
|
.call(competitionModel -> categoryRepository.list("compet = ?1", competitionModel)
|
||||||
|
.call(pouleModels -> pouleModels.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.join().all(pouleModels.stream()
|
||||||
|
.map(pouleModel -> Panache.withTransaction(
|
||||||
|
() -> matchRepository.delete("poule = ?1", pouleModel.getId())))
|
||||||
|
.toList())
|
||||||
|
.andCollectFailures()))
|
||||||
|
.call(competitionModel -> Panache.withTransaction(
|
||||||
|
() -> categoryRepository.delete("compet = ?1", competitionModel)))
|
||||||
|
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
|
||||||
|
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
|
||||||
|
.call(__ -> cache.invalidate(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<SimpleCompetData> getSafcaData(SecurityCtx securityCtx, Long id) {
|
||||||
|
return permService.getSafcaConfig(id)
|
||||||
|
.call(Unchecked.function(o -> {
|
||||||
|
if (!securityCtx.getSubject().equals(o.owner())
|
||||||
|
&& !securityCtx.roleHas("federation_admin")
|
||||||
|
&& !securityCtx.roleHas("safca_super_admin")
|
||||||
|
&& !o.admin().contains(UUID.fromString(securityCtx.getSubject()))
|
||||||
|
&& !o.table().contains(UUID.fromString(securityCtx.getSubject())))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}))
|
||||||
|
.chain(simpleCompet -> {
|
||||||
|
SimpleCompetData data = SimpleCompetData.fromModel(simpleCompet);
|
||||||
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
data.setAdmin(simpleCompet.admin().stream().map(uuid -> keycloakService.getUser(uuid))
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(user -> user.get().getUsername())
|
||||||
|
.toList());
|
||||||
|
data.setTable(simpleCompet.table().stream().map(uuid -> keycloakService.getUser(uuid))
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(user -> user.get().getUsername())
|
||||||
|
.toList());
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> setSafcaData(SecurityCtx securityCtx, SimpleCompetData data) {
|
||||||
|
return permService.hasEditPerm(securityCtx, data.getId())
|
||||||
|
.chain(__ -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
ArrayList<UUID> admin = new ArrayList<>();
|
||||||
|
ArrayList<UUID> table = new ArrayList<>();
|
||||||
|
for (String username : data.getAdmin()) {
|
||||||
|
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||||
|
if (opt.isEmpty())
|
||||||
|
throw new DBadRequestException("User " + username + " not found");
|
||||||
|
admin.add(UUID.fromString(opt.get().getId()));
|
||||||
|
}
|
||||||
|
for (String username : data.getTable()) {
|
||||||
|
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||||
|
if (opt.isEmpty())
|
||||||
|
throw new DBadRequestException("User " + username + " not found");
|
||||||
|
table.add(UUID.fromString(opt.get().getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleCompet(data.getId(), "", data.isShow_blason(),
|
||||||
|
data.isShow_flag(), admin, table);
|
||||||
|
}))
|
||||||
|
.invoke(simpleCompet -> SReqCompet.sendUpdate(serverCustom.clients, simpleCompet))
|
||||||
|
.call(simpleCompet -> permService.getSafcaConfig(data.getId())
|
||||||
|
.call(c -> {
|
||||||
|
List<Uni<Void>> list = Stream.concat(
|
||||||
|
Stream.concat(
|
||||||
|
c.admin().stream().filter(uuid -> !simpleCompet.admin().contains(uuid)),
|
||||||
|
simpleCompet.admin().stream().filter(uuid -> !c.admin().contains(uuid))),
|
||||||
|
Stream.concat(
|
||||||
|
c.table().stream().filter(uuid -> !simpleCompet.table().contains(uuid)),
|
||||||
|
simpleCompet.table().stream().filter(uuid -> !c.table().contains(uuid)))
|
||||||
|
).map(uuid -> cacheAccess.invalidate(uuid.toString())).toList();
|
||||||
|
|
||||||
|
if (list.isEmpty())
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
return Uni.join().all(list).andCollectFailures();
|
||||||
|
}))
|
||||||
|
.call(__ -> cache.invalidate(data.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> unregisterHelloAsso(NotificationData data) {
|
||||||
|
if (!data.getState().equals("Refunded"))
|
||||||
|
return Uni.createFrom().item(Response.ok().build());
|
||||||
|
|
||||||
|
return helloAssoRepository.list("orderId = ?1", data.getOrder().getId())
|
||||||
|
.chain(regs -> {
|
||||||
|
Uni<?> uni = Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
for (HelloAssoRegisterModel reg : regs) {
|
||||||
|
if (reg.getCompetition().getRegisterMode() != RegisterMode.HELLOASSO)
|
||||||
|
continue;
|
||||||
|
if (!data.getOrder().getOrganizationSlug().equalsIgnoreCase(reg.getCompetition().getData1()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uni = uni.call(__ -> Panache.withTransaction(
|
||||||
|
() -> registerRepository.delete("competition = ?1 AND membre = ?2",
|
||||||
|
reg.getCompetition(), reg.getMembre())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return uni;
|
||||||
|
})
|
||||||
|
.onFailure().invoke(Throwable::printStackTrace)
|
||||||
|
.map(__ -> Response.ok().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> registerHelloAsso(NotificationData data) {
|
||||||
|
String organizationSlug = data.getOrganizationSlug();
|
||||||
|
String formSlug = data.getFormSlug();
|
||||||
|
RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false);
|
||||||
|
|
||||||
|
return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult()
|
||||||
|
.onFailure().recoverWithNull()
|
||||||
|
.chain(cm -> {
|
||||||
|
Uni<?> uni = Uni.createFrom().nullItem();
|
||||||
|
if (cm == null || cm.getRegisterMode() != RegisterMode.HELLOASSO)
|
||||||
|
return uni;
|
||||||
|
|
||||||
|
List<String> place = List.of(cm.getData3().toLowerCase().split(";"));
|
||||||
|
List<String> fail = new ArrayList<>();
|
||||||
|
|
||||||
|
for (NotificationData.Item item : data.getItems()) {
|
||||||
|
if (!place.contains(item.getName().toLowerCase()))
|
||||||
|
continue;
|
||||||
|
if (item.getCustomFields() == null || item.getCustomFields().isEmpty()) {
|
||||||
|
fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(),
|
||||||
|
item.getUser().getFirstName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Long> optional = item.getCustomFields().stream()
|
||||||
|
.filter(cf -> cf.getName().equalsIgnoreCase("Numéro de licence")).findAny().map(
|
||||||
|
NotificationData.CustomField::getAnswer).map(Long::valueOf);
|
||||||
|
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
uni = uni.call(__ -> membreService.getByLicence(optional.get())
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null)
|
||||||
|
throw new NotFoundException();
|
||||||
|
}))
|
||||||
|
.call(m -> Panache.withTransaction(() ->
|
||||||
|
helloAssoRepository.persist(
|
||||||
|
new HelloAssoRegisterModel(cm, m, data.getId()))))
|
||||||
|
.chain(m -> updateRegister(req, cm, m, true)))
|
||||||
|
.onFailure().recoverWithItem(throwable -> {
|
||||||
|
fail.add("%s %s - licence n°%d".formatted(item.getUser().getLastName(),
|
||||||
|
item.getUser().getFirstName(), optional.get()));
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.replaceWithVoid();
|
||||||
|
} else {
|
||||||
|
fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(),
|
||||||
|
item.getUser().getFirstName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uni.call(__ -> fail.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
reactiveMailer.send(
|
||||||
|
Mail.withText(cm.getData4(),
|
||||||
|
"FFSAF - Compétition - Erreur HelloAsso",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Une erreur a été rencontrée lors de l'enregistrement d'une inscription à votre compétition %s pour les combattants suivants:
|
||||||
|
%s
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'intranet de la FFSAF
|
||||||
|
""", cm.getName(), String.join("\r\n", fail))
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
|
||||||
|
).onFailure().invoke(e -> LOGGER.error("Fail to send email", e)));
|
||||||
|
})
|
||||||
|
.onFailure().invoke(Throwable::printStackTrace)
|
||||||
|
.map(__ -> Response.ok().build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.rest.client.HelloAssoAuthClient;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.TokenResponse;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class HelloAssoTokenService {
|
||||||
|
private static final Logger LOG = Logger.getLogger(HelloAssoTokenService.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
HelloAssoAuthClient authClient;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "helloasso.client-id")
|
||||||
|
String clientId;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "helloasso.client-secret")
|
||||||
|
String clientSecret;
|
||||||
|
|
||||||
|
private TokenResponse currentToken; // Stockage en mémoire (pour un seul pod)
|
||||||
|
|
||||||
|
// Récupère un token valide (en le rafraîchissant si nécessaire)
|
||||||
|
public Uni<String> getValidAccessToken() {
|
||||||
|
if (currentToken == null || currentToken.isExpired()) {
|
||||||
|
return fetchNewToken(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
return Uni.createFrom().item(currentToken.accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupère un nouveau token (via client_credentials ou refresh_token)
|
||||||
|
private Uni<String> fetchNewToken(String clientId, String clientSecret) {
|
||||||
|
if (currentToken != null && currentToken.refreshToken != null) {
|
||||||
|
// On utilise le refresh_token si disponible
|
||||||
|
return authClient.refreshToken("refresh_token", clientId, currentToken.refreshToken)
|
||||||
|
.onItem().invoke(token -> {
|
||||||
|
LOG.info("Token rafraîchi avec succès");
|
||||||
|
currentToken = token;
|
||||||
|
})
|
||||||
|
.onFailure().recoverWithItem(e -> {
|
||||||
|
LOG.warn("Échec du rafraîchissement, utilisation des credentials", e);
|
||||||
|
return null; // Force l'utilisation des credentials
|
||||||
|
})
|
||||||
|
.flatMap(token -> token != null ?
|
||||||
|
Uni.createFrom().item(token.accessToken) :
|
||||||
|
getTokenWithCredentials(clientId, clientSecret)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return getTokenWithCredentials(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupère un token avec client_id/client_secret
|
||||||
|
private Uni<String> getTokenWithCredentials(String clientId, String clientSecret) {
|
||||||
|
return authClient.getToken("client_credentials", clientId, clientSecret)
|
||||||
|
.onItem().invoke(token -> {
|
||||||
|
LOG.info("Nouveau token obtenu");
|
||||||
|
currentToken = token;
|
||||||
|
})
|
||||||
|
.onFailure().invoke(e -> LOG.error("Erreur lors de l'obtention du token", e))
|
||||||
|
.map(token -> token.accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,10 @@ package fr.titionfire.ffsaf.domain.service;
|
|||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
import fr.titionfire.ffsaf.utils.*;
|
import fr.titionfire.ffsaf.utils.*;
|
||||||
|
import io.quarkus.mailer.Mail;
|
||||||
|
import io.quarkus.mailer.reactive.ReactiveMailer;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
@ -23,6 +26,7 @@ import java.text.Normalizer;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class KeycloakService {
|
public class KeycloakService {
|
||||||
@ -40,6 +44,12 @@ public class KeycloakService {
|
|||||||
@ConfigProperty(name = "keycloak.realm")
|
@ConfigProperty(name = "keycloak.realm")
|
||||||
String realm;
|
String realm;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "email.enabled")
|
||||||
|
boolean enabled_email;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ReactiveMailer reactiveMailer;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vertx vertx;
|
Vertx vertx;
|
||||||
|
|
||||||
@ -49,68 +59,134 @@ public class KeycloakService {
|
|||||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
GroupRepresentation clubGroup =
|
GroupRepresentation clubGroup =
|
||||||
keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
||||||
.findAny().orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
.findAny()
|
||||||
|
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||||
|
|
||||||
GroupRepresentation groupRepresentation = new GroupRepresentation();
|
GroupRepresentation groupRepresentation = new GroupRepresentation();
|
||||||
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
||||||
|
|
||||||
try (Response response =
|
try (Response response =
|
||||||
keycloak.realm(realm).groups().group(clubGroup.getId()).subGroup(groupRepresentation)) {
|
keycloak.realm(realm).groups().group(clubGroup.getId()).subGroup(groupRepresentation)) {
|
||||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo()
|
||||||
throw new KeycloakException("Fail to set group parent for club: %s (reason=%s)".formatted(club.getName(),
|
.equals(Response.Status.CONFLICT))
|
||||||
response.getStatusInfo().getReasonPhrase()));
|
throw new KeycloakException(
|
||||||
|
"Fail to set group parent for club: %s (reason=%s)".formatted(club.getName(),
|
||||||
|
response.getStatusInfo().getReasonPhrase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
|
return keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
|
||||||
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny().map(GroupRepresentation::getId)
|
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny()
|
||||||
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s*".formatted(club.getId() + "-")));
|
.map(GroupRepresentation::getId)
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new KeycloakException("Fail to fetch group %s*".formatted(club.getId() + "-")));
|
||||||
}
|
}
|
||||||
).call(id -> clubService.setClubId(club.getId(), id));
|
).call(id -> clubService.setClubId(club.getId(), id));
|
||||||
}
|
}
|
||||||
return Uni.createFrom().item(club::getClubId);
|
return Uni.createFrom().item(club::getClubId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<String> updateGroupFromClub(ClubModel club) {
|
||||||
|
if (club.getClubId() == null) {
|
||||||
|
return getGroupFromClub(club);
|
||||||
|
} else {
|
||||||
|
LOGGER.infof("Updating name of club group %d-%s...", club.getId(), club.getName());
|
||||||
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
GroupRepresentation clubGroup =
|
||||||
|
keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
||||||
|
.findAny()
|
||||||
|
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||||
|
|
||||||
|
keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
|
||||||
|
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny()
|
||||||
|
.ifPresent(groupRepresentation -> {
|
||||||
|
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
||||||
|
keycloak.realm(realm).groups().group(groupRepresentation.getId())
|
||||||
|
.update(groupRepresentation);
|
||||||
|
});
|
||||||
|
|
||||||
|
return club.getClubId();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Uni<String> getUserFromMember(MembreModel membreModel) {
|
public Uni<String> getUserFromMember(MembreModel membreModel) {
|
||||||
if (membreModel.getUserId() == null) {
|
if (membreModel.getUserId() == null) {
|
||||||
return Uni.createFrom().failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId()));
|
return Uni.createFrom()
|
||||||
|
.failure(new DInternalError("No keycloak user linked to the user id=" + membreModel.getId()));
|
||||||
}
|
}
|
||||||
return Uni.createFrom().item(membreModel::getUserId);
|
return Uni.createFrom().item(membreModel::getUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<String> setClubGroupMembre(MembreModel membreModel, ClubModel club) {
|
public Uni<String> setClubGroupMembre(MembreModel membreModel, ClubModel club) {
|
||||||
return getGroupFromClub(club).chain(
|
if (club == null)
|
||||||
clubId -> getUserFromMember(membreModel).chain(userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
return getUserFromMember(membreModel).chain(
|
||||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
user.groups().stream().filter(g -> g.getPath().startsWith("/club")).forEach(g -> user.leaveGroup(g.getId()));
|
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||||
user.joinGroup(clubId);
|
user.groups().stream().filter(g -> g.getPath().startsWith("/club"))
|
||||||
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId,
|
.forEach(g -> user.leaveGroup(g.getId()));
|
||||||
user.toRepresentation().getUsername());
|
return "OK";
|
||||||
return "OK";
|
}));
|
||||||
})));
|
else
|
||||||
|
return getGroupFromClub(club).chain(
|
||||||
|
clubId -> getUserFromMember(membreModel).chain(
|
||||||
|
userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||||
|
user.groups().stream().filter(g -> g.getPath().startsWith("/club"))
|
||||||
|
.forEach(g -> user.leaveGroup(g.getId()));
|
||||||
|
user.joinGroup(clubId);
|
||||||
|
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId,
|
||||||
|
user.toRepresentation().getUsername());
|
||||||
|
return "OK";
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<?> setEmail(String userId, String email) {
|
public Uni<?> setEmail(String userId, String email) {
|
||||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||||
UserRepresentation user2 = user.toRepresentation();
|
UserRepresentation user2 = user.toRepresentation();
|
||||||
|
String oldEmail = user2.getEmail();
|
||||||
if (email.equals(user2.getEmail()))
|
if (email.equals(user2.getEmail()))
|
||||||
return "";
|
return null;
|
||||||
user2.setEmail(email);
|
user2.setEmail(email);
|
||||||
user2.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name()));
|
user2.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name()));
|
||||||
user.update(user2);
|
user.update(user2);
|
||||||
return "";
|
if (enabled_email)
|
||||||
});
|
user.sendVerifyEmail();
|
||||||
|
return oldEmail;
|
||||||
|
}).call(oldEmail -> oldEmail == null || !enabled_email ? Uni.createFrom().item("") :
|
||||||
|
reactiveMailer.send(
|
||||||
|
Mail.withText(oldEmail,
|
||||||
|
"FFSAF - Changement de votre adresse email",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF,
|
||||||
|
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.
|
||||||
|
|
||||||
|
Ancienne adresse email : %s
|
||||||
|
Nouvelle adresse email : %s
|
||||||
|
|
||||||
|
Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr.
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe de la FFSAF
|
||||||
|
""", oldEmail, email)
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
|
||||||
|
).onFailure().invoke(e -> LOGGER.error("Fail to send email", e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<?> setAutoRoleMembre(String id, RoleAsso role, GradeArbitrage gradeArbitrage) {
|
public Uni<?> setAutoRoleMembre(String id, RoleAsso role, GradeArbitrage gradeArbitrage) {
|
||||||
List<String> toRemove = new ArrayList<>(List.of("club_president", "club_tresorier", "club_secretaire",
|
List<String> toRemove = new ArrayList<>(List.of("club_president", "club_tresorier", "club_secretaire",
|
||||||
"asseseur", "arbitre"));
|
"club_respo_intra", "asseseur", "arbitre"));
|
||||||
List<String> toAdd = new ArrayList<>();
|
List<String> toAdd = new ArrayList<>();
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case PRESIDENT -> toAdd.add("club_president");
|
case PRESIDENT, VPRESIDENT -> toAdd.add("club_president");
|
||||||
case TRESORIER -> toAdd.add("club_tresorier");
|
case TRESORIER, VTRESORIER -> toAdd.add("club_tresorier");
|
||||||
case SECRETAIRE -> toAdd.add("club_secretaire");
|
case SECRETAIRE, VSECRETAIRE -> toAdd.add("club_secretaire");
|
||||||
|
case MEMBREBUREAU -> toAdd.add("club_respo_intra");
|
||||||
}
|
}
|
||||||
switch (gradeArbitrage) {
|
switch (gradeArbitrage) {
|
||||||
case ARBITRE -> toAdd.addAll(List.of("asseseur", "arbitre"));
|
case ARBITRE -> toAdd.addAll(List.of("asseseur", "arbitre"));
|
||||||
@ -132,7 +208,8 @@ public class KeycloakService {
|
|||||||
|
|
||||||
public Uni<List<String>> fetchRole(String id) {
|
public Uni<List<String>> fetchRole(String id) {
|
||||||
return vertx.getOrCreateContext().executeBlocking(() ->
|
return vertx.getOrCreateContext().executeBlocking(() ->
|
||||||
keycloak.realm(realm).users().get(id).roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList());
|
keycloak.realm(realm).users().get(id).roles().realmLevel().listEffective().stream()
|
||||||
|
.map(RoleRepresentation::getName).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<?> updateRole(String id, List<String> toAdd, List<String> toRemove) {
|
public Uni<?> updateRole(String id, List<String> toAdd, List<String> toRemove) {
|
||||||
@ -147,17 +224,16 @@ public class KeycloakService {
|
|||||||
|
|
||||||
public Uni<String> initCompte(long id) {
|
public Uni<String> initCompte(long id) {
|
||||||
return membreService.getById(id).invoke(Unchecked.consumer(membreModel -> {
|
return membreService.getById(id).invoke(Unchecked.consumer(membreModel -> {
|
||||||
if (membreModel.getUserId() != null)
|
if (membreModel.getUserId() != null)
|
||||||
throw new KeycloakException("User already linked to the user id=" + id);
|
throw new KeycloakException("User already linked to the user id=" + id);
|
||||||
if (membreModel.getEmail() == null)
|
if (membreModel.getEmail() == null)
|
||||||
throw new KeycloakException("User email is null");
|
throw new KeycloakException("User email is null");
|
||||||
if (membreModel.getFname() == null || membreModel.getLname() == null)
|
if (membreModel.getFname() == null || membreModel.getLname() == null)
|
||||||
throw new KeycloakException("User name is null");
|
throw new KeycloakException("User name is null");
|
||||||
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
|
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
|
||||||
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
|
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
|
||||||
return membreService.setUserId(membreModel.getId(), user.getId());
|
return membreService.setUserId(membreModel.getId(), user.getId()).map(__ -> user.getId());
|
||||||
}))
|
}));
|
||||||
.map(__ -> "OK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uni<UserRepresentation> creatUser(MembreModel membreModel) {
|
private Uni<UserRepresentation> creatUser(MembreModel membreModel) {
|
||||||
@ -180,22 +256,44 @@ public class KeycloakService {
|
|||||||
user.setEmail(membreModel.getEmail());
|
user.setEmail(membreModel.getEmail());
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
|
|
||||||
user.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
|
||||||
RequiredAction.UPDATE_PASSWORD.name()));
|
|
||||||
|
|
||||||
try (Response response = keycloak.realm(realm).users().create(user)) {
|
try (Response response = keycloak.realm(realm).users().create(user)) {
|
||||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo()
|
||||||
|
.equals(Response.Status.CONFLICT))
|
||||||
throw new KeycloakException("Fail to creat user %s (reason=%s)".formatted(login,
|
throw new KeycloakException("Fail to creat user %s (reason=%s)".formatted(login,
|
||||||
response.getStatusInfo().getReasonPhrase()));
|
response.getStatusInfo().getReasonPhrase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String finalLogin = login;
|
String finalLogin = login;
|
||||||
return getUser(login).orElseThrow(() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin)));
|
return getUser(login).orElseThrow(
|
||||||
|
() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin)));
|
||||||
})
|
})
|
||||||
.invoke(user -> keycloak.realm(realm).users().get(user.getId())
|
|
||||||
.executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
|
||||||
RequiredAction.UPDATE_PASSWORD.name())))
|
|
||||||
.invoke(user -> membreModel.setUserId(user.getId()))
|
.invoke(user -> membreModel.setUserId(user.getId()))
|
||||||
|
.call(user -> updateRole(user.getId(), List.of("safca_user"), List.of()))
|
||||||
|
.call(user -> enabled_email ? reactiveMailer.send(
|
||||||
|
Mail.withText(user.getEmail(),
|
||||||
|
"FFSAF - Creation de votre compte sur l'intranet",
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Suite à votre première inscription %sà la Fédération Française de Soft Armored Fighting (FFSAF), votre compte intranet a été créé.
|
||||||
|
Ce compte vous permettra de consulter vos informations et, dans un futur proche, de vous inscrire aux compétitions ainsi que d'en consulter les résultats.
|
||||||
|
|
||||||
|
L'intranet est accessible à l'adresse suivante : https://intra.ffsaf.fr
|
||||||
|
Votre nom d'utilisateur est : %s
|
||||||
|
|
||||||
|
Pour définir votre mot de passe, rendez-vous sur l'intranet > "Connexion" > "Mot de passe oublié ?"
|
||||||
|
|
||||||
|
Si vous n'avez pas demandé cette inscription, veuillez contacter le support à l'adresse support@ffsaf.fr.
|
||||||
|
(Pas de panique, nous ne vous enverrons pas de message autre que ce concernant votre compte)
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe de la FFSAF
|
||||||
|
""",
|
||||||
|
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
|
||||||
|
.getName() + ") " : "", user.getUsername())
|
||||||
|
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
|
||||||
|
) : Uni.createFrom().nullItem())
|
||||||
.call(user -> membreService.setUserId(membreModel.getId(), user.getId()))
|
.call(user -> membreService.setUserId(membreModel.getId(), user.getId()))
|
||||||
.call(user -> setClubGroupMembre(membreModel, membreModel.getClub()));
|
.call(user -> setClubGroupMembre(membreModel, membreModel.getClub()));
|
||||||
}
|
}
|
||||||
@ -216,7 +314,31 @@ public class KeycloakService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<UserRepresentation> getUser(String username) {
|
public Uni<?> removeClubGroup(String clubId) {
|
||||||
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
keycloak.realm(realm).groups().group(clubId).remove();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> clearUser(String userId) {
|
||||||
|
List<String> toRemove = new ArrayList<>(
|
||||||
|
List.of("club_president", "club_tresorier", "club_secretaire", "club_respo_intra"));
|
||||||
|
|
||||||
|
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
|
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||||
|
|
||||||
|
RoleScopeResource resource = user.roles().realmLevel();
|
||||||
|
List<RoleRepresentation> roles = keycloak.realm(realm).roles().list();
|
||||||
|
resource.remove(roles.stream().filter(r -> toRemove.contains(r.getName())).toList());
|
||||||
|
|
||||||
|
user.groups().stream().filter(g -> g.getPath().startsWith("/club"))
|
||||||
|
.forEach(g -> user.leaveGroup(g.getId()));
|
||||||
|
return "OK";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<UserRepresentation> getUser(String username) {
|
||||||
List<UserRepresentation> users = keycloak.realm(realm).users().searchByUsername(username, true);
|
List<UserRepresentation> users = keycloak.realm(realm).users().searchByUsername(username, true);
|
||||||
|
|
||||||
if (users.isEmpty())
|
if (users.isEmpty())
|
||||||
@ -225,8 +347,19 @@ public class KeycloakService {
|
|||||||
return Optional.of(users.get(0));
|
return Optional.of(users.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Optional<UserRepresentation> getUser(UUID userId) {
|
||||||
|
UserResource user = keycloak.realm(realm).users().get(userId.toString());
|
||||||
|
if (user == null)
|
||||||
|
return Optional.empty();
|
||||||
|
else
|
||||||
|
return Optional.of(user.toRepresentation());
|
||||||
|
}
|
||||||
|
|
||||||
private String makeLogin(MembreModel model) {
|
private String makeLogin(MembreModel model) {
|
||||||
return Normalizer.normalize((model.getFname().toLowerCase() + "." + model.getLname().toLowerCase()).replace(' ', '_'), Normalizer.Form.NFD)
|
return Normalizer.normalize(
|
||||||
|
(model.getFname().toLowerCase() + "." + model.getLname().toLowerCase()).replace(' ', '_'),
|
||||||
|
Normalizer.Form.NFD)
|
||||||
.replaceAll("\\p{M}", "");
|
.replaceAll("\\p{M}", "");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
package fr.titionfire.ffsaf.domain.service;
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.LicenceModel;
|
import fr.titionfire.ffsaf.data.model.LicenceModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||||
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.SequenceRepository;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||||
import fr.titionfire.ffsaf.utils.Utils;
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
@ -12,16 +17,17 @@ import io.smallrye.mutiny.Uni;
|
|||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.BadRequestException;
|
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
|
||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class LicenceService {
|
public class LicenceService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(LicenceService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
LicenceRepository repository;
|
LicenceRepository repository;
|
||||||
@ -29,60 +35,155 @@ public class LicenceService {
|
|||||||
@Inject
|
@Inject
|
||||||
CombRepository combRepository;
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SequenceRepository sequenceRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeycloakService keycloakService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerService ls;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CheckoutService checkoutService;
|
||||||
|
|
||||||
public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) {
|
public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) {
|
||||||
return combRepository.findById(id).invoke(checkPerm).chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
|
return combRepository.findById(id).invoke(checkPerm)
|
||||||
|
.chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<List<LicenceModel>> getCurrentSaisonLicence(JsonWebToken idToken) {
|
public Uni<List<LicenceModel>> getCurrentSaisonLicence(SecurityCtx securityCtx) {
|
||||||
if (idToken == null)
|
if (securityCtx == null || securityCtx.getSubject() == null)
|
||||||
return repository.find("saison = ?1", Utils.getSaison()).list();
|
return repository.find("saison = ?1", Utils.getSaison()).list();
|
||||||
|
|
||||||
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().map(MembreModel::getClub)
|
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult().map(MembreModel::getClub)
|
||||||
.chain(clubModel -> combRepository.find("club = ?1", clubModel).list())
|
.chain(clubModel -> combRepository.find("club = ?1", clubModel).list())
|
||||||
.chain(membres -> repository.find("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres).list());
|
.chain(membres -> repository.find("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres).list());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<?> valideLicences(List<Long> ids) {
|
||||||
|
Uni<String> uni = Uni.createFrom().nullItem();
|
||||||
|
|
||||||
|
for (Long id : ids) {
|
||||||
|
uni = uni.chain(__ -> repository.find("membre.id = ?1 AND saison = ?2", id, Utils.getSaison()).firstResult()
|
||||||
|
.chain(model -> {
|
||||||
|
if (!model.isValidate())
|
||||||
|
ls.logUpdate("validation de la licence", model);
|
||||||
|
return validateLicences(model);
|
||||||
|
}))
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
|
return uni.call(__ -> ls.append());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Uni<LicenceModel> validateLicences(LicenceModel model) {
|
||||||
|
model.setValidate(true);
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model)
|
||||||
|
.call(m -> Mutiny.fetch(m.getMembre())
|
||||||
|
.call(genLicenceNumberAndAccountIfNeed())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
public Uni<LicenceModel> setLicence(long id, LicenceForm form) {
|
public Uni<LicenceModel> setLicence(long id, LicenceForm form) {
|
||||||
if (form.getId() == -1) {
|
if (form.getId() == -1) {
|
||||||
return combRepository.findById(id).chain(combRepository -> {
|
return combRepository.findById(id).chain(membreModel -> {
|
||||||
LicenceModel model = new LicenceModel();
|
LicenceModel model = new LicenceModel();
|
||||||
model.setMembre(combRepository);
|
model.setMembre(membreModel);
|
||||||
|
model.setClub_id((membreModel.getClub() == null) ? null : membreModel.getClub().getId());
|
||||||
model.setSaison(form.getSaison());
|
model.setSaison(form.getSaison());
|
||||||
model.setCertificate(form.isCertificate());
|
model.setCertificate(form.getCertificate());
|
||||||
model.setValidate(form.isValidate());
|
model.setValidate(form.isValidate());
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
model.setPay(form.isPay());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model)
|
||||||
|
.call(m -> m.isValidate() ? Uni.createFrom().item(membreModel)
|
||||||
|
.call(genLicenceNumberAndAccountIfNeed())
|
||||||
|
: Uni.createFrom().nullItem()
|
||||||
|
))
|
||||||
|
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
|
||||||
|
licenceModel));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return repository.findById(form.getId()).chain(model -> {
|
return repository.findById(form.getId()).chain(model -> {
|
||||||
model.setCertificate(form.isCertificate());
|
ls.logChange("Certificate", model.getCertificate(), form.getCertificate(), model);
|
||||||
|
ls.logChange("Validate", model.isValidate(), form.isValidate(), model);
|
||||||
|
ls.logChange("Pay", model.isPay(), form.isPay(), model);
|
||||||
|
model.setCertificate(form.getCertificate());
|
||||||
model.setValidate(form.isValidate());
|
model.setValidate(form.isValidate());
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
model.setPay(form.isPay());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model)
|
||||||
|
.call(m -> m.isValidate() ? Mutiny.fetch(m.getMembre())
|
||||||
|
.call(genLicenceNumberAndAccountIfNeed())
|
||||||
|
: Uni.createFrom().nullItem()
|
||||||
|
))
|
||||||
|
.call(__ -> ls.append());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Function<MembreModel, Uni<?>> genLicenceNumberAndAccountIfNeed() {
|
||||||
|
return membreModel -> ((membreModel.getLicence() <= 0) ?
|
||||||
|
sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||||
|
.invoke(i -> membreModel.setLicence(Math.toIntExact(i)))
|
||||||
|
.chain(() -> combRepository.persist(membreModel))
|
||||||
|
: Uni.createFrom().nullItem())
|
||||||
|
.call(__ -> (membreModel.getUserId() == null) ?
|
||||||
|
keycloakService.initCompte(membreModel.getId()).onFailure()
|
||||||
|
.invoke(t -> LOGGER.infof("Failed to init account: %s", t.getMessage())).onFailure()
|
||||||
|
.recoverWithNull()
|
||||||
|
: Uni.createFrom().nullItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> payLicences(List<Long> ids, Consumer<MembreModel> checkPerm, SecurityCtx securityCtx) {
|
||||||
|
return repository.list("membre.id IN ?1 AND saison = ?2 AND pay = FALSE", ids, Utils.getSaison())
|
||||||
|
.invoke(Unchecked.consumer(models -> {
|
||||||
|
if (models.size() != ids.size())
|
||||||
|
throw new DBadRequestException("Erreur lors de la sélection des membres");
|
||||||
|
}))
|
||||||
|
.call(models -> {
|
||||||
|
Uni<?> uni = Uni.createFrom().nullItem();
|
||||||
|
for (LicenceModel model : models)
|
||||||
|
uni = uni.chain(__ -> Mutiny.fetch(model.getMembre()).invoke(checkPerm));
|
||||||
|
return uni;
|
||||||
|
})
|
||||||
|
.chain(models -> checkoutService.create(models.stream().map(LicenceModel::getId).toList(),
|
||||||
|
securityCtx));
|
||||||
|
}
|
||||||
|
|
||||||
public Uni<?> deleteLicence(long id) {
|
public Uni<?> deleteLicence(long id) {
|
||||||
return Panache.withTransaction(() -> repository.deleteById(id));
|
return repository.findById(id)
|
||||||
|
.call(__ -> checkoutService.canDeleteLicence(id)
|
||||||
|
.invoke(Unchecked.consumer(b -> {
|
||||||
|
if (!b) throw new DBadRequestException(
|
||||||
|
"Impossible de supprimer une licence pour laquelle un paiement est en cours");
|
||||||
|
})))
|
||||||
|
.call(model -> ls.logADelete(model))
|
||||||
|
.chain(model -> repository.delete(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<LicenceModel> askLicence(long id, LicenceForm form, Consumer<MembreModel> checkPerm) {
|
public Uni<LicenceModel> askLicence(long id, LicenceForm form, Consumer<MembreModel> checkPerm) {
|
||||||
return combRepository.findById(id).invoke(checkPerm).chain(membreModel -> {
|
return combRepository.findById(id).invoke(checkPerm).chain(membreModel -> {
|
||||||
if (form.getId() == -1) {
|
if (form.getId() == -1) {
|
||||||
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count().invoke(Unchecked.consumer(count -> {
|
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count()
|
||||||
if (count > 0)
|
.invoke(Unchecked.consumer(count -> {
|
||||||
throw new BadRequestException();
|
if (count > 0)
|
||||||
})).chain(__ -> combRepository.findById(id).chain(combRepository -> {
|
throw new DBadRequestException("Licence déjà demandée");
|
||||||
LicenceModel model = new LicenceModel();
|
})).chain(__ -> combRepository.findById(id).chain(membreModel2 -> {
|
||||||
model.setMembre(combRepository);
|
LicenceModel model = new LicenceModel();
|
||||||
model.setSaison(Utils.getSaison());
|
model.setClub_id((membreModel2.getClub() == null) ? null : membreModel2.getClub().getId());
|
||||||
model.setCertificate(form.isCertificate());
|
model.setMembre(membreModel2);
|
||||||
model.setValidate(false);
|
model.setSaison(Utils.getSaison());
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
model.setCertificate(form.getCertificate());
|
||||||
}));
|
model.setValidate(false);
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
|
}))
|
||||||
|
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
|
||||||
|
licenceModel));
|
||||||
} else {
|
} else {
|
||||||
return repository.findById(form.getId()).chain(model -> {
|
return repository.findById(form.getId()).chain(model -> {
|
||||||
model.setCertificate(form.isCertificate());
|
ls.logChange("Certificate", model.getCertificate(), form.getCertificate(), model);
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
model.setCertificate(form.getCertificate());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model))
|
||||||
|
.call(__ -> ls.append());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -91,6 +192,44 @@ public class LicenceService {
|
|||||||
public Uni<?> deleteAskLicence(long id, Consumer<MembreModel> checkPerm) {
|
public Uni<?> deleteAskLicence(long id, Consumer<MembreModel> checkPerm) {
|
||||||
return repository.findById(id)
|
return repository.findById(id)
|
||||||
.call(licenceModel -> Mutiny.fetch(licenceModel.getMembre()).invoke(checkPerm))
|
.call(licenceModel -> Mutiny.fetch(licenceModel.getMembre()).invoke(checkPerm))
|
||||||
|
.call(__ -> checkoutService.canDeleteLicence(id)
|
||||||
|
.invoke(Unchecked.consumer(b -> {
|
||||||
|
if (!b) throw new DBadRequestException(
|
||||||
|
"Impossible de supprimer une licence pour laquelle un paiement est en cours");
|
||||||
|
})))
|
||||||
|
.invoke(Unchecked.consumer(licenceModel -> {
|
||||||
|
if (licenceModel.isValidate())
|
||||||
|
throw new DBadRequestException("Impossible de supprimer une licence déjà validée");
|
||||||
|
if (licenceModel.isPay())
|
||||||
|
throw new DBadRequestException("Impossible de supprimer une licence déjà payée");
|
||||||
|
}))
|
||||||
|
.call(model -> ls.logADelete(model))
|
||||||
.chain(__ -> Panache.withTransaction(() -> repository.deleteById(id)));
|
.chain(__ -> Panache.withTransaction(() -> repository.deleteById(id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<?> setImport(int licence, int saison, boolean valid) {
|
||||||
|
return combRepository.find("licence = ?1", licence).firstResult()
|
||||||
|
.chain(membreModel ->
|
||||||
|
repository.find("saison = ?1 AND membre = ?2", saison, membreModel).firstResult()
|
||||||
|
.chain(licenceModel -> {
|
||||||
|
if (licenceModel != null) {
|
||||||
|
if (licenceModel.getClub_id() == null)
|
||||||
|
licenceModel.setClub_id(
|
||||||
|
(membreModel.getClub() == null) ? null : membreModel.getClub()
|
||||||
|
.getId());
|
||||||
|
licenceModel.setValidate(valid);
|
||||||
|
return Panache.withTransaction(() -> repository.persist(licenceModel));
|
||||||
|
} else {
|
||||||
|
LicenceModel model = new LicenceModel();
|
||||||
|
model.setClub_id(
|
||||||
|
(membreModel.getClub() == null) ? null : membreModel.getClub().getId());
|
||||||
|
model.setMembre(membreModel);
|
||||||
|
model.setSaison(saison);
|
||||||
|
model.setCertificate("¤");
|
||||||
|
model.setValidate(valid);
|
||||||
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,109 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel.ActionType;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LogModel.ObjectType;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LoggableModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.LogRepository;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@RequestScoped
|
||||||
|
public class LoggerService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LogRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
private final List<LogModel> buffer = new ArrayList<>();
|
||||||
|
|
||||||
|
public Uni<?> logA(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
|
||||||
|
return Panache.withTransaction(() -> repository.persist(
|
||||||
|
new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
|
||||||
|
message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> logA(ActionType action, String message, LoggableModel model) {
|
||||||
|
return logA(action, model.getObjectType(), message, model.getObjectName(), model.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> logAAdd(LoggableModel model) {
|
||||||
|
return logA(ActionType.ADD, "", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> logAUpdate(String message, LoggableModel model) {
|
||||||
|
return logA(ActionType.UPDATE, message, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> logAChange(String champ, Object o1, Object o2, LoggableModel model) {
|
||||||
|
if (Objects.equals(o1, o2))
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
return logA(ActionType.UPDATE, champ + ": " + o1.toString() + " -> " + o2.toString(), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> logADelete(LoggableModel model) {
|
||||||
|
return logA(ActionType.REMOVE, "", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> append() {
|
||||||
|
return Panache.withTransaction(() -> repository.persist(buffer))
|
||||||
|
.invoke(__ -> buffer.clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
|
||||||
|
buffer.add(new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
|
||||||
|
message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logAnonymous(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
|
||||||
|
buffer.add(new LogModel(null, null, new Date(), action, object, target_id, target_name, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(ActionType action, String message, LoggableModel model) {
|
||||||
|
log(action, model.getObjectType(), message, model.getObjectName(), model.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logAnonymous(ActionType action, String message, LoggableModel model) {
|
||||||
|
logAnonymous(action, model.getObjectType(), message, model.getObjectName(), model.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logAdd(LoggableModel model) {
|
||||||
|
log(ActionType.ADD, "", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logUpdate(String message, LoggableModel model) {
|
||||||
|
log(ActionType.UPDATE, message, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logUpdateAnonymous(String message, LoggableModel model) {
|
||||||
|
logAnonymous(ActionType.UPDATE, message, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logChange(String champ, Object o1, Object o2, LoggableModel model) {
|
||||||
|
if (Objects.equals(o1, o2))
|
||||||
|
return;
|
||||||
|
log(ActionType.UPDATE,
|
||||||
|
champ + ": " + (o1 == null ? "null" : o1.toString()) + " -> " + (o2 == null ? "null" : o2.toString()),
|
||||||
|
model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logDelete(LoggableModel model) {
|
||||||
|
log(ActionType.REMOVE, "", model);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MatchData;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class MatchService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MatchRepository repository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetPermService permService;
|
||||||
|
|
||||||
|
public Uni<MatchData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
|
||||||
|
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCategory().getCompet()))
|
||||||
|
.map(MatchData::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<List<MatchData>> getAllByPouleAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||||
|
return categoryRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
|
||||||
|
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet()))
|
||||||
|
.chain(data -> repository.list("poule = ?1", data.getId())
|
||||||
|
.map(o -> o.stream().map(MatchData::fromModel).toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<MatchData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, MatchData data) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
|
||||||
|
.chain(o -> {
|
||||||
|
if (o == null) {
|
||||||
|
return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
|
||||||
|
.firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
|
||||||
|
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||||
|
.map(categoryModel -> {
|
||||||
|
MatchModel model = new MatchModel();
|
||||||
|
|
||||||
|
model.setId(null);
|
||||||
|
model.setSystem(system);
|
||||||
|
model.setSystemId(data.getId());
|
||||||
|
model.setCategory(categoryModel);
|
||||||
|
return model;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
|
||||||
|
.firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
|
||||||
|
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||||
|
.map(__ -> o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.chain(o -> {
|
||||||
|
o.setC1_str(data.getC1_str());
|
||||||
|
o.setC2_str(data.getC2_str());
|
||||||
|
o.setCategory_ord(data.getCategory_ord());
|
||||||
|
o.getScores().clear();
|
||||||
|
o.getScores().addAll(data.getScores());
|
||||||
|
|
||||||
|
return Uni.createFrom().nullItem()
|
||||||
|
.chain(() -> (data.getC1_id() == null) ?
|
||||||
|
Uni.createFrom().nullItem() : combRepository.findById(data.getC1_id()))
|
||||||
|
.invoke(o::setC1_id)
|
||||||
|
.chain(() -> (data.getC1_id() == null) ?
|
||||||
|
Uni.createFrom().nullItem() : combRepository.findById(data.getC2_id()))
|
||||||
|
.invoke(o::setC2_id)
|
||||||
|
.chain(() -> Panache.withTransaction(() -> repository.persist(o)));
|
||||||
|
})
|
||||||
|
.map(MatchData::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> updateScore(SecurityCtx securityCtx, CompetitionSystem system, Long id,
|
||||||
|
List<ScoreEmbeddable> scores) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
|
||||||
|
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet()))
|
||||||
|
.invoke(data -> {
|
||||||
|
data.getScores().clear();
|
||||||
|
data.getScores().addAll(scores);
|
||||||
|
})
|
||||||
|
.chain(data -> Panache.withTransaction(() -> repository.persist(data)))
|
||||||
|
.map(o -> "OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||||
|
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||||
|
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
|
||||||
|
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet()))
|
||||||
|
.chain(data -> Panache.withTransaction(() -> repository.delete(data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,19 @@
|
|||||||
package fr.titionfire.ffsaf.domain.service;
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LicenceModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
|
||||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||||
import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
|
import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
|
||||||
import fr.titionfire.ffsaf.net2.request.SReqComb;
|
import fr.titionfire.ffsaf.net2.request.SReqComb;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
||||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||||
import fr.titionfire.ffsaf.utils.*;
|
import fr.titionfire.ffsaf.utils.*;
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
@ -17,20 +21,24 @@ import io.quarkus.hibernate.reactive.panache.PanacheQuery;
|
|||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
import io.quarkus.panache.common.Page;
|
import io.quarkus.panache.common.Page;
|
||||||
import io.quarkus.panache.common.Sort;
|
import io.quarkus.panache.common.Sort;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import io.quarkus.scheduler.Scheduled;
|
||||||
import io.quarkus.vertx.VertxContextSupport;
|
import io.quarkus.vertx.VertxContextSupport;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import jakarta.ws.rs.ForbiddenException;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class MembreService {
|
public class MembreService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(MembreService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CombRepository repository;
|
CombRepository repository;
|
||||||
@ -39,50 +47,167 @@ public class MembreService {
|
|||||||
@Inject
|
@Inject
|
||||||
LicenceRepository licenceRepository;
|
LicenceRepository licenceRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionRepository competitionRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ServerCustom serverCustom;
|
ServerCustom serverCustom;
|
||||||
@Inject
|
@Inject
|
||||||
KeycloakService keycloakService;
|
KeycloakService keycloakService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RegisterRepository registerRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerService ls;
|
||||||
|
|
||||||
public SimpleCombModel find(int licence, String np) throws Throwable {
|
public SimpleCombModel find(int licence, String np) throws Throwable {
|
||||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
|
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
|
||||||
repository.find("licence = ?1 AND (lname ILIKE ?2 OR fname ILIKE ?2)",
|
repository.find(
|
||||||
|
"licence = ?1 AND (unaccent(lname) ILIKE unaccent(?2) OR unaccent(fname) ILIKE unaccent(?2))",
|
||||||
licence, np).firstResult().map(SimpleCombModel::fromModel)));
|
licence, np).firstResult().map(SimpleCombModel::fromModel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleCombModel findByIdOptionalComb(long id) throws Throwable {
|
public SimpleCombModel findByIdOptionalComb(long id) throws Throwable {
|
||||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleCombModel::fromModel)));
|
return VertxContextSupport.subscribeAndAwait(
|
||||||
|
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleCombModel::fromModel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, String club) {
|
final static String FIND_NAME_REQUEST = "unaccent(fname) ILIKE unaccent(?1) OR unaccent(lname) ILIKE unaccent(?1) " +
|
||||||
if (search == null)
|
"OR unaccent(fname || ' ' || lname) ILIKE unaccent(?1) OR unaccent(lname || ' ' || fname) ILIKE unaccent(?1)";
|
||||||
search = "";
|
|
||||||
search = search + "%";
|
|
||||||
|
|
||||||
PanacheQuery<MembreModel> query;
|
private Uni<List<LicenceModel>> getLicenceListe(int licenceRequest, int payState) {
|
||||||
|
Uni<List<LicenceModel>> baseUni;
|
||||||
if (club == null || club.isBlank())
|
String queryStr = "saison = ?1";
|
||||||
query = repository.find("(lname LIKE ?1 OR fname LIKE ?1)",
|
if (payState == 0)
|
||||||
Sort.ascending("fname", "lname"), search).page(Page.ofSize(limit));
|
queryStr += " AND pay = FALSE";
|
||||||
|
if (payState == 1)
|
||||||
|
queryStr += " AND pay = TRUE";
|
||||||
|
if (licenceRequest == 0 || licenceRequest == 1)
|
||||||
|
baseUni = licenceRepository.list(queryStr, Utils.getSaison());
|
||||||
|
else if (licenceRequest == 2)
|
||||||
|
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE", Utils.getSaison());
|
||||||
|
else if (licenceRequest == 5)
|
||||||
|
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE AND LENGTH(certificate) >= 3",
|
||||||
|
Utils.getSaison());
|
||||||
|
else if (licenceRequest == 6)
|
||||||
|
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE AND LENGTH(certificate) <= 2",
|
||||||
|
Utils.getSaison());
|
||||||
|
else if (licenceRequest == 3)
|
||||||
|
baseUni = licenceRepository.list(queryStr + " AND validate = TRUE", Utils.getSaison());
|
||||||
else
|
else
|
||||||
query = repository.find("club.name LIKE ?2 AND (lname LIKE ?1 OR fname LIKE ?1)",
|
baseUni = Uni.createFrom().item(new ArrayList<>());
|
||||||
Sort.ascending("fname", "lname"), search, club + "%").page(Page.ofSize(limit));
|
return baseUni;
|
||||||
return getPageResult(query, limit, page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, String subject) {
|
private Sort getSort(String order) {
|
||||||
|
|
||||||
|
Sort sort;
|
||||||
|
if (order == null || order.isBlank()) {
|
||||||
|
sort = Sort.ascending("fname", "lname");
|
||||||
|
} else {
|
||||||
|
sort = Sort.empty();
|
||||||
|
|
||||||
|
for (String e : order.split(",")) {
|
||||||
|
String[] split = e.split(" ");
|
||||||
|
if (split.length == 2) {
|
||||||
|
sort = sort.and(split[0],
|
||||||
|
split[1].equals("n") ? Sort.Direction.Ascending : Sort.Direction.Descending);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, String club,
|
||||||
|
int licenceRequest, int payState, String order, String categorie) {
|
||||||
if (search == null)
|
if (search == null)
|
||||||
search = "";
|
search = "";
|
||||||
search = search + "%";
|
search = "%" + search.replaceAll(" ", "% %") + "%";
|
||||||
|
|
||||||
|
String categorieFilter;
|
||||||
|
if (categorie == null || categorie.isBlank())
|
||||||
|
categorieFilter = " True";
|
||||||
|
else
|
||||||
|
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
|
||||||
|
|
||||||
String finalSearch = search;
|
String finalSearch = search;
|
||||||
return repository.find("userId = ?1", subject).firstResult()
|
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
|
||||||
.chain(membreModel -> {
|
|
||||||
PanacheQuery<MembreModel> query = repository.find("club = ?1 AND (lname LIKE ?2 OR fname LIKE ?2)",
|
Sort sort = getSort(order);
|
||||||
Sort.ascending("fname", "lname"), membreModel.getClub(), finalSearch).page(Page.ofSize(limit));
|
if (sort == null)
|
||||||
|
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie"));
|
||||||
|
|
||||||
|
return baseUni
|
||||||
|
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
|
||||||
|
.chain(ids -> {
|
||||||
|
PanacheQuery<MembreModel> query;
|
||||||
|
|
||||||
|
String idf = ((licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN");
|
||||||
|
|
||||||
|
if (club == null || club.isBlank()) {
|
||||||
|
query = repository.find(
|
||||||
|
"id " + idf + " ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||||
|
sort, finalSearch, ids)
|
||||||
|
.page(Page.ofSize(limit));
|
||||||
|
} else {
|
||||||
|
if (club.equals("null")) {
|
||||||
|
query = repository.find(
|
||||||
|
"id " + idf + " ?2 AND club IS NULL AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||||
|
sort, finalSearch, ids).page(Page.ofSize(limit));
|
||||||
|
} else {
|
||||||
|
query = repository.find(
|
||||||
|
"id " + idf + " ?3 AND LOWER(club.name) LIKE LOWER(?2) AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||||
|
sort, finalSearch, club, ids)
|
||||||
|
.page(Page.ofSize(limit));
|
||||||
|
}
|
||||||
|
}
|
||||||
return getPageResult(query, limit, page);
|
return getPageResult(query, limit, page);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, int licenceRequest, int payState,
|
||||||
|
String order, String categorie, String subject) {
|
||||||
|
if (search == null)
|
||||||
|
search = "";
|
||||||
|
search = "%" + search.replaceAll(" ", "% %") + "%";
|
||||||
|
|
||||||
|
String finalSearch = search;
|
||||||
|
|
||||||
|
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
|
||||||
|
|
||||||
|
String categorieFilter;
|
||||||
|
if (categorie == null || categorie.isBlank())
|
||||||
|
categorieFilter = " True";
|
||||||
|
else
|
||||||
|
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
|
||||||
|
|
||||||
|
Sort sort = getSort(order);
|
||||||
|
if (sort == null)
|
||||||
|
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie"));
|
||||||
|
|
||||||
|
return baseUni
|
||||||
|
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
|
||||||
|
.chain(ids -> {
|
||||||
|
String idf = ((licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN");
|
||||||
|
|
||||||
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.chain(membreModel -> {
|
||||||
|
PanacheQuery<MembreModel> query = repository.find(
|
||||||
|
"id " + idf + " ?3 AND club = ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||||
|
sort, finalSearch, membreModel.getClub(), ids)
|
||||||
|
.page(Page.ofSize(limit));
|
||||||
|
return getPageResult(query, limit, page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Uni<PageResult<SimpleMembre>> getPageResult(PanacheQuery<MembreModel> query, int limit, int page) {
|
private Uni<PageResult<SimpleMembre>> getPageResult(PanacheQuery<MembreModel> query, int limit, int page) {
|
||||||
return Uni.createFrom().item(new PageResult<SimpleMembre>())
|
return Uni.createFrom().item(new PageResult<SimpleMembre>())
|
||||||
.invoke(result -> result.setPage(page))
|
.invoke(result -> result.setPage(page))
|
||||||
@ -90,7 +215,7 @@ public class MembreService {
|
|||||||
.call(result -> query.count().invoke(result::setResult_count))
|
.call(result -> query.count().invoke(result::setResult_count))
|
||||||
.call(result -> query.pageCount()
|
.call(result -> query.pageCount()
|
||||||
.invoke(Unchecked.consumer(pages -> {
|
.invoke(Unchecked.consumer(pages -> {
|
||||||
if (page > pages) throw new BadRequestException();
|
if (page > pages) throw new DBadRequestException("Page out of range");
|
||||||
}))
|
}))
|
||||||
.invoke(result::setPage_count))
|
.invoke(result::setPage_count))
|
||||||
.call(result -> query.page(Page.of(page, limit)).list()
|
.call(result -> query.page(Page.of(page, limit)).list()
|
||||||
@ -98,92 +223,310 @@ public class MembreService {
|
|||||||
.invoke(result::setResult));
|
.invoke(result::setResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) {
|
||||||
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.chain(membreModel -> repository.list("club = ?1", membreModel.getClub()))
|
||||||
|
.chain(membres -> licenceRepository.list("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres)
|
||||||
|
.map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> allImporte(String subject, List<SimpleMembreInOutData> data) {
|
||||||
|
if (data == null)
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
final List<SimpleMembreInOutData> data2 = data.stream()
|
||||||
|
.filter(dataIn -> dataIn.getNom() != null && !dataIn.getNom()
|
||||||
|
.isBlank() && dataIn.getPrenom() != null && !dataIn.getPrenom().isBlank()).toList();
|
||||||
|
if (data2.isEmpty())
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
AtomicReference<ClubModel> clubModel = new AtomicReference<>();
|
||||||
|
|
||||||
|
LOGGER.debugf("Membre import (size=%d)", data2.size());
|
||||||
|
for (SimpleMembreInOutData simpleMembreInOutData : data2) {
|
||||||
|
LOGGER.debugf("-> %s", simpleMembreInOutData.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.chain(membreModel -> {
|
||||||
|
clubModel.set(membreModel.getClub());
|
||||||
|
if (data2.stream().noneMatch(d -> d.getLicence() != null))
|
||||||
|
return Uni.createFrom().item(new ArrayList<MembreModel>());
|
||||||
|
return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2 OR email IN ?3",
|
||||||
|
data2.stream().map(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(),
|
||||||
|
data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).toList(),
|
||||||
|
data2.stream().map(SimpleMembreInOutData::getEmail).filter(o -> o != null && !o.isBlank())
|
||||||
|
.toList());
|
||||||
|
})
|
||||||
|
.call(Unchecked.function(membres -> {
|
||||||
|
for (MembreModel membreModel : membres) {
|
||||||
|
if (!Objects.equals(membreModel.getClub(), clubModel.get())) {
|
||||||
|
LOGGER.info("Similar membres found: " + membreModel);
|
||||||
|
throw new DForbiddenException(
|
||||||
|
"Le membre n°" + membreModel.getLicence() + " n'appartient pas à votre club");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Uni<Void> uniResult = Uni.createFrom().voidItem();
|
||||||
|
for (SimpleMembreInOutData dataIn : data2) {
|
||||||
|
MembreModel model = membres.stream()
|
||||||
|
.filter(m -> (dataIn.getLicence() != null && Objects.equals(m.getLicence(),
|
||||||
|
dataIn.getLicence())) || m.getLname().equals(dataIn.getNom()) && m.getFname()
|
||||||
|
.equals(dataIn.getPrenom()) || (dataIn.getEmail() != null && !dataIn.getEmail()
|
||||||
|
.isBlank() && Objects.equals(m.getFname(), dataIn.getEmail()))).findFirst()
|
||||||
|
.orElseGet(() -> {
|
||||||
|
MembreModel mm = new MembreModel();
|
||||||
|
mm.setClub(clubModel.get());
|
||||||
|
mm.setLicences(new ArrayList<>());
|
||||||
|
mm.setCountry("FR");
|
||||||
|
return mm;
|
||||||
|
});
|
||||||
|
if (model.getId() != null) {
|
||||||
|
LOGGER.debugf("updating -> %s", dataIn.toString());
|
||||||
|
} else {
|
||||||
|
LOGGER.debugf("creating -> %s", dataIn.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.getEmail() != null && !model.getEmail().isBlank()) {
|
||||||
|
if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) {
|
||||||
|
LOGGER.info("Similar membres found: " + model);
|
||||||
|
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringSimilarity.similarity(model.getLname().toUpperCase(),
|
||||||
|
dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity(
|
||||||
|
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) {
|
||||||
|
LOGGER.info("Similar membres found: " + model);
|
||||||
|
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean add = model.getId() == null;
|
||||||
|
|
||||||
|
if ((!add && StringSimilarity.similarity(model.getLname().toUpperCase(),
|
||||||
|
dataIn.getNom().toUpperCase()) > 3) || (!add && StringSimilarity.similarity(
|
||||||
|
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) {
|
||||||
|
LOGGER.info("Similar membres found: " + model);
|
||||||
|
throw new DBadRequestException(
|
||||||
|
"Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model);
|
||||||
|
ls.logChange("Prénom", model.getFname(),
|
||||||
|
dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model);
|
||||||
|
|
||||||
|
model.setLname(dataIn.getNom().toUpperCase());
|
||||||
|
model.setFname(dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1));
|
||||||
|
|
||||||
|
|
||||||
|
if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) {
|
||||||
|
ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model);
|
||||||
|
model.setEmail(dataIn.getEmail());
|
||||||
|
}
|
||||||
|
model.setGenre(Genre.fromString(dataIn.getGenre()));
|
||||||
|
if (dataIn.getBirthdate() != null) {
|
||||||
|
if (model.getBirth_date() == null || !Objects.equals(model.getBirth_date().getTime(),
|
||||||
|
dataIn.getBirthdate().getTime()))
|
||||||
|
ls.logChange("Date de naissance", model.getBirth_date(), dataIn.getBirthdate(), model);
|
||||||
|
model.setBirth_date(dataIn.getBirthdate());
|
||||||
|
model.setCategorie(Utils.getCategoryFormBirthDate(model.getBirth_date(), new Date()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uniResult = uniResult
|
||||||
|
.call(() -> Panache.withTransaction(() -> repository.persist(model)
|
||||||
|
.chain(membreModel1 -> dataIn.isLicenceCurrent() ? licenceRepository.find(
|
||||||
|
"membre.id = ?1 AND saison = ?2", membreModel1.getId(),
|
||||||
|
Utils.getSaison())
|
||||||
|
.firstResult()
|
||||||
|
.call(l -> {
|
||||||
|
if (l == null) {
|
||||||
|
l = new LicenceModel();
|
||||||
|
l.setMembre(membreModel1);
|
||||||
|
l.setClub_id(clubModel.get().getId());
|
||||||
|
l.setValidate(false);
|
||||||
|
l.setSaison(Utils.getSaison());
|
||||||
|
}
|
||||||
|
l.setCertificate(dataIn.getCertif());
|
||||||
|
return licenceRepository.persist(l);
|
||||||
|
}) : licenceRepository.delete(
|
||||||
|
"membre = ?1 AND saison = ?2 AND validate = false", membreModel1,
|
||||||
|
Utils.getSaison()))));
|
||||||
|
if (add)
|
||||||
|
uniResult = uniResult.call(() -> ls.logAAdd(model));
|
||||||
|
else
|
||||||
|
uniResult = uniResult.call(() -> ls.append());
|
||||||
|
}
|
||||||
|
return uniResult;
|
||||||
|
}))
|
||||||
|
.map(__ -> "OK");
|
||||||
|
}
|
||||||
|
|
||||||
public Uni<MembreModel> getById(long id) {
|
public Uni<MembreModel> getById(long id) {
|
||||||
return repository.findById(id);
|
return repository.findById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<String> update(long id, FullMemberForm membre) {
|
public Uni<MembreModel> getByIdWithLicence(long id) {
|
||||||
return repository.findById(id)
|
return repository.findById(id)
|
||||||
.chain(membreModel -> clubRepository.findById(membre.getClub()).map(club -> new Pair<>(membreModel, club)))
|
.call(m -> Mutiny.fetch(m.getLicences()));
|
||||||
.onItem().transformToUni(pair -> {
|
|
||||||
MembreModel m = pair.getKey();
|
|
||||||
m.setFname(membre.getFname());
|
|
||||||
m.setLname(membre.getLname());
|
|
||||||
m.setClub(pair.getValue());
|
|
||||||
m.setCountry(membre.getCountry());
|
|
||||||
m.setBirth_date(membre.getBirth_date());
|
|
||||||
m.setGenre(membre.getGenre());
|
|
||||||
m.setCategorie(membre.getCategorie());
|
|
||||||
m.setRole(membre.getRole());
|
|
||||||
m.setGrade_arbitrage(membre.getGrade_arbitrage());
|
|
||||||
m.setEmail(membre.getEmail());
|
|
||||||
return Panache.withTransaction(() -> repository.persist(m));
|
|
||||||
})
|
|
||||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
|
||||||
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) : Uni.createFrom().nullItem())
|
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
|
||||||
keycloakService.setAutoRoleMembre(membreModel.getUserId(), membreModel.getRole(),
|
|
||||||
membreModel.getGrade_arbitrage()) : Uni.createFrom().nullItem())
|
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
|
||||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom().nullItem())
|
|
||||||
.map(__ -> "OK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<String> update(long id, ClubMemberForm membre, JsonWebToken idToken, SecurityIdentity securityIdentity) {
|
public Uni<MembreModel> getByAccountId(String subject) {
|
||||||
return repository.findById(id)
|
return repository.find("userId = ?1", subject).firstResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<MembreModel> getByLicence(long licence) {
|
||||||
|
return repository.find("licence = ?1", licence).firstResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> update(long id, FullMemberForm membre) {
|
||||||
|
return update(repository.findById(id)
|
||||||
|
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
|
||||||
|
.invoke(Unchecked.consumer(c -> {
|
||||||
|
if (c > 0 && !membre.getEmail().isBlank())
|
||||||
|
throw new DBadRequestException("Email déjà utiliser");
|
||||||
|
})))
|
||||||
|
.chain(membreModel -> clubRepository.findById(membre.getClub())
|
||||||
|
.map(club -> new Pair<>(membreModel, club)))
|
||||||
|
.onItem().transform(pair -> {
|
||||||
|
MembreModel m = pair.getKey();
|
||||||
|
|
||||||
|
ls.logChange("Rôle", m.getRole(), membre.getRole(), m);
|
||||||
|
m.setRole(membre.getRole());
|
||||||
|
ls.logChange("Club", m.getClub(), pair.getValue(), m);
|
||||||
|
m.setClub(pair.getValue());
|
||||||
|
ls.logChange("Grade d'arbitrage", m.getGrade_arbitrage(), membre.getGrade_arbitrage(), m);
|
||||||
|
m.setGrade_arbitrage(membre.getGrade_arbitrage());
|
||||||
|
return m;
|
||||||
|
}), membre, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<String> update(long id, FullMemberForm membre, SecurityCtx securityCtx) {
|
||||||
|
return update(repository.findById(id)
|
||||||
|
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
|
||||||
|
.invoke(Unchecked.consumer(c -> {
|
||||||
|
if (c > 0 && !membre.getEmail().isBlank())
|
||||||
|
throw new DBadRequestException("Email déjà utiliser");
|
||||||
|
})))
|
||||||
.invoke(Unchecked.consumer(membreModel -> {
|
.invoke(Unchecked.consumer(membreModel -> {
|
||||||
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||||
throw new ForbiddenException();
|
throw new DForbiddenException();
|
||||||
|
if (StringSimilarity.similarity(membreModel.getLname().toUpperCase(),
|
||||||
|
membre.getLname().toUpperCase()) > 3 || StringSimilarity.similarity(
|
||||||
|
membreModel.getFname().toUpperCase(), membre.getFname().toUpperCase()) > 3) {
|
||||||
|
throw new DBadRequestException(
|
||||||
|
"Pour enregistrer un nouveau membre, veuillez utilisez le bouton prévue a cette effet.");
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
.invoke(Unchecked.consumer(membreModel -> {
|
.invoke(Unchecked.consumer(membreModel -> {
|
||||||
RoleAsso source = RoleAsso.MEMBRE;
|
RoleAsso source = RoleAsso.MEMBRE;
|
||||||
if (securityIdentity.getRoles().contains("club_president")) source = RoleAsso.PRESIDENT;
|
if (securityCtx.roleHas("club_president")) source = RoleAsso.PRESIDENT;
|
||||||
else if (securityIdentity.getRoles().contains("club_secretaire")) source = RoleAsso.SECRETAIRE;
|
else if (securityCtx.roleHas("club_secretaire")) source = RoleAsso.SECRETAIRE;
|
||||||
else if (securityIdentity.getRoles().contains("club_respo_intra")) source = RoleAsso.SECRETAIRE;
|
else if (securityCtx.roleHas("club_respo_intra")) source = RoleAsso.MEMBREBUREAU;
|
||||||
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level > source.level)
|
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level)
|
||||||
throw new ForbiddenException();
|
throw new DForbiddenException("Permission insuffisante");
|
||||||
}))
|
}))
|
||||||
.onItem().transformToUni(target -> {
|
.onItem().transform(target -> {
|
||||||
target.setFname(membre.getFname());
|
if (!securityCtx.getSubject().equals(target.getUserId())) {
|
||||||
target.setLname(membre.getLname());
|
ls.logChange("Rôle", target.getRole(), membre.getRole(), target);
|
||||||
target.setCountry(membre.getCountry());
|
|
||||||
target.setBirth_date(membre.getBirth_date());
|
|
||||||
target.setGenre(membre.getGenre());
|
|
||||||
target.setCategorie(membre.getCategorie());
|
|
||||||
target.setEmail(membre.getEmail());
|
|
||||||
if (!idToken.getSubject().equals(target.getUserId()))
|
|
||||||
target.setRole(membre.getRole());
|
target.setRole(membre.getRole());
|
||||||
return Panache.withTransaction(() -> repository.persist(target));
|
}
|
||||||
|
return target;
|
||||||
|
}), membre, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) {
|
||||||
|
return uni.chain(target -> {
|
||||||
|
ls.logChange("Prénom", target.getFname(), membre.getFname(), target);
|
||||||
|
target.setFname(membre.getFname());
|
||||||
|
ls.logChange("Nom", target.getLname(), membre.getLname(), target);
|
||||||
|
target.setLname(membre.getLname().toUpperCase());
|
||||||
|
ls.logChange("Pays", target.getCountry(), membre.getCountry(), target);
|
||||||
|
target.setCountry(membre.getCountry());
|
||||||
|
if (membre.getBirth_date() != null && (target.getBirth_date() == null || !Objects.equals(
|
||||||
|
target.getBirth_date().getTime(), membre.getBirth_date().getTime()))) {
|
||||||
|
ls.logChange("Date de naissance", target.getBirth_date(), membre.getBirth_date(), target);
|
||||||
|
target.setBirth_date(membre.getBirth_date());
|
||||||
|
target.setCategorie(Utils.getCategoryFormBirthDate(membre.getBirth_date(), new Date()));
|
||||||
|
}
|
||||||
|
ls.logChange("Genre", target.getGenre(), membre.getGenre(), target);
|
||||||
|
target.setGenre(membre.getGenre());
|
||||||
|
ls.logChange("Email", target.getEmail(), membre.getEmail(), target);
|
||||||
|
target.setEmail(membre.getEmail());
|
||||||
|
|
||||||
|
return Panache.withTransaction(() -> repository.persist(target)).call(() -> ls.append());
|
||||||
})
|
})
|
||||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients,
|
||||||
|
SimpleCombModel.fromModel(membreModel)))
|
||||||
|
.call(membreModel -> (admin && membreModel.getUserId() != null) ?
|
||||||
|
((membreModel.getClub() != null) ?
|
||||||
|
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) :
|
||||||
|
keycloakService.clearUser(membreModel.getUserId()))
|
||||||
|
: Uni.createFrom().nullItem())
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||||
keycloakService.setAutoRoleMembre(membreModel.getUserId(), membreModel.getRole(),
|
keycloakService.setAutoRoleMembre(membreModel.getUserId(), membreModel.getRole(),
|
||||||
membreModel.getGrade_arbitrage()) : Uni.createFrom().nullItem())
|
membreModel.getGrade_arbitrage()) : Uni.createFrom().nullItem())
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom().nullItem())
|
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom()
|
||||||
|
.nullItem())
|
||||||
|
.call(membreModel -> {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.add(Calendar.DAY_OF_YEAR, -7);
|
||||||
|
Date dateLimit = calendar.getTime();
|
||||||
|
|
||||||
|
return competitionRepository.list("date > ?1", dateLimit)
|
||||||
|
.call(l -> l.isEmpty() ? Uni.createFrom().nullItem() :
|
||||||
|
Uni.join().all(l.stream().map(competitionModel ->
|
||||||
|
registerRepository.update(
|
||||||
|
"categorie = ?1, club = ?2 where competition = ?3 AND membre = ?4",
|
||||||
|
membreModel.getCategorie(), membreModel.getClub(), competitionModel,
|
||||||
|
membreModel)
|
||||||
|
).toList()).andFailFast());
|
||||||
|
})
|
||||||
|
.call(membreModel -> licenceRepository.update("club_id = ?1 where membre = ?2 AND saison = ?3",
|
||||||
|
(membreModel.getClub() == null) ? null : membreModel.getClub().getId(), membreModel,
|
||||||
|
Utils.getSaison()))
|
||||||
|
.call(membreModel -> membre.getPhoto_data().length > 0 ? ls.logAUpdate("Photo",
|
||||||
|
membreModel) : Uni.createFrom().nullItem())
|
||||||
.map(__ -> "OK");
|
.map(__ -> "OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Uni<Long> add(FullMemberForm input) {
|
public Uni<Long> add(FullMemberForm input) {
|
||||||
return clubRepository.findById(input.getClub())
|
return clubRepository.findById(input.getClub())
|
||||||
|
.call(__ -> repository.count("email LIKE ?1", input.getEmail())
|
||||||
|
.invoke(Unchecked.consumer(c -> {
|
||||||
|
if (c > 0) throw new DBadRequestException("Email déjà utiliser");
|
||||||
|
})))
|
||||||
.chain(clubModel -> {
|
.chain(clubModel -> {
|
||||||
MembreModel model = getMembreModel(input, clubModel);
|
MembreModel model = getMembreModel(input, clubModel);
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
})
|
})
|
||||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
.call(membreModel -> ls.logAAdd(membreModel))
|
||||||
|
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
|
||||||
|
SimpleCombModel.fromModel(membreModel)))
|
||||||
.map(MembreModel::getId);
|
.map(MembreModel::getId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<Long> add(FullMemberForm input, String subject) {
|
public Uni<Long> add(FullMemberForm input, String subject) {
|
||||||
return repository.find("userId = ?1", subject).firstResult()
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.call(__ -> repository.count("email LIKE ?1", input.getEmail())
|
||||||
|
.invoke(Unchecked.consumer(c -> {
|
||||||
|
if (c > 0) throw new DBadRequestException("Email déjà utiliser");
|
||||||
|
})))
|
||||||
|
.call(membreModel ->
|
||||||
|
repository.count(
|
||||||
|
"unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2) AND club = ?3",
|
||||||
|
input.getLname(), input.getFname(), membreModel.getClub())
|
||||||
|
.invoke(Unchecked.consumer(c -> {
|
||||||
|
if (c > 0)
|
||||||
|
throw new DBadRequestException("Membre déjà existent");
|
||||||
|
})))
|
||||||
.chain(membreModel -> {
|
.chain(membreModel -> {
|
||||||
MembreModel model = getMembreModel(input, membreModel.getClub());
|
MembreModel model = getMembreModel(input, membreModel.getClub());
|
||||||
model.setRole(RoleAsso.MEMBRE);
|
model.setRole(RoleAsso.MEMBRE);
|
||||||
model.setGrade_arbitrage(GradeArbitrage.NA);
|
model.setGrade_arbitrage(GradeArbitrage.NA);
|
||||||
return Panache.withTransaction(() -> repository.persist(model));
|
return Panache.withTransaction(() -> repository.persist(model));
|
||||||
})
|
})
|
||||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
.call(membreModel -> ls.logAAdd(membreModel))
|
||||||
|
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
|
||||||
|
SimpleCombModel.fromModel(membreModel)))
|
||||||
.map(MembreModel::getId);
|
.map(MembreModel::getId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,33 +534,44 @@ public class MembreService {
|
|||||||
return repository.findById(id)
|
return repository.findById(id)
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||||
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
|
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
|
||||||
|
.call(membreModel -> ls.logADelete(membreModel))
|
||||||
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
|
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
|
||||||
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
|
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
|
||||||
.map(__ -> "Ok");
|
.map(__ -> "Ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<String> delete(long id, JsonWebToken idToken) {
|
public Uni<String> delete(long id, SecurityCtx securityCtx) {
|
||||||
return repository.findById(id)
|
return repository.findById(id)
|
||||||
.invoke(Unchecked.consumer(membreModel -> {
|
.invoke(Unchecked.consumer(membreModel -> {
|
||||||
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||||
throw new ForbiddenException();
|
throw new DForbiddenException();
|
||||||
|
}))
|
||||||
|
.invoke(Unchecked.consumer(membreModel -> {
|
||||||
|
if (membreModel.getLicence() != null) {
|
||||||
|
throw new DBadRequestException(
|
||||||
|
"Impossible de supprimer un membre qui a déjà un numéro de licence");
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
|
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
|
||||||
.invoke(Unchecked.consumer(l -> {
|
.invoke(Unchecked.consumer(l -> {
|
||||||
if (l > 0)
|
if (l > 0)
|
||||||
throw new BadRequestException();
|
throw new DBadRequestException("Impossible de supprimer un membre avec des licences");
|
||||||
})))
|
})))
|
||||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||||
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
|
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
|
||||||
|
.call(membreModel -> ls.logADelete(membreModel))
|
||||||
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
|
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
|
||||||
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
|
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
|
||||||
|
.call(__ -> Utils.deleteMedia(id, media, "ppMembre"))
|
||||||
.map(__ -> "Ok");
|
.map(__ -> "Ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<?> setUserId(Long id, String id1) {
|
public Uni<?> setUserId(Long id, String id1) {
|
||||||
return repository.findById(id).chain(membreModel -> {
|
return repository.findById(id).chain(membreModel -> {
|
||||||
|
ls.logChange("KC UUID", membreModel.getUserId(), id1, membreModel);
|
||||||
membreModel.setUserId(id1);
|
membreModel.setUserId(id1);
|
||||||
return Panache.withTransaction(() -> repository.persist(membreModel));
|
return Panache.withTransaction(() -> repository.persist(membreModel))
|
||||||
|
.call(() -> ls.append());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,13 +580,42 @@ public class MembreService {
|
|||||||
model.setFname(input.getFname());
|
model.setFname(input.getFname());
|
||||||
model.setLname(input.getLname());
|
model.setLname(input.getLname());
|
||||||
model.setEmail(input.getEmail());
|
model.setEmail(input.getEmail());
|
||||||
|
model.setLicence(null);
|
||||||
model.setGenre(input.getGenre());
|
model.setGenre(input.getGenre());
|
||||||
model.setCountry(input.getCountry());
|
model.setCountry(input.getCountry());
|
||||||
model.setBirth_date(input.getBirth_date());
|
model.setBirth_date(input.getBirth_date());
|
||||||
model.setCategorie(input.getCategorie());
|
model.setCategorie(Utils.getCategoryFormBirthDate(input.getBirth_date(), new Date()));
|
||||||
model.setClub(clubModel);
|
model.setClub(clubModel);
|
||||||
model.setRole(input.getRole());
|
model.setRole(input.getRole());
|
||||||
model.setGrade_arbitrage(input.getGrade_arbitrage());
|
model.setGrade_arbitrage(input.getGrade_arbitrage());
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<List<SimpleMembre>> getSimilar(String fname, String lname) {
|
||||||
|
return repository.listAll().map(membreModels -> membreModels.stream()
|
||||||
|
.filter(m -> StringSimilarity.similarity(m.getFname(), fname) <= 3 &&
|
||||||
|
StringSimilarity.similarity(m.getLname(), lname) <= 3)
|
||||||
|
.map(SimpleMembre::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<MeData> getMembre(String subject) {
|
||||||
|
MeData meData = new MeData();
|
||||||
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.invoke(meData::setMembre)
|
||||||
|
.chain(membreModel -> Mutiny.fetch(membreModel.getLicences()))
|
||||||
|
.map(licences -> licences.stream().map(SimpleLicence::fromModel).toList())
|
||||||
|
.invoke(meData::setLicences)
|
||||||
|
.map(__ -> meData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 1 9 ?")
|
||||||
|
Uni<Void> everySeason() {
|
||||||
|
return repository.list("birth_date IS NOT NULL")
|
||||||
|
.chain(l -> Uni.join().all(l.stream().map(m -> {
|
||||||
|
m.setCategorie(Utils.getCategoryFormBirthDate(m.getBirth_date(), new Date()));
|
||||||
|
return Panache.withTransaction(() -> repository.persist(m));
|
||||||
|
}).toList()).andCollectFailures())
|
||||||
|
.map(__ -> null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
232
src/main/java/fr/titionfire/ffsaf/domain/service/PDFService.java
Normal file
232
src/main/java/fr/titionfire/ffsaf/domain/service/PDFService.java
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.LicenceModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class PDFService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(PDFService.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CombRepository combRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ClubRepository clubRepository;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "pdf-maker.jar-path")
|
||||||
|
String pdfMakerJarPath;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "pdf-maker.sign-file")
|
||||||
|
String sign_file;
|
||||||
|
|
||||||
|
|
||||||
|
public Uni<Response> getLicencePdf(String subject) {
|
||||||
|
return getLicencePdf(combRepository.find("userId = ?1", subject).firstResult()
|
||||||
|
.call(m -> Mutiny.fetch(m.getLicences())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> getLicencePdf(Uni<MembreModel> uniBase) {
|
||||||
|
return uniBase
|
||||||
|
.map(Unchecked.function(m -> {
|
||||||
|
LicenceModel licence = m.getLicences().stream()
|
||||||
|
.filter(licenceModel -> licenceModel.getSaison() == Utils.getSaison() && licenceModel.isValidate())
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new DNotFoundException("Pas de licence pour la saison en cours"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] buff = make_pdf(m, licence);
|
||||||
|
if (buff == null)
|
||||||
|
throw new IOException("Error making pdf");
|
||||||
|
|
||||||
|
String mimeType = "application/pdf";
|
||||||
|
|
||||||
|
Response.ResponseBuilder resp = Response.ok(buff);
|
||||||
|
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
resp.header(HttpHeaders.CONTENT_LENGTH, buff.length);
|
||||||
|
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||||
|
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"inline; " + "filename=\"Attestation d'adhésion " + Utils.getSaison() + "-" +
|
||||||
|
(Utils.getSaison() + 1) + " de " + m.getLname() + " " + m.getFname() + ".pdf\"");
|
||||||
|
return resp.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] make_pdf(MembreModel m, LicenceModel licence) throws IOException, InterruptedException {
|
||||||
|
List<String> cmd = new ArrayList<>();
|
||||||
|
cmd.add("java");
|
||||||
|
cmd.add("-jar");
|
||||||
|
cmd.add(pdfMakerJarPath);
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
cmd.add("/tmp/" + uuid + ".pdf");
|
||||||
|
cmd.add("membre");
|
||||||
|
cmd.add(m.getFname());
|
||||||
|
cmd.add(m.getLname());
|
||||||
|
cmd.add(m.getGenre().str);
|
||||||
|
cmd.add(m.getCategorie().getName());
|
||||||
|
cmd.add(licence.getCertificate() == null ? "" : licence.getCertificate());
|
||||||
|
cmd.add(Utils.getSaison() + "");
|
||||||
|
cmd.add(m.getLicence() + "");
|
||||||
|
cmd.add(m.getClub().getName());
|
||||||
|
cmd.add(m.getClub().getNo_affiliation() + "");
|
||||||
|
cmd.add(m.getBirth_date() == null ? "--" : new SimpleDateFormat("dd/MM/yyyy").format(m.getBirth_date()));
|
||||||
|
|
||||||
|
FilenameFilter filter = (directory, filename) -> filename.startsWith(m.getId() + ".");
|
||||||
|
File[] files = new File(media, "ppMembre").listFiles(filter);
|
||||||
|
if (files != null && files.length > 0) {
|
||||||
|
File file = files[0];
|
||||||
|
cmd.add(file.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
cmd.add("/dev/null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getPdf(cmd, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> getAffiliationPdf(String subject) {
|
||||||
|
return getAffiliationPdf(
|
||||||
|
combRepository.find("userId = ?1", subject).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null || m.getClub() == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
}))
|
||||||
|
.map(MembreModel::getClub)
|
||||||
|
.call(m -> Mutiny.fetch(m.getAffiliations())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> getAffiliationPdf(long id) {
|
||||||
|
return getAffiliationPdf(
|
||||||
|
clubRepository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
}))
|
||||||
|
.call(m -> Mutiny.fetch(m.getAffiliations())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Uni<Response> getAffiliationPdf(Uni<ClubModel> uniBase) {
|
||||||
|
return uniBase
|
||||||
|
.map(Unchecked.function(m -> {
|
||||||
|
if (m.getAffiliations().stream()
|
||||||
|
.noneMatch(licenceModel -> licenceModel.getSaison() == Utils.getSaison()))
|
||||||
|
throw new DNotFoundException("Pas d'affiliation pour la saison en cours");
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] buff = make_pdf(m);
|
||||||
|
if (buff == null)
|
||||||
|
throw new IOException("Error making pdf");
|
||||||
|
|
||||||
|
String mimeType = "application/pdf";
|
||||||
|
|
||||||
|
Response.ResponseBuilder resp = Response.ok(buff);
|
||||||
|
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
resp.header(HttpHeaders.CONTENT_LENGTH, buff.length);
|
||||||
|
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||||
|
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"inline; " + "filename=\"Attestation d'affiliation " + Utils.getSaison() + "-" +
|
||||||
|
(Utils.getSaison() + 1) + " de " + m.getName() + ".pdf\"");
|
||||||
|
return resp.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] make_pdf(ClubModel m) throws IOException, InterruptedException {
|
||||||
|
List<String> cmd = new ArrayList<>();
|
||||||
|
cmd.add("java");
|
||||||
|
cmd.add("-jar");
|
||||||
|
cmd.add(pdfMakerJarPath);
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
cmd.add("/tmp/" + uuid + ".pdf");
|
||||||
|
cmd.add("club");
|
||||||
|
cmd.add(m.getName());
|
||||||
|
cmd.add(Utils.getSaison() + "");
|
||||||
|
cmd.add(m.getNo_affiliation() + "");
|
||||||
|
cmd.add(new File(sign_file).getAbsolutePath());
|
||||||
|
|
||||||
|
return getPdf(cmd, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] getPdf(List<String> cmd, UUID uuid) throws IOException, InterruptedException {
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
Process process = processBuilder.start();
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Thread t = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null)
|
||||||
|
builder.append(line).append("\n");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
int code = -1;
|
||||||
|
if (!process.waitFor(30, TimeUnit.SECONDS)) {
|
||||||
|
process.destroy();
|
||||||
|
builder.append("Timeout...");
|
||||||
|
} else {
|
||||||
|
code = process.exitValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.isAlive())
|
||||||
|
t.interrupt();
|
||||||
|
|
||||||
|
PDFService.LOGGER.debug("PDF maker: " + builder);
|
||||||
|
|
||||||
|
if (code != 0) {
|
||||||
|
throw new IOException("Error code: " + code);
|
||||||
|
} else {
|
||||||
|
File file = new File("/tmp/" + uuid + ".pdf");
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
byte[] buff = fis.readAllBytes();
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.delete();
|
||||||
|
return buff;
|
||||||
|
} catch (IOException e) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.LicenceStats;
|
||||||
|
import fr.titionfire.ffsaf.utils.Categorie;
|
||||||
|
import fr.titionfire.ffsaf.utils.Genre;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.smallrye.mutiny.Multi;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
public class StatsService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LicenceRepository licenceRepository;
|
||||||
|
|
||||||
|
public Uni<LicenceStats> getStats() {
|
||||||
|
ConcurrentHashMap<Categorie, Integer> categories = new ConcurrentHashMap<>();
|
||||||
|
LicenceStats stats = new LicenceStats();
|
||||||
|
int currentSaison = Utils.getSaison();
|
||||||
|
|
||||||
|
//noinspection ReactiveStreamsUnusedPublisher
|
||||||
|
return licenceRepository.listAll()
|
||||||
|
.onItem().transformToMulti(licences -> Multi.createFrom().iterable(licences))
|
||||||
|
.call(licence -> Mutiny.fetch(licence.getMembre()))
|
||||||
|
.invoke(licence -> {
|
||||||
|
if (!stats.getLicences().containsKey(licence.getSaison()))
|
||||||
|
stats.getLicences().put(licence.getSaison(), new LicenceStats.YearStats());
|
||||||
|
|
||||||
|
LicenceStats.YearStats yearStats = stats.getLicences().get(licence.getSaison());
|
||||||
|
|
||||||
|
System.out.println("stats: " + licence.getMembre().getFname());
|
||||||
|
if (licence.isValidate()) {
|
||||||
|
if (licence.getMembre().getGenre() == Genre.H)
|
||||||
|
yearStats.addH();
|
||||||
|
else if (licence.getMembre().getGenre() == Genre.F)
|
||||||
|
yearStats.addF();
|
||||||
|
else
|
||||||
|
yearStats.addNa();
|
||||||
|
|
||||||
|
if (licence.getSaison() == currentSaison && licence.getMembre().getCategorie() != null) {
|
||||||
|
categories.put(licence.getMembre().getCategorie(),
|
||||||
|
categories.getOrDefault(licence.getMembre().getCategorie(), 0) + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yearStats.addNotValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.collect().asList()
|
||||||
|
.map(__ -> {
|
||||||
|
for (Categorie c : Categorie.values()) {
|
||||||
|
LicenceStats.CategoriesStats o = new LicenceStats.CategoriesStats();
|
||||||
|
|
||||||
|
o.setName(c.getName());
|
||||||
|
o.setCount(categories.getOrDefault(c, 0));
|
||||||
|
|
||||||
|
stats.getCategories().add(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.service;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.HelloassoNotification;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class WebhookService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CheckoutService checkoutService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionService competitionService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "helloasso.organizationSlug")
|
||||||
|
String organizationSlug;
|
||||||
|
|
||||||
|
public Uni<Response> helloAssoNotification(HelloassoNotification notification) {
|
||||||
|
if (notification.getEventType().equals("Payment")) {
|
||||||
|
if (notification.getData().getOrder().getFormType().equals("Checkout")) {
|
||||||
|
if (notification.getData().getOrder().getOrganizationSlug().equalsIgnoreCase(organizationSlug)) {
|
||||||
|
return checkoutService.paymentStatusChange(notification.getData().getState(),
|
||||||
|
notification.getMetadata());
|
||||||
|
}
|
||||||
|
} else if (notification.getData().getOrder().getFormType().equals("Event")) {
|
||||||
|
return competitionService.unregisterHelloAsso(notification.getData());
|
||||||
|
}
|
||||||
|
}else if (notification.getEventType().equals("Order")){
|
||||||
|
if (notification.getData().getFormType().equals("Event")) {
|
||||||
|
return competitionService.registerHelloAsso(notification.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uni.createFrom().item(Response.ok().build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -40,7 +40,7 @@ public class Client_Thread extends Thread {
|
|||||||
|
|
||||||
private boolean isAuth;
|
private boolean isAuth;
|
||||||
|
|
||||||
private final HashMap<UUID, JsonConsumer<Object>> waitResult = new HashMap<>();
|
private final HashMap<UUID, JsonConsumer<?>> waitResult = new HashMap<>();
|
||||||
|
|
||||||
public Client_Thread(ServerCustom serv, Socket s, PublicKey publicKey) throws IOException {
|
public Client_Thread(ServerCustom serv, Socket s, PublicKey publicKey) throws IOException {
|
||||||
this.serv = serv;
|
this.serv = serv;
|
||||||
@ -162,7 +162,7 @@ public class Client_Thread extends Thread {
|
|||||||
sendReq(object, type, null);
|
sendReq(object, type, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendReq(Object object, String code, JsonConsumer<Object> consumer) {
|
public void sendReq(Object object, String code, JsonConsumer<?> consumer) {
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
do {
|
do {
|
||||||
uuid = UUID.randomUUID();
|
uuid = UUID.randomUUID();
|
||||||
|
|||||||
@ -22,6 +22,7 @@ public class SimpleClubModel {
|
|||||||
if (model == null)
|
if (model == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), model.getShieldURL());
|
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(),
|
||||||
|
"/api/club/" + model.getClubId() + "/logo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,14 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
|
@Schema(hidden = true)
|
||||||
public class SimpleCombModel {
|
public class SimpleCombModel {
|
||||||
Long id;
|
Long id;
|
||||||
String lname = "";
|
String lname = "";
|
||||||
@ -24,12 +26,12 @@ public class SimpleCombModel {
|
|||||||
int licence = 0;
|
int licence = 0;
|
||||||
String country = "fr";
|
String country = "fr";
|
||||||
|
|
||||||
public static SimpleCombModel fromModel(MembreModel model) {
|
public static SimpleCombModel fromModel(MembreModel model) {
|
||||||
if (model == null)
|
if (model == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new SimpleCombModel(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
|
return new SimpleCombModel(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
|
||||||
(model.getClub() == null) ? null : SimpleClubModel.fromModel(model.getClub()),
|
(model.getClub() == null) ? null : SimpleClubModel.fromModel(model.getClub()),
|
||||||
model.getGenre(), model.getLicence(), model.getCountry());
|
model.getGenre(), (model.getLicence() == null) ? -1 : model.getLicence(), model.getCountry());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
package fr.titionfire.ffsaf.net2.data;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RegisterForReflection
|
||||||
|
public record SimpleCompet(long id, String owner, boolean show_blason, boolean show_flag, List<UUID> admin, List<UUID> table) {
|
||||||
|
}
|
||||||
@ -11,16 +11,6 @@ import java.util.HashMap;
|
|||||||
public class RComb {
|
public class RComb {
|
||||||
private static final Logger LOGGER = Logger.getLogger(RComb.class);
|
private static final Logger LOGGER = Logger.getLogger(RComb.class);
|
||||||
|
|
||||||
final IAction findComb = (client_Thread, message) -> {
|
|
||||||
try {
|
|
||||||
SimpleCombModel combModel = ServerCustom.getInstance().membreService.find(message.data().get("licence").asInt(), message.data().get("np").asText());
|
|
||||||
client_Thread.sendRepTo(combModel, message);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
|
||||||
client_Thread.sendErrTo(e.getMessage(), message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final CIA<Long> findByIdOptionalComb = new CIA<>(Long.class, (client_Thread, message) -> {
|
final CIA<Long> findByIdOptionalComb = new CIA<>(Long.class, (client_Thread, message) -> {
|
||||||
try {
|
try {
|
||||||
SimpleCombModel combModel = ServerCustom.getInstance().membreService.findByIdOptionalComb(message.data());
|
SimpleCombModel combModel = ServerCustom.getInstance().membreService.findByIdOptionalComb(message.data());
|
||||||
@ -34,7 +24,6 @@ public class RComb {
|
|||||||
public static void register(HashMap<String, IAction> iMap) {
|
public static void register(HashMap<String, IAction> iMap) {
|
||||||
RComb rComb = new RComb();
|
RComb rComb = new RComb();
|
||||||
|
|
||||||
iMap.put("findComb", rComb.findComb);
|
|
||||||
iMap.put("findByIdOptionalComb", rComb.findByIdOptionalComb);
|
iMap.put("findByIdOptionalComb", rComb.findByIdOptionalComb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.net2.packet;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.ws.FileSocket;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
public class RFile {
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(RFile.class);
|
|
||||||
|
|
||||||
final IAction requestSend = (client_Thread, message) -> {
|
|
||||||
try {
|
|
||||||
switch (message.data().get("type").asText()) {
|
|
||||||
case "match":
|
|
||||||
String code = UUID.randomUUID() + "-" + UUID.randomUUID();
|
|
||||||
|
|
||||||
FileSocket.FileRecv fileRecv = new FileSocket.FileRecv(null, message.data().get("name").asText(), null, null,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
FileSocket.sessions.put(code, fileRecv);
|
|
||||||
|
|
||||||
client_Thread.sendRepTo(code, message);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
client_Thread.sendErrTo("", message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
|
||||||
client_Thread.sendErrTo(e.getMessage(), message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void register(HashMap<String, IAction> iMap) {
|
|
||||||
RFile rFile = new RFile();
|
|
||||||
|
|
||||||
iMap.put("requestSend", rFile.requestSend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,6 +9,5 @@ public class RegisterAction {
|
|||||||
|
|
||||||
RComb.register(iMap);
|
RComb.register(iMap);
|
||||||
RClub.register(iMap);
|
RClub.register(iMap);
|
||||||
RFile.register(iMap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
package fr.titionfire.ffsaf.net2.request;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.net2.Client_Thread;
|
||||||
|
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||||
|
import fr.titionfire.ffsaf.utils.JsonConsumer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class SReqCompet {
|
||||||
|
|
||||||
|
public static void sendUpdate(ArrayList<Client_Thread> client_Thread, SimpleCompet compet) {
|
||||||
|
for (Client_Thread client : client_Thread) {
|
||||||
|
client.sendNotify(compet, "sendConfig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void getConfig(ArrayList<Client_Thread> client_Thread, long id_compet,
|
||||||
|
CompletableFuture<SimpleCompet> future) {
|
||||||
|
if (client_Thread.isEmpty()) return;
|
||||||
|
client_Thread.get(0).sendReq(id_compet, "getConfig",
|
||||||
|
new JsonConsumer<>(SimpleCompet.class, future::complete));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void getAllHaveAccess(ArrayList<Client_Thread> client_Thread, String userId,
|
||||||
|
CompletableFuture<HashMap<String, String>> future) {
|
||||||
|
if (client_Thread.isEmpty()) return;
|
||||||
|
client_Thread.get(0).sendReq(userId, "getAllHaveAccess",
|
||||||
|
new JsonConsumer<>(HashMap.class, future::complete));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void rmCompet(ArrayList<Client_Thread> client_Thread, long id_compet) {
|
||||||
|
for (Client_Thread client : client_Thread) {
|
||||||
|
client.sendNotify(id_compet, "rmCompet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package fr.titionfire.ffsaf.net2.request;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.net2.Client_Thread;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class SReqRegister {
|
||||||
|
public static void sendIfNeed(ArrayList<Client_Thread> client_Thread, CompetitionData.SimpleRegister simpleRegister, Long competitionId) {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
map.put("simpleRegister", simpleRegister);
|
||||||
|
map.put("competitionId", competitionId);
|
||||||
|
|
||||||
|
for (Client_Thread client : client_Thread) {
|
||||||
|
client.sendNotify(map, "sendRegister");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendRmIfNeed(ArrayList<Client_Thread> clients, Long combId, Long id) {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
map.put("combId", combId);
|
||||||
|
map.put("competitionId", id);
|
||||||
|
|
||||||
|
for (Client_Thread client : clients) {
|
||||||
|
client.sendNotify(map, "sendRmRegister");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,29 +1,94 @@
|
|||||||
package fr.titionfire.ffsaf.rest;
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
import fr.titionfire.ffsaf.domain.service.AffiliationService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.*;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Tag(name = "Affiliation API", description = "API pour gérer les affiliations")
|
||||||
@Path("api/affiliation")
|
@Path("api/affiliation")
|
||||||
public class AffiliationEndpoints {
|
public class AffiliationEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AffiliationService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
Consumer<Long> checkPerm = Unchecked.consumer(id -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/current")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les affiliations pour la saison en cours", description = "Cette méthode renvoie les affiliations pour la saison en cours. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<List<SimpleAffiliation>> getCurrentSaisonAffiliationAdmin() {
|
||||||
|
return service.getCurrentSaisonAffiliation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les affiliations pour un club", description = "Cette méthode renvoie les affiliations pour un club donné. Seuls les administrateurs de la fédération et les présidents, secrétaires et responsables intranet du club peuvent accéder à cette méthode.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Club non trouvé")
|
||||||
|
})
|
||||||
|
public Uni<List<SimpleAffiliation>> getAffiliation(
|
||||||
|
@Parameter(description = "L'identifiant du club") @PathParam("id") long id) {
|
||||||
|
return Uni.createFrom().item(id).invoke(checkPerm).chain(__ -> service.getAffiliation(id));
|
||||||
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("save")
|
@Path("{id}")
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@RolesAllowed("federation_admin")
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
@Operation(summary = "Ajoute une affiliation pour un club", description = "Cette méthode ajoute une affiliation pour un club et une saison donné. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
|
||||||
System.out.println(form);
|
@APIResponses(value = {
|
||||||
return Uni.createFrom().item("OK");
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Club non trouvé")
|
||||||
|
})
|
||||||
|
public Uni<SimpleAffiliation> setAffiliation(
|
||||||
|
@Parameter(description = "L'identifiant du club") @PathParam("id") long id,
|
||||||
|
@Parameter(description = "La saison à pour la quelle ajoute l'affiliation") @QueryParam("saison") int saison) {
|
||||||
|
return service.setAffiliation(id, saison);
|
||||||
}
|
}
|
||||||
/*@POST
|
|
||||||
@Path("affiliation")
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed("federation_admin")
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Operation(summary = "Supprime une affiliation", description = "Cette méthode supprime l'affiliation {id}. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
|
||||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
@APIResponses(value = {
|
||||||
System.out.println(form);
|
@APIResponse(responseCode = "204", description = "Réussite"),
|
||||||
return service.save(form);
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
}*/
|
})
|
||||||
|
public Uni<?> deleteAffiliation(
|
||||||
|
@Parameter(description = "L'identifiant de l'affiliation") @PathParam("id") long id) {
|
||||||
|
return service.deleteAffiliation(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,191 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.AffiliationService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Path("api/affiliation/request")
|
||||||
|
public class AffiliationRequestEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AffiliationService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
Consumer<Long> checkPerm = Unchecked.consumer(id -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie toutes les demandes d'affiliation", description = "Cette méthode renvoie toutes les " +
|
||||||
|
"demandes d'affiliation sous forme de résumés.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<List<SimpleReqAffiliationResume>> getAllAffRequest() {
|
||||||
|
return service.getAllReq().map(o -> o.stream().map(SimpleReqAffiliationResume::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Enregistre une nouvelle demande d'affiliation", description = "Cette méthode enregistre une " +
|
||||||
|
"nouvelle demande d'affiliation à partir des données soumises dans le formulaire. Ne nécessite pas d'authentification.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
||||||
|
return service.save(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie une demande d'affiliation", description = "Cette méthode renvoie une demande d'affiliation " +
|
||||||
|
"pour l'identifiant spécifié.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Demande d'affiliation non trouvée")
|
||||||
|
})
|
||||||
|
public Uni<SimpleReqAffiliation> getAffRequest(
|
||||||
|
@Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) {
|
||||||
|
return service.getRequest(id).invoke(Unchecked.consumer(o -> {
|
||||||
|
if (o.getClub() == null && !securityCtx.roleHas("federation_admin"))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
})).invoke(o -> checkPerm.accept(o.getClub()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Supprime une demande d'affiliation", description = "Cette méthode supprime une demande " +
|
||||||
|
"d'affiliation pour l'identifiant spécifié.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Demande d'affiliation non trouvée")
|
||||||
|
})
|
||||||
|
public Uni<?> getDelAffRequest(
|
||||||
|
@Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id, @QueryParam("reason") String reason) {
|
||||||
|
return service.getRequest(id).invoke(Unchecked.consumer(o -> {
|
||||||
|
if (o.getClub() == null && !securityCtx.roleHas("federation_admin"))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
})).invoke(o -> checkPerm.accept(o.getClub()))
|
||||||
|
.chain(o -> service.deleteReqAffiliation(id, reason, securityCtx.roleHas("federation_admin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/save")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Enregistre une demande d'affiliation en tant qu'admin", description = "Cette méthode " +
|
||||||
|
"enregistre une demande d'affiliation en tant qu'admin à partir des données soumises dans le formulaire.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<?> saveAdminAffRequest(AffiliationRequestSaveForm form) {
|
||||||
|
return service.saveAdmin(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/edit")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Modifie une demande d'affiliation", description = "Cette méthode modifie une demande " +
|
||||||
|
"d'affiliation à partir des données soumises dans le formulaire.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<?> saveEditAffRequest(AffiliationRequestForm form) {
|
||||||
|
return service.saveEdit(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/apply")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Accepte une demande d'affiliation", description = "Cette méthode accepte une demande " +
|
||||||
|
"d'affiliation à partir des données soumises dans le formulaire.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé")
|
||||||
|
})
|
||||||
|
public Uni<?> acceptAffRequest(AffiliationRequestSaveForm form) {
|
||||||
|
return service.accept(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/logo")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Operation(summary = "Renvoie le logo d'une demande d'affiliation", description = "Cette méthode renvoie le logo" +
|
||||||
|
" d'une demande d'affiliation pour l'identifiant spécifié.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Logo non trouvé")
|
||||||
|
})
|
||||||
|
public Uni<Response> getLogo(
|
||||||
|
@Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException {
|
||||||
|
return Utils.getMediaFile(id, media, "aff_request/logo", Uni.createFrom().nullItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/status")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Operation(summary = "Renvoie le statut d'une demande d'affiliation", description = "Cette méthode renvoie le statut" +
|
||||||
|
" d'une demande d'affiliation pour l'identifiant spécifié.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Statut non trouvé")
|
||||||
|
})
|
||||||
|
public Uni<Response> getStatus(
|
||||||
|
@Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException {
|
||||||
|
return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id,
|
||||||
|
Uni.createFrom().nullItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,53 +1,38 @@
|
|||||||
package fr.titionfire.ffsaf.rest;
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.domain.service.AffiliationService;
|
|
||||||
import fr.titionfire.ffsaf.rest.client.SirenService;
|
import fr.titionfire.ffsaf.rest.client.SirenService;
|
||||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
import fr.titionfire.ffsaf.rest.client.StateIdService;
|
||||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
import fr.titionfire.ffsaf.rest.data.AssoData;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.ws.rs.*;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jodd.net.MimeTypes;
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|
||||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
@Path("api/asso")
|
@Path("api/asso")
|
||||||
public class AssoEndpoints {
|
public class AssoEndpoints {
|
||||||
|
|
||||||
|
@RestClient
|
||||||
|
StateIdService stateIdService;
|
||||||
|
|
||||||
@RestClient
|
@RestClient
|
||||||
SirenService sirenService;
|
SirenService sirenService;
|
||||||
|
|
||||||
@Inject
|
|
||||||
AffiliationService service;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "upload_dir")
|
|
||||||
String media;
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("siren/{siren}")
|
@Path("state_id/{stateId}")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Uni<UniteLegaleRoot> getInfoSiren(@PathParam("siren") String siren) {
|
@Operation(hidden = true)
|
||||||
return sirenService.get_unite(siren).onFailure().transform(throwable -> {
|
public Uni<AssoData> getAssoInfo(@PathParam("stateId") String stateId) {
|
||||||
|
return ((stateId.charAt(0) == 'W') ? stateIdService.get_rna(stateId) : sirenService.get_unite(
|
||||||
|
stateId).chain(stateIdService::getAssoDataFromUnit)).onFailure().transform(throwable -> {
|
||||||
if (throwable instanceof WebApplicationException exception) {
|
if (throwable instanceof WebApplicationException exception) {
|
||||||
|
if (exception.getResponse().getStatus() == 404)
|
||||||
|
return new DNotFoundException("Service momentanément indisponible");
|
||||||
if (exception.getResponse().getStatus() == 400)
|
if (exception.getResponse().getStatus() == 400)
|
||||||
return new BadRequestException("Not found");
|
return new DNotFoundException("Asso introuvable");
|
||||||
}
|
}
|
||||||
return throwable;
|
return throwable;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("affiliation")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
|
||||||
return service.save(form);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,9 @@ import jakarta.ws.rs.core.MediaType;
|
|||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -25,10 +28,16 @@ public class AuthEndpoints {
|
|||||||
SecurityIdentity securityIdentity;
|
SecurityIdentity securityIdentity;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JsonWebToken accessToken;
|
JsonWebToken IdToken;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Vérifie si l'utilisateur est authentifié", description = "Cette méthode renvoie true si " +
|
||||||
|
"l'utilisateur est authentifié et false sinon.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite")
|
||||||
|
})
|
||||||
|
|
||||||
public Boolean auth() {
|
public Boolean auth() {
|
||||||
return !securityIdentity.isAnonymous();
|
return !securityIdentity.isAnonymous();
|
||||||
}
|
}
|
||||||
@ -37,14 +46,21 @@ public class AuthEndpoints {
|
|||||||
@Path("/userinfo")
|
@Path("/userinfo")
|
||||||
@Authenticated
|
@Authenticated
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les informations de l'utilisateur authentifié", description = "Cette méthode renvoie les" +
|
||||||
|
" informations de l'utilisateur authentifié sous forme d'objet JSON.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Réussite"),
|
||||||
|
@APIResponse(responseCode = "401", description = "Utilisateur non authentifié")
|
||||||
|
})
|
||||||
public UserInfo userinfo() {
|
public UserInfo userinfo() {
|
||||||
return UserInfo.makeUserInfo(accessToken, securityIdentity);
|
return UserInfo.makeUserInfo(IdToken, securityIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/login")
|
@Path("/login")
|
||||||
@Authenticated
|
@Authenticated
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(hidden = true)
|
||||||
public Response login() throws URISyntaxException {
|
public Response login() throws URISyntaxException {
|
||||||
return Response.temporaryRedirect(new URI(redirect)).build();
|
return Response.temporaryRedirect(new URI(redirect)).build();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.CategoryService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CategoryData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CategoryFullData;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Authenticated
|
||||||
|
@Path("api/poule/{system}/admin/")
|
||||||
|
public class CategoryAdminEndpoints {
|
||||||
|
|
||||||
|
@PathParam("system")
|
||||||
|
private CompetitionSystem system;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CategoryService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<CategoryData> getByIdAdmin(@PathParam("id") Long id) {
|
||||||
|
return service.getByIdAdmin(securityCtx, system, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<CategoryData>> getAllAdmin() {
|
||||||
|
return service.getAllAdmin(securityCtx, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<CategoryData> addOrUpdate(CategoryData data) {
|
||||||
|
return service.addOrUpdate(securityCtx, system, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("sync")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> syncCategory(CategoryFullData data) {
|
||||||
|
return service.syncCategory(securityCtx, system, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> delete(@PathParam("id") Long id) {
|
||||||
|
return service.delete(securityCtx, system, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,28 +1,374 @@
|
|||||||
package fr.titionfire.ffsaf.rest;
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||||
import fr.titionfire.ffsaf.domain.service.ClubService;
|
import fr.titionfire.ffsaf.domain.service.ClubService;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.PDFService;
|
||||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.*;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.PartClubForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.Contact;
|
||||||
|
import fr.titionfire.ffsaf.utils.PageResult;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
import io.quarkus.security.Authenticated;
|
import io.quarkus.security.Authenticated;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Tag(name = "Club", description = "Gestion des clubs")
|
||||||
@Path("api/club")
|
@Path("api/club")
|
||||||
public class ClubEndpoints {
|
public class ClubEndpoints {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ClubService clubService;
|
ClubService clubService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PDFService pdfService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(clubModel.getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
Consumer<Long> checkPerm2 = Unchecked.consumer(id -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/no_detail")
|
@Path("/no_detail")
|
||||||
@Authenticated
|
@Authenticated
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie la liste de tous les clubs sans détails", description = "Renvoie la liste de tous les " +
|
||||||
|
"clubs sans les détails des membres et des affiliations")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste de tous les clubs sans détails"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<List<SimpleClubModel>> getAll() {
|
public Uni<List<SimpleClubModel>> getAll() {
|
||||||
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList());
|
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).sorted(
|
||||||
|
Comparator.comparing(SimpleClubModel::getName)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/contact_type")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les types de contacts pour les clubs", description = "Renvoie la liste des types de " +
|
||||||
|
"contacts possibles pour les clubs")
|
||||||
|
public Uni<HashMap<String, String>> getConcatType() {
|
||||||
|
return Uni.createFrom().item(Contact.toSite());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/find")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Recherche des clubs en fonction de critères de recherche", description = "Recherche des clubs " +
|
||||||
|
"en fonction de critères de recherche tels que le nom, le pays, etc.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des clubs correspondant aux critères de recherche"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<PageResult<SimpleClubList>> getFindAdmin(
|
||||||
|
@Parameter(description = "Nombre max de résulta (max 50)") @QueryParam("limit") Integer limit,
|
||||||
|
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
|
||||||
|
@Parameter(description = "Text à rechercher") @QueryParam("search") String search,
|
||||||
|
@Parameter(description = "Pays à filter") @QueryParam("country") String country) {
|
||||||
|
if (limit == null)
|
||||||
|
limit = 50;
|
||||||
|
if (page == null || page < 1)
|
||||||
|
page = 1;
|
||||||
|
return clubService.search(limit, page - 1, search, country);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les détails d'un club en fonction de son identifiant", description = "Renvoie les " +
|
||||||
|
"détails d'un club en fonction de son identifiant, y compris les informations sur les membres et les affiliations")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les détails du club"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<SimpleClub> getById(
|
||||||
|
@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel).invoke(m -> {
|
||||||
|
m.setContactMap(Contact.toSite());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Met à jour les informations d'un club en fonction de son identifiant", description = "Met à " +
|
||||||
|
"jour les informations d'un club en fonction de son identifiant, y compris les informations sur les membres" +
|
||||||
|
" et les affiliations")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le club a été mis à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> setAdminClub(
|
||||||
|
@Parameter(description = "Identifiant de club") @PathParam("id") long id, FullClubForm input) {
|
||||||
|
return clubService.update(id, input)
|
||||||
|
.invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
||||||
|
})).chain(() -> {
|
||||||
|
if (input.getLogo().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub"
|
||||||
|
)).invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK"))
|
||||||
|
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||||
|
})); // TODO log
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}).chain(() -> {
|
||||||
|
if (input.getStatus().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
|
||||||
|
)).invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK"))
|
||||||
|
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||||
|
})); // TODO log
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Ajoute un nouveau club", description = "Ajoute un nouveau club avec les informations fournies" +
|
||||||
|
" dans le formulaire")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le club a été ajouté avec succès"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Les données envoyées sont invalides"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Long> addAdminClub(FullClubForm input) {
|
||||||
|
return clubService.add(input)
|
||||||
|
.invoke(Unchecked.consumer(id -> {
|
||||||
|
if (id == null) throw new InternalError("Fail to create club data");
|
||||||
|
})).call(id -> {
|
||||||
|
if (input.getLogo().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub"
|
||||||
|
)); // TODO log
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}).call(id -> {
|
||||||
|
if (input.getStatus().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
|
||||||
|
)); // TODO log
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Supprime un club en fonction de son identifiant", description = "Supprime un club en fonction" +
|
||||||
|
" de son identifiant, ainsi que toutes les informations associées")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Le club a été supprimé avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<?> deleteAdminClub(
|
||||||
|
@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return clubService.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/affiliation")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Operation(summary = "Renvoie l'attestation d'affiliation du club en fonction de son identifiant", description =
|
||||||
|
"Renvoie l'attestation d'affiliation du club en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas ou n'a pas d'affiliation active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getAffiliation(@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return pdfService.getAffiliationPdf(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/me")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les informations du club de l'utilisateur connecté", description = "Renvoie les " +
|
||||||
|
"informations du club de l'utilisateur connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les informations du club de l'utilisateur connecté"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "L'utilisateur n'est pas membre d'un club"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<SimpleClub> getOfUser() {
|
||||||
|
return clubService.getOfUser(securityCtx).map(SimpleClub::fromModel)
|
||||||
|
.invoke(m -> m.setContactMap(Contact.toSite()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/me")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Met à jour les informations du club de l'utilisateur connecté", description = "Met à jour les" +
|
||||||
|
" informations du club de l'utilisateur connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les informations du club de l'utilisateur connecté ont été mises à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "L'utilisateur n'est pas membre d'un club"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> setClubOfUser(PartClubForm form) {
|
||||||
|
return clubService.updateOfUser(securityCtx, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/me/affiliation")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté", description =
|
||||||
|
"Renvoie l'attestation d'affiliation du club de l'utilisateur connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'a pas d'affiliation active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getMeAffiliation() {
|
||||||
|
return pdfService.getAffiliationPdf(securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/members")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra", "club_tresorier"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Revoie tout les membres de votre club")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "List des membres"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "L'utilisateur n'est pas membre d'un club"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<List<VerySimpleMembre>> getMembers() {
|
||||||
|
return clubService.getMembers(securityCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/renew/{id}")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(hidden = true)
|
||||||
|
public Uni<RenewAffData> getRenew(@PathParam("id") long id, @QueryParam("m1") long m1_id,
|
||||||
|
@QueryParam("m2") long m2_id, @QueryParam("m3") long m3_id) {
|
||||||
|
return Uni.createFrom().item(id).invoke(checkPerm2)
|
||||||
|
.chain(__ -> clubService.getRenewData(id, List.of(m1_id, m2_id, m3_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/desk/{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie la liste des membres du bureau du club", description = "Renvoie la liste des membres " +
|
||||||
|
"du bureau du club spécifié")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des membres du bureau du club"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<List<DeskMember>> getClubDesk(
|
||||||
|
@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return clubService.getClubDesk(checkPerm, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{clubId}/logo")
|
||||||
|
@Operation(summary = "Renvoie le logo du club", description = "Renvoie le logo du club spécifié")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le logo du club"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getLogo(
|
||||||
|
@Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) {
|
||||||
|
return clubService.getByClubId(clubId).chain(Unchecked.function(clubModel -> {
|
||||||
|
try {
|
||||||
|
return Utils.getMediaFile((clubModel != null) ? clubModel.getId() : -1, media, "ppClub",
|
||||||
|
Uni.createFrom().nullItem());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}/status")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie le statut du club", description = "Renvoie le statut du club spécifié")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le statut du club"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getStatus(
|
||||||
|
@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return clubService.getById(id).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> {
|
||||||
|
try {
|
||||||
|
return Utils.getMediaFile(clubModel.getId(), media, "clubStatus",
|
||||||
|
"statue-" + clubModel.getName(), Uni.createFrom().nullItem());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("get_map_data")
|
||||||
|
public Uni<List<ClubMapData>> getMapData() {
|
||||||
|
return clubService.getMapData();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,249 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.rest;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
|
||||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
|
||||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
|
||||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
|
||||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
|
||||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
|
||||||
import fr.titionfire.ffsaf.utils.PageResult;
|
|
||||||
import fr.titionfire.ffsaf.utils.Pair;
|
|
||||||
import fr.titionfire.ffsaf.utils.Utils;
|
|
||||||
import io.quarkus.oidc.IdToken;
|
|
||||||
import io.quarkus.security.Authenticated;
|
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
|
||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.ws.rs.*;
|
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
import jakarta.ws.rs.core.Response;
|
|
||||||
import jodd.net.MimeTypes;
|
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Authenticated
|
|
||||||
@Path("api/member")
|
|
||||||
public class CombEndpoints {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MembreService membreService;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "upload_dir")
|
|
||||||
String media;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@IdToken
|
|
||||||
JsonWebToken idToken;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
SecurityIdentity securityIdentity;
|
|
||||||
|
|
||||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
|
||||||
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
|
||||||
throw new ForbiddenException();
|
|
||||||
});
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/find/admin")
|
|
||||||
@RolesAllowed({"federation_admin"})
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Uni<PageResult<SimpleMembre>> getFindAdmin(@QueryParam("limit") Integer limit,
|
|
||||||
@QueryParam("page") Integer page,
|
|
||||||
@QueryParam("search") String search,
|
|
||||||
@QueryParam("club") String club) {
|
|
||||||
if (limit == null)
|
|
||||||
limit = 50;
|
|
||||||
if (page == null || page < 1)
|
|
||||||
page = 1;
|
|
||||||
return membreService.searchAdmin(limit, page - 1, search, club);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/find/club")
|
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Uni<PageResult<SimpleMembre>> getFindClub(@QueryParam("limit") Integer limit,
|
|
||||||
@QueryParam("page") Integer page,
|
|
||||||
@QueryParam("search") String search) {
|
|
||||||
if (limit == null)
|
|
||||||
limit = 50;
|
|
||||||
if (page == null || page < 1)
|
|
||||||
page = 1;
|
|
||||||
return membreService.search(limit, page - 1, search, idToken.getSubject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{id}")
|
|
||||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Uni<SimpleMembre> getById(@PathParam("id") long id) {
|
|
||||||
return membreService.getById(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Path("{id}")
|
|
||||||
@RolesAllowed({"federation_admin"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
public Uni<String> setAdminMembre(@PathParam("id") long id, FullMemberForm input) {
|
|
||||||
return membreService.update(id, input)
|
|
||||||
.invoke(Unchecked.consumer(out -> {
|
|
||||||
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
|
||||||
})).chain(() -> {
|
|
||||||
if (input.getPhoto_data().length > 0)
|
|
||||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
|
||||||
)).invoke(Unchecked.consumer(out -> {
|
|
||||||
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out);
|
|
||||||
}));
|
|
||||||
else
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@RolesAllowed({"federation_admin"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
public Uni<Long> addAdminMembre(FullMemberForm input) {
|
|
||||||
return membreService.add(input)
|
|
||||||
.invoke(Unchecked.consumer(id -> {
|
|
||||||
if (id == null) throw new InternalError("Fail to creat member data");
|
|
||||||
})).call(id -> {
|
|
||||||
if (input.getPhoto_data().length > 0)
|
|
||||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
|
||||||
));
|
|
||||||
else
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Path("{id}")
|
|
||||||
@RolesAllowed({"federation_admin"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public Uni<String> deleteAdminMembre(@PathParam("id") long id) {
|
|
||||||
return membreService.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Path("club/{id}")
|
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
public Uni<String> setMembre(@PathParam("id") long id, ClubMemberForm input) {
|
|
||||||
return membreService.update(id, input, idToken, securityIdentity)
|
|
||||||
.invoke(Unchecked.consumer(out -> {
|
|
||||||
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
|
||||||
})).chain(() -> {
|
|
||||||
if (input.getPhoto_data().length > 0)
|
|
||||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
|
||||||
)).invoke(Unchecked.consumer(out -> {
|
|
||||||
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out);
|
|
||||||
}));
|
|
||||||
else
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("club")
|
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
public Uni<Long> addMembre(FullMemberForm input) {
|
|
||||||
return membreService.add(input, idToken.getSubject())
|
|
||||||
.invoke(Unchecked.consumer(id -> {
|
|
||||||
if (id == null) throw new InternalError("Fail to creat member data");
|
|
||||||
})).call(id -> {
|
|
||||||
if (input.getPhoto_data().length > 0)
|
|
||||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
|
||||||
));
|
|
||||||
else
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Path("club/{id}")
|
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public Uni<String> deleteMembre(@PathParam("id") long id) {
|
|
||||||
return membreService.delete(id, idToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Future<String> replacePhoto(long id, byte[] input) {
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) {
|
|
||||||
String mimeType = URLConnection.guessContentTypeFromStream(is);
|
|
||||||
String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(mimeType, false);
|
|
||||||
if (detectedExtensions.length == 0)
|
|
||||||
throw new IOException("Fail to detect file extension for MIME type " + mimeType);
|
|
||||||
|
|
||||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
|
||||||
File[] files = new File(media, "ppMembre").listFiles(filter);
|
|
||||||
if (files != null) {
|
|
||||||
for (File file : files) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String extension = "." + detectedExtensions[0];
|
|
||||||
Files.write(new File(media, "ppMembre/" + id + extension).toPath(), input);
|
|
||||||
return "OK";
|
|
||||||
} catch (IOException e) {
|
|
||||||
return e.getMessage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{id}/photo")
|
|
||||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
|
||||||
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
|
||||||
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
|
|
||||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
|
||||||
File[] files = new File(media, "ppMembre").listFiles(filter);
|
|
||||||
if (files != null && files.length > 0) {
|
|
||||||
File file = files[0];
|
|
||||||
try {
|
|
||||||
byte[] data = Files.readAllBytes(file.toPath());
|
|
||||||
return new Pair<>(file, data);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
URI uri = new URI("https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava2.webp");
|
|
||||||
|
|
||||||
return membreService.getById(id).onItem().invoke(checkPerm).chain(__ -> Uni.createFrom().future(future)
|
|
||||||
.map(filePair -> {
|
|
||||||
if (filePair == null)
|
|
||||||
return Response.temporaryRedirect(uri).build();
|
|
||||||
|
|
||||||
String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName());
|
|
||||||
|
|
||||||
Response.ResponseBuilder resp = Response.ok(filePair.getValue());
|
|
||||||
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length);
|
|
||||||
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
|
||||||
resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; ");
|
|
||||||
|
|
||||||
return resp.build();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.CompetitionService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Path("api/competition/admin")
|
||||||
|
public class CompetitionAdminEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<CompetitionData> getByIdAdmin(@PathParam("id") Long id) {
|
||||||
|
return service.getByIdAdmin(securityCtx, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("all")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<CompetitionData>> getAllAdmin() {
|
||||||
|
return service.getAllAdmin(securityCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("all/{system}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) {
|
||||||
|
return service.getAllSystemAdmin(securityCtx, system);
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/main/java/fr/titionfire/ffsaf/rest/CompetitionEndpoints.java
Normal file
105
src/main/java/fr/titionfire/ffsaf/rest/CompetitionEndpoints.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.CompetitionService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.RegisterRequestData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Path("api/competition/")
|
||||||
|
public class CompetitionEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CompetitionService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<CompetitionData> getById(@PathParam("id") Long id, @QueryParam("light") boolean light) {
|
||||||
|
if (light)
|
||||||
|
return service.getById(securityCtx, id);
|
||||||
|
else
|
||||||
|
return service.getByIdAdmin(securityCtx, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}/register/{source}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<SimpleRegisterComb>> getRegister(@PathParam("id") Long id, @PathParam("source") String source) {
|
||||||
|
return service.getRegister(securityCtx, id, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{id}/register/{source}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(hidden = true)
|
||||||
|
public Uni<SimpleRegisterComb> addRegisterComb(@PathParam("id") Long id, @PathParam("source") String source,
|
||||||
|
RegisterRequestData data) {
|
||||||
|
return service.addRegisterComb(securityCtx, id, data, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}/register/{comb_id}/{source}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(hidden = true)
|
||||||
|
public Uni<Void> removeRegisterComb(@PathParam("id") Long id, @PathParam("comb_id") Long combId,
|
||||||
|
@PathParam("source") String source, @QueryParam("ban") boolean ban) {
|
||||||
|
return service.removeRegisterComb(securityCtx, id, combId, source, ban);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}/safcaData")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<SimpleCompetData> getSafcaData(@PathParam("id") Long id) {
|
||||||
|
return service.getSafcaData(securityCtx, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("all")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<CompetitionData>> getAll() {
|
||||||
|
return service.getAll(securityCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<CompetitionData> addOrUpdate(CompetitionData data) {
|
||||||
|
return service.addOrUpdate(securityCtx, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/safcaData")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> setSafcaData(SimpleCompetData data) {
|
||||||
|
return service.setSafcaData(securityCtx, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> delete(@PathParam("id") Long id) {
|
||||||
|
return service.delete(securityCtx, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +1,28 @@
|
|||||||
package fr.titionfire.ffsaf.rest;
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.domain.service.KeycloakService;
|
import fr.titionfire.ffsaf.domain.service.KeycloakService;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
import fr.titionfire.ffsaf.rest.from.MemberPermForm;
|
import fr.titionfire.ffsaf.rest.from.MemberPermForm;
|
||||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
|
||||||
import fr.titionfire.ffsaf.utils.Pair;
|
import fr.titionfire.ffsaf.utils.Pair;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import io.vertx.mutiny.core.Vertx;
|
import io.vertx.mutiny.core.Vertx;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.*;
|
import jakarta.ws.rs.GET;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import jakarta.ws.rs.PUT;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "Compte", description = "Gestion des comptes utilisateurs")
|
||||||
@Path("api/compte")
|
@Path("api/compte")
|
||||||
public class CompteEndpoints {
|
public class CompteEndpoints {
|
||||||
|
|
||||||
@ -23,10 +30,7 @@ public class CompteEndpoints {
|
|||||||
KeycloakService service;
|
KeycloakService service;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JsonWebToken accessToken;
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
@Inject
|
|
||||||
SecurityIdentity securityIdentity;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vertx vertx;
|
Vertx vertx;
|
||||||
@ -34,11 +38,20 @@ public class CompteEndpoints {
|
|||||||
@GET
|
@GET
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie les informations d'un compte utilisateur", description = "Renvoie les informations d'un" +
|
||||||
|
" compte utilisateur en fonction de son identifiant long (UUID)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les informations du compte utilisateur"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<KeycloakService.UserCompteState> getCompte(@PathParam("id") String id) {
|
public Uni<KeycloakService.UserCompteState> getCompte(@PathParam("id") String id) {
|
||||||
return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> {
|
return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||||
if (!securityIdentity.getRoles().contains("federation_admin") && pair.getKey().groups().stream().map(GroupRepresentation::getPath)
|
if (!securityCtx.roleHas("federation_admin") && pair.getKey().groups().stream()
|
||||||
.noneMatch(s -> s.startsWith("/club/") && GroupeUtils.contains(s, accessToken)))
|
.map(GroupRepresentation::getPath)
|
||||||
throw new ForbiddenException();
|
.noneMatch(s -> s.startsWith("/club/") && securityCtx.contains(s)))
|
||||||
|
throw new DForbiddenException();
|
||||||
return pair;
|
return pair;
|
||||||
})).map(Pair::getValue);
|
})).map(Pair::getValue);
|
||||||
}
|
}
|
||||||
@ -46,6 +59,14 @@ public class CompteEndpoints {
|
|||||||
@PUT
|
@PUT
|
||||||
@Path("{id}/init")
|
@Path("{id}/init")
|
||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
|
@Operation(summary = "Initialise un compte utilisateur", description = "Initialise un compte utilisateur en fonction" +
|
||||||
|
" de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Le compte utilisateur a été initialisé avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<?> initCompte(@PathParam("id") long id) {
|
public Uni<?> initCompte(@PathParam("id") long id) {
|
||||||
return service.initCompte(id);
|
return service.initCompte(id);
|
||||||
}
|
}
|
||||||
@ -53,6 +74,7 @@ public class CompteEndpoints {
|
|||||||
@PUT
|
@PUT
|
||||||
@Path("{id}/setUUID/{nid}")
|
@Path("{id}/setUUID/{nid}")
|
||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
|
@Operation(hidden = true)
|
||||||
public Uni<?> initCompte(@PathParam("id") long id, @PathParam("nid") String nid) {
|
public Uni<?> initCompte(@PathParam("id") long id, @PathParam("nid") String nid) {
|
||||||
return service.setId(id, nid);
|
return service.setId(id, nid);
|
||||||
}
|
}
|
||||||
@ -60,13 +82,29 @@ public class CompteEndpoints {
|
|||||||
@GET
|
@GET
|
||||||
@Path("{id}/roles")
|
@Path("{id}/roles")
|
||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
public Uni<?> getRole(@PathParam("id") String id) {
|
@Operation(summary = "Renvoie les rôles d'un compte utilisateur", description = "Renvoie les rôles d'un compte" +
|
||||||
|
" utilisateur en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les rôles du compte utilisateur"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<List<String>> getRole(@PathParam("id") String id) {
|
||||||
return service.fetchRole(id);
|
return service.fetchRole(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{id}/roles")
|
@Path("{id}/roles")
|
||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
|
@Operation(summary = "Met à jour les rôles d'un compte utilisateur", description = "Met à jour les rôles d'un compte" +
|
||||||
|
" utilisateur en fonction de son identifiant et des rôles fournis dans le formulaire")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Les rôles du compte utilisateur ont été mis à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<?> updateRole(@PathParam("id") String id, MemberPermForm form) {
|
public Uni<?> updateRole(@PathParam("id") String id, MemberPermForm form) {
|
||||||
List<String> toAdd = new ArrayList<>();
|
List<String> toAdd = new ArrayList<>();
|
||||||
List<String> toRemove = new ArrayList<>();
|
List<String> toRemove = new ArrayList<>();
|
||||||
@ -77,8 +115,8 @@ public class CompteEndpoints {
|
|||||||
else toRemove.add("safca_super_admin");
|
else toRemove.add("safca_super_admin");
|
||||||
if (form.isSafca_user()) toAdd.add("safca_user");
|
if (form.isSafca_user()) toAdd.add("safca_user");
|
||||||
else toRemove.add("safca_user");
|
else toRemove.add("safca_user");
|
||||||
if (form.isSafca_create_compet()) toAdd.add("safca_create_compet");
|
if (form.isCreate_compet()) toAdd.add("create_compet");
|
||||||
else toRemove.add("safca_create_compet");
|
else toRemove.add("create_compet");
|
||||||
|
|
||||||
return service.updateRole(id, toAdd, toRemove);
|
return service.updateRole(id, toAdd, toRemove);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Path("api/countries")
|
||||||
|
public class CountriesEndpoints {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{lang}/{code}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(hidden = true)
|
||||||
|
public Uni<HashMap<String, String>> getCountries(@PathParam("lang") String lang, @PathParam("code") String code) {
|
||||||
|
Locale locale = new Locale(lang, code);
|
||||||
|
return Uni.createFrom().item(new HashMap<String, String>())
|
||||||
|
.invoke(map -> {
|
||||||
|
String[] locales = Locale.getISOCountries();
|
||||||
|
for (String countryCode : locales) {
|
||||||
|
if (countryCode.equals("AN"))
|
||||||
|
continue;
|
||||||
|
Locale obj = new Locale("", countryCode);
|
||||||
|
map.put(countryCode, obj.getDisplayName(locale));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,17 +3,19 @@ package fr.titionfire.ffsaf.rest;
|
|||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.domain.service.LicenceService;
|
import fr.titionfire.ffsaf.domain.service.LicenceService;
|
||||||
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
||||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
import io.quarkus.oidc.IdToken;
|
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.*;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -25,39 +27,73 @@ public class LicenceEndpoints {
|
|||||||
LicenceService licenceService;
|
LicenceService licenceService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@IdToken
|
SecurityCtx securityCtx;
|
||||||
JsonWebToken idToken;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
SecurityIdentity securityIdentity;
|
|
||||||
|
|
||||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||||
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||||
throw new ForbiddenException();
|
throw new DForbiddenException();
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les licences d'un membre", description = "Renvoie les licences d'un membre en fonction " +
|
||||||
|
"de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des licences du membre"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<List<SimpleLicence>> getLicence(@PathParam("id") long id) {
|
public Uni<List<SimpleLicence>> getLicence(@PathParam("id") long id) {
|
||||||
return licenceService.getLicence(id, checkPerm).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
return licenceService.getLicence(id, checkPerm)
|
||||||
|
.map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("current/admin")
|
@Path("current/admin")
|
||||||
@RolesAllowed({"federation_admin"})
|
@RolesAllowed({"federation_admin"})
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les licences de la saison en cours (pour les administrateurs)", description = "Renvoie" +
|
||||||
|
" les licences de la saison en cours (pour les administrateurs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des licences de la saison en cours"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceAdmin() {
|
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceAdmin() {
|
||||||
return licenceService.getCurrentSaisonLicence(null).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
return licenceService.getCurrentSaisonLicence(null)
|
||||||
|
.map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("current/club")
|
@Path("current/club")
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les licences de la saison en cours (pour les clubs)", description = "Renvoie les " +
|
||||||
|
"licences de la saison en cours (pour les clubs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des licences de la saison en cours"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceClub() {
|
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceClub() {
|
||||||
return licenceService.getCurrentSaisonLicence(idToken).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
return licenceService.getCurrentSaisonLicence(securityCtx)
|
||||||
|
.map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("pay")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Paiement des licence", description = "Retourne le lien de paiement pour les licence des membre fournie")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Commande avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> payLicences(@Parameter(description = "Id des membres") List<Long> ids) {
|
||||||
|
return licenceService.payLicences(ids, checkPerm, securityCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -65,14 +101,45 @@ public class LicenceEndpoints {
|
|||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Créer une licence", description = "Créer unr licence en fonction de son identifiant et des " +
|
||||||
|
"informations fournies dans le formulaire (pour les administrateurs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La licence a été mise à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "La licence n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<SimpleLicence> setLicence(@PathParam("id") long id, LicenceForm form) {
|
public Uni<SimpleLicence> setLicence(@PathParam("id") long id, LicenceForm form) {
|
||||||
return licenceService.setLicence(id, form).map(SimpleLicence::fromModel);
|
return licenceService.setLicence(id, form).map(SimpleLicence::fromModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("validate")
|
||||||
|
@RolesAllowed("federation_admin")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Validation licence", description = "Valide en masse les licence de l'année en cours (pour les administrateurs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<?> valideLicences(@Parameter(description = "Id des membres a valider") List<Long> ids) {
|
||||||
|
return licenceService.valideLicences(ids);
|
||||||
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed("federation_admin")
|
@RolesAllowed("federation_admin")
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Supprime une licence", description = "Supprime une licence en fonction de son identifiant " +
|
||||||
|
"(pour les administrateurs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "La licence a été supprimée avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "La licence n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<?> deleteLicence(@PathParam("id") long id) {
|
public Uni<?> deleteLicence(@PathParam("id") long id) {
|
||||||
return licenceService.deleteLicence(id);
|
return licenceService.deleteLicence(id);
|
||||||
}
|
}
|
||||||
@ -82,6 +149,14 @@ public class LicenceEndpoints {
|
|||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Demande une nouvelle licence", description = "Demande une nouvelle licence en fonction de" +
|
||||||
|
" l'identifiant du membre et des informations fournies dans le formulaire (pour les clubs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La demande de licence a été envoyée avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<SimpleLicence> askLicence(@PathParam("id") long id, LicenceForm form) {
|
public Uni<SimpleLicence> askLicence(@PathParam("id") long id, LicenceForm form) {
|
||||||
return licenceService.askLicence(id, form, checkPerm).map(SimpleLicence::fromModel);
|
return licenceService.askLicence(id, form, checkPerm).map(SimpleLicence::fromModel);
|
||||||
}
|
}
|
||||||
@ -90,7 +165,25 @@ public class LicenceEndpoints {
|
|||||||
@Path("club/{id}")
|
@Path("club/{id}")
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Supprime une demande de licence", description = "Supprime une demande de licence en fonction " +
|
||||||
|
"de son identifiant (pour les clubs)")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "La demande de licence a été supprimée avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "La demande de licence n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
public Uni<?> deleteAskLicence(@PathParam("id") long id) {
|
public Uni<?> deleteAskLicence(@PathParam("id") long id) {
|
||||||
return licenceService.deleteAskLicence(id, checkPerm);
|
return licenceService.deleteAskLicence(id, checkPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove after importe all data
|
||||||
|
@GET
|
||||||
|
@Path("import")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> setImport(@QueryParam("licence") int licence, @QueryParam("saison") int saison,
|
||||||
|
@QueryParam("valid") int valid) {
|
||||||
|
return licenceService.setImport(licence, saison, valid != 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.MatchService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MatchData;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Authenticated
|
||||||
|
@Path("api/match/{system}/admin")
|
||||||
|
public class MatchAdminEndpoints {
|
||||||
|
|
||||||
|
@PathParam("system")
|
||||||
|
private CompetitionSystem system;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MatchService service;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<MatchData> getByIdAdmin(@PathParam("id") Long id) {
|
||||||
|
return service.getByIdAdmin(securityCtx, system, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("getAllByPoule/{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<List<MatchData>> getAllByPouleAdmin(@PathParam("id") Long id) {
|
||||||
|
return service.getAllByPouleAdmin(securityCtx, system, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<MatchData> addOrUpdate(MatchData data) {
|
||||||
|
return service.addOrUpdate(securityCtx, system, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("score/{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> updateScore(@PathParam("id") Long id, List<ScoreEmbeddable> scores) {
|
||||||
|
return service.updateScore(securityCtx, system, id, scores);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<?> delete(@PathParam("id") Long id) {
|
||||||
|
return service.delete(securityCtx, system, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
148
src/main/java/fr/titionfire/ffsaf/rest/MembreAdminEndpoints.java
Normal file
148
src/main/java/fr/titionfire/ffsaf/rest/MembreAdminEndpoints.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.PageResult;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Tag(name = "Membre admin", description = "Gestion des membres (pour les administrateurs)")
|
||||||
|
@Path("api/member")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
public class MembreAdminEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MembreService membreService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/find/admin")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Recherche des membres par critères ", description = "Recherche des membres en fonction de " +
|
||||||
|
"critères tels que le nom, le prénom, le club, etc. ")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des membres correspondant aux critères de recherche"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<PageResult<SimpleMembre>> getFindAdmin(
|
||||||
|
@Parameter(description = "Nombre max de résulta (max 50)") @QueryParam("limit") Integer limit,
|
||||||
|
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
|
||||||
|
@Parameter(description = "Text à rechercher") @QueryParam("search") String search,
|
||||||
|
@Parameter(description = "Club à filter") @QueryParam("club") String club,
|
||||||
|
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
|
||||||
|
@Parameter(description = "État de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
|
||||||
|
@Parameter(description = "État du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
|
||||||
|
@Parameter(description = "Ordre") @QueryParam("order") String order) {
|
||||||
|
if (limit == null)
|
||||||
|
limit = 50;
|
||||||
|
if (page == null || page < 1)
|
||||||
|
page = 1;
|
||||||
|
return membreService.searchAdmin(limit, page - 1, search, club, licenceRequest, payment, order, categorie);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/find/similar")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(hidden = true)
|
||||||
|
public Uni<List<SimpleMembre>> getSimilar(@QueryParam("fname") String fname, @QueryParam("lname") String lname) {
|
||||||
|
return membreService.getSimilar(fname, lname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Met à jour les informations d'un membre en fonction de son identifiant", description = "Met à " +
|
||||||
|
"jour les informations d'un membre en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le membre a été mis à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> setAdminMembre(
|
||||||
|
@Parameter(description = "Identifiant de membre") @PathParam("id") long id, FullMemberForm input) {
|
||||||
|
return membreService.update(id, input)
|
||||||
|
.invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK"))
|
||||||
|
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||||
|
})).chain(() -> {
|
||||||
|
if (input.getPhoto_data().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||||
|
)).invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK"))
|
||||||
|
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Ajoute un nouveau membre", description = "Ajoute un nouveau membre avec les informations " +
|
||||||
|
"fournies dans le formulaire")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le membre a été ajouté avec succès"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Les données envoyées sont invalides"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Long> addAdminMembre(FullMemberForm input) {
|
||||||
|
return membreService.add(input)
|
||||||
|
.invoke(Unchecked.consumer(id -> {
|
||||||
|
if (id == null) throw new InternalError("Fail to creat member data");
|
||||||
|
})).call(id -> {
|
||||||
|
if (input.getPhoto_data().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||||
|
));
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Supprime un membre en fonction de son identifiant", description = "Supprime un membre en " +
|
||||||
|
"fonction de son identifiant, ainsi que toutes les informations associées")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Le membre a été supprimé avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> deleteAdminMembre(
|
||||||
|
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||||
|
return membreService.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java
Normal file
160
src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||||
|
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||||
|
import fr.titionfire.ffsaf.utils.PageResult;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "Membre club", description = "Gestion des membres (pour les clubs)")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Path("api/member")
|
||||||
|
public class MembreClubEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MembreService membreService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/find/club")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Recherche des membres par critères", description = "Recherche des membres en " +
|
||||||
|
"fonction de critères tels que le nom, le prénom, etc.")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La liste des membres correspondant aux critères de recherche"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<PageResult<SimpleMembre>> getFindClub(
|
||||||
|
@Parameter(description = "Nombre max de résulta (max 50)") @QueryParam("limit") Integer limit,
|
||||||
|
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
|
||||||
|
@Parameter(description = "Text à rechercher") @QueryParam("search") String search,
|
||||||
|
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
|
||||||
|
@Parameter(description = "Etat de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
|
||||||
|
@Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
|
||||||
|
@Parameter(description = "Ordre") @QueryParam("order") String order) {
|
||||||
|
if (limit == null)
|
||||||
|
limit = 50;
|
||||||
|
if (page == null || page < 1)
|
||||||
|
page = 1;
|
||||||
|
return membreService.search(limit, page - 1, search, licenceRequest, payment, order, categorie, securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("club/export")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Exporte les membres du club", description = "Exporte les membres du club")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les membres du club ont été exportés avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<List<SimpleMembreInOutData>> exportMembre() {
|
||||||
|
return membreService.getAllExport(securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("club/import")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Importer les membres du club", description = "Importer tout ou en partie les membres du club")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les membres du club ont été importés avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<?> importMembre(List<SimpleMembreInOutData> dataIn) {
|
||||||
|
System.out.println("importMembre");
|
||||||
|
return membreService.allImporte(securityCtx.getSubject(), dataIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("club/{id}")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Met à jour les informations d'un membre en fonction de son identifiant",
|
||||||
|
description = "Met à jour les informations d'un membre en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le membre a été mis à jour avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> setMembre(
|
||||||
|
@Parameter(description = "Identifiant de membre") @PathParam("id") long id, FullMemberForm input) {
|
||||||
|
return membreService.update(id, input, securityCtx)
|
||||||
|
.invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
||||||
|
})).chain(() -> {
|
||||||
|
if (input.getPhoto_data().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||||
|
)).invoke(Unchecked.consumer(out -> {
|
||||||
|
if (!out.equals("OK"))
|
||||||
|
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("club")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
@Operation(summary = "Ajoute un nouveau membre", description = "Ajoute un nouveau membre avec les informations " +
|
||||||
|
"fournies dans le formulaire")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le membre a été ajouté avec succès"),
|
||||||
|
@APIResponse(responseCode = "400", description = "Les données envoyées sont invalides"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Long> addMembre(FullMemberForm input) {
|
||||||
|
return membreService.add(input, securityCtx.getSubject())
|
||||||
|
.invoke(Unchecked.consumer(id -> {
|
||||||
|
if (id == null) throw new InternalError("Fail to creat member data");
|
||||||
|
})).call(id -> {
|
||||||
|
if (input.getPhoto_data().length > 0)
|
||||||
|
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||||
|
));
|
||||||
|
else
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("club/{id}")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
@Operation(summary = "Supprime un membre en fonction de son identifiant", description = "Supprime " +
|
||||||
|
"un membre en fonction de son identifiant, ainsi que toutes les informations associées")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "204", description = "Le membre a été supprimé avec succès"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<String> deleteMembre(
|
||||||
|
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||||
|
return membreService.delete(id, securityCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/main/java/fr/titionfire/ffsaf/rest/MembreEndpoints.java
Normal file
160
src/main/java/fr/titionfire/ffsaf/rest/MembreEndpoints.java
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.PDFService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||||
|
import fr.titionfire.ffsaf.utils.Utils;
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Tag(name = "Membre", description = "Gestion des membres")
|
||||||
|
@Authenticated
|
||||||
|
@Path("api/member")
|
||||||
|
public class MembreEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MembreService membreService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PDFService pdfService;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "upload_dir")
|
||||||
|
String media;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
|
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||||
|
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||||
|
throw new DForbiddenException();
|
||||||
|
});
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les détails d'un membre en fonction de son identifiant", description = "Renvoie les " +
|
||||||
|
"détails d'un membre en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les détails du membre"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<SimpleMembre> getById(
|
||||||
|
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||||
|
return membreService.getById(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/find/licence")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie les détails d'un membre en fonction de son numéro de licence", description = "Renvoie " +
|
||||||
|
"les détails d'un membre en fonction de son numéro de licence")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les détails du membre"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<SimpleMembre> getByLicence(@QueryParam("id") long id) {
|
||||||
|
return membreService.getByLicence(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("me")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie les informations du membre connecté", description = "Renvoie les informations du " +
|
||||||
|
"membre connecté, y compris le club et les licences")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Les informations du membre connecté"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<MeData> getMe() {
|
||||||
|
return membreService.getMembre(securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("me/licence")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Renvoie l'attestation d'adhesion du membre connecté", description = "Renvoie l'attestation d'adhesion du " +
|
||||||
|
"membre connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "L'attestation d'adhesion"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'a pas de licence active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getMeLicence() {
|
||||||
|
return pdfService.getLicencePdf(securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("me/photo")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "Renvoie la photo du membre connecté", description = "Renvoie la photo du membre connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La photo"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getMePhoto() {
|
||||||
|
return membreService.getByAccountId(securityCtx.getSubject())
|
||||||
|
.chain(Unchecked.function(
|
||||||
|
m -> Utils.getMediaFile(m.getId(), media, "ppMembre", Uni.createFrom().nullItem())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}/photo")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie la photo d'un membre", description = "Renvoie la photo d'un membre en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "La photo du membre"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de photo"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
||||||
|
return Utils.getMediaFile(id, media, "ppMembre", membreService.getById(id).onItem()
|
||||||
|
.call(m -> Objects.equals(m.getUserId(), securityCtx.getSubject()) ?
|
||||||
|
Uni.createFrom().nullItem() : Uni.createFrom().item(m).invoke(checkPerm)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}/licence")
|
||||||
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie le pdf de la licence d'un membre", description = "Renvoie le pdf de la licence d'un membre en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "Le pdf de la licence"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de licence active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getLicencePDF(@PathParam("id") long id) {
|
||||||
|
return pdfService.getLicencePdf(membreService.getByIdWithLicence(id).onItem().invoke(checkPerm));
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/java/fr/titionfire/ffsaf/rest/StatsEndpoints.java
Normal file
25
src/main/java/fr/titionfire/ffsaf/rest/StatsEndpoints.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.StatsService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.LicenceStats;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
@Path("api/stats")
|
||||||
|
public class StatsEndpoints {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
StatsService service;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@RolesAllowed("federation_admin")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<LicenceStats> getStats() {
|
||||||
|
return service.getStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/main/java/fr/titionfire/ffsaf/rest/WebhookEndpoints.java
Normal file
53
src/main/java/fr/titionfire/ffsaf/rest/WebhookEndpoints.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.WebhookService;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.HelloassoNotification;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.vertx.ext.web.RoutingContext;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@Path("api/webhook")
|
||||||
|
public class WebhookEndpoints {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(WebhookEndpoints.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookService webhookService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RoutingContext context;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "helloasso.webhook.ip-source")
|
||||||
|
String helloassoIp;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "quarkus.http.proxy.proxy-address-forwarding")
|
||||||
|
boolean proxyForwarding;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("ha")
|
||||||
|
@Operation(hidden = true)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<Response> helloAsso(HelloassoNotification notification) {
|
||||||
|
String ip;
|
||||||
|
if (proxyForwarding) {
|
||||||
|
ip = context.request().getHeader("X-Forwarded-For");
|
||||||
|
if (ip == null)
|
||||||
|
ip = context.request().authority().host();
|
||||||
|
} else {
|
||||||
|
ip = context.request().authority().host();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!helloassoIp.equals(ip)) {
|
||||||
|
LOGGER.infof("helloAsso webhook reject : bas ip (%s)", ip);
|
||||||
|
return Uni.createFrom().item(Response.status(Response.Status.FORBIDDEN).build());
|
||||||
|
}
|
||||||
|
return webhookService.helloAssoNotification(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.TokenResponse;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
@RegisterRestClient(configKey = "helloasso-auth")
|
||||||
|
public interface HelloAssoAuthClient {
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/token")
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
Uni<TokenResponse> getToken(
|
||||||
|
@FormParam("grant_type") String grantType,
|
||||||
|
@FormParam("client_id") String clientId,
|
||||||
|
@FormParam("client_secret") String clientSecret
|
||||||
|
);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/token")
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
Uni<TokenResponse> refreshToken(
|
||||||
|
@FormParam("grant_type") String grantType,
|
||||||
|
@FormParam("client_id") String clientId,
|
||||||
|
@FormParam("refresh_token") String refreshToken
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.service.HelloAssoTokenService;
|
||||||
|
import io.quarkus.rest.client.reactive.ReactiveClientHeadersFactory;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
|
import org.jboss.resteasy.reactive.common.util.MultivaluedTreeMap;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class HelloAssoHeadersFactory extends ReactiveClientHeadersFactory {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HelloAssoTokenService helloAssoTokenService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uni<MultivaluedMap<String, String>> getHeaders(MultivaluedMap<String, String> incomingHeaders,
|
||||||
|
MultivaluedMap<String, String> clientOutgoingHeaders) {
|
||||||
|
MultivaluedMap<String, String> map = new MultivaluedTreeMap<>();
|
||||||
|
return helloAssoTokenService.getValidAccessToken()
|
||||||
|
.invoke(token -> map.putSingle("Authorization", "Bearer " + token)).map(__ -> map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
|
||||||
|
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
|
||||||
|
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
@RegisterRestClient(configKey = "helloasso-api")
|
||||||
|
@RegisterClientHeaders(HelloAssoHeadersFactory.class)
|
||||||
|
public interface HelloAssoService {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/users/me/organizations")
|
||||||
|
@Produces("text/plain")
|
||||||
|
Uni<String> test();
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("organizations/{organizationSlug}/checkout-intents")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
Uni<CheckoutIntentsResponse> checkout(@PathParam("organizationSlug") String organizationSlug,
|
||||||
|
CheckoutIntentsRequest data);
|
||||||
|
|
||||||
|
@ClientExceptionMapper
|
||||||
|
static RuntimeException toException(Response response, Method method) {
|
||||||
|
if (!method.getDeclaringClass().getName().equals("fr.titionfire.ffsaf.rest.client.HelloAssoService"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (method.getName().equals("checkout")) {
|
||||||
|
if (response.getStatus() == 400) {
|
||||||
|
if (response.getEntity() instanceof ByteArrayInputStream) {
|
||||||
|
ByteArrayInputStream error = response.readEntity(ByteArrayInputStream.class);
|
||||||
|
return new RuntimeException(new String(error.readAllBytes(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RuntimeException("The remote service responded with HTTP 400");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package fr.titionfire.ffsaf.rest.client;
|
package fr.titionfire.ffsaf.rest.client;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||||
|
import io.quarkus.cache.CacheResult;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
@ -15,5 +16,6 @@ public interface SirenService {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/v3/unites_legales/{SIREN}")
|
@Path("/v3/unites_legales/{SIREN}")
|
||||||
|
@CacheResult(cacheName = "AssoData_siren")
|
||||||
Uni<UniteLegaleRoot> get_unite(@PathParam("SIREN") String siren);
|
Uni<UniteLegaleRoot> get_unite(@PathParam("SIREN") String siren);
|
||||||
}
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.rest.data.AssoData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||||
|
import io.quarkus.cache.CacheResult;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
@RegisterRestClient
|
||||||
|
public interface StateIdService {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/associations/{rna}")
|
||||||
|
@CacheResult(cacheName = "AssoData_rna")
|
||||||
|
Uni<AssoData> get_rna(@PathParam("rna") String rna);
|
||||||
|
|
||||||
|
default Uni<AssoData> getAssoDataFromUnit(UniteLegaleRoot u) {
|
||||||
|
AssoData assoData = new AssoData();
|
||||||
|
assoData.setSiren(u.getUnite_legale().getSiren());
|
||||||
|
assoData.setRna(u.getUnite_legale().getIdentifiant_association());
|
||||||
|
|
||||||
|
AssoData.Identite identite = new AssoData.Identite();
|
||||||
|
identite.setNom(u.getUnite_legale().getDenomination());
|
||||||
|
identite.setSiret_siege(u.getUnite_legale().getEtablissement_siege().getSiret());
|
||||||
|
assoData.setIdentite(identite);
|
||||||
|
|
||||||
|
AssoData.Address address = new AssoData.Address();
|
||||||
|
StringBuilder voie = new StringBuilder();
|
||||||
|
if (u.getUnite_legale().getEtablissement_siege().getNumero_voie() != null)
|
||||||
|
voie.append(u.getUnite_legale().getEtablissement_siege().getNumero_voie()).append(' ');
|
||||||
|
if (u.getUnite_legale().getEtablissement_siege().getType_voie() != null)
|
||||||
|
voie.append(u.getUnite_legale().getEtablissement_siege().getType_voie()).append(' ');
|
||||||
|
if (u.getUnite_legale().getEtablissement_siege().getLibelle_voie() != null)
|
||||||
|
voie.append(u.getUnite_legale().getEtablissement_siege().getLibelle_voie()).append(' ');
|
||||||
|
address.setVoie(voie.toString().trim());
|
||||||
|
address.setComplement(u.getUnite_legale().getEtablissement_siege().getComplement_adresse());
|
||||||
|
address.setCode_postal(u.getUnite_legale().getEtablissement_siege().getCode_postal());
|
||||||
|
address.setCommune(
|
||||||
|
new AssoData.Commune(u.getUnite_legale().getEtablissement_siege().getLibelle_commune()));
|
||||||
|
assoData.setCoordonnees(new AssoData.Coordonnee(address));
|
||||||
|
|
||||||
|
return Uni.createFrom().item(assoData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
|
@RegisterForReflection
|
||||||
|
public class ApiError {
|
||||||
|
public String error;
|
||||||
|
public String error_description;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return error + ": " + error_description;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CheckoutIntentsRequest {
|
||||||
|
public int totalAmount;
|
||||||
|
public int initialAmount;
|
||||||
|
public String itemName;
|
||||||
|
public String backUrl;
|
||||||
|
public String errorUrl;
|
||||||
|
public String returnUrl;
|
||||||
|
public boolean containsDonation;
|
||||||
|
public Payer payer;
|
||||||
|
public CheckoutMetadata metadata;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Payer {
|
||||||
|
public String firstName;
|
||||||
|
public String lastName;
|
||||||
|
public String email;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CheckoutIntentsResponse {
|
||||||
|
public int id;
|
||||||
|
public String redirectUrl;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CheckoutMetadata {
|
||||||
|
public long checkoutDBId;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class HelloassoNotification {
|
||||||
|
private NotificationData data;
|
||||||
|
private String eventType;
|
||||||
|
private CheckoutMetadata metadata;
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class NotificationData {
|
||||||
|
private Order order;
|
||||||
|
private Integer id;
|
||||||
|
private String formSlug;
|
||||||
|
private String formType;
|
||||||
|
private String organizationSlug;
|
||||||
|
private String checkoutIntentId;
|
||||||
|
private String oldSlugOrganization; // Pour les changements de nom d'association
|
||||||
|
private String newSlugOrganization;
|
||||||
|
private String state; // Pour les formulaires
|
||||||
|
private List<Item> items;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Order {
|
||||||
|
private Integer id;
|
||||||
|
private String organizationSlug;
|
||||||
|
private String formSlug;
|
||||||
|
private String formType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Item {
|
||||||
|
private String name;
|
||||||
|
private User user;
|
||||||
|
private List<CustomField> customFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class User {
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class CustomField {
|
||||||
|
private String name;
|
||||||
|
private String answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.client.dto;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
|
@RegisterForReflection
|
||||||
|
public class TokenResponse {
|
||||||
|
@JsonProperty("access_token")
|
||||||
|
public String accessToken;
|
||||||
|
|
||||||
|
@JsonProperty("refresh_token")
|
||||||
|
public String refreshToken;
|
||||||
|
|
||||||
|
@JsonProperty("token_type")
|
||||||
|
public String tokenType; // Toujours "bearer"
|
||||||
|
|
||||||
|
@JsonProperty("expires_in")
|
||||||
|
public long expiresIn; // Durée de validité en secondes (1800s = 30min)
|
||||||
|
|
||||||
|
// Pour stocker l'heure d'obtention du token
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public TokenResponse() {
|
||||||
|
this.timestamp = System.currentTimeMillis() / 1000; // Timestamp en secondes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifie si le token est expiré
|
||||||
|
public boolean isExpired() {
|
||||||
|
return (System.currentTimeMillis() / 1000) - timestamp >= expiresIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main/java/fr/titionfire/ffsaf/rest/data/AssoData.java
Normal file
48
src/main/java/fr/titionfire/ffsaf/rest/data/AssoData.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public class AssoData {
|
||||||
|
String siren;
|
||||||
|
String rna;
|
||||||
|
Identite identite;
|
||||||
|
Coordonnee coordonnees;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Identite {
|
||||||
|
String nom;
|
||||||
|
String siret_siege;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Coordonnee {
|
||||||
|
Address adresse_gestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Address {
|
||||||
|
String voie;
|
||||||
|
String complement;
|
||||||
|
String code_postal;
|
||||||
|
String pays;
|
||||||
|
Commune commune;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Commune {
|
||||||
|
String nom;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CategoryModel;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CategoryData {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Long compet;
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
public static CategoryData fromModel(CategoryModel model) {
|
||||||
|
if (model == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new CategoryData(model.getSystemId(), model.getName(), model.getCompet().getId(), model.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CategoryFullData {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Long compet;
|
||||||
|
private Integer type;
|
||||||
|
private List<MatchData> matches;
|
||||||
|
private List<TreeData> trees;
|
||||||
|
|
||||||
|
/*public static PouleFullData fromModel(PouleModel pouleModel) {
|
||||||
|
if (pouleModel == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
PouleEntity pouleEntity = PouleEntity.fromModel(pouleModel);
|
||||||
|
|
||||||
|
return new PouleFullData(pouleEntity.getId(), pouleEntity.getName(), pouleEntity.getCompet().getId(),
|
||||||
|
pouleEntity.getType(), pouleModel.getMatchs().stream().map(MatchData::fromModel).toList(),
|
||||||
|
pouleEntity.getTrees().stream().map(TreeData::fromEntity).toList());
|
||||||
|
}*/
|
||||||
|
}
|
||||||
27
src/main/java/fr/titionfire/ffsaf/rest/data/ClubMapData.java
Normal file
27
src/main/java/fr/titionfire/ffsaf/rest/data/ClubMapData.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.utils.Contact;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public class ClubMapData {
|
||||||
|
public String name;
|
||||||
|
public String uuid;
|
||||||
|
public List<Location> training_location = new ArrayList<>();
|
||||||
|
public String training_day_time;
|
||||||
|
public Map<Contact, String> contact;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class Location {
|
||||||
|
public double lat;
|
||||||
|
public double lng;
|
||||||
|
public String addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
||||||
|
import fr.titionfire.ffsaf.utils.Categorie;
|
||||||
|
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||||
|
import fr.titionfire.ffsaf.utils.RegisterMode;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CompetitionData {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String adresse;
|
||||||
|
private String uuid;
|
||||||
|
private Date date;
|
||||||
|
private Date toDate;
|
||||||
|
private CompetitionSystem system;
|
||||||
|
private RegisterMode registerMode;
|
||||||
|
private Date startRegister;
|
||||||
|
private Date endRegister;
|
||||||
|
private boolean publicVisible;
|
||||||
|
private Long club;
|
||||||
|
private String clubName;
|
||||||
|
private String owner;
|
||||||
|
private List<SimpleRegister> registers;
|
||||||
|
private boolean canEdit;
|
||||||
|
private String data1;
|
||||||
|
private String data2;
|
||||||
|
private String data3;
|
||||||
|
private String data4;
|
||||||
|
|
||||||
|
public static CompetitionData fromModel(CompetitionModel model) {
|
||||||
|
if (model == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new CompetitionData(model.getId(), model.getName(), model.getDescription(), model.getAdresse(),
|
||||||
|
model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(),
|
||||||
|
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
|
||||||
|
model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false,
|
||||||
|
model.getData1(), model.getData2(), model.getData3(), model.getData4());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CompetitionData fromModelLight(CompetitionModel model) {
|
||||||
|
if (model == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
CompetitionData out = new CompetitionData(model.getId(), model.getName(), model.getDescription(),
|
||||||
|
model.getAdresse(), "", model.getDate(), model.getTodate(), null,
|
||||||
|
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
|
||||||
|
null, model.getClub().getName(), "", null, false,
|
||||||
|
"","", "","");
|
||||||
|
|
||||||
|
if (model.getRegisterMode() == RegisterMode.HELLOASSO){
|
||||||
|
out.setData1(model.getData1());
|
||||||
|
out.setData2(model.getData2());
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompetitionData addInsc(List<RegisterModel> insc) {
|
||||||
|
this.registers = insc.stream()
|
||||||
|
.map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(),
|
||||||
|
i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId())).toList();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class SimpleRegister {
|
||||||
|
long id;
|
||||||
|
int overCategory;
|
||||||
|
Integer weight;
|
||||||
|
Categorie categorie;
|
||||||
|
Long club;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
35
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
@Schema(name = "BureauMembre")
|
||||||
|
public class DeskMember {
|
||||||
|
@Schema(description = "Identifiant du membre", example = "1")
|
||||||
|
private Long id;
|
||||||
|
@Schema(description = "Nom du membre", example = "Doe")
|
||||||
|
private String lname;
|
||||||
|
@Schema(description = "Prénom du membre", example = "John")
|
||||||
|
private String fname;
|
||||||
|
@Schema(description = "Rôle du membre", example = "Président")
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
public static DeskMember fromModel(MembreModel membreModel) {
|
||||||
|
if (membreModel == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
DeskMember deskMember = new DeskMember();
|
||||||
|
deskMember.setId(membreModel.getId());
|
||||||
|
deskMember.setLname(membreModel.getLname());
|
||||||
|
deskMember.setFname(membreModel.getFname());
|
||||||
|
deskMember.setRole(membreModel.getRole().toString());
|
||||||
|
|
||||||
|
return deskMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user