T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/public_html/src/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/lib/barreau-verification.ts
import { prisma } from './prisma';
import { verifyBarreauDirectory } from './barreau-scraper';

export interface BarreauLawyerData {
  id: string;
  name: string;
  email?: string;
  phone?: string;
  address?: string;
  barNumber: string;
  specializations: string[];
  regions: string[];
  acceptsLegalAid: boolean;
  status: 'ACTIVE' | 'SUSPENDED' | 'INACTIVE';
  verifiedAt: Date;
}

export interface BarreauVerificationRequest {
  barNumber: string; // Stored but not used for verification
  name: string;
  email: string;
  address: string; // Used for verification
  phone: string; // Used for verification
  password?: string; // For new user creation
  specializations?: string[];
  regions?: string[];
  // Business profile information (for law firm creation)
  businessProfileId?: string;
  newBusinessName?: string;
  newBusinessType?: string;
  newBusinessIndustry?: string;
  newBusinessDescription?: string;
}

export interface BarreauVerificationResponse {
  success: boolean;
  verified: boolean;
  lawyerData?: BarreauLawyerData;
  error?: string;
  message?: string;
}

export class BarreauVerificationService {
  private static instance: BarreauVerificationService;
  private verificationCache = new Map<string, BarreauLawyerData>();

  static getInstance(): BarreauVerificationService {
    if (!BarreauVerificationService.instance) {
      BarreauVerificationService.instance = new BarreauVerificationService();
    }
    return BarreauVerificationService.instance;
  }

  /**
   * Verify a lawyer against the Barreau du Québec database
   */
  async verifyLawyer(request: BarreauVerificationRequest): Promise<BarreauVerificationResponse> {
    try {
      console.log(`🔍 Verifying lawyer: ${request.name} at ${request.address}`);

      // Check cache first (using name + address as key)
      const cacheKey = `${request.name}-${request.address}`;
      if (this.verificationCache.has(cacheKey)) {
        const cachedData = this.verificationCache.get(cacheKey)!;
        return {
          success: true,
          verified: true,
          lawyerData: cachedData,
          message: 'Verification successful (cached)'
        };
      }

      // Vérification automatique Barreau (scraping)
      const isVerified = await verifyBarreauDirectory(request.name, request.address || '');
      if (!isVerified) {
        return {
          success: false,
          verified: false,
          error: 'Avocat introuvable dans le répertoire du Barreau du Québec. Vous pouvez demander une vérification manuelle en téléversant un document ou en contactant l\'administration.'
        };
      }

      // Verify using name + address against Barreau directory
      const verificationResult = await this.verifyAgainstBarreauDirectory(request);

      if (verificationResult.verified && verificationResult.lawyerData) {
        // Cache the result
        this.verificationCache.set(cacheKey, verificationResult.lawyerData);
        
        // Handle business profile creation/association first
        let businessProfileId = request.businessProfileId;
        if (request.newBusinessName) {
          businessProfileId = await this.createBusinessProfile(request, verificationResult.lawyerData.id);
        }
        
        // Update or create lawyer profile in our database
        const lawyer = await this.updateLawyerProfile(request, verificationResult.lawyerData);
        
        // Associate lawyer with business profile if needed
        if (businessProfileId && lawyer) {
          await this.associateLawyerWithBusiness(lawyer.id, businessProfileId);
        }
      }

      return verificationResult;

    } catch (error) {
      console.error('❌ Barreau verification error:', error);
      return {
        success: false,
        verified: false,
        error: 'Verification service temporarily unavailable'
      };
    }
  }

  /**
   * Verify lawyer against Barreau directory using name + address
   */
  private async verifyAgainstBarreauDirectory(request: BarreauVerificationRequest): Promise<BarreauVerificationResponse> {
    // Simulate API delay
    await new Promise(resolve => setTimeout(resolve, 1000));

    // For demo purposes, accept certain names, addresses, and phone numbers
    // In production, this would query the actual Barreau directory
    const validLawyers = [
      { name: 'Justin Wee', address: 'Montréal', phone: '514-555-0101' },
      { name: 'Marie-Claude Bouchard', address: 'Québec', phone: '418-555-0202' },
      { name: 'Jean-François Tremblay', address: 'Laval', phone: '450-555-0303' },
      { name: 'Sophie Martin', address: 'Gatineau', phone: '819-555-0404' },
      { name: 'Pierre Dubois', address: 'Sherbrooke', phone: '819-555-0505' },
      { name: 'Isabelle Roy', address: 'Trois-Rivières', phone: '819-555-0606' },
      { name: 'Marc-André Gagnon', address: 'Saguenay', phone: '418-555-0707' },
      { name: 'Nathalie Bergeron', address: 'Rimouski', phone: '418-555-0808' }
    ];

    // Check if name + address + phone combination exists
    const isFound = validLawyers.some(lawyer => 
      lawyer.name.toLowerCase() === request.name.toLowerCase() &&
      lawyer.address.toLowerCase().includes(request.address.toLowerCase()) &&
      this.normalizePhoneNumber(lawyer.phone) === this.normalizePhoneNumber(request.phone)
    );

    if (isFound) {
      const lawyerData: BarreauLawyerData = {
        id: this.generateBarreauId(request.barNumber),
        name: request.name,
        email: request.email,
        address: request.address,
        phone: request.phone,
        barNumber: request.barNumber, // Stored but not used for verification
        specializations: request.specializations || ['Droit civil', 'Droit commercial'],
        regions: request.regions || [this.extractRegionFromAddress(request.address)],
        acceptsLegalAid: true,
        status: 'ACTIVE',
        verifiedAt: new Date()
      };

      return {
        success: true,
        verified: true,
        lawyerData,
        message: 'Lawyer verified with Barreau du Québec directory'
      };
    }

    return {
      success: true,
      verified: false,
      error: 'Lawyer not found in Barreau directory. Please verify your name and address.'
    };
  }

