1
This commit is contained in:
102
server-node/src/repositories/smsAuthEventRepository.ts
Normal file
102
server-node/src/repositories/smsAuthEventRepository.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user