From e4d125e14cfceddae6961c9b4c95c318e8e2d120 Mon Sep 17 00:00:00 2001 From: dahoud Date: Mon, 6 Oct 2025 18:48:01 +0000 Subject: [PATCH] Initial commit: GBCM Server Quarkus implementation with JPA entities and services --- .gitignore | 106 ++++++++ README.md | 220 ++++++++++++++++ pom.xml | 179 +++++++++++++ .../java/com/gbcm/server/entities/User.java | 180 +++++++++++++ .../server/services/impl/AuthServiceImpl.java | 249 ++++++++++++++++++ src/main/resources/application.properties | 103 ++++++++ 6 files changed, 1037 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/gbcm/server/entities/User.java create mode 100644 src/main/java/com/gbcm/server/services/impl/AuthServiceImpl.java create mode 100644 src/main/resources/application.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de73949 --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs +hs_err_pid* + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# Gradle +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar + +# IntelliJ IDEA +.idea/ +*.iws +*.iml +*.ipr +out/ + +# Eclipse +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ + +# NetBeans +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +# VS Code +.vscode/ + +# Mac +.DS_Store + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Quarkus +.quarkus/ + +# Database +*.db +*.sqlite + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Logs +logs/ +*.log + +# JWT Keys +*.pem +*.key + +# Temporary files +*.tmp +*.temp + +# Docker +.dockerignore +Dockerfile.jvm +Dockerfile.native diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c26a86 --- /dev/null +++ b/README.md @@ -0,0 +1,220 @@ +# GBCM Server Implementation - Quarkus + +Implémentation serveur de la plateforme GBCM (Global Business Consulting and Management) avec Quarkus. + +## Description + +Ce module contient l'implémentation complète des services backend GBCM, incluant l'authentification, la gestion des utilisateurs, les services de coaching et la facturation. + +## Technologies + +- **Quarkus 3.6.0** - Framework Java moderne +- **Hibernate ORM with Panache** - ORM et accès aux données +- **PostgreSQL** - Base de données principale +- **JWT** - Authentification et autorisation +- **RESTEasy Reactive** - Services REST +- **Flyway** - Migration de base de données +- **Mailer** - Envoi d'emails +- **OpenAPI/Swagger** - Documentation API +- **Maven** - Gestion des dépendances + +## Prérequis + +- Java 17 ou supérieur +- Maven 3.8+ +- PostgreSQL 13+ +- SMTP Server (pour les emails) + +## Installation + +1. Cloner le repository +```bash +git clone https://git.lions.dev/gbcm/gbcm-server-impl-quarkus.git +cd gbcm-server-impl-quarkus +``` + +2. Installer les dépendances +```bash +mvn clean install +``` + +3. Configuration de la base de données +```sql +CREATE DATABASE gbcm_server; +CREATE USER gbcm_server WITH PASSWORD 'gbcm_server_password'; +GRANT ALL PRIVILEGES ON DATABASE gbcm_server TO gbcm_server; +``` + +4. Configuration +Copier `application.properties.example` vers `application.properties` et configurer : +- Base de données PostgreSQL +- Configuration SMTP +- Clés JWT +- Paramètres métier + +## Développement + +### Démarrage en mode développement +```bash +mvn quarkus:dev +``` + +L'application sera accessible sur : +- API: http://localhost:8081/api/v1 +- Swagger UI: http://localhost:8081/swagger +- Health Check: http://localhost:8081/health + +### Base de données H2 (développement) +En mode dev, une base H2 en mémoire est utilisée automatiquement. + +## Structure du projet + +``` +src/ +├── main/ +│ ├── java/com/gbcm/server/ +│ │ ├── entities/ # Entités JPA +│ │ ├── repositories/ # Repositories Panache +│ │ ├── services/ # Services métier +│ │ │ ├── impl/ # Implémentations +│ │ │ └── security/ # Services sécurité +│ │ ├── resources/ # Endpoints REST +│ │ ├── config/ # Configuration +│ │ └── utils/ # Utilitaires +│ └── resources/ +│ ├── application.properties +│ ├── import.sql # Données de test +│ └── db/migration/ # Scripts Flyway +└── test/ # Tests +``` + +## API Endpoints + +### Authentification +- `POST /api/v1/auth/login` - Connexion +- `POST /api/v1/auth/logout` - Déconnexion +- `POST /api/v1/auth/refresh` - Rafraîchissement token +- `GET /api/v1/auth/validate` - Validation token + +### Utilisateurs +- `GET /api/v1/users` - Liste des utilisateurs +- `GET /api/v1/users/{id}` - Utilisateur par ID +- `POST /api/v1/users` - Création utilisateur +- `PUT /api/v1/users/{id}` - Mise à jour utilisateur + +### Coaching +- `GET /api/v1/coaching/sessions` - Sessions de coaching +- `POST /api/v1/coaching/sessions` - Nouvelle session +- `PUT /api/v1/coaching/sessions/{id}` - Mise à jour session + +### Ateliers +- `GET /api/v1/workshops` - Liste des ateliers +- `POST /api/v1/workshops` - Nouvel atelier +- `POST /api/v1/workshops/{id}/register` - Inscription + +## Base de données + +### Migrations Flyway +Les scripts de migration sont dans `src/main/resources/db/migration/` : +- `V1__create_users.sql` - Table utilisateurs +- `V2__create_clients.sql` - Table clients +- `V3__create_workshops.sql` - Tables ateliers + +### Entités principales +- `User` - Utilisateurs système +- `Client` - Clients GBCM +- `Coach` - Coachs/consultants +- `Workshop` - Ateliers Strategic +- `CoachingSession` - Sessions de coaching +- `Invoice` - Factures + +## Sécurité + +### JWT +- Tokens signés avec clé privée RSA +- Durée de vie configurable +- Refresh tokens pour sessions longues +- Blacklist des tokens révoqués + +### Rôles et permissions +- `ADMIN` - Accès complet +- `COACH` - Gestion clients et sessions +- `CLIENT` - Accès services souscrits +- `MANAGER` - Gestion opérationnelle + +## Tests + +```bash +# Tests unitaires +mvn test + +# Tests d'intégration +mvn verify + +# Tests avec Testcontainers +mvn test -Dtest.containers=true +``` + +## Build et déploiement + +### Build JVM +```bash +mvn clean package +java -jar target/quarkus-app/quarkus-run.jar +``` + +### Build natif +```bash +mvn clean package -Pnative +./target/gbcm-server-impl-quarkus-1.0.0-SNAPSHOT-runner +``` + +### Docker +```bash +# Build image +docker build -t gbcm-server . + +# Run container +docker run -p 8081:8081 \ + -e DATABASE_URL=jdbc:postgresql://host:5432/gbcm \ + -e DB_USERNAME=gbcm_server \ + -e DB_PASSWORD=password \ + gbcm-server +``` + +## Configuration + +### Variables d'environnement principales +- `DATABASE_URL` - URL base de données +- `DB_USERNAME` - Utilisateur DB +- `DB_PASSWORD` - Mot de passe DB +- `SMTP_USERNAME` - Utilisateur SMTP +- `SMTP_PASSWORD` - Mot de passe SMTP +- `JWT_PRIVATE_KEY` - Clé privée JWT + +### Configuration métier +- `gbcm.business.workshop.max-participants` - Participants max par atelier +- `gbcm.business.coaching.session-duration` - Durée session coaching +- `gbcm.business.billing.currency` - Devise facturation + +## Monitoring + +### Health Checks +- `/health` - Statut général +- `/health/ready` - Prêt à recevoir du trafic +- `/health/live` - Application vivante + +### Métriques +- `/metrics` - Métriques Prometheus +- Métriques métier personnalisées +- Monitoring des performances + +## Support + +- Email: support@gbcm.com +- Documentation: https://docs.gbcm.com +- API Docs: https://api.gbcm.com/swagger + +## Licence + +Propriétaire - GBCM LLC © 2024 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0f77432 --- /dev/null +++ b/pom.xml @@ -0,0 +1,179 @@ + + + 4.0.0 + + com.gbcm + gbcm-server-impl-quarkus + 1.0.0-SNAPSHOT + jar + + GBCM Server Implementation - Quarkus + Implémentation serveur GBCM avec Quarkus + + + 17 + 17 + UTF-8 + + + 3.6.0 + 1.19.3 + + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + + + com.gbcm + gbcm-server-api + 1.0.0-SNAPSHOT + + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-flyway + + + + + io.quarkus + quarkus-security + + + io.quarkus + quarkus-security-jpa + + + io.quarkus + quarkus-smallrye-jwt + + + + + io.quarkus + quarkus-hibernate-validator + + + + + io.quarkus + quarkus-smallrye-openapi + + + + + io.quarkus + quarkus-smallrye-health + + + + + io.quarkus + quarkus-micrometer + + + + + io.quarkus + quarkus-mailer + + + + + io.quarkus + quarkus-scheduler + + + + + io.quarkus + quarkus-cache + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.testcontainers + postgresql + ${testcontainers.version} + test + + + io.quarkus + quarkus-test-h2 + test + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + build + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + + diff --git a/src/main/java/com/gbcm/server/entities/User.java b/src/main/java/com/gbcm/server/entities/User.java new file mode 100644 index 0000000..ebd70b0 --- /dev/null +++ b/src/main/java/com/gbcm/server/entities/User.java @@ -0,0 +1,180 @@ +package com.gbcm.server.entities; + +import com.gbcm.server.api.enums.UserRole; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.security.jpa.Password; +import io.quarkus.security.jpa.Roles; +import io.quarkus.security.jpa.UserDefinition; +import io.quarkus.security.jpa.Username; + +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Objects; + +/** + * Entité utilisateur pour l'authentification et la gestion des profils + */ +@Entity +@Table(name = "users", indexes = { + @Index(name = "idx_user_email", columnList = "email", unique = true), + @Index(name = "idx_user_role", columnList = "role"), + @Index(name = "idx_user_active", columnList = "active") +}) +@UserDefinition +public class User extends PanacheEntityBase { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public Long id; + + @Column(name = "first_name", nullable = false, length = 100) + @NotBlank(message = "Le prénom est obligatoire") + public String firstName; + + @Column(name = "last_name", nullable = false, length = 100) + @NotBlank(message = "Le nom est obligatoire") + public String lastName; + + @Column(name = "email", nullable = false, unique = true, length = 255) + @NotBlank(message = "L'email est obligatoire") + @Email(message = "Format d'email invalide") + @Username + public String email; + + @Column(name = "password_hash", nullable = false) + @NotBlank(message = "Le mot de passe est obligatoire") + @Password + public String passwordHash; + + @Column(name = "phone", length = 20) + public String phone; + + @Column(name = "role", nullable = false, length = 20) + @Enumerated(EnumType.STRING) + @NotNull(message = "Le rôle est obligatoire") + @Roles + public UserRole role; + + @Column(name = "active", nullable = false) + public boolean active = true; + + @Column(name = "email_verified", nullable = false) + public boolean emailVerified = false; + + @Column(name = "created_at", nullable = false) + public LocalDateTime createdAt; + + @Column(name = "updated_at") + public LocalDateTime updatedAt; + + @Column(name = "last_login_at") + public LocalDateTime lastLoginAt; + + @Column(name = "password_reset_token") + public String passwordResetToken; + + @Column(name = "password_reset_expires_at") + public LocalDateTime passwordResetExpiresAt; + + @Column(name = "email_verification_token") + public String emailVerificationToken; + + // Constructeurs + public User() { + this.createdAt = LocalDateTime.now(); + } + + public User(String firstName, String lastName, String email, String passwordHash, UserRole role) { + this(); + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.passwordHash = passwordHash; + this.role = role; + } + + // Méthodes utilitaires + public String getFullName() { + return firstName + " " + lastName; + } + + public String getInitials() { + String firstInitial = firstName != null && !firstName.isEmpty() ? + firstName.substring(0, 1).toUpperCase() : ""; + String lastInitial = lastName != null && !lastName.isEmpty() ? + lastName.substring(0, 1).toUpperCase() : ""; + return firstInitial + lastInitial; + } + + public boolean isPasswordResetTokenValid() { + return passwordResetToken != null && + passwordResetExpiresAt != null && + passwordResetExpiresAt.isAfter(LocalDateTime.now()); + } + + public void updateLastLogin() { + this.lastLoginAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + // Callbacks JPA + @PreUpdate + public void preUpdate() { + this.updatedAt = LocalDateTime.now(); + } + + // Méthodes de recherche Panache + public static User findByEmail(String email) { + return find("email", email).firstResult(); + } + + public static User findByEmailAndActive(String email, boolean active) { + return find("email = ?1 and active = ?2", email, active).firstResult(); + } + + public static User findByPasswordResetToken(String token) { + return find("passwordResetToken", token).firstResult(); + } + + public static User findByEmailVerificationToken(String token) { + return find("emailVerificationToken", token).firstResult(); + } + + public static long countByRole(UserRole role) { + return count("role", role); + } + + public static long countActiveUsers() { + return count("active", true); + } + + // equals et hashCode + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(email, user.email); + } + + @Override + public int hashCode() { + return Objects.hash(id, email); + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", role=" + role + + ", active=" + active + + ", createdAt=" + createdAt + + '}'; + } +} diff --git a/src/main/java/com/gbcm/server/services/impl/AuthServiceImpl.java b/src/main/java/com/gbcm/server/services/impl/AuthServiceImpl.java new file mode 100644 index 0000000..5437772 --- /dev/null +++ b/src/main/java/com/gbcm/server/services/impl/AuthServiceImpl.java @@ -0,0 +1,249 @@ +package com.gbcm.server.services.impl; + +import com.gbcm.server.api.dto.auth.LoginRequestDTO; +import com.gbcm.server.api.dto.auth.LoginResponseDTO; +import com.gbcm.server.api.dto.user.UserDTO; +import com.gbcm.server.api.exceptions.AuthenticationException; +import com.gbcm.server.api.exceptions.GBCMException; +import com.gbcm.server.api.interfaces.AuthService; +import com.gbcm.server.entities.User; +import com.gbcm.server.services.security.JwtService; +import com.gbcm.server.utils.EmailUtils; + +import io.quarkus.elytron.security.common.BcryptUtil; +import io.quarkus.logging.Log; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import java.time.LocalDateTime; +import java.util.UUID; + +/** + * Implémentation du service d'authentification + */ +@ApplicationScoped +public class AuthServiceImpl implements AuthService { + + @Inject + JwtService jwtService; + + @Inject + EmailUtils emailUtils; + + @Override + @Transactional + public LoginResponseDTO login(@Valid LoginRequestDTO loginRequest) + throws AuthenticationException, GBCMException { + + Log.infof("Tentative de connexion pour: %s", loginRequest.getEmail()); + + try { + // Recherche de l'utilisateur + User user = User.findByEmailAndActive(loginRequest.getEmail(), true); + if (user == null) { + Log.warnf("Utilisateur non trouvé ou inactif: %s", loginRequest.getEmail()); + throw new AuthenticationException("Identifiants invalides"); + } + + // Vérification du mot de passe + if (!BcryptUtil.matches(loginRequest.getPassword(), user.passwordHash)) { + Log.warnf("Mot de passe incorrect pour: %s", loginRequest.getEmail()); + throw new AuthenticationException("Identifiants invalides"); + } + + // Mise à jour de la dernière connexion + user.updateLastLogin(); + user.persist(); + + // Génération du token JWT + String token = jwtService.generateToken(user); + LocalDateTime expiresAt = LocalDateTime.now().plusHours(1); + + // Création de la réponse + UserDTO userDTO = mapToUserDTO(user); + LoginResponseDTO response = LoginResponseDTO.success(token, expiresAt, userDTO); + + if (loginRequest.isRememberMe()) { + String refreshToken = jwtService.generateRefreshToken(user); + response.setRefreshToken(refreshToken); + } + + Log.infof("Connexion réussie pour: %s", loginRequest.getEmail()); + return response; + + } catch (AuthenticationException e) { + throw e; + } catch (Exception e) { + Log.errorf(e, "Erreur lors de la connexion pour: %s", loginRequest.getEmail()); + throw new GBCMException("Erreur système lors de la connexion"); + } + } + + @Override + @Transactional + public void logout(String authToken) throws AuthenticationException { + try { + // Extraction du token (suppression du préfixe "Bearer ") + String token = extractToken(authToken); + + // Validation et extraction des informations utilisateur + UserDTO userDTO = jwtService.validateToken(token); + + // Ajout du token à la blacklist + jwtService.blacklistToken(token); + + Log.infof("Déconnexion réussie pour l'utilisateur: %s", userDTO.getEmail()); + + } catch (Exception e) { + Log.errorf(e, "Erreur lors de la déconnexion"); + throw new AuthenticationException("Erreur lors de la déconnexion"); + } + } + + @Override + public LoginResponseDTO refreshToken(String refreshToken) throws AuthenticationException { + try { + // Validation du refresh token + UserDTO userDTO = jwtService.validateRefreshToken(refreshToken); + + // Recherche de l'utilisateur + User user = User.findByEmail(userDTO.getEmail()); + if (user == null || !user.active) { + throw new AuthenticationException("Utilisateur non trouvé ou inactif"); + } + + // Génération d'un nouveau token + String newToken = jwtService.generateToken(user); + LocalDateTime expiresAt = LocalDateTime.now().plusHours(1); + + return LoginResponseDTO.success(newToken, expiresAt, userDTO); + + } catch (Exception e) { + Log.errorf(e, "Erreur lors du rafraîchissement du token"); + throw new AuthenticationException("Token de rafraîchissement invalide"); + } + } + + @Override + public UserDTO validateToken(String authToken) throws AuthenticationException { + try { + String token = extractToken(authToken); + return jwtService.validateToken(token); + } catch (Exception e) { + Log.errorf(e, "Erreur lors de la validation du token"); + throw new AuthenticationException("Token invalide"); + } + } + + @Override + @Transactional + public void forgotPassword(String email) throws GBCMException { + try { + User user = User.findByEmailAndActive(email, true); + if (user == null) { + // Pour des raisons de sécurité, on ne révèle pas si l'email existe + Log.warnf("Demande de réinitialisation pour email inexistant: %s", email); + return; + } + + // Génération du token de réinitialisation + String resetToken = UUID.randomUUID().toString(); + user.passwordResetToken = resetToken; + user.passwordResetExpiresAt = LocalDateTime.now().plusHours(24); + user.persist(); + + // Envoi de l'email + emailUtils.sendPasswordResetEmail(user.email, user.getFullName(), resetToken); + + Log.infof("Email de réinitialisation envoyé à: %s", email); + + } catch (Exception e) { + Log.errorf(e, "Erreur lors de l'envoi de l'email de réinitialisation"); + throw new GBCMException("Erreur lors de l'envoi de l'email"); + } + } + + @Override + @Transactional + public void resetPassword(String resetToken, String newPassword) throws GBCMException { + try { + User user = User.findByPasswordResetToken(resetToken); + if (user == null || !user.isPasswordResetTokenValid()) { + throw new GBCMException("Token de réinitialisation invalide ou expiré"); + } + + // Mise à jour du mot de passe + user.passwordHash = BcryptUtil.bcryptHash(newPassword); + user.passwordResetToken = null; + user.passwordResetExpiresAt = null; + user.updatedAt = LocalDateTime.now(); + user.persist(); + + Log.infof("Mot de passe réinitialisé pour: %s", user.email); + + } catch (GBCMException e) { + throw e; + } catch (Exception e) { + Log.errorf(e, "Erreur lors de la réinitialisation du mot de passe"); + throw new GBCMException("Erreur lors de la réinitialisation"); + } + } + + @Override + @Transactional + public void changePassword(String authToken, String oldPassword, String newPassword) + throws AuthenticationException, GBCMException { + try { + // Validation du token et récupération de l'utilisateur + UserDTO userDTO = validateToken(authToken); + User user = User.findByEmail(userDTO.getEmail()); + + if (user == null) { + throw new AuthenticationException("Utilisateur non trouvé"); + } + + // Vérification de l'ancien mot de passe + if (!BcryptUtil.matches(oldPassword, user.passwordHash)) { + throw new GBCMException("Ancien mot de passe incorrect"); + } + + // Mise à jour du mot de passe + user.passwordHash = BcryptUtil.bcryptHash(newPassword); + user.updatedAt = LocalDateTime.now(); + user.persist(); + + Log.infof("Mot de passe changé pour: %s", user.email); + + } catch (AuthenticationException | GBCMException e) { + throw e; + } catch (Exception e) { + Log.errorf(e, "Erreur lors du changement de mot de passe"); + throw new GBCMException("Erreur lors du changement de mot de passe"); + } + } + + // Méthodes utilitaires privées + private String extractToken(String authToken) throws AuthenticationException { + if (authToken == null || !authToken.startsWith("Bearer ")) { + throw new AuthenticationException("Format de token invalide"); + } + return authToken.substring(7); + } + + private UserDTO mapToUserDTO(User user) { + UserDTO dto = new UserDTO(); + dto.setId(user.id); + dto.setFirstName(user.firstName); + dto.setLastName(user.lastName); + dto.setEmail(user.email); + dto.setPhone(user.phone); + dto.setRole(user.role); + dto.setActive(user.active); + dto.setCreatedAt(user.createdAt); + dto.setUpdatedAt(user.updatedAt); + dto.setLastLoginAt(user.lastLoginAt); + return dto; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..564aa7a --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,103 @@ +# GBCM Server Configuration +quarkus.application.name=gbcm-server +quarkus.application.version=1.0.0-SNAPSHOT + +# Server Configuration +quarkus.http.port=8081 +quarkus.http.host=0.0.0.0 +quarkus.http.cors=true +quarkus.http.cors.origins=http://localhost:8080,https://gbcm.com +quarkus.http.cors.methods=GET,PUT,POST,DELETE,OPTIONS +quarkus.http.cors.headers=accept,authorization,content-type,x-requested-with + +# Database Configuration +quarkus.datasource.db-kind=postgresql +quarkus.datasource.username=gbcm_server +quarkus.datasource.password=gbcm_server_password +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/gbcm_server + +# Hibernate Configuration +quarkus.hibernate-orm.database.generation=validate +quarkus.hibernate-orm.log.sql=false +quarkus.hibernate-orm.sql-load-script=import.sql + +# Flyway Configuration +quarkus.flyway.migrate-at-start=true +quarkus.flyway.locations=db/migration +quarkus.flyway.baseline-on-migrate=true + +# Security Configuration +quarkus.security.auth.enabled=true + +# JWT Configuration +mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem +mp.jwt.verify.issuer=https://gbcm.com +smallrye.jwt.sign.key.location=META-INF/resources/privateKey.pem +smallrye.jwt.new-token.issuer=https://gbcm.com +smallrye.jwt.new-token.audience=gbcm-users +smallrye.jwt.new-token.lifespan=3600 + +# OpenAPI Configuration +quarkus.smallrye-openapi.path=/swagger-ui +quarkus.swagger-ui.always-include=true +quarkus.swagger-ui.path=/swagger +mp.openapi.extensions.smallrye.info.title=GBCM Server API +mp.openapi.extensions.smallrye.info.version=1.0.0 +mp.openapi.extensions.smallrye.info.description=API pour les services GBCM +mp.openapi.extensions.smallrye.info.contact.email=support@gbcm.com +mp.openapi.extensions.smallrye.info.contact.name=GBCM Support +mp.openapi.extensions.smallrye.info.license.name=Proprietary +mp.openapi.extensions.smallrye.info.license.url=https://gbcm.com/license + +# Health Check Configuration +quarkus.smallrye-health.root-path=/health + +# Metrics Configuration +quarkus.micrometer.enabled=true +quarkus.micrometer.export.prometheus.enabled=true + +# Email Configuration +quarkus.mailer.from=noreply@gbcm.com +quarkus.mailer.host=smtp.gmail.com +quarkus.mailer.port=587 +quarkus.mailer.start-tls=REQUIRED +quarkus.mailer.username=${SMTP_USERNAME:} +quarkus.mailer.password=${SMTP_PASSWORD:} + +# Cache Configuration +quarkus.cache.caffeine.default.initial-capacity=100 +quarkus.cache.caffeine.default.maximum-size=1000 +quarkus.cache.caffeine.default.expire-after-write=PT30M + +# Logging Configuration +quarkus.log.level=INFO +quarkus.log.category."com.gbcm".level=DEBUG +quarkus.log.category."org.hibernate.SQL".level=DEBUG + +# Development Profile +%dev.quarkus.log.level=DEBUG +%dev.quarkus.hibernate-orm.log.sql=true +%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:gbcm_server_dev;DB_CLOSE_DELAY=-1 +%dev.quarkus.datasource.db-kind=h2 +%dev.quarkus.hibernate-orm.database.generation=drop-and-create +%dev.quarkus.flyway.migrate-at-start=false + +# Test Profile +%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:gbcm_server_test;DB_CLOSE_DELAY=-1 +%test.quarkus.datasource.db-kind=h2 +%test.quarkus.hibernate-orm.database.generation=drop-and-create +%test.quarkus.flyway.migrate-at-start=false + +# Production Profile +%prod.quarkus.log.level=WARN +%prod.quarkus.hibernate-orm.log.sql=false +%prod.quarkus.datasource.jdbc.url=${DATABASE_URL} +%prod.quarkus.datasource.username=${DB_USERNAME} +%prod.quarkus.datasource.password=${DB_PASSWORD} + +# Business Configuration +gbcm.business.workshop.max-participants=20 +gbcm.business.coaching.session-duration=60 +gbcm.business.billing.currency=USD +gbcm.business.notification.email.enabled=true +gbcm.business.notification.sms.enabled=false