Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

73
config/api.ts Normal file
View File

@@ -0,0 +1,73 @@
/**
* API Configuration for BTP Xpress Client
*/
export const API_CONFIG = {
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080',
timeout: parseInt(process.env.NEXT_PUBLIC_API_TIMEOUT || '10000'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
} as const;
export const KEYCLOAK_CONFIG = {
url: process.env.NEXT_PUBLIC_KEYCLOAK_URL || 'https://security.lions.dev',
realm: process.env.NEXT_PUBLIC_KEYCLOAK_REALM || 'btpxpress',
clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID || 'btpxpress-frontend',
// Configuration pour l'authentification
'ssl-required': 'external',
'public-client': true,
'confidential-port': 0,
'use-resource-role-mappings': true,
'cors': true,
'enable-cors': true,
'cors-max-age': 1000,
'cors-allowed-methods': 'POST, PUT, DELETE, GET',
'cors-allowed-headers': 'X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers',
'bearer-only': false,
'autodetect-bearer-only': true,
'connection-pool-size': 20,
'socket-timeout-millis': 5000,
'connection-timeout-millis': 5000,
'connection-ttl-millis': 5000,
'disable-trust-manager': false,
'allow-any-hostname': false,
'truststore': '',
'truststore-password': '',
'client-keystore': '',
'client-keystore-password': '',
'client-key-password': '',
'token-minimum-time-to-live': 10,
'min-time-between-jwks-requests': 10,
'public-key-cache-ttl': 86400,
'redirect-rewrite-rules': {},
} as const;
// Configuration des rôles et permissions
export const KEYCLOAK_ROLES = {
ADMIN: 'admin',
MANAGER: 'manager',
USER: 'user',
CHEF_CHANTIER: 'chef_chantier',
EMPLOYE: 'employe',
CLIENT: 'client',
} as const;
// Configuration des scopes OAuth2
export const KEYCLOAK_SCOPES = [
'openid',
'profile',
'email',
] as const;
export const APP_CONFIG = {
name: process.env.NEXT_PUBLIC_APP_NAME || 'BTP Xpress',
version: process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
description: process.env.NEXT_PUBLIC_APP_DESCRIPTION || 'Plateforme de gestion BTP',
} as const;
export const THEME_CONFIG = {
defaultTheme: process.env.NEXT_PUBLIC_DEFAULT_THEME || 'blue',
defaultMode: process.env.NEXT_PUBLIC_DEFAULT_THEME_MODE || 'light',
} as const;

334
config/keycloak.ts Normal file
View File

@@ -0,0 +1,334 @@
/**
* Configuration Keycloak pour BTP Xpress
* Intégration complète avec security.lions.dev
*/
import Keycloak from 'keycloak-js';
import { KEYCLOAK_CONFIG, KEYCLOAK_ROLES, KEYCLOAK_SCOPES } from './api';
// Configuration de l'instance Keycloak
export const keycloakConfig = {
url: KEYCLOAK_CONFIG.url,
realm: KEYCLOAK_CONFIG.realm,
clientId: KEYCLOAK_CONFIG.clientId,
};
// Instance Keycloak globale avec gestion SSR et développement
let keycloak: any = null;
// Fonction pour initialiser Keycloak côté client uniquement
export const initKeycloak = () => {
if (typeof window === 'undefined') {
// Côté serveur - retourner un mock
return {
init: () => Promise.resolve(false),
login: () => console.log('Mock Keycloak login'),
logout: () => console.log('Mock Keycloak logout'),
updateToken: () => Promise.resolve(false),
authenticated: false,
token: null,
refreshToken: null,
tokenParsed: null,
onTokenExpired: null,
onAuthRefreshSuccess: null,
onAuthRefreshError: null,
onAuthLogout: null,
};
}
// Côté client - initialiser Keycloak réel
if (!keycloak) {
try {
keycloak = new Keycloak(keycloakConfig);
} catch (error) {
console.warn('Keycloak non disponible en mode développement:', error);
// Mock Keycloak pour le développement local
keycloak = {
init: () => Promise.resolve(false),
login: () => console.log('Mock Keycloak login'),
logout: () => console.log('Mock Keycloak logout'),
updateToken: () => Promise.resolve(false),
authenticated: false,
token: null,
refreshToken: null,
tokenParsed: null,
onTokenExpired: null,
onAuthRefreshSuccess: null,
onAuthRefreshError: null,
onAuthLogout: null,
};
}
}
return keycloak;
};
// Export pour compatibilité
export { keycloak };
// Options d'initialisation Keycloak
export const keycloakInitOptions = {
onLoad: 'login-required' as const,
checkLoginIframe: false,
pkceMethod: 'S256' as const,
scope: KEYCLOAK_SCOPES.join(' '),
responseMode: 'fragment' as const,
flow: 'standard' as const,
enableLogging: process.env.NODE_ENV === 'development',
};
// Configuration des rôles BTP Xpress
export const BTP_ROLES = {
// Rôles administratifs
SUPER_ADMIN: 'super_admin',
ADMIN: 'admin',
// Rôles de gestion
DIRECTEUR: 'directeur',
MANAGER: 'manager',
CHEF_CHANTIER: 'chef_chantier',
// Rôles opérationnels
CONDUCTEUR_TRAVAUX: 'conducteur_travaux',
CHEF_EQUIPE: 'chef_equipe',
EMPLOYE: 'employe',
OUVRIER: 'ouvrier',
// Rôles clients
CLIENT_ENTREPRISE: 'client_entreprise',
CLIENT_PARTICULIER: 'client_particulier',
// Rôles techniques
COMPTABLE: 'comptable',
COMMERCIAL: 'commercial',
LOGISTICIEN: 'logisticien',
// Rôles de consultation
VIEWER: 'viewer',
GUEST: 'guest',
} as const;
// Hiérarchie des rôles (du plus élevé au plus bas)
export const ROLE_HIERARCHY = [
BTP_ROLES.SUPER_ADMIN,
BTP_ROLES.ADMIN,
BTP_ROLES.DIRECTEUR,
BTP_ROLES.MANAGER,
BTP_ROLES.CHEF_CHANTIER,
BTP_ROLES.CONDUCTEUR_TRAVAUX,
BTP_ROLES.CHEF_EQUIPE,
BTP_ROLES.COMMERCIAL,
BTP_ROLES.COMPTABLE,
BTP_ROLES.LOGISTICIEN,
BTP_ROLES.EMPLOYE,
BTP_ROLES.OUVRIER,
BTP_ROLES.CLIENT_ENTREPRISE,
BTP_ROLES.CLIENT_PARTICULIER,
BTP_ROLES.VIEWER,
BTP_ROLES.GUEST,
] as const;
// Permissions par rôle
export const ROLE_PERMISSIONS = {
[BTP_ROLES.SUPER_ADMIN]: ['*'], // Toutes les permissions
[BTP_ROLES.ADMIN]: [
'users.manage',
'chantiers.manage',
'materiels.manage',
'employes.manage',
'clients.manage',
'devis.manage',
'factures.manage',
'reports.view',
'dashboard.admin',
],
[BTP_ROLES.DIRECTEUR]: [
'chantiers.manage',
'employes.manage',
'clients.manage',
'devis.manage',
'factures.manage',
'reports.view',
'dashboard.management',
],
[BTP_ROLES.MANAGER]: [
'chantiers.view',
'chantiers.edit',
'employes.view',
'employes.edit',
'materiels.view',
'materiels.edit',
'devis.view',
'devis.edit',
'factures.view',
'reports.view',
'dashboard.management',
],
[BTP_ROLES.CHEF_CHANTIER]: [
'chantiers.view',
'chantiers.edit',
'employes.view',
'materiels.view',
'materiels.reserve',
'planning.view',
'planning.edit',
'dashboard.chantier',
],
[BTP_ROLES.CONDUCTEUR_TRAVAUX]: [
'chantiers.view',
'employes.view',
'materiels.view',
'planning.view',
'dashboard.chantier',
],
[BTP_ROLES.CHEF_EQUIPE]: [
'chantiers.view',
'employes.view',
'materiels.view',
'planning.view',
'dashboard.equipe',
],
[BTP_ROLES.COMMERCIAL]: [
'clients.manage',
'devis.manage',
'factures.view',
'chantiers.view',
'dashboard.commercial',
],
[BTP_ROLES.COMPTABLE]: [
'factures.manage',
'devis.view',
'clients.view',
'reports.financial',
'dashboard.financial',
],
[BTP_ROLES.LOGISTICIEN]: [
'materiels.manage',
'chantiers.view',
'planning.view',
'dashboard.logistique',
],
[BTP_ROLES.EMPLOYE]: [
'chantiers.view',
'planning.view',
'profile.edit',
'dashboard.employe',
],
[BTP_ROLES.OUVRIER]: [
'chantiers.view',
'planning.view',
'profile.edit',
'dashboard.employe',
],
[BTP_ROLES.CLIENT_ENTREPRISE]: [
'chantiers.view.own',
'devis.view.own',
'factures.view.own',
'profile.edit',
'dashboard.client',
],
[BTP_ROLES.CLIENT_PARTICULIER]: [
'chantiers.view.own',
'devis.view.own',
'factures.view.own',
'profile.edit',
'dashboard.client',
],
[BTP_ROLES.VIEWER]: [
'chantiers.view',
'dashboard.readonly',
],
[BTP_ROLES.GUEST]: [
'dashboard.public',
],
} as const;
// Utilitaires pour la gestion des rôles
export const RoleUtils = {
/**
* Vérifie si l'utilisateur a un rôle spécifique
*/
hasRole: (userRoles: string[], requiredRole: string): boolean => {
return userRoles.includes(requiredRole);
},
/**
* Vérifie si l'utilisateur a au moins un des rôles requis
*/
hasAnyRole: (userRoles: string[], requiredRoles: string[]): boolean => {
return requiredRoles.some(role => userRoles.includes(role));
},
/**
* Vérifie si l'utilisateur a tous les rôles requis
*/
hasAllRoles: (userRoles: string[], requiredRoles: string[]): boolean => {
return requiredRoles.every(role => userRoles.includes(role));
},
/**
* Obtient le rôle le plus élevé de l'utilisateur
*/
getHighestRole: (userRoles: string[]): string | null => {
for (const role of ROLE_HIERARCHY) {
if (userRoles.includes(role)) {
return role;
}
}
return null;
},
/**
* Vérifie si l'utilisateur a une permission spécifique
*/
hasPermission: (userRoles: string[], permission: string): boolean => {
for (const role of userRoles) {
const rolePermissions = ROLE_PERMISSIONS[role as keyof typeof ROLE_PERMISSIONS];
if (rolePermissions?.includes('*') || rolePermissions?.includes(permission)) {
return true;
}
}
return false;
},
/**
* Obtient toutes les permissions de l'utilisateur
*/
getUserPermissions: (userRoles: string[]): string[] => {
const permissions = new Set<string>();
for (const role of userRoles) {
const rolePermissions = ROLE_PERMISSIONS[role as keyof typeof ROLE_PERMISSIONS];
if (rolePermissions) {
rolePermissions.forEach(permission => permissions.add(permission));
}
}
return Array.from(permissions);
},
/**
* Vérifie si un rôle est supérieur à un autre
*/
isRoleHigher: (role1: string, role2: string): boolean => {
const index1 = ROLE_HIERARCHY.indexOf(role1 as any);
const index2 = ROLE_HIERARCHY.indexOf(role2 as any);
return index1 !== -1 && index2 !== -1 && index1 < index2;
},
};
// Configuration des redirections après authentification
export const KEYCLOAK_REDIRECTS = {
LOGIN_SUCCESS: '/dashboard',
LOGIN_ERROR: '/auth/error',
LOGOUT_SUCCESS: '/auth/logout',
UNAUTHORIZED: '/auth/unauthorized',
FORBIDDEN: '/auth/forbidden',
} as const;
// Configuration des timeouts
export const KEYCLOAK_TIMEOUTS = {
TOKEN_REFRESH_BEFORE_EXPIRY: 30, // secondes
SESSION_CHECK_INTERVAL: 60, // secondes
SILENT_CHECK_SSO_TIMEOUT: 5000, // millisecondes
} as const;
export default keycloak;