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/xp-system.ts
import { prisma } from './prisma';

// XP Earning Activities
export const XP_ACTIVITIES = {
  // General Legal Activities
  CASE_APPLICATION_REVIEW: 10,
  CLIENT_INTERVIEW: 25,
  DOCUMENT_ANALYSIS: 15,
  LEGAL_RESEARCH: 20,
  COURT_FILING: 50,
  SUCCESSFUL_OUTCOME: 100,
  PRO_BONO_WORK: 30,
  MENTORSHIP: 25,
  SOCIETY_CONTRIBUTION: 20,
  
  // Bordeaux Case Specific (Bonus XP)
  BORDEAUX_CASE_REVIEW: 50,
  BORDEAUX_CLIENT_INTERVIEW: 75,
  BORDEAUX_DOCUMENT_ANALYSIS: 40,
  BORDEAUX_LEGAL_RESEARCH: 60,
  BORDEAUX_COURT_FILING: 100,
  BORDEAUX_CASE_SUCCESS: 200,
  BORDEAUX_PRO_BONO: 50,
  
  // Society Activities
  DEGREE_CEREMONY: 100,
  LODGE_MEETING: 15,
  MENTORSHIP_SESSION: 30,
  SOCIETY_EVENT: 25,
  
  // Achievement Milestones
  FIRST_CASE: 50,
  TENTH_CASE: 100,
  HUNDREDTH_CASE: 500,
  FIRST_DEGREE: 25,
  TENTH_DEGREE: 200,
  XP_MILESTONE_1000: 50,
  XP_MILESTONE_5000: 100,
  XP_MILESTONE_10000: 200
};

// Achievement Types
export const ACHIEVEMENT_TYPES = {
  CASE_BASED: 'case_based',
  XP_MILESTONE: 'xp_milestone',
  DEGREE_BASED: 'degree_based',
  BORDEAUX_SPECIALIST: 'bordeaux_specialist',
  SOCIETY_CONTRIBUTOR: 'society_contributor'
};

// Achievement Definitions
export const ACHIEVEMENTS = {
  FIRST_CASE: {
    id: 'first_case',
    name: 'First Case',
    description: 'Successfully handled your first case',
    type: ACHIEVEMENT_TYPES.CASE_BASED,
    xpReward: 50,
    icon: '📋'
  },
  TENTH_CASE: {
    id: 'tenth_case',
    name: 'Case Veteran',
    description: 'Successfully handled 10 cases',
    type: ACHIEVEMENT_TYPES.CASE_BASED,
    xpReward: 100,
    icon: '⚖️'
  },
  HUNDREDTH_CASE: {
    id: 'hundredth_case',
    name: 'Case Master',
    description: 'Successfully handled 100 cases',
    type: ACHIEVEMENT_TYPES.CASE_BASED,
    xpReward: 500,
    icon: '👑'
  },
  FIRST_DEGREE: {
    id: 'first_degree',
    name: 'Degree Initiate',
    description: 'Achieved your first degree in the Society',
    type: ACHIEVEMENT_TYPES.DEGREE_BASED,
    xpReward: 25,
    icon: '🎓'
  },
  TENTH_DEGREE: {
    id: 'tenth_degree',
    name: 'Degree Scholar',
    description: 'Achieved 10 degrees in the Society',
    type: ACHIEVEMENT_TYPES.DEGREE_BASED,
    xpReward: 200,
    icon: '🏛️'
  },
  BORDEAUX_SPECIALIST: {
    id: 'bordeaux_specialist',
    name: 'Bordeaux Specialist',
    description: 'Worked on 5 Bordeaux case applications',
    type: ACHIEVEMENT_TYPES.BORDEAUX_SPECIALIST,
    xpReward: 150,
    icon: '🏛️'
  },
  XP_MILESTONE_1000: {
    id: 'xp_1000',
    name: 'XP Novice',
    description: 'Reached 1,000 XP points',
    type: ACHIEVEMENT_TYPES.XP_MILESTONE,
    xpReward: 50,
    icon: '⭐'
  },
  XP_MILESTONE_5000: {
    id: 'xp_5000',
    name: 'XP Expert',
    description: 'Reached 5,000 XP points',
    type: ACHIEVEMENT_TYPES.XP_MILESTONE,
    xpReward: 100,
    icon: '🌟'
  },
  XP_MILESTONE_10000: {
    id: 'xp_10000',
    name: 'XP Master',
    description: 'Reached 10,000 XP points',
    type: ACHIEVEMENT_TYPES.XP_MILESTONE,
    xpReward: 200,
    icon: '💫'
  }
};

