feat: strengthen risk controls and review workflows
This commit is contained in:
+80
-8
@@ -988,6 +988,36 @@ export type RiskReviewTask = {
|
||||
_count?: { messageRecords: number };
|
||||
};
|
||||
|
||||
export type RiskRuleItem = {
|
||||
id: string;
|
||||
tenantId?: string | null;
|
||||
applicationId?: string | null;
|
||||
code: 'MAX_PHONES_PER_TASK' | 'NON_WORKING_MARKETING_BULK' | 'TASK_CREATE_FREQUENCY';
|
||||
name: string;
|
||||
description?: string | null;
|
||||
metric: string;
|
||||
thresholdValue: number;
|
||||
action: 'block' | 'manual_review';
|
||||
status: 'active' | 'inactive';
|
||||
priority: number;
|
||||
config?: { startTime?: string; endTime?: string; timeZone?: string } | null;
|
||||
application?: { id: string; name: string; tenantId: string; tenant?: { id: string; name: string } } | null;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type RiskTaskMessagePage = {
|
||||
items: Array<{
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
province?: string | null;
|
||||
carrier?: string | null;
|
||||
status: string;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
export type TenantAccount = {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
@@ -1144,10 +1174,13 @@ export type EnterpriseApplication = {
|
||||
name: string;
|
||||
scene?: string | null;
|
||||
status: string;
|
||||
disablingAt?: string | null;
|
||||
autoDisableAt?: string | null;
|
||||
disableReason?: string | null;
|
||||
deactivation?: ApplicationDeactivationPreview | null;
|
||||
dailyLimit?: number | null;
|
||||
customerUnitPrice?: number | null;
|
||||
queuePriority?: 'normal' | 'priority' | string | null;
|
||||
maxPhonesPerTask?: number | null;
|
||||
templateMismatchMode?: string | null;
|
||||
downstreamReceiptRetryEnabled?: boolean | null;
|
||||
downstreamUplinkRetryEnabled?: boolean | null;
|
||||
@@ -1284,6 +1317,20 @@ export type DownstreamDeliveryRecord = {
|
||||
}>;
|
||||
};
|
||||
|
||||
export type ApplicationDeactivationPreview = {
|
||||
status: string;
|
||||
reason?: string | null;
|
||||
disablingAt?: string | null;
|
||||
autoDisableAt?: string | null;
|
||||
awaitingSupplierReceipt: number;
|
||||
waitingToSend: number;
|
||||
awaitingClientAck: number;
|
||||
retryableFailures: number;
|
||||
pendingUplinks: number;
|
||||
activeConnections: number;
|
||||
totalOutstanding: number;
|
||||
};
|
||||
|
||||
export type BatchRequeueResponse = {
|
||||
total: number;
|
||||
successCount: number;
|
||||
@@ -1463,7 +1510,8 @@ export const adminApi = {
|
||||
changeTenantStatus: (id: string, status: string) =>
|
||||
request<TenantOption>(`/admin/tenants/${id}/status`, { method: 'POST', body: JSON.stringify({ status }) }),
|
||||
deleteTenant: (id: string) => request<TenantOption>(`/admin/tenants/${id}`, { method: 'DELETE' }),
|
||||
listUsers: (query: { tenantId?: string; roleCode?: string } = {}) => request<ManagedUser[]>(withQuery('/admin/users', query)),
|
||||
listUsers: (query: { tenantId?: string; roleCode?: string; displayName?: string; login?: string; status?: string } = {}) =>
|
||||
request<ManagedUser[]>(withQuery('/admin/users', query)),
|
||||
createUser: (body: UserPayload) => request<ManagedUser>('/admin/users', { method: 'POST', body: JSON.stringify(body) }),
|
||||
updateUser: (id: string, body: Omit<UserPayload, 'password'>) => request<ManagedUser>(`/admin/users/${id}`, { method: 'PUT', body: JSON.stringify(body) }),
|
||||
changeUserStatus: (id: string, status: string, operatorId?: string) =>
|
||||
@@ -1491,14 +1539,16 @@ export const adminApi = {
|
||||
request<EnterpriseApplication[]>(withQuery('/admin/enterprise-applications', query)),
|
||||
getEnterpriseApplication: (id: string) =>
|
||||
request<EnterpriseApplication>(`/admin/enterprise-applications/${id}`),
|
||||
createEnterpriseApplication: (body: { tenantId: string; name: string; scene?: string; dailyLimit?: number; customerUnitPrice?: number; queuePriority?: 'normal' | 'priority'; maxPhonesPerTask?: number; templateMismatchMode?: string; downstreamReceiptRetryEnabled?: boolean; downstreamUplinkRetryEnabled?: boolean; cmppAccount?: string; cmppEnterpriseCode?: string; cmppApplicationExtension?: string; cmppAccessNumberFillEnabled?: boolean; cmppAccessNumberFillPrefix?: string; passwordCipher?: string; interfaceEnabled?: boolean; interfaceType?: 'cmpp20'; cmppMaxConnections?: number; cmppWindowSize?: number; ipAllowlist?: string[] }) =>
|
||||
createEnterpriseApplication: (body: { tenantId: string; name: string; scene?: string; dailyLimit?: number; customerUnitPrice?: number; queuePriority?: 'normal' | 'priority'; templateMismatchMode?: string; downstreamReceiptRetryEnabled?: boolean; downstreamUplinkRetryEnabled?: boolean; cmppAccount?: string; cmppEnterpriseCode?: string; cmppApplicationExtension?: string; cmppAccessNumberFillEnabled?: boolean; cmppAccessNumberFillPrefix?: string; passwordCipher?: string; interfaceEnabled?: boolean; interfaceType?: 'cmpp20'; cmppMaxConnections?: number; cmppWindowSize?: number; ipAllowlist?: string[] }) =>
|
||||
request<EnterpriseApplication>('/admin/enterprise-applications', { method: 'POST', body: JSON.stringify(body) }),
|
||||
updateEnterpriseApplication: (id: string, body: { name?: string; scene?: string; dailyLimit?: number; customerUnitPrice?: number; queuePriority?: 'normal' | 'priority'; maxPhonesPerTask?: number; templateMismatchMode?: string; downstreamReceiptRetryEnabled?: boolean; downstreamUplinkRetryEnabled?: boolean; cmppAccount?: string; cmppEnterpriseCode?: string; cmppApplicationExtension?: string; cmppAccessNumberFillEnabled?: boolean; cmppAccessNumberFillPrefix?: string; passwordCipher?: string; interfaceEnabled?: boolean; interfaceType?: 'cmpp20'; cmppMaxConnections?: number; cmppWindowSize?: number; ipAllowlist?: string[] }) =>
|
||||
updateEnterpriseApplication: (id: string, body: { name?: string; scene?: string; dailyLimit?: number; customerUnitPrice?: number; queuePriority?: 'normal' | 'priority'; templateMismatchMode?: string; downstreamReceiptRetryEnabled?: boolean; downstreamUplinkRetryEnabled?: boolean; cmppAccount?: string; cmppEnterpriseCode?: string; cmppApplicationExtension?: string; cmppAccessNumberFillEnabled?: boolean; cmppAccessNumberFillPrefix?: string; passwordCipher?: string; interfaceEnabled?: boolean; interfaceType?: 'cmpp20'; cmppMaxConnections?: number; cmppWindowSize?: number; ipAllowlist?: string[] }) =>
|
||||
request<EnterpriseApplication>(`/admin/enterprise-applications/${id}`, { method: 'PUT', body: JSON.stringify(body) }),
|
||||
changeApplicationStatus: (id: string, status: string, reason?: string) =>
|
||||
getApplicationDeactivationPreview: (id: string) =>
|
||||
request<ApplicationDeactivationPreview>(`/admin/enterprise-applications/${id}/deactivation-preview`),
|
||||
changeApplicationStatus: (id: string, status: string, reason?: string, force = false) =>
|
||||
request<EnterpriseApplication>(`/admin/enterprise-applications/${id}/status`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ status, reason }),
|
||||
body: JSON.stringify({ status, reason, force }),
|
||||
}),
|
||||
listApplicationConnections: (applicationId: string) =>
|
||||
request<ApplicationConnectionsResponse>(`/admin/enterprise-applications/${applicationId}/connections`),
|
||||
@@ -1706,6 +1756,26 @@ export const adminApi = {
|
||||
batchRequeueDownstreamDeliveries: (ids: string[]) =>
|
||||
request<BatchRequeueResponse>('/admin/operations/downstream-deliveries/requeue', { method: 'POST', body: JSON.stringify({ ids }) }),
|
||||
listRiskReviewTasks: (query: { tenantId?: string; status?: string } = {}) => request<RiskReviewTask[]>(withQuery('/admin/risk-review/tasks', query)),
|
||||
listRiskRules: (applicationId?: string) =>
|
||||
request<RiskRuleItem[]>(withQuery('/admin/risk-review/rules', { applicationId })),
|
||||
createRiskRule: (body: {
|
||||
applicationId?: string;
|
||||
code: RiskRuleItem['code'];
|
||||
thresholdValue: number;
|
||||
action: RiskRuleItem['action'];
|
||||
status: RiskRuleItem['status'];
|
||||
priority?: number;
|
||||
config?: RiskRuleItem['config'];
|
||||
}) => request<RiskRuleItem>('/admin/risk-review/rules', { method: 'POST', body: JSON.stringify(body) }),
|
||||
updateRiskRule: (id: string, body: {
|
||||
thresholdValue?: number;
|
||||
action?: RiskRuleItem['action'];
|
||||
status?: RiskRuleItem['status'];
|
||||
priority?: number;
|
||||
config?: RiskRuleItem['config'];
|
||||
}) => request<RiskRuleItem>(`/admin/risk-review/rules/${id}`, { method: 'PUT', body: JSON.stringify(body) }),
|
||||
listRiskReviewTaskMessages: (id: string, query: { phone?: string; page?: number; pageSize?: number } = {}) =>
|
||||
request<RiskTaskMessagePage>(withQuery(`/admin/risk-review/tasks/${id}/messages`, query)),
|
||||
approveRiskReviewTask: (id: string, reason?: string) =>
|
||||
request<RiskReviewTask>(`/admin/risk-review/tasks/${id}/approve`, { method: 'POST', body: JSON.stringify({ reason }) }),
|
||||
rejectRiskReviewTask: (id: string, reason?: string) =>
|
||||
@@ -1778,8 +1848,10 @@ export const clientApi = {
|
||||
getCaptcha: () => request<CaptchaResponse>('/client/auth/captcha'),
|
||||
login: (body: { login: string; password: string; captchaId: string; captchaText: string }) =>
|
||||
request<LoginSession>('/client/auth/login', { method: 'POST', body: JSON.stringify(body) }),
|
||||
listUsers: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser[]>('/client/users', { tenantId }),
|
||||
listUsers: (
|
||||
query: { displayName?: string; login?: string; status?: string } = {},
|
||||
tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID,
|
||||
) => request<ManagedUser[]>(withQuery('/client/users', query), { tenantId }),
|
||||
createUser: (body: UserPayload, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>('/client/users', { method: 'POST', tenantId, body: JSON.stringify(body) }),
|
||||
updateUser: (id: string, body: Omit<UserPayload, 'password'>, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
|
||||
Reference in New Issue
Block a user