This commit is contained in:
2026-04-10 15:37:02 +08:00
parent 161cd32277
commit f19e482c8f
233 changed files with 43987 additions and 5127 deletions

View File

@@ -0,0 +1,102 @@
import crypto from 'node:crypto';
import type { QueryResultRow } from 'pg';
import type { AppDatabase } from '../db.js';
export type SmsAuthScene = 'login' | 'bind_phone' | 'change_phone';
export type SmsAuthAction = 'send_code' | 'verify_code';
type SmsAuthEventRow = QueryResultRow & {
total: number;
};
export class SmsAuthEventRepository {
constructor(private readonly db: AppDatabase) {}
async create(input: {
phoneNumber: string;
scene: SmsAuthScene;
action: SmsAuthAction;
success: boolean;
ip: string | null;
userAgent: string | null;
}) {
const id = `smsev_${crypto.randomBytes(16).toString('hex')}`;
await this.db.query(
`INSERT INTO sms_auth_events (
id,
phone_number,
scene,
action,
success,
ip,
user_agent,
created_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
[
id,
input.phoneNumber,
input.scene,
input.action,
input.success,
input.ip,
input.userAgent,
new Date().toISOString(),
],
);
}
async countSinceByPhone(params: {
phoneNumber: string;
action: SmsAuthAction;
success?: boolean;
since: string;
}) {
const result = await this.db.query<SmsAuthEventRow>(
`SELECT COUNT(*)::int AS total
FROM sms_auth_events
WHERE phone_number = $1
AND action = $2
AND ($3::boolean IS NULL OR success = $3)
AND created_at >= $4`,
[
params.phoneNumber,
params.action,
params.success ?? null,
params.since,
],
);
return result.rows[0]?.total ?? 0;
}
async countSinceByIp(params: {
ip: string | null;
action: SmsAuthAction;
success?: boolean;
since: string;
}) {
if (!params.ip) {
return 0;
}
const result = await this.db.query<SmsAuthEventRow>(
`SELECT COUNT(*)::int AS total
FROM sms_auth_events
WHERE ip = $1
AND action = $2
AND ($3::boolean IS NULL OR success = $3)
AND created_at >= $4`,
[
params.ip,
params.action,
params.success ?? null,
params.since,
],
);
return result.rows[0]?.total ?? 0;
}
}