@kanadi/core
Version:
Multi-Layer CAPTCHA Framework with customizable validators and challenge bundles
171 lines (156 loc) • 4.25 kB
text/typescript
import { sql } from "bun";
export interface KanadiValidatorSession {
id: string;
bundle_session_id?: string;
validator_id: string;
session_id?: string;
solve_id?: string;
result?: string;
score?: number;
confidence?: number;
processing_time?: number;
data: any;
evidence?: any;
decrypted_data?: any;
created_at: Date;
updated_at: Date;
}
export class ValidatorSessionRepository {
async create(data: {
bundleSessionId?: string;
validatorId: string;
sessionId?: string;
solveId?: string;
result?: string;
score?: number;
confidence?: number;
processingTime?: number;
evidence?: any;
decryptedData?: any;
}): Promise<KanadiValidatorSession> {
const result = await sql`
INSERT INTO kanadi_validator_sessions (
bundle_session_id,
validator_id,
session_id,
solve_id,
result,
score,
confidence,
processing_time,
evidence,
decrypted_data
)
VALUES (
${data.bundleSessionId || null},
${data.validatorId},
${data.sessionId || null},
${data.solveId || null},
${data.result || null},
${data.score || null},
${data.confidence || null},
${data.processingTime || null},
${data.evidence ? JSON.stringify(data.evidence) : null},
${data.decryptedData ? JSON.stringify(data.decryptedData) : null}
)
RETURNING *
`;
return result[0] as KanadiValidatorSession;
}
async findBySolveId(solveId: string): Promise<KanadiValidatorSession[]> {
const result = await sql`
SELECT * FROM kanadi_validator_sessions
WHERE solve_id = ${solveId}
ORDER BY created_at DESC
`;
return result as KanadiValidatorSession[];
}
async findBySessionId(sessionId: string): Promise<KanadiValidatorSession[]> {
const result = await sql`
SELECT * FROM kanadi_validator_sessions
WHERE session_id = ${sessionId}
ORDER BY created_at DESC
`;
return result as KanadiValidatorSession[];
}
async findByValidatorId(
validatorId: string,
limit: number = 100,
): Promise<KanadiValidatorSession[]> {
const result = await sql`
SELECT * FROM kanadi_validator_sessions
WHERE validator_id = ${validatorId}
ORDER BY created_at DESC
LIMIT ${limit}
`;
return result as KanadiValidatorSession[];
}
async getStats(): Promise<{
total: number;
byResult: { pass: number; fail: number; suspicious: number };
byValidator: Record<string, number>;
avgScore: number;
avgConfidence: number;
}> {
const totalResult = await sql`
SELECT COUNT(*) as count FROM kanadi_validator_sessions
`;
const byResultResult = await sql`
SELECT
result,
COUNT(*) as count
FROM kanadi_validator_sessions
WHERE result IS NOT NULL
GROUP BY result
`;
const byValidatorResult = await sql`
SELECT
validator_id,
COUNT(*) as count
FROM kanadi_validator_sessions
GROUP BY validator_id
`;
const avgResult = await sql`
SELECT
AVG(score) as avg_score,
AVG(confidence) as avg_confidence
FROM kanadi_validator_sessions
WHERE score IS NOT NULL AND confidence IS NOT NULL
`;
const byResult = {
pass: 0,
fail: 0,
suspicious: 0,
};
for (const row of byResultResult) {
const result = (row as any).result?.toLowerCase();
const count = Number((row as any).count || 0);
if (result === "pass") byResult.pass = count;
else if (result === "fail") byResult.fail = count;
else if (result === "suspicious") byResult.suspicious = count;
}
const byValidator: Record<string, number> = {};
for (const row of byValidatorResult) {
const validatorId = (row as any).validator_id;
const count = Number((row as any).count || 0);
byValidator[validatorId] = count;
}
return {
total: Number(totalResult[0]?.count || 0),
byResult,
byValidator,
avgScore: Number(avgResult[0]?.avg_score || 0),
avgConfidence: Number(avgResult[0]?.avg_confidence || 0),
};
}
async findSuspiciousActivity(days: number = 7): Promise<KanadiValidatorSession[]> {
const result = await sql`
SELECT * FROM kanadi_validator_sessions
WHERE result IN ('FAIL', 'SUSPICIOUS')
AND created_at >= CURRENT_TIMESTAMP - INTERVAL '${sql.raw(days.toString())} days'
ORDER BY created_at DESC
LIMIT 1000
`;
return result as KanadiValidatorSession[];
}
}