1
This commit is contained in:
@@ -6,11 +6,71 @@ import type { AppDatabase } from '../db.js';
|
||||
|
||||
export type SmsAuthScene = 'login' | 'bind_phone' | 'change_phone';
|
||||
export type SmsAuthAction = 'send_code' | 'verify_code';
|
||||
export type SmsDeliveryStatus = 'pending' | 'delivered' | 'failed' | 'unknown';
|
||||
|
||||
export type SmsAuthEventRecord = {
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
scene: SmsAuthScene;
|
||||
action: SmsAuthAction;
|
||||
success: boolean;
|
||||
ip: string | null;
|
||||
userAgent: string | null;
|
||||
provider: string | null;
|
||||
providerRequestId: string | null;
|
||||
providerBizId: string | null;
|
||||
providerOutId: string | null;
|
||||
deliveryStatus: SmsDeliveryStatus;
|
||||
deliveryReportRawJson: Record<string, unknown> | null;
|
||||
deliveryReportedAt: string | null;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type SmsAuthEventRow = QueryResultRow & {
|
||||
id: string;
|
||||
phone_number: string;
|
||||
scene: SmsAuthScene;
|
||||
action: SmsAuthAction;
|
||||
success: boolean;
|
||||
ip: string | null;
|
||||
user_agent: string | null;
|
||||
provider: string | null;
|
||||
provider_request_id: string | null;
|
||||
provider_biz_id: string | null;
|
||||
provider_out_id: string | null;
|
||||
delivery_status: SmsDeliveryStatus;
|
||||
delivery_report_raw_json: Record<string, unknown> | null;
|
||||
delivery_reported_at: string | null;
|
||||
created_at: string;
|
||||
total: number;
|
||||
};
|
||||
|
||||
function toSmsAuthEventRecord(
|
||||
row: SmsAuthEventRow | undefined,
|
||||
): SmsAuthEventRecord | null {
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
phoneNumber: row.phone_number,
|
||||
scene: row.scene,
|
||||
action: row.action,
|
||||
success: row.success,
|
||||
ip: row.ip,
|
||||
userAgent: row.user_agent,
|
||||
provider: row.provider,
|
||||
providerRequestId: row.provider_request_id,
|
||||
providerBizId: row.provider_biz_id,
|
||||
providerOutId: row.provider_out_id,
|
||||
deliveryStatus: row.delivery_status,
|
||||
deliveryReportRawJson: row.delivery_report_raw_json,
|
||||
deliveryReportedAt: row.delivery_reported_at,
|
||||
createdAt: row.created_at,
|
||||
};
|
||||
}
|
||||
|
||||
export class SmsAuthEventRepository {
|
||||
constructor(private readonly db: AppDatabase) {}
|
||||
|
||||
@@ -21,9 +81,17 @@ export class SmsAuthEventRepository {
|
||||
success: boolean;
|
||||
ip: string | null;
|
||||
userAgent: string | null;
|
||||
provider?: string | null;
|
||||
providerRequestId?: string | null;
|
||||
providerBizId?: string | null;
|
||||
providerOutId?: string | null;
|
||||
deliveryStatus?: SmsDeliveryStatus;
|
||||
deliveryReportRawJson?: Record<string, unknown> | null;
|
||||
deliveryReportedAt?: string | null;
|
||||
}) {
|
||||
const id = `smsev_${crypto.randomBytes(16).toString('hex')}`;
|
||||
await this.db.query(
|
||||
const createdAt = new Date().toISOString();
|
||||
const result = await this.db.query<SmsAuthEventRow>(
|
||||
`INSERT INTO sms_auth_events (
|
||||
id,
|
||||
phone_number,
|
||||
@@ -32,9 +100,34 @@ export class SmsAuthEventRepository {
|
||||
success,
|
||||
ip,
|
||||
user_agent,
|
||||
provider,
|
||||
provider_request_id,
|
||||
provider_biz_id,
|
||||
provider_out_id,
|
||||
delivery_status,
|
||||
delivery_report_raw_json,
|
||||
delivery_reported_at,
|
||||
created_at
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
phone_number,
|
||||
scene,
|
||||
action,
|
||||
success,
|
||||
ip,
|
||||
user_agent,
|
||||
provider,
|
||||
provider_request_id,
|
||||
provider_biz_id,
|
||||
provider_out_id,
|
||||
delivery_status,
|
||||
delivery_report_raw_json,
|
||||
delivery_reported_at,
|
||||
created_at`,
|
||||
[
|
||||
id,
|
||||
input.phoneNumber,
|
||||
@@ -43,9 +136,18 @@ export class SmsAuthEventRepository {
|
||||
input.success,
|
||||
input.ip,
|
||||
input.userAgent,
|
||||
new Date().toISOString(),
|
||||
input.provider ?? null,
|
||||
input.providerRequestId ?? null,
|
||||
input.providerBizId ?? null,
|
||||
input.providerOutId ?? null,
|
||||
input.deliveryStatus ?? 'pending',
|
||||
input.deliveryReportRawJson ?? null,
|
||||
input.deliveryReportedAt ?? null,
|
||||
createdAt,
|
||||
],
|
||||
);
|
||||
|
||||
return toSmsAuthEventRecord(result.rows[0]);
|
||||
}
|
||||
|
||||
async countSinceByPhone(params: {
|
||||
@@ -99,4 +201,102 @@ export class SmsAuthEventRepository {
|
||||
|
||||
return result.rows[0]?.total ?? 0;
|
||||
}
|
||||
|
||||
async findLatestByProviderBizId(providerBizId: string) {
|
||||
const result = await this.db.query<SmsAuthEventRow>(
|
||||
`SELECT
|
||||
id,
|
||||
phone_number,
|
||||
scene,
|
||||
action,
|
||||
success,
|
||||
ip,
|
||||
user_agent,
|
||||
provider,
|
||||
provider_request_id,
|
||||
provider_biz_id,
|
||||
provider_out_id,
|
||||
delivery_status,
|
||||
delivery_report_raw_json,
|
||||
delivery_reported_at,
|
||||
created_at,
|
||||
0::int AS total
|
||||
FROM sms_auth_events
|
||||
WHERE provider_biz_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1`,
|
||||
[providerBizId],
|
||||
);
|
||||
|
||||
return toSmsAuthEventRecord(result.rows[0]);
|
||||
}
|
||||
|
||||
async findLatestByProviderOutId(providerOutId: string) {
|
||||
const result = await this.db.query<SmsAuthEventRow>(
|
||||
`SELECT
|
||||
id,
|
||||
phone_number,
|
||||
scene,
|
||||
action,
|
||||
success,
|
||||
ip,
|
||||
user_agent,
|
||||
provider,
|
||||
provider_request_id,
|
||||
provider_biz_id,
|
||||
provider_out_id,
|
||||
delivery_status,
|
||||
delivery_report_raw_json,
|
||||
delivery_reported_at,
|
||||
created_at,
|
||||
0::int AS total
|
||||
FROM sms_auth_events
|
||||
WHERE provider_out_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1`,
|
||||
[providerOutId],
|
||||
);
|
||||
|
||||
return toSmsAuthEventRecord(result.rows[0]);
|
||||
}
|
||||
|
||||
async updateDeliveryStatus(params: {
|
||||
id: string;
|
||||
deliveryStatus: SmsDeliveryStatus;
|
||||
deliveryReportRawJson: Record<string, unknown> | null;
|
||||
deliveryReportedAt: string;
|
||||
}) {
|
||||
const result = await this.db.query<SmsAuthEventRow>(
|
||||
`UPDATE sms_auth_events
|
||||
SET delivery_status = $1,
|
||||
delivery_report_raw_json = $2,
|
||||
delivery_reported_at = $3
|
||||
WHERE id = $4
|
||||
RETURNING
|
||||
id,
|
||||
phone_number,
|
||||
scene,
|
||||
action,
|
||||
success,
|
||||
ip,
|
||||
user_agent,
|
||||
provider,
|
||||
provider_request_id,
|
||||
provider_biz_id,
|
||||
provider_out_id,
|
||||
delivery_status,
|
||||
delivery_report_raw_json,
|
||||
delivery_reported_at,
|
||||
created_at,
|
||||
0::int AS total`,
|
||||
[
|
||||
params.deliveryStatus,
|
||||
params.deliveryReportRawJson,
|
||||
params.deliveryReportedAt,
|
||||
params.id,
|
||||
],
|
||||
);
|
||||
|
||||
return toSmsAuthEventRecord(result.rows[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user