export interface XPEarningActivity {
  userId: string;
  activityType: keyof typeof XP_ACTIVITIES;
  xpAmount: number;
  description: string;
  metadata?: any;
  caseId?: string;
  isBordeauxCase?: boolean;
}

export interface Achievement {
  id: string;
  name: string;
  description: string;
  type: string;
  xpReward: number;
  icon: string;
  unlockedAt?: Date;
}

export class XPSystem {
  /**
   * Award XP to a user for an activity
   */
  static async awardXP(activity: XPEarningActivity): Promise<{
    xpAwarded: number;
    newTotal: number;
    achievements: Achievement[];
  }> {
    const { userId, activityType, xpAmount, description, metadata, caseId, isBordeauxCase } = activity;

    try {
      // Start transaction
      const result = await prisma.$transaction(async (tx) => {
        // Get current user
        const user = await tx.user.findUnique({
          where: { id: userId },
          select: { id: true, xpPoints: true }
        });

        if (!user) {
          throw new Error('User not found');
        }

        // Calculate XP amount (with Bordeaux bonus if applicable)
        let finalXPAmount = xpAmount;
        if (isBordeauxCase && caseId) {
          // Check if this is a Bordeaux case
          const caseInfo = await tx.legalCase.findFirst({
            where: { 
              id: caseId,
              title: { contains: 'Bordeaux' }
            }
          });
          
          if (caseInfo) {
            // Apply Bordeaux bonus
            const bordeauxBonus = Math.floor(xpAmount * 0.5); // 50% bonus
            finalXPAmount += bordeauxBonus;
          }
        }

        // Update user XP
        const updatedUser = await tx.user.update({
          where: { id: userId },
          data: {
            xpPoints: {
              increment: finalXPAmount
            }
          },
          select: { id: true, xpPoints: true }
        });

        // Log XP earning activity - commented out due to missing model
        // await tx.xpEarningActivity.create({
        //   data: {
        //     userId,
        //     activityType,
        //     xpAmount: finalXPAmount,
        //     description,
        //     metadata: metadata ? JSON.stringify(metadata) : null,
        //     caseId,
        //     isBordeauxCase: isBordeauxCase || false
        //   }
        // });

        // Check for achievements
        const achievements = await this.checkAchievements(tx, userId, updatedUser.xpPoints);

        return {
          xpAwarded: finalXPAmount,
          newTotal: updatedUser.xpPoints,
          achievements
        };
      });

      return result;

    } catch (error) {
      console.error('Error awarding XP:', error);
      throw error;
    }
  }