  /**
   * Update or create lawyer profile in our database using the same logic as signup
   */
  private async updateLawyerProfile(request: BarreauVerificationRequest, barreauData: BarreauLawyerData) {
    try {
      const existingLawyer = await prisma.user.findFirst({
        where: {
          OR: [
            { email: request.email },
            { barNumber: request.barNumber }
          ]
        }
      });

      if (existingLawyer) {
        // Update existing lawyer with Barreau verification
        await prisma.user.update({
          where: { id: existingLawyer.id },
          data: {
            isVerified: true,
            barreauVerifiedAt: new Date(),
            barreauId: barreauData.id,
            specializations: JSON.stringify(barreauData.specializations),
            regions: JSON.stringify(barreauData.regions),
            acceptsLegalAid: barreauData.acceptsLegalAid,
            verificationStatus: 'VERIFIED_BARREAU',
            phone: request.phone,
            address: request.address,
            barreauProfileJson: {
              ...barreauData,
              verifiedAt: barreauData.verifiedAt.toISOString()
            } // Store full scraped profile as JSON
          }
        });
        console.log(`âś… Updated existing lawyer: ${existingLawyer.name}`);
        return existingLawyer;
      } else {
        // Create new verified lawyer profile using the same logic as signup
        const bcrypt = require('bcryptjs');
        const hashedPassword = await bcrypt.hash(request.password || `Barreau${Date.now()}`, 10);
        
        const newLawyer = await prisma.user.create({
          data: {
            name: request.name,
            email: request.email,
            role: 'LAWYER',
            password: hashedPassword,
            barNumber: request.barNumber,
            phone: request.phone,
            address: request.address,
            isVerified: true,
            isProfilePublic: true,
            barreauVerifiedAt: new Date(),
            barreauId: barreauData.id,
            specializations: JSON.stringify(barreauData.specializations),
            regions: JSON.stringify(barreauData.regions),
            acceptsLegalAid: barreauData.acceptsLegalAid,
            verificationStatus: 'VERIFIED_BARREAU',
            barreauProfileJson: {
              ...barreauData,
              verifiedAt: barreauData.verifiedAt.toISOString()
            } // Store full scraped profile as JSON
          }
        });
        console.log(`âś… Created new verified lawyer: ${newLawyer.name}`);
        return newLawyer;
      }
    } catch (error) {
      console.error('❌ Error updating lawyer profile:', error);
      throw error;
    }
  }

  /**
   * Generate a Barreau-style ID (simulating their hash format)
   */
  private generateBarreauId(barNumber: string): string {
    // Simulate the hash format they use
    const timestamp = Date.now().toString(16);
    const random = Math.random().toString(16).substring(2);
    return `${barNumber}${timestamp}${random}`.substring(0, 64);
  }

  /**
   * Create a new business profile (law firm)
   */
  private async createBusinessProfile(request: BarreauVerificationRequest, ownerId: string): Promise<string> {
    try {
      const businessProfile = await prisma.businessProfile.create({
        data: {
          businessName: request.newBusinessName!,
          businessType: request.newBusinessType || 'LAW_FIRM',
          industry: request.newBusinessIndustry || 'LEGAL_SERVICES',
          description: request.newBusinessDescription || '',
          isPublic: true,
          isVerified: true,
          owner: { connect: { id: ownerId } }
        }
      });
      
      console.log(`âś… Created new business profile: ${businessProfile.businessName}`);
      return businessProfile.id;
    } catch (error) {
      console.error('❌ Error creating business profile:', error);
      throw error;
    }
  }

  /**
   * Associate lawyer with business profile
   */
  private async associateLawyerWithBusiness(lawyerId: string, businessProfileId: string): Promise<void> {
    try {
      await prisma.businessProfile.update({
        where: { id: businessProfileId },
        data: {
          members: {
            connect: { id: lawyerId }
          }
        }
      });
      
      console.log(`âś… Associated lawyer ${lawyerId} with business profile ${businessProfileId}`);
    } catch (error) {
      console.error('❌ Error associating lawyer with business profile:', error);
      throw error;
    }
  }

