fixed merge conflicts
This commit is contained in:
+2
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
/* build-hook-start *//*00001*/try { require('c:\\Users\\magdo\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.475\\out\\buildHook\\index.js').default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'}); } catch(cjsError) { try { import('file:///c:/Users/magdo/.vscode/extensions/wallabyjs.console-ninja-1.0.475/out/buildHook/index.js').then(m => m.default.default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */
|
||||||
/*!
|
/*!
|
||||||
* /**
|
* /**
|
||||||
* * Copyright (c) Meta Platforms, Inc. and affiliates.
|
* * Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
|||||||
@@ -100,7 +100,6 @@
|
|||||||
* type: string
|
* type: string
|
||||||
* format: email
|
* format: email
|
||||||
*
|
*
|
||||||
<<<<<<< HEAD
|
|
||||||
* ForgotPasswordRequest:
|
* ForgotPasswordRequest:
|
||||||
* type: object
|
* type: object
|
||||||
* required:
|
* required:
|
||||||
@@ -132,8 +131,6 @@
|
|||||||
* message:
|
* message:
|
||||||
* type: string
|
* type: string
|
||||||
*
|
*
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
* Organization:
|
* Organization:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
@@ -463,7 +460,6 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
<<<<<<< HEAD
|
|
||||||
* /api/users/verify-email/{token}:
|
* /api/users/verify-email/{token}:
|
||||||
* get:
|
* get:
|
||||||
* tags: [Users]
|
* tags: [Users]
|
||||||
@@ -543,8 +539,6 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
* /api/organizations/search:
|
* /api/organizations/search:
|
||||||
* get:
|
* get:
|
||||||
* tags: [Organizations]
|
* tags: [Organizations]
|
||||||
|
|||||||
@@ -1,32 +1,17 @@
|
|||||||
import { GameField, BoardData } from '../../Domain/Game/GameAggregate';
|
import { GameField, BoardData } from '../../Domain/Game/GameAggregate';
|
||||||
import { logOther, logError } from '../Services/Logger';
|
import { logOther, logError } from '../Services/Logger';
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
interface TargetField {
|
|
||||||
fieldNumber: number;
|
|
||||||
distance: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
interface SpecialFieldInfo {
|
interface SpecialFieldInfo {
|
||||||
position: number;
|
position: number;
|
||||||
type: 'positive' | 'negative' | 'luck';
|
type: 'positive' | 'negative' | 'luck';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BoardGenerationService {
|
export class BoardGenerationService {
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
private readonly MAX_GENERATION_TIME = parseInt(process.env.MAX_GENERATION_TIME_SECONDS || '20') * 1000;
|
|
||||||
private readonly ERROR_TOLERANCE = parseInt(process.env.GENERATION_ERROR_TOLERANCE || '15');
|
|
||||||
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
async generateBoard(
|
async generateBoard(
|
||||||
positiveFieldCount: number,
|
positiveFieldCount: number,
|
||||||
negativeFieldCount: number,
|
negativeFieldCount: number,
|
||||||
luckFieldCount: number
|
luckFieldCount: number
|
||||||
): Promise<BoardData> {
|
): Promise<BoardData> {
|
||||||
<<<<<<< HEAD
|
|
||||||
// Pattern-based approach has 100% success rate, no retry needed
|
// Pattern-based approach has 100% success rate, no retry needed
|
||||||
const result = this.generateSingleAttempt(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
const result = this.generateSingleAttempt(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
||||||
|
|
||||||
@@ -39,36 +24,6 @@ export class BoardGenerationService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
=======
|
|
||||||
const startTime = Date.now();
|
|
||||||
let bestAttempt: BoardData | null = null;
|
|
||||||
let attemptCount = 0;
|
|
||||||
|
|
||||||
while (Date.now() - startTime < this.MAX_GENERATION_TIME) {
|
|
||||||
attemptCount++;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const attempt = this.generateSingleAttempt(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
|
||||||
|
|
||||||
if (attempt.totalErrorRate <= this.ERROR_TOLERANCE) {
|
|
||||||
logOther(`Board generation successful on attempt ${attemptCount}. Error rate: ${attempt.totalErrorRate}%`);
|
|
||||||
return attempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bestAttempt || attempt.totalErrorRate < bestAttempt.totalErrorRate) {
|
|
||||||
bestAttempt = attempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
logOther(`Attempt ${attemptCount}: Error rate ${attempt.totalErrorRate}% (target: ${this.ERROR_TOLERANCE}%)`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logError(`Board generation attempt ${attemptCount} failed:`, error as Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logOther(`Using best attempt with error rate: ${bestAttempt?.totalErrorRate || 100}%`);
|
|
||||||
return bestAttempt || this.generateFallbackBoard(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateSingleAttempt(
|
private generateSingleAttempt(
|
||||||
@@ -83,42 +38,11 @@ export class BoardGenerationService {
|
|||||||
luckFieldCount
|
luckFieldCount
|
||||||
);
|
);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Step 2: Calculate step values using pattern-based approach
|
// Step 2: Calculate step values using pattern-based approach
|
||||||
const fields = this.calculatePatternBasedStepValues(specialFieldPositions);
|
const fields = this.calculatePatternBasedStepValues(specialFieldPositions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fields
|
fields
|
||||||
=======
|
|
||||||
// Step 2: Select target fields for each special field (6 targets per field for dice 1-6)
|
|
||||||
const targetFieldsMap = this.selectTargetFields(specialFieldPositions);
|
|
||||||
|
|
||||||
// Step 3: Create border with strategic placement
|
|
||||||
const border = this.createStrategicBorder(targetFieldsMap);
|
|
||||||
|
|
||||||
// Step 4: Calculate step values based on border positions
|
|
||||||
const fields = this.calculateStepValues(specialFieldPositions, targetFieldsMap, border);
|
|
||||||
|
|
||||||
// Step 5: Validate against 20-30 rule and calculate error rate
|
|
||||||
const validationResults = this.validateBoardGeneration(fields, border);
|
|
||||||
|
|
||||||
// Log generation statistics
|
|
||||||
logOther('Board generation attempt completed', {
|
|
||||||
totalFields: fields.length,
|
|
||||||
specialFields: fields.filter(f => f.type !== 'regular').length,
|
|
||||||
positiveFields: fields.filter(f => f.type === 'positive').length,
|
|
||||||
negativeFields: fields.filter(f => f.type === 'negative').length,
|
|
||||||
luckFields: fields.filter(f => f.type === 'luck').length,
|
|
||||||
errorRate: validationResults.errorRate,
|
|
||||||
targetCount: Array.from(targetFieldsMap.values()).reduce((sum, targets) => sum + targets.length, 0)
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
fields,
|
|
||||||
border,
|
|
||||||
validationResults: validationResults.validationResults,
|
|
||||||
totalErrorRate: validationResults.errorRate
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +52,6 @@ export class BoardGenerationService {
|
|||||||
luckFieldCount: number
|
luckFieldCount: number
|
||||||
): SpecialFieldInfo[] {
|
): SpecialFieldInfo[] {
|
||||||
const totalSpecial = positiveFieldCount + negativeFieldCount + luckFieldCount;
|
const totalSpecial = positiveFieldCount + negativeFieldCount + luckFieldCount;
|
||||||
<<<<<<< HEAD
|
|
||||||
const specialFields: SpecialFieldInfo[] = [];
|
const specialFields: SpecialFieldInfo[] = [];
|
||||||
|
|
||||||
// Generate unique random positions
|
// Generate unique random positions
|
||||||
@@ -140,29 +63,6 @@ export class BoardGenerationService {
|
|||||||
|
|
||||||
// Convert to sorted array
|
// Convert to sorted array
|
||||||
const sortedPositions = Array.from(positions).sort((a, b) => a - b);
|
const sortedPositions = Array.from(positions).sort((a, b) => a - b);
|
||||||
=======
|
|
||||||
const positions: number[] = [];
|
|
||||||
const specialFields: SpecialFieldInfo[] = [];
|
|
||||||
|
|
||||||
// Random placement with retry for good distribution
|
|
||||||
let attempts = 0;
|
|
||||||
while (positions.length < totalSpecial && attempts < 100) {
|
|
||||||
const position = Math.floor(Math.random() * 100) + 1; // 1-100
|
|
||||||
|
|
||||||
if (!positions.includes(position)) {
|
|
||||||
// Check minimum distance from existing positions
|
|
||||||
const tooClose = positions.some(existingPos => Math.abs(existingPos - position) < 3);
|
|
||||||
|
|
||||||
if (!tooClose || attempts > 50) { // Relax distance requirement after many attempts
|
|
||||||
positions.push(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort positions and assign types
|
|
||||||
positions.sort((a, b) => a - b);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
|
|
||||||
// Distribute types randomly
|
// Distribute types randomly
|
||||||
const types: ('positive' | 'negative' | 'luck')[] = [
|
const types: ('positive' | 'negative' | 'luck')[] = [
|
||||||
@@ -177,11 +77,7 @@ export class BoardGenerationService {
|
|||||||
[types[i], types[j]] = [types[j], types[i]];
|
[types[i], types[j]] = [types[j], types[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
sortedPositions.forEach((position, index) => {
|
sortedPositions.forEach((position, index) => {
|
||||||
=======
|
|
||||||
positions.forEach((position, index) => {
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
specialFields.push({
|
specialFields.push({
|
||||||
position,
|
position,
|
||||||
type: types[index] || 'positive'
|
type: types[index] || 'positive'
|
||||||
@@ -191,156 +87,14 @@ export class BoardGenerationService {
|
|||||||
return specialFields;
|
return specialFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
private calculatePatternBasedStepValues(specialFields: SpecialFieldInfo[]): GameField[] {
|
private calculatePatternBasedStepValues(specialFields: SpecialFieldInfo[]): GameField[] {
|
||||||
=======
|
|
||||||
private selectTargetFields(specialFields: SpecialFieldInfo[]): Map<number, TargetField[]> {
|
|
||||||
const targetFieldsMap = new Map<number, TargetField[]>();
|
|
||||||
|
|
||||||
specialFields.forEach(field => {
|
|
||||||
if (field.type === 'luck') {
|
|
||||||
// Luck fields don't need target calculations
|
|
||||||
targetFieldsMap.set(field.position, []);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const targets: TargetField[] = [];
|
|
||||||
const usedTargets = new Set<number>();
|
|
||||||
|
|
||||||
// Generate 6 different target fields (for dice 1-6) with 20-30 rule compliance
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
let targetField: number;
|
|
||||||
let distance: number;
|
|
||||||
let attempts = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Determine max distance based on field position (20-30 rule)
|
|
||||||
let maxDistance: number;
|
|
||||||
let maxBackward: number;
|
|
||||||
|
|
||||||
if (field.position <= 85) {
|
|
||||||
maxDistance = 20;
|
|
||||||
maxBackward = 20;
|
|
||||||
} else {
|
|
||||||
maxDistance = 20; // forward
|
|
||||||
maxBackward = 30; // backward
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create variety in distances within the allowed range
|
|
||||||
const distanceType = Math.random();
|
|
||||||
if (distanceType < 0.5) {
|
|
||||||
// Close distance (50% chance) - 1 to 1/3 of max
|
|
||||||
distance = Math.floor(Math.random() * Math.floor(maxDistance / 3)) + 1;
|
|
||||||
} else {
|
|
||||||
// Far distance (50% chance) - 1/3 to max
|
|
||||||
distance = Math.floor(Math.random() * (maxDistance - Math.floor(maxDistance / 3))) + Math.floor(maxDistance / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomly choose forward or backward
|
|
||||||
if (Math.random() < 0.5) {
|
|
||||||
distance = -Math.min(distance, maxBackward);
|
|
||||||
} else {
|
|
||||||
distance = Math.min(distance, maxDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
targetField = field.position + distance;
|
|
||||||
|
|
||||||
// Ensure target is within valid range
|
|
||||||
if (targetField < 1) targetField = 1;
|
|
||||||
if (targetField > 100) targetField = 100;
|
|
||||||
|
|
||||||
// Recalculate actual distance after clamping
|
|
||||||
distance = Math.abs(targetField - field.position);
|
|
||||||
|
|
||||||
attempts++;
|
|
||||||
} while (usedTargets.has(targetField) && attempts < 30);
|
|
||||||
|
|
||||||
if (!usedTargets.has(targetField)) {
|
|
||||||
usedTargets.add(targetField);
|
|
||||||
targets.push({
|
|
||||||
fieldNumber: targetField,
|
|
||||||
distance: Math.abs(targetField - field.position)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Fallback: use a nearby valid target
|
|
||||||
let fallbackTarget = field.position + (i - 3); // Create some variety around current position
|
|
||||||
if (fallbackTarget < 1) fallbackTarget = 1;
|
|
||||||
if (fallbackTarget > 100) fallbackTarget = 100;
|
|
||||||
|
|
||||||
targets.push({
|
|
||||||
fieldNumber: fallbackTarget,
|
|
||||||
distance: Math.abs(fallbackTarget - field.position)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetFieldsMap.set(field.position, targets);
|
|
||||||
});
|
|
||||||
|
|
||||||
return targetFieldsMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createStrategicBorder(targetFieldsMap: Map<number, TargetField[]>): number[] {
|
|
||||||
// Collect all target field numbers
|
|
||||||
const targetNumbers = new Set<number>();
|
|
||||||
targetFieldsMap.forEach(targets => {
|
|
||||||
targets.forEach(target => targetNumbers.add(target.fieldNumber));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create array of all numbers 1-100
|
|
||||||
const allNumbers = Array.from({ length: 100 }, (_, i) => i + 1);
|
|
||||||
|
|
||||||
// Separate target numbers from remaining numbers
|
|
||||||
const remainingNumbers = allNumbers.filter(num => !targetNumbers.has(num));
|
|
||||||
|
|
||||||
// Shuffle remaining numbers
|
|
||||||
for (let i = remainingNumbers.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
[remainingNumbers[i], remainingNumbers[j]] = [remainingNumbers[j], remainingNumbers[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create border with strategic placement
|
|
||||||
const border: number[] = [];
|
|
||||||
const targetArray = Array.from(targetNumbers);
|
|
||||||
|
|
||||||
// Encourage overlap by placing target numbers first, then fill with random
|
|
||||||
let targetIndex = 0;
|
|
||||||
let remainingIndex = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
// Alternate between target numbers and remaining numbers, but favor targets when available
|
|
||||||
if (targetIndex < targetArray.length && (remainingIndex >= remainingNumbers.length || Math.random() < 0.6)) {
|
|
||||||
border.push(targetArray[targetIndex]);
|
|
||||||
targetIndex++;
|
|
||||||
} else if (remainingIndex < remainingNumbers.length) {
|
|
||||||
border.push(remainingNumbers[remainingIndex]);
|
|
||||||
remainingIndex++;
|
|
||||||
} else {
|
|
||||||
// Fallback - should not happen if logic is correct
|
|
||||||
border.push((i % 100) + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateStepValues(
|
|
||||||
specialFields: SpecialFieldInfo[],
|
|
||||||
targetFieldsMap: Map<number, TargetField[]>,
|
|
||||||
border: number[]
|
|
||||||
): GameField[] {
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
// Initialize all fields as regular
|
// Initialize all fields as regular
|
||||||
const fields: GameField[] = Array.from({ length: 100 }, (_, i) => ({
|
const fields: GameField[] = Array.from({ length: 100 }, (_, i) => ({
|
||||||
position: i + 1,
|
position: i + 1,
|
||||||
type: 'regular' as const
|
type: 'regular' as const
|
||||||
}));
|
}));
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Update special fields with pattern-based step values
|
// Update special fields with pattern-based step values
|
||||||
=======
|
|
||||||
// Update special fields with calculated step values
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
specialFields.forEach(specialField => {
|
specialFields.forEach(specialField => {
|
||||||
const fieldIndex = specialField.position - 1; // Convert to 0-based index
|
const fieldIndex = specialField.position - 1; // Convert to 0-based index
|
||||||
fields[fieldIndex].type = specialField.type;
|
fields[fieldIndex].type = specialField.type;
|
||||||
@@ -350,7 +104,6 @@ export class BoardGenerationService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Calculate step values based on position rules
|
// Calculate step values based on position rules
|
||||||
let maxStepValue: number;
|
let maxStepValue: number;
|
||||||
let minStepValue: number;
|
let minStepValue: number;
|
||||||
@@ -374,64 +127,12 @@ export class BoardGenerationService {
|
|||||||
// Negative fields: use negative step values (-3 to -8 range)
|
// Negative fields: use negative step values (-3 to -8 range)
|
||||||
const stepValue = -(Math.floor(Math.random() * 6) + 3); // -3 to -8
|
const stepValue = -(Math.floor(Math.random() * 6) + 3); // -3 to -8
|
||||||
fields[fieldIndex].stepValue = Math.max(stepValue, minStepValue);
|
fields[fieldIndex].stepValue = Math.max(stepValue, minStepValue);
|
||||||
=======
|
|
||||||
const targets = targetFieldsMap.get(specialField.position) || [];
|
|
||||||
if (targets.length === 0) return;
|
|
||||||
|
|
||||||
// NEW APPROACH: Calculate step value that will land on first target with dice=1
|
|
||||||
// This ensures we have a baseline that works, then dice 2-6 will hit other targets
|
|
||||||
const firstTarget = targets[0];
|
|
||||||
const targetIndexInBorder = border.indexOf(firstTarget.fieldNumber);
|
|
||||||
|
|
||||||
if (targetIndexInBorder !== -1) {
|
|
||||||
// Start from field position in border (field position = border index + 1, but we want 0-based)
|
|
||||||
const startBorderIndex = (specialField.position - 1) % border.length;
|
|
||||||
|
|
||||||
// Calculate step value needed to reach target with dice=1
|
|
||||||
let stepValue: number;
|
|
||||||
|
|
||||||
if (specialField.type === 'positive') {
|
|
||||||
// For positive: move right to target, then +1 more for dice=1
|
|
||||||
stepValue = targetIndexInBorder - startBorderIndex - 1; // -1 for dice offset
|
|
||||||
|
|
||||||
// Handle wrap-around
|
|
||||||
if (stepValue < 0) {
|
|
||||||
stepValue += border.length;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// For negative: move left to target, then -1 more for dice=1
|
|
||||||
stepValue = startBorderIndex - targetIndexInBorder + 1; // +1 for dice offset
|
|
||||||
|
|
||||||
// Handle wrap-around
|
|
||||||
if (stepValue > border.length) {
|
|
||||||
stepValue -= border.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make negative for negative fields
|
|
||||||
stepValue = -stepValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fields[fieldIndex].stepValue = stepValue;
|
|
||||||
|
|
||||||
// Debug logging for step value calculation
|
|
||||||
logOther(`Calculated step value for ${specialField.type} field at position ${specialField.position}`, {
|
|
||||||
targetField: firstTarget.fieldNumber,
|
|
||||||
targetIndexInBorder,
|
|
||||||
startBorderIndex,
|
|
||||||
calculatedStepValue: stepValue,
|
|
||||||
fieldType: specialField.type
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Fallback if target not found in border (shouldn't happen)
|
|
||||||
fields[fieldIndex].stepValue = specialField.type === 'positive' ? 1 : -1;
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// This method can be used by FieldEffectService for movement calculations
|
// This method can be used by FieldEffectService for movement calculations
|
||||||
public calculatePatternBasedMovement(
|
public calculatePatternBasedMovement(
|
||||||
currentPosition: number,
|
currentPosition: number,
|
||||||
@@ -474,89 +175,6 @@ export class BoardGenerationService {
|
|||||||
} else {
|
} else {
|
||||||
return 0; // Other even positions
|
return 0; // Other even positions
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
private validateBoardGeneration(fields: GameField[], border: number[]): {
|
|
||||||
validationResults: { [fieldIndex: number]: number[] };
|
|
||||||
errorRate: number;
|
|
||||||
} {
|
|
||||||
const validationResults: { [fieldIndex: number]: number[] } = {};
|
|
||||||
let totalCombinations = 0;
|
|
||||||
let invalidCombinations = 0;
|
|
||||||
|
|
||||||
fields.forEach((field, fieldIndex) => {
|
|
||||||
if (field.type !== 'positive' && field.type !== 'negative') {
|
|
||||||
return; // Skip non-special fields
|
|
||||||
}
|
|
||||||
|
|
||||||
const diceOutcomes: number[] = [];
|
|
||||||
|
|
||||||
for (let diceValue = 1; diceValue <= 6; diceValue++) {
|
|
||||||
totalCombinations++;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = this.calculateBorderMovement(
|
|
||||||
field.position,
|
|
||||||
field.stepValue || 0,
|
|
||||||
diceValue,
|
|
||||||
border,
|
|
||||||
field.type === 'positive'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate 20-30 rule
|
|
||||||
const distance = Math.abs(result - field.position);
|
|
||||||
const isValid = this.validate20_30Rule(field.position, result, distance);
|
|
||||||
|
|
||||||
if (isValid) {
|
|
||||||
diceOutcomes.push(result);
|
|
||||||
} else {
|
|
||||||
diceOutcomes.push(-1); // Mark as invalid
|
|
||||||
invalidCombinations++;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
diceOutcomes.push(-1); // Mark as invalid
|
|
||||||
invalidCombinations++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validationResults[fieldIndex] = diceOutcomes;
|
|
||||||
});
|
|
||||||
|
|
||||||
const errorRate = totalCombinations > 0 ? (invalidCombinations / totalCombinations) * 100 : 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
validationResults,
|
|
||||||
errorRate: Math.round(errorRate * 100) / 100 // Round to 2 decimal places
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateBorderMovement(
|
|
||||||
currentPosition: number,
|
|
||||||
stepValue: number,
|
|
||||||
diceValue: number,
|
|
||||||
border: number[],
|
|
||||||
isPositive: boolean
|
|
||||||
): number {
|
|
||||||
// Step 1: Find border index for current field (field position corresponds to border index)
|
|
||||||
let borderIndex = (currentPosition - 1) % border.length; // Convert to 0-based, handle wraparound
|
|
||||||
|
|
||||||
// Step 2: Apply field step value (handle negative step values for negative fields)
|
|
||||||
if (isPositive) {
|
|
||||||
borderIndex = (borderIndex + Math.abs(stepValue)) % border.length;
|
|
||||||
} else {
|
|
||||||
// For negative fields, stepValue is already negative, so we subtract it (which adds its absolute value)
|
|
||||||
borderIndex = (borderIndex - stepValue + border.length) % border.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Apply dice value
|
|
||||||
if (isPositive) {
|
|
||||||
borderIndex = (borderIndex + diceValue) % border.length;
|
|
||||||
} else {
|
|
||||||
borderIndex = (borderIndex - diceValue + border.length) % border.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4: Return the field number at final border position
|
|
||||||
return border[borderIndex];
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private validate20_30Rule(currentPosition: number, targetPosition: number, distance: number): boolean {
|
private validate20_30Rule(currentPosition: number, targetPosition: number, distance: number): boolean {
|
||||||
@@ -578,46 +196,4 @@ export class BoardGenerationService {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
|
|
||||||
private generateFallbackBoard(
|
|
||||||
positiveFieldCount: number,
|
|
||||||
negativeFieldCount: number,
|
|
||||||
luckFieldCount: number
|
|
||||||
): BoardData {
|
|
||||||
// Simple fallback: create basic board with minimal special fields
|
|
||||||
const fields: GameField[] = Array.from({ length: 100 }, (_, i) => ({
|
|
||||||
position: i + 1,
|
|
||||||
type: 'regular' as const
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Add a few special fields with safe step values
|
|
||||||
let specialCount = 0;
|
|
||||||
for (let i = 10; i < 90 && specialCount < positiveFieldCount + negativeFieldCount; i += 10) {
|
|
||||||
if (specialCount < positiveFieldCount) {
|
|
||||||
fields[i].type = 'positive';
|
|
||||||
fields[i].stepValue = 1;
|
|
||||||
} else {
|
|
||||||
fields[i].type = 'negative';
|
|
||||||
fields[i].stepValue = -1;
|
|
||||||
}
|
|
||||||
specialCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple border: shuffled 1-100
|
|
||||||
const border = Array.from({ length: 100 }, (_, i) => i + 1);
|
|
||||||
for (let i = border.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
[border[i], border[j]] = [border[j], border[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
fields,
|
|
||||||
border,
|
|
||||||
validationResults: {},
|
|
||||||
totalErrorRate: 100 // Mark as fallback
|
|
||||||
};
|
|
||||||
}
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
@@ -37,11 +37,7 @@ export class GenerateBoardCommandHandler {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const executionTime = Date.now() - startTime;
|
const executionTime = Date.now() - startTime;
|
||||||
<<<<<<< HEAD
|
|
||||||
logOther(`Board generation completed for game ${cmd.gameId} in ${executionTime}ms using pattern-based approach`);
|
logOther(`Board generation completed for game ${cmd.gameId} in ${executionTime}ms using pattern-based approach`);
|
||||||
=======
|
|
||||||
logOther(`Board generation completed for game ${cmd.gameId} in ${executionTime}ms. Error rate: ${boardData.totalErrorRate}%`);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(`Board generation failed for game ${cmd.gameId}:`, error as Error);
|
logError(`Board generation failed for game ${cmd.gameId}:`, error as Error);
|
||||||
@@ -50,12 +46,6 @@ export class GenerateBoardCommandHandler {
|
|||||||
const errorData: BoardData = {
|
const errorData: BoardData = {
|
||||||
gameId: cmd.gameId,
|
gameId: cmd.gameId,
|
||||||
fields: [],
|
fields: [],
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
border: [],
|
|
||||||
validationResults: {},
|
|
||||||
totalErrorRate: 100,
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
generationComplete: false,
|
generationComplete: false,
|
||||||
error: error instanceof Error ? error.message : 'Unknown error',
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
generatedAt: new Date()
|
generatedAt: new Date()
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ export class JoinGameCommandHandler {
|
|||||||
isOnline: true
|
isOnline: true
|
||||||
};
|
};
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Check if player name is already in use by a different player
|
// Check if player name is already in use by a different player
|
||||||
const existingPlayerWithName = gameData.currentPlayers.find(
|
const existingPlayerWithName = gameData.currentPlayers.find(
|
||||||
p => p.playerName === command.playerName && p.playerId !== command.playerId
|
p => p.playerName === command.playerName && p.playerId !== command.playerId
|
||||||
@@ -161,8 +160,6 @@ export class JoinGameCommandHandler {
|
|||||||
throw new Error(`Player name "${command.playerName}" is already in use in this game`);
|
throw new Error(`Player name "${command.playerName}" is already in use in this game`);
|
||||||
}
|
}
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
// Update players list (remove if exists, then add)
|
// Update players list (remove if exists, then add)
|
||||||
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== command.playerId);
|
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== command.playerId);
|
||||||
gameData.currentPlayers.push(newPlayer);
|
gameData.currentPlayers.push(newPlayer);
|
||||||
@@ -173,12 +170,6 @@ export class JoinGameCommandHandler {
|
|||||||
// Store updated data in Redis with TTL (24 hours)
|
// Store updated data in Redis with TTL (24 hours)
|
||||||
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
// Add player to active players set
|
|
||||||
await this.redisService.setAdd(`active_players:${game.id}`, command.playerId);
|
|
||||||
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
logOther('Game data updated in Redis', {
|
logOther('Game data updated in Redis', {
|
||||||
gameId: game.id,
|
gameId: game.id,
|
||||||
gameCode: game.gamecode,
|
gameCode: game.gamecode,
|
||||||
@@ -219,10 +210,6 @@ export class JoinGameCommandHandler {
|
|||||||
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== playerId);
|
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== playerId);
|
||||||
|
|
||||||
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
await this.redisService.setRemove(`active_players:${gameId}`, playerId);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('Failed to remove player from Redis', error instanceof Error ? error : new Error(String(error)));
|
logError('Failed to remove player from Redis', error instanceof Error ? error : new Error(String(error)));
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ export class StartGameCommandHandler {
|
|||||||
gamecode,
|
gamecode,
|
||||||
maxplayers: command.maxplayers,
|
maxplayers: command.maxplayers,
|
||||||
logintype: command.logintype,
|
logintype: command.logintype,
|
||||||
<<<<<<< HEAD
|
|
||||||
createdby: command.userid!,
|
createdby: command.userid!,
|
||||||
=======
|
|
||||||
createdby: command.userid || null,
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
orgid: command.orgid || null,
|
orgid: command.orgid || null,
|
||||||
gamedecks,
|
gamedecks,
|
||||||
players: [],
|
players: [],
|
||||||
|
|||||||
@@ -28,11 +28,7 @@ export interface ActiveGamePlayData {
|
|||||||
turnSequence: string[]; // Ordered array of player IDs based on turnOrder
|
turnSequence: string[]; // Ordered array of player IDs based on turnOrder
|
||||||
websocketRoom: string;
|
websocketRoom: string;
|
||||||
gamePhase: 'starting' | 'playing' | 'paused' | 'finished';
|
gamePhase: 'starting' | 'playing' | 'paused' | 'finished';
|
||||||
<<<<<<< HEAD
|
|
||||||
boardData: BoardData; // Generated board with fields
|
boardData: BoardData; // Generated board with fields
|
||||||
=======
|
|
||||||
boardData: BoardData; // Generated board with fields and border
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameStartResult {
|
export interface GameStartResult {
|
||||||
@@ -366,13 +362,7 @@ export class StartGamePlayCommandHandler {
|
|||||||
logOther(`Board data found for game ${gameId}`, {
|
logOther(`Board data found for game ${gameId}`, {
|
||||||
generationComplete: boardData.generationComplete,
|
generationComplete: boardData.generationComplete,
|
||||||
hasError: !!boardData.error,
|
hasError: !!boardData.error,
|
||||||
<<<<<<< HEAD
|
|
||||||
fieldsCount: boardData.fields ? boardData.fields.length : 0
|
fieldsCount: boardData.fields ? boardData.fields.length : 0
|
||||||
=======
|
|
||||||
fieldsCount: boardData.fields ? boardData.fields.length : 0,
|
|
||||||
borderLength: boardData.border ? boardData.border.length : 0,
|
|
||||||
totalErrorRate: boardData.totalErrorRate
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (boardData.generationComplete) {
|
if (boardData.generationComplete) {
|
||||||
@@ -382,13 +372,7 @@ export class StartGamePlayCommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logOther(`Board generation completed for game ${gameId}`, {
|
logOther(`Board generation completed for game ${gameId}`, {
|
||||||
<<<<<<< HEAD
|
|
||||||
fieldCount: boardData.fields.length,
|
fieldCount: boardData.fields.length,
|
||||||
=======
|
|
||||||
errorRate: boardData.totalErrorRate,
|
|
||||||
fieldCount: boardData.fields.length,
|
|
||||||
borderLength: boardData.border.length,
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
waitTime: Date.now() - startTime
|
waitTime: Date.now() - startTime
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -79,11 +79,7 @@ export async function authRequired(req: Request, res: Response, next: NextFuncti
|
|||||||
orgId: payload.orgId
|
orgId: payload.orgId
|
||||||
}, req);
|
}, req);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
const refreshed = jwtService.refreshIfNeeded(payload, res, req);
|
const refreshed = jwtService.refreshIfNeeded(payload, res, req);
|
||||||
=======
|
|
||||||
const refreshed = jwtService.refreshIfNeeded(payload, res);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
logAuth('Token refreshed', payload.userId, undefined, req);
|
logAuth('Token refreshed', payload.userId, undefined, req);
|
||||||
}
|
}
|
||||||
@@ -136,11 +132,7 @@ export async function adminRequired(req: Request, res: Response, next: NextFunct
|
|||||||
orgId: payload.orgId
|
orgId: payload.orgId
|
||||||
}, req);
|
}, req);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
const refreshed = jwtService.refreshIfNeeded(payload, res, req);
|
const refreshed = jwtService.refreshIfNeeded(payload, res, req);
|
||||||
=======
|
|
||||||
const refreshed = jwtService.refreshIfNeeded(payload, res);
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
logAuth('Admin token refreshed', payload.userId, undefined, req);
|
logAuth('Admin token refreshed', payload.userId, undefined, req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,12 +60,9 @@ import { EmailService } from './EmailService';
|
|||||||
import { GameTokenService } from './GameTokenService';
|
import { GameTokenService } from './GameTokenService';
|
||||||
import { ContactEmailService } from './ContactEmailService';
|
import { ContactEmailService } from './ContactEmailService';
|
||||||
import { DeckImportExportService } from './DeckImportExportService';
|
import { DeckImportExportService } from './DeckImportExportService';
|
||||||
<<<<<<< HEAD
|
|
||||||
import { FieldEffectService } from './FieldEffectService';
|
import { FieldEffectService } from './FieldEffectService';
|
||||||
import { CardDrawingService } from './CardDrawingService';
|
import { CardDrawingService } from './CardDrawingService';
|
||||||
import { GamemasterService } from './GamemasterService';
|
import { GamemasterService } from './GamemasterService';
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
import { RedisService } from './RedisService';
|
import { RedisService } from './RedisService';
|
||||||
import { GameService } from '../Game/GameService';
|
import { GameService } from '../Game/GameService';
|
||||||
import { BoardGenerationService } from '../Game/BoardGenerationService';
|
import { BoardGenerationService } from '../Game/BoardGenerationService';
|
||||||
@@ -93,12 +90,9 @@ export class DIContainer {
|
|||||||
private _gameTokenService: GameTokenService | null = null;
|
private _gameTokenService: GameTokenService | null = null;
|
||||||
private _contactEmailService: ContactEmailService | null = null;
|
private _contactEmailService: ContactEmailService | null = null;
|
||||||
private _deckImportExportService: DeckImportExportService | null = null;
|
private _deckImportExportService: DeckImportExportService | null = null;
|
||||||
<<<<<<< HEAD
|
|
||||||
private _cardDrawingService: CardDrawingService | null = null;
|
private _cardDrawingService: CardDrawingService | null = null;
|
||||||
private _gamemasterService: GamemasterService | null = null;
|
private _gamemasterService: GamemasterService | null = null;
|
||||||
private _fieldEffectService: FieldEffectService | null = null;
|
private _fieldEffectService: FieldEffectService | null = null;
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
private _gameService: GameService | null = null;
|
private _gameService: GameService | null = null;
|
||||||
private _boardGenerationService: BoardGenerationService | null = null;
|
private _boardGenerationService: BoardGenerationService | null = null;
|
||||||
|
|
||||||
@@ -238,7 +232,6 @@ export class DIContainer {
|
|||||||
return this._deckImportExportService;
|
return this._deckImportExportService;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
public get cardDrawingService(): CardDrawingService {
|
public get cardDrawingService(): CardDrawingService {
|
||||||
if (!this._cardDrawingService) {
|
if (!this._cardDrawingService) {
|
||||||
this._cardDrawingService = new CardDrawingService();
|
this._cardDrawingService = new CardDrawingService();
|
||||||
@@ -263,8 +256,6 @@ export class DIContainer {
|
|||||||
return this._fieldEffectService;
|
return this._fieldEffectService;
|
||||||
}
|
}
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
public get gameService(): GameService {
|
public get gameService(): GameService {
|
||||||
if (!this._gameService) {
|
if (!this._gameService) {
|
||||||
this._gameService = new GameService();
|
this._gameService = new GameService();
|
||||||
|
|||||||
@@ -262,14 +262,6 @@ export class WebSocketService {
|
|||||||
socket.on('chat:archive:delete', (data: DeleteChatArchiveData) => this.handleDeleteChatArchive(socket, data));
|
socket.on('chat:archive:delete', (data: DeleteChatArchiveData) => this.handleDeleteChatArchive(socket, data));
|
||||||
socket.on('message:delete', (data: DeleteMessageData) => this.handleDeleteMessage(socket, data));
|
socket.on('message:delete', (data: DeleteMessageData) => this.handleDeleteMessage(socket, data));
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
// Game event handlers (prepared for future implementation)
|
|
||||||
socket.on('game:join', (data: JoinGameRoomData) => this.handleJoinGameRoom(socket, data));
|
|
||||||
socket.on('game:leave', (data: LeaveGameRoomData) => this.handleLeaveGameRoom(socket, data));
|
|
||||||
socket.on('game:action', (data: GameActionData) => this.handleGameAction(socket, data));
|
|
||||||
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
socket.on('disconnect', () => this.handleDisconnection(socket));
|
socket.on('disconnect', () => this.handleDisconnection(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export class LogoutCommandHandler {
|
|||||||
try {
|
try {
|
||||||
logAuth('Logout process started', userId);
|
logAuth('Logout process started', userId);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// 1. Get tokens from request to blacklist them
|
// 1. Get tokens from request to blacklist them
|
||||||
let accessTokenToBlacklist: string | null = null;
|
let accessTokenToBlacklist: string | null = null;
|
||||||
let refreshTokenToBlacklist: string | null = null;
|
let refreshTokenToBlacklist: string | null = null;
|
||||||
@@ -42,32 +41,10 @@ export class LogoutCommandHandler {
|
|||||||
// 2. Blacklist both access and refresh tokens in Redis
|
// 2. Blacklist both access and refresh tokens in Redis
|
||||||
if (accessTokenToBlacklist && req) {
|
if (accessTokenToBlacklist && req) {
|
||||||
try {
|
try {
|
||||||
=======
|
|
||||||
// 1. Get token from request to blacklist it
|
|
||||||
let tokenToBlacklist: string | null = null;
|
|
||||||
if (req) {
|
|
||||||
// Extract token from cookie
|
|
||||||
tokenToBlacklist = req.cookies['auth_token'];
|
|
||||||
|
|
||||||
// Also check Authorization header as fallback
|
|
||||||
if (!tokenToBlacklist && req.headers.authorization) {
|
|
||||||
const authHeader = req.headers.authorization;
|
|
||||||
if (authHeader.startsWith('Bearer ')) {
|
|
||||||
tokenToBlacklist = authHeader.substring(7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Blacklist the current JWT token in Redis (if available)
|
|
||||||
if (tokenToBlacklist && req) {
|
|
||||||
try {
|
|
||||||
// Store token in blacklist with expiration matching token expiry
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
const decoded = this.jwtService.verify(req);
|
const decoded = this.jwtService.verify(req);
|
||||||
if (decoded && decoded.exp) {
|
if (decoded && decoded.exp) {
|
||||||
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
|
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
|
||||||
if (ttl > 0) {
|
if (ttl > 0) {
|
||||||
<<<<<<< HEAD
|
|
||||||
await this.redisService.setWithExpiry(`blacklist:${accessTokenToBlacklist}`, 'true', ttl);
|
await this.redisService.setWithExpiry(`blacklist:${accessTokenToBlacklist}`, 'true', ttl);
|
||||||
logAuth('Access token blacklisted', userId, { tokenExpiry: ttl });
|
logAuth('Access token blacklisted', userId, { tokenExpiry: ttl });
|
||||||
}
|
}
|
||||||
@@ -97,24 +74,6 @@ export class LogoutCommandHandler {
|
|||||||
if (req) {
|
if (req) {
|
||||||
this.jwtService.logout(req, res);
|
this.jwtService.logout(req, res);
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
await this.redisService.setWithExpiry(`blacklist:${tokenToBlacklist}`, 'true', ttl);
|
|
||||||
logAuth('JWT token blacklisted', userId, { tokenExpiry: ttl });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logWarning('Failed to blacklist token', { userId, error: (error as Error).message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Clear authentication cookie
|
|
||||||
res.clearCookie('auth_token', {
|
|
||||||
httpOnly: true,
|
|
||||||
secure: process.env.NODE_ENV === 'production',
|
|
||||||
sameSite: 'strict',
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
|
|
||||||
// 4. Remove user from active sessions in Redis
|
// 4. Remove user from active sessions in Redis
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -55,4 +55,4 @@ export class ResetPasswordCommandHandler {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,13 +31,7 @@ export enum ConsequenceType {
|
|||||||
MOVE_BACKWARD = 1,
|
MOVE_BACKWARD = 1,
|
||||||
LOSE_TURN = 2,
|
LOSE_TURN = 2,
|
||||||
EXTRA_TURN = 3,
|
EXTRA_TURN = 3,
|
||||||
<<<<<<< HEAD
|
|
||||||
GO_TO_START = 5
|
GO_TO_START = 5
|
||||||
=======
|
|
||||||
SWAP_POSITION = 4,
|
|
||||||
GO_TO_START = 5,
|
|
||||||
TURN_AGAIN = 6
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Consequence {
|
export interface Consequence {
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||||
<<<<<<< HEAD
|
|
||||||
import { Consequence, CardType } from '../Deck/DeckAggregate';
|
import { Consequence, CardType } from '../Deck/DeckAggregate';
|
||||||
=======
|
|
||||||
import { Consequence } from '../Deck/DeckAggregate';
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
|
|
||||||
export enum GameState {
|
export enum GameState {
|
||||||
WAITING = 0,
|
WAITING = 0,
|
||||||
@@ -27,12 +23,8 @@ export enum DeckType {
|
|||||||
export interface GameCard {
|
export interface GameCard {
|
||||||
cardid: string;
|
cardid: string;
|
||||||
question?: string;
|
question?: string;
|
||||||
<<<<<<< HEAD
|
|
||||||
answer?: any; // Support complex answer structures (string, object, array)
|
answer?: any; // Support complex answer structures (string, object, array)
|
||||||
type?: CardType; // Card type for validation logic
|
type?: CardType; // Card type for validation logic
|
||||||
=======
|
|
||||||
answer?: string;
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
consequence?: Consequence | null;
|
consequence?: Consequence | null;
|
||||||
played?: boolean;
|
played?: boolean;
|
||||||
playerid?: string;
|
playerid?: string;
|
||||||
@@ -58,7 +50,6 @@ export class GameAggregate {
|
|||||||
@Column({ type: 'int', default: LoginType.PUBLIC })
|
@Column({ type: 'int', default: LoginType.PUBLIC })
|
||||||
logintype!: LoginType;
|
logintype!: LoginType;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@Column({ type: 'int', default: 50 })
|
@Column({ type: 'int', default: 50 })
|
||||||
boardsize!: number;
|
boardsize!: number;
|
||||||
|
|
||||||
@@ -72,18 +63,6 @@ export class GameAggregate {
|
|||||||
gamedecks!: GameDeck[];
|
gamedecks!: GameDeck[];
|
||||||
|
|
||||||
@Column({ type: 'uuid', array: true, default: () => "'{}'", name: 'playerids' })
|
@Column({ type: 'uuid', array: true, default: () => "'{}'", name: 'playerids' })
|
||||||
=======
|
|
||||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
||||||
createdby!: string | null;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
||||||
orgid!: string | null;
|
|
||||||
|
|
||||||
@Column({ type: 'json' })
|
|
||||||
gamedecks!: GameDeck[];
|
|
||||||
|
|
||||||
@Column({ type: 'json', default: () => "'[]'" })
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
players!: string[];
|
players!: string[];
|
||||||
|
|
||||||
@Column({ type: 'boolean', default: false })
|
@Column({ type: 'boolean', default: false })
|
||||||
@@ -92,37 +71,22 @@ export class GameAggregate {
|
|||||||
@Column({ type: 'boolean', default: false })
|
@Column({ type: 'boolean', default: false })
|
||||||
finished!: boolean;
|
finished!: boolean;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@Column({ type: 'uuid', nullable: true, name: 'winnerid' })
|
@Column({ type: 'uuid', nullable: true, name: 'winnerid' })
|
||||||
=======
|
|
||||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
winner!: string | null;
|
winner!: string | null;
|
||||||
|
|
||||||
@Column({ type: 'int', default: GameState.WAITING })
|
@Column({ type: 'int', default: GameState.WAITING })
|
||||||
state!: GameState;
|
state!: GameState;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@CreateDateColumn({ name: 'createDate' })
|
@CreateDateColumn({ name: 'createDate' })
|
||||||
=======
|
|
||||||
@CreateDateColumn({ name: 'create_date' })
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
createdate!: Date;
|
createdate!: Date;
|
||||||
|
|
||||||
@Column({ type: 'timestamp', nullable: true, name: 'start_date' })
|
@Column({ type: 'timestamp', nullable: true, name: 'start_date' })
|
||||||
startdate!: Date | null;
|
startdate!: Date | null;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@Column({ type: 'timestamp', nullable: true, name: 'finishDate' })
|
@Column({ type: 'timestamp', nullable: true, name: 'finishDate' })
|
||||||
enddate!: Date | null;
|
enddate!: Date | null;
|
||||||
|
|
||||||
@UpdateDateColumn({ name: 'updateDate' })
|
@UpdateDateColumn({ name: 'updateDate' })
|
||||||
=======
|
|
||||||
@Column({ type: 'timestamp', nullable: true, name: 'end_date' })
|
|
||||||
enddate!: Date | null;
|
|
||||||
|
|
||||||
@UpdateDateColumn({ name: 'update_date' })
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
updatedate!: Date;
|
updatedate!: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,12 +100,6 @@ export interface GameField {
|
|||||||
export interface BoardData {
|
export interface BoardData {
|
||||||
gameId?: string;
|
gameId?: string;
|
||||||
fields: GameField[];
|
fields: GameField[];
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
border: number[];
|
|
||||||
validationResults: { [fieldIndex: number]: number[] };
|
|
||||||
totalErrorRate: number;
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
generationComplete?: boolean;
|
generationComplete?: boolean;
|
||||||
generatedAt?: Date;
|
generatedAt?: Date;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user