  /**
   * Check for new achievements based on user's current state
   */
  static async checkAchievements(tx: any, userId: string, currentXP: number): Promise<Achievement[]> {
    const newAchievements: Achievement[] = [];

    // Get user's current achievements
    const existingAchievements = await tx.userAchievement.findMany({
      where: { userId },
      select: { achievementId: true }
    });

    const existingAchievementIds = new Set(existingAchievements.map((a: any) => a.achievementId));

    // Check XP milestone achievements
    if (currentXP >= 1000 && !existingAchievementIds.has('xp_1000')) {
      newAchievements.push(ACHIEVEMENTS.XP_MILESTONE_1000);
    }
    if (currentXP >= 5000 && !existingAchievementIds.has('xp_5000')) {
      newAchievements.push(ACHIEVEMENTS.XP_MILESTONE_5000);
    }
    if (currentXP >= 10000 && !existingAchievementIds.has('xp_10000')) {
      newAchievements.push(ACHIEVEMENTS.XP_MILESTONE_10000);
    }

    // Check case-based achievements
    const caseCount = await tx.caseAssignment.count({
      where: { 
        userId,
        isActive: true
      }
    });

    if (caseCount >= 1 && !existingAchievementIds.has('first_case')) {
      newAchievements.push(ACHIEVEMENTS.FIRST_CASE);
    }
    if (caseCount >= 10 && !existingAchievementIds.has('tenth_case')) {
      newAchievements.push(ACHIEVEMENTS.TENTH_CASE);
    }
    if (caseCount >= 100 && !existingAchievementIds.has('hundredth_case')) {
      newAchievements.push(ACHIEVEMENTS.HUNDREDTH_CASE);
    }

    // Check degree-based achievements
    const degreeCount = await tx.userDegree.count({
      where: { 
        userId,
        ceremonyCompleted: true
      }
    });

    if (degreeCount >= 1 && !existingAchievementIds.has('first_degree')) {
      newAchievements.push(ACHIEVEMENTS.FIRST_DEGREE);
    }
    if (degreeCount >= 10 && !existingAchievementIds.has('tenth_degree')) {
      newAchievements.push(ACHIEVEMENTS.TENTH_DEGREE);
    }

    // Check Bordeaux specialist achievement
    const bordeauxCaseCount = await tx.caseAssignment.count({
      where: {
        userId,
        isActive: true,
        registration: {
          caseId: {
            not: null
          }
        }
      }
    });

    if (bordeauxCaseCount >= 5 && !existingAchievementIds.has('bordeaux_specialist')) {
      newAchievements.push(ACHIEVEMENTS.BORDEAUX_SPECIALIST);
    }

    // Award achievements and additional XP
    for (const achievement of newAchievements) {
      await tx.userAchievement.create({
        data: {
          userId,
          achievementId: achievement.id,
          lastUpdated: new Date(),
          currentProgress: 100,
          isCompleted: true,
          completedAt: new Date()
        }
      });

      // Award XP for achievement
      await tx.user.update({
        where: { id: userId },
        data: {
          xpPoints: {
            increment: achievement.xpReward
          }
        }
      });
    }

    return newAchievements;
  }

  /**
   * Get user's XP history
   */
  static async getUserXPHistory(userId: string, limit: number = 20) {
    // Commented out due to missing xpEarningActivity model
    // return await prisma.xpEarningActivity.findMany({
    //   where: { userId },
    //   orderBy: { createdAt: 'desc' },
    //   take: limit,
    //   include: {
    //     case: {
    //       select: {
    //         title: true,
    //         caseNumber: true
    //       }
    //     }
    //   }
    // });
    return [];
  }

  /**
   * Get user's achievements
   */
  static async getUserAchievements(userId: string) {
    return await prisma.userAchievement.findMany({
      where: { userId },
      include: {
        achievement: true
      },
      orderBy: { lastUpdated: 'desc' }
    });
  }

  /**
   * Get leaderboard data
   */
  static async getLeaderboard(limit: number = 10) {
    return await prisma.user.findMany({
      where: {
        role: { in: ['LAWYER', 'ADMIN', 'SUPERADMIN', 'SUPERADMIN'] }
      },
      select: {
        id: true,
        name: true,
        xpPoints: true,
        role: true,
        degrees: {
          where: { ceremonyCompleted: true },
          include: {
            degree: {
              select: {
                degreeNumber: true,
                name: true,
                symbol: true
              }
            }
          },
          orderBy: {
            degree: { degreeNumber: 'desc' }
          },
          take: 1
        }
      },
      orderBy: { xpPoints: 'desc' },
      take: limit
    });
  }

  /**
   * Get Bordeaux case leaderboard
   */
  static async getBordeauxLeaderboard(limit: number = 10) {
    // For now, return users with highest XP points as a fallback
    return await prisma.user.findMany({
      where: {
        role: { in: ['LAWYER', 'ADMIN', 'SUPERADMIN', 'SUPERADMIN'] }
      },
      select: {
        id: true,
        name: true,
        xpPoints: true,
        role: true
      },
      orderBy: { xpPoints: 'desc' },
      take: limit
    });
  }
} 

CasperSecurity Mini