  /**
   * Extract region from address
   */
  private extractRegionFromAddress(address: string): string {
    const addressLower = address.toLowerCase();
    
    if (addressLower.includes('montréal')) return 'Montréal';
    if (addressLower.includes('québec')) return 'Québec';
    if (addressLower.includes('laval')) return 'Laval';
    if (addressLower.includes('gatineau')) return 'Gatineau';
    if (addressLower.includes('sherbrooke')) return 'Sherbrooke';
    if (addressLower.includes('trois-rivières')) return 'Trois-Rivières';
    if (addressLower.includes('saguenay')) return 'Saguenay';
    if (addressLower.includes('rimouski')) return 'Rimouski';
    
    return 'Montréal'; // Default
  }

  /**
   * Normalize phone number for comparison (remove spaces, dashes, parentheses)
   */
  private normalizePhoneNumber(phone: string): string {
    return phone.replace(/[\s\-\(\)]/g, '');
  }

  /**
   * Get legal domains from Barreau (based on their website)
   */
  static getLegalDomains(): string[] {
    return [
      'Accompagnement de victime de violence à caractère sexuel',
      'Accompagnement de victime de violence familiale ou conjugale',
      'Accès à l\'information',
      'Accès à la justice',
      'Administratif',
      'Constitutionnel',
      'Droit de l\'éducation',
      'Droit électoral',
      'Droit fédéral',
      'Droit linguistique',
      'Droit militaire',
      'Droit municipal',
      'Droit social',
      'Évaluation foncière',
      'Public',
      'Affaires',
      'Droit bancaire & institutions financières',
      'Droit commercial',
      'Droit commercial international',
      'Droit de la concurrence',
      'Droit des compagnies et des sociétés',
      'Droit des coopératives',
      'Droit des organismes sans but lucratif',
      'Droit des valeurs mobilières',
      'Financement',
      'Franchisage',
      'Intermédiaires financiers',
      'Droit des autochtones',
      'Exerce au Nunavik',
      'Action collective',
      'Assurances',
      'Civil',
      'Construction',
      'Contrats',
      'Copropriété',
      'Crédit-bail',
      'Droit de la consommation',
      'Enquête en matière d\'harcèlement psychologique',
      'Expropriation',
      'Fiducies',
      'Immobilier',
      'Litige',
      'Louage',
      'Obligations',
      'Prescription',
      'Preuve civile',
      'Procédure civile',
      'Publicité des droits',
      'Responsabilité civile',
      'Responsabilité professionnelle',
      'Servitudes',
      'Sûretés',
      'Vente',
      'Vices cachés',
      'Communications',
      'Criminel',
      'Droit carcéral',
      'Justice pour adolescents',
      'Pénal',
      'Preuve pénale',
      'Procédure pénale',
      'Développement des habiletés professionnelles',
      'Développement et pratique professionnels',
      'Recherche documentaire',
      'Recherche et législation',
      'Rédaction juridique',
      'Aménagement et urbanisme',
      'Animaux',
      'Droit agricole',
      'Énergie',
      'Environnement',
      'Ressources naturelles',
      'Déontologie',
      'Droit disciplinaire',
      'Droit professionnel',
      'Éthique',
      'Faillite et insolvabilité',
      'Adoption internationale',
      'Adoption nationale',
      'Aînés',
      'Divorce',
      'Famille',
      'Filiation',
      'Jeunesse',
      'Régime de protection',
      'Successions & testaments',
      'Fiscalité',
      'Planification successorale',
      'Finance et comptabilité',
      'Gestion',
      'Gouvernance',
      'Immigration',
      'Droit humanitaire',
      'Droit international privé',
      'Droit international public',
      'International',
      'Interprétation des lois',
      'Médias',
      'Arbitrage civil',
      'Arbitrage commercial',
      'Arbitrage en droit du travail',
      'Droit collaboratif',
      'Médiation aux petites créances',
      'Médiation civile',
      'Médiation commerciale',
      'Médiation en droit du travail',
      'Médiation familiale',
      'Modes de résolution des conflits',
      'Droit des minorités',
      'Droits et libertés',
      'Personnes',
      'Brevets',
      'Divertissement',
      'Droit des auteurs',
      'Marques de commerce',
      'Propriété intellectuelle',
      'Autre',
      'Bioéthique',
      'Responsabilité médicale',
      'Santé',
      'Sport',
      'Technologies de l\'information',
      'Tourisme',
      'Transports',
      'Accidents du travail et maladies professionnelles',
      'Droits fondamentaux',
      'Rapports collectifs',
      'Rapports individuels',
      'Régimes de retraite',
      'Travail'
    ];
  }

  /**
   * Get Québec regions from Barreau
   */
  static getQuebecRegions(): string[] {
    return [
      'Abitibi-Témiscamingue',
      'Arthabaska',
      'Bas-St-Laurent / Gaspésie-IdlM',
      'Bedford',
      'CĂ´te-Nord',
      'Laurentides / Lanaudière',
      'Laval',
      'Longueuil',
      'Mauricie',
      'Montréal',
      'Outaouais',
      'Québec',
      'Richelieu',
      'Saguenay / Lac-Saint-Jean',
      'Saint-François'
    ];
  }
}

export const barreauVerification = BarreauVerificationService.getInstance(); 

CasperSecurity Mini