feat: 定义消息推送前端契约
This commit is contained in:
parent
25a5bc3499
commit
edb77bd67c
@ -391,4 +391,57 @@ describe('core menu api', () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps every supported message-push menu name once while filtering unknown nodes', async () => {
|
||||||
|
const messagePushNames = [
|
||||||
|
'QqBotMessageSubscription',
|
||||||
|
'QqBotMessageTemplate',
|
||||||
|
'QqBotMessageSubscriptionList',
|
||||||
|
'QqBotMessageSubscriptionCreate',
|
||||||
|
'QqBotMessageSubscriptionUpdate',
|
||||||
|
'QqBotMessageSubscriptionDelete',
|
||||||
|
'QqBotMessageSubscriptionToggle',
|
||||||
|
'QqBotMessageTemplateList',
|
||||||
|
'QqBotMessageTemplateCreate',
|
||||||
|
'QqBotMessageTemplateUpdate',
|
||||||
|
'QqBotMessageTemplateDelete',
|
||||||
|
'QqBotMessageTemplateToggle',
|
||||||
|
'QqBotMessageTemplatePreview',
|
||||||
|
'QqBotAccountMessagePushList',
|
||||||
|
'QqBotAccountMessagePushCreate',
|
||||||
|
'QqBotAccountMessagePushUpdate',
|
||||||
|
'QqBotAccountMessagePushDelete',
|
||||||
|
'QqBotAccountMessagePushToggle',
|
||||||
|
];
|
||||||
|
requestClientGet.mockResolvedValue([
|
||||||
|
{
|
||||||
|
name: 'QqBot',
|
||||||
|
path: '/qqbot',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'QqBotAccount',
|
||||||
|
path: '/qqbot/account',
|
||||||
|
},
|
||||||
|
...messagePushNames.map((name) => ({
|
||||||
|
authCode: `QqBot:${name}`,
|
||||||
|
name,
|
||||||
|
type: 'button',
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'UnsupportedMessagePushNode',
|
||||||
|
type: 'button',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { getAllMenusApi } = await import('./menu');
|
||||||
|
const menus = await getAllMenusApi();
|
||||||
|
const qqbot = menus.find((menu) => menu.name === 'QqBot');
|
||||||
|
const retainedNames = qqbot?.children?.map((child) => child.name) ?? [];
|
||||||
|
|
||||||
|
expect(retainedNames).toEqual(['QqBotAccount', ...messagePushNames]);
|
||||||
|
expect(new Set(retainedNames).size).toBe(retainedNames.length);
|
||||||
|
expect(retainedNames).not.toContain('UnsupportedMessagePushNode');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -33,6 +33,11 @@ const SUPPORTED_ADMIN_MENU_NAMES = new Set([
|
|||||||
'QqBotAccountDelete',
|
'QqBotAccountDelete',
|
||||||
'QqBotAccountEdit',
|
'QqBotAccountEdit',
|
||||||
'QqBotAccountKick',
|
'QqBotAccountKick',
|
||||||
|
'QqBotAccountMessagePushCreate',
|
||||||
|
'QqBotAccountMessagePushDelete',
|
||||||
|
'QqBotAccountMessagePushList',
|
||||||
|
'QqBotAccountMessagePushToggle',
|
||||||
|
'QqBotAccountMessagePushUpdate',
|
||||||
'QqBotAccountNapcatWebui',
|
'QqBotAccountNapcatWebui',
|
||||||
'QqBotAccountRefreshLogin',
|
'QqBotAccountRefreshLogin',
|
||||||
'QqBotAccountWebUI',
|
'QqBotAccountWebUI',
|
||||||
@ -45,6 +50,19 @@ const SUPPORTED_ADMIN_MENU_NAMES = new Set([
|
|||||||
'QqBotConversation',
|
'QqBotConversation',
|
||||||
'QqBotDashboard',
|
'QqBotDashboard',
|
||||||
'QqBotMessage',
|
'QqBotMessage',
|
||||||
|
'QqBotMessageSubscription',
|
||||||
|
'QqBotMessageSubscriptionCreate',
|
||||||
|
'QqBotMessageSubscriptionDelete',
|
||||||
|
'QqBotMessageSubscriptionList',
|
||||||
|
'QqBotMessageSubscriptionToggle',
|
||||||
|
'QqBotMessageSubscriptionUpdate',
|
||||||
|
'QqBotMessageTemplate',
|
||||||
|
'QqBotMessageTemplateCreate',
|
||||||
|
'QqBotMessageTemplateDelete',
|
||||||
|
'QqBotMessageTemplateList',
|
||||||
|
'QqBotMessageTemplatePreview',
|
||||||
|
'QqBotMessageTemplateToggle',
|
||||||
|
'QqBotMessageTemplateUpdate',
|
||||||
'QqBotPermission',
|
'QqBotPermission',
|
||||||
'QqBotPermissionCreate',
|
'QqBotPermissionCreate',
|
||||||
'QqBotPermissionDelete',
|
'QqBotPermissionDelete',
|
||||||
|
|||||||
224
apps/web-antdv-next/src/api/qqbot/message-push.spec.ts
Normal file
224
apps/web-antdv-next/src/api/qqbot/message-push.spec.ts
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
import * as messagePushApi from './message-push';
|
||||||
|
import {
|
||||||
|
createAccountMessagePushBinding,
|
||||||
|
createMessageSubscription,
|
||||||
|
createMessageTemplate,
|
||||||
|
deleteAccountMessagePushBinding,
|
||||||
|
deleteMessageSubscription,
|
||||||
|
deleteMessageTemplate,
|
||||||
|
getAccountMessagePushBindings,
|
||||||
|
getAccountMessagePushTargets,
|
||||||
|
getMessagePushSourceDetail,
|
||||||
|
getMessagePushSources,
|
||||||
|
getMessageSubscriptionList,
|
||||||
|
getMessageTemplateList,
|
||||||
|
getStunMappingPortChangedOptions,
|
||||||
|
previewMessageTemplate,
|
||||||
|
setAccountMessagePushBindingEnabled,
|
||||||
|
setMessageSubscriptionEnabled,
|
||||||
|
setMessageTemplateEnabled,
|
||||||
|
updateAccountMessagePushBinding,
|
||||||
|
updateMessageSubscription,
|
||||||
|
updateMessageTemplate,
|
||||||
|
} from './message-push';
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
delete: vi.fn(),
|
||||||
|
get: vi.fn(),
|
||||||
|
post: vi.fn(),
|
||||||
|
put: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('qqbot message push api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('owns the exact source, subscription, and template callers', async () => {
|
||||||
|
const sourceKey = 'network.stun/a b';
|
||||||
|
const id = '10000000000000001';
|
||||||
|
const previewContent = '端口 $' + '{{port}}';
|
||||||
|
const subscriptionInput = {
|
||||||
|
enabled: true,
|
||||||
|
name: 'STUN 端口变更',
|
||||||
|
sourceConfig: {
|
||||||
|
ddnsRecordId: '10000000000000003',
|
||||||
|
portForwardId: '10000000000000002',
|
||||||
|
},
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
};
|
||||||
|
const templateInput = {
|
||||||
|
content: previewContent,
|
||||||
|
enabled: true,
|
||||||
|
name: '端口通知',
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
};
|
||||||
|
const subscriptionQuery = {
|
||||||
|
enabled: true,
|
||||||
|
name: 'STUN',
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
};
|
||||||
|
const templateQuery = {
|
||||||
|
enabled: false,
|
||||||
|
name: '通知',
|
||||||
|
pageNo: 2,
|
||||||
|
pageSize: 20,
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
};
|
||||||
|
|
||||||
|
await getMessagePushSources();
|
||||||
|
await getMessagePushSourceDetail(sourceKey);
|
||||||
|
await getStunMappingPortChangedOptions();
|
||||||
|
await getMessageSubscriptionList(subscriptionQuery);
|
||||||
|
await createMessageSubscription(subscriptionInput);
|
||||||
|
await updateMessageSubscription('id/a b', subscriptionInput);
|
||||||
|
await setMessageSubscriptionEnabled(id, false);
|
||||||
|
await deleteMessageSubscription('id/a b');
|
||||||
|
await getMessageTemplateList(templateQuery);
|
||||||
|
await createMessageTemplate(templateInput);
|
||||||
|
await updateMessageTemplate('template/a b', templateInput);
|
||||||
|
await setMessageTemplateEnabled(id, false);
|
||||||
|
await deleteMessageTemplate('template/a b');
|
||||||
|
await previewMessageTemplate({
|
||||||
|
content: previewContent,
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'/qqbot/message-push/sources',
|
||||||
|
);
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'/qqbot/message-push/sources/network.stun%2Fa%20b',
|
||||||
|
);
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
3,
|
||||||
|
'/qqbot/message-push/sources/network.stun.mapping-port-changed/options',
|
||||||
|
);
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
4,
|
||||||
|
'/qqbot/message-push/subscriptions',
|
||||||
|
{ params: subscriptionQuery },
|
||||||
|
);
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
5,
|
||||||
|
'/qqbot/message-push/templates',
|
||||||
|
{ params: templateQuery },
|
||||||
|
);
|
||||||
|
expect(requestClient.post).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'/qqbot/message-push/subscriptions',
|
||||||
|
subscriptionInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.post).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'/qqbot/message-push/templates',
|
||||||
|
templateInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.post).toHaveBeenNthCalledWith(
|
||||||
|
3,
|
||||||
|
'/qqbot/message-push/templates/preview',
|
||||||
|
{
|
||||||
|
content: previewContent,
|
||||||
|
sourceKey: 'network.stun.mapping-port-changed',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'/qqbot/message-push/subscriptions/id%2Fa%20b',
|
||||||
|
subscriptionInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'/qqbot/message-push/subscriptions/10000000000000001/enabled',
|
||||||
|
{ enabled: false },
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
3,
|
||||||
|
'/qqbot/message-push/templates/template%2Fa%20b',
|
||||||
|
templateInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
4,
|
||||||
|
'/qqbot/message-push/templates/10000000000000001/enabled',
|
||||||
|
{ enabled: false },
|
||||||
|
);
|
||||||
|
expect(requestClient.delete).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'/qqbot/message-push/subscriptions/id%2Fa%20b',
|
||||||
|
);
|
||||||
|
expect(requestClient.delete).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'/qqbot/message-push/templates/template%2Fa%20b',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps account IDs as strings and encodes every account path segment', async () => {
|
||||||
|
const selfId = '10000000000000001/a b';
|
||||||
|
const bindingId = 'binding/a b';
|
||||||
|
const bindingInput = {
|
||||||
|
enabled: true,
|
||||||
|
subscriptionId: '10000000000000002',
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
targetId: '10000000000000003',
|
||||||
|
targetName: '测试群',
|
||||||
|
targetType: 'group' as const,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
templateId: '10000000000000004',
|
||||||
|
};
|
||||||
|
|
||||||
|
await getAccountMessagePushBindings(selfId);
|
||||||
|
await createAccountMessagePushBinding(selfId, bindingInput);
|
||||||
|
await updateAccountMessagePushBinding(selfId, bindingId, bindingInput);
|
||||||
|
await setAccountMessagePushBindingEnabled(selfId, bindingId, false);
|
||||||
|
await deleteAccountMessagePushBinding(selfId, bindingId);
|
||||||
|
await getAccountMessagePushTargets(selfId);
|
||||||
|
|
||||||
|
const accountPath =
|
||||||
|
'/qqbot/accounts/10000000000000001%2Fa%20b/message-push';
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
`${accountPath}/bindings`,
|
||||||
|
);
|
||||||
|
expect(requestClient.get).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`${accountPath}/targets`,
|
||||||
|
);
|
||||||
|
expect(requestClient.post).toHaveBeenCalledWith(
|
||||||
|
`${accountPath}/bindings`,
|
||||||
|
bindingInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
`${accountPath}/bindings/binding%2Fa%20b`,
|
||||||
|
bindingInput,
|
||||||
|
);
|
||||||
|
expect(requestClient.put).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`${accountPath}/bindings/binding%2Fa%20b/enabled`,
|
||||||
|
{ enabled: false },
|
||||||
|
);
|
||||||
|
expect(requestClient.delete).toHaveBeenCalledWith(
|
||||||
|
`${accountPath}/bindings/binding%2Fa%20b`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not expose internal publish, event, delivery, or worker callers', () => {
|
||||||
|
expect(Object.keys(messagePushApi)).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.stringMatching(/delivery|event|publish|worker/i),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
447
apps/web-antdv-next/src/api/qqbot/message-push.ts
Normal file
447
apps/web-antdv-next/src/api/qqbot/message-push.ts
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace QqbotMessagePushApi {
|
||||||
|
export type SystemMessageScalar = boolean | null | number | string;
|
||||||
|
|
||||||
|
export type QqbotMessagePushTargetType = 'group' | 'private';
|
||||||
|
|
||||||
|
export interface SystemMessageSourceVariableDefinition {
|
||||||
|
description: string;
|
||||||
|
example: string;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
type: 'boolean' | 'number' | 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemMessageSourceFieldDefinition {
|
||||||
|
dependsOn?: string;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
optionCollection: 'ddnsRecords' | 'portForwards';
|
||||||
|
required: true;
|
||||||
|
type: 'select';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemMessageSourceDefinition {
|
||||||
|
description: string;
|
||||||
|
displayName: string;
|
||||||
|
sourceKey: string;
|
||||||
|
subscriptionFields: SystemMessageSourceFieldDefinition[];
|
||||||
|
variables: SystemMessageSourceVariableDefinition[];
|
||||||
|
version: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StunMappingPortChangedSubscriptionConfig {
|
||||||
|
ddnsRecordId: string;
|
||||||
|
portForwardId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StunMappingPortChangedOptionsResponse {
|
||||||
|
ddnsRecords: Array<{
|
||||||
|
disabledReasonCode: null | string;
|
||||||
|
eligible: boolean;
|
||||||
|
fqdn: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
portForwardId: string;
|
||||||
|
}>;
|
||||||
|
portForwards: Array<{
|
||||||
|
disabledReasonCode: null | string;
|
||||||
|
eligible: boolean;
|
||||||
|
externalPort: number;
|
||||||
|
id: string;
|
||||||
|
internalPort: number;
|
||||||
|
name: string;
|
||||||
|
protocol: 'tcp' | 'udp';
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageSubscriptionView {
|
||||||
|
createTime: string;
|
||||||
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
invalidReasonCode: null | string;
|
||||||
|
name: string;
|
||||||
|
remark: null | string;
|
||||||
|
sourceConfig: StunMappingPortChangedSubscriptionConfig;
|
||||||
|
sourceKey: string;
|
||||||
|
sourceName: string;
|
||||||
|
sourceSummary: string;
|
||||||
|
updateTime: string;
|
||||||
|
valid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageSubscriptionListQuery {
|
||||||
|
enabled?: boolean;
|
||||||
|
name?: string;
|
||||||
|
pageNo?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
sourceKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageSubscriptionInput {
|
||||||
|
enabled: boolean;
|
||||||
|
name: string;
|
||||||
|
remark?: string;
|
||||||
|
sourceConfig: Record<string, unknown>;
|
||||||
|
sourceKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageTemplateView {
|
||||||
|
content: string;
|
||||||
|
createTime: string;
|
||||||
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
referenceCount: number;
|
||||||
|
remark: null | string;
|
||||||
|
sourceKey: string;
|
||||||
|
sourceName: string;
|
||||||
|
updateTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageTemplateListQuery {
|
||||||
|
enabled?: boolean;
|
||||||
|
name?: string;
|
||||||
|
pageNo?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
sourceKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageTemplateInput {
|
||||||
|
content: string;
|
||||||
|
enabled: boolean;
|
||||||
|
name: string;
|
||||||
|
remark?: string;
|
||||||
|
sourceKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageTemplatePreviewInput {
|
||||||
|
content: string;
|
||||||
|
sourceKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageTemplatePreview {
|
||||||
|
renderedMessage: string;
|
||||||
|
variables: Record<string, boolean | number | string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePublishTargetInput {
|
||||||
|
targetId: string;
|
||||||
|
targetName?: string;
|
||||||
|
targetType: QqbotMessagePushTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePublishBindingInput {
|
||||||
|
enabled: boolean;
|
||||||
|
subscriptionId: string;
|
||||||
|
targets: QqbotMessagePublishTargetInput[];
|
||||||
|
templateId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePushTargetOption {
|
||||||
|
label: string;
|
||||||
|
targetId: string;
|
||||||
|
targetType: QqbotMessagePushTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePushTargetOptionsResponse {
|
||||||
|
available: boolean;
|
||||||
|
options: QqbotMessagePushTargetOption[];
|
||||||
|
reasonCode: null | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePublishTargetView {
|
||||||
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
targetId: string;
|
||||||
|
targetName: null | string;
|
||||||
|
targetType: QqbotMessagePushTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QqbotMessagePublishBindingView {
|
||||||
|
available: boolean;
|
||||||
|
createTime: string;
|
||||||
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
invalidReasonCode: null | string;
|
||||||
|
sourceKey: string;
|
||||||
|
sourceName: string;
|
||||||
|
subscriptionId: string;
|
||||||
|
subscriptionName: string;
|
||||||
|
targets: QqbotMessagePublishTargetView[];
|
||||||
|
templateId: string;
|
||||||
|
templateName: string;
|
||||||
|
updateTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageResult<T> {
|
||||||
|
items: T[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists registered system message sources available to the Admin UI.
|
||||||
|
* @returns Public source definitions without internal adapter details.
|
||||||
|
*/
|
||||||
|
export function getMessagePushSources() {
|
||||||
|
return requestClient.get<QqbotMessagePushApi.SystemMessageSourceDefinition[]>(
|
||||||
|
'/qqbot/message-push/sources',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads one public source definition by its stable source key.
|
||||||
|
* @param sourceKey - String source identity, encoded before becoming a URL segment.
|
||||||
|
* @returns The requested source definition.
|
||||||
|
*/
|
||||||
|
export function getMessagePushSourceDetail(sourceKey: string) {
|
||||||
|
return requestClient.get<QqbotMessagePushApi.SystemMessageSourceDefinition>(
|
||||||
|
`/qqbot/message-push/sources/${encodeURIComponent(sourceKey)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads server-evaluated options for the built-in STUN port-change source.
|
||||||
|
* @returns Eligible and disabled port-forward and DDNS options.
|
||||||
|
*/
|
||||||
|
export function getStunMappingPortChangedOptions() {
|
||||||
|
return requestClient.get<QqbotMessagePushApi.StunMappingPortChangedOptionsResponse>(
|
||||||
|
'/qqbot/message-push/sources/network.stun.mapping-port-changed/options',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pages global message subscriptions using independent list filters.
|
||||||
|
* @param params - Pagination, source, name, and enabled-state filters.
|
||||||
|
* @returns A strict `{ items, total }` subscription page.
|
||||||
|
*/
|
||||||
|
export function getMessageSubscriptionList(
|
||||||
|
params: QqbotMessagePushApi.MessageSubscriptionListQuery,
|
||||||
|
) {
|
||||||
|
return requestClient.get<
|
||||||
|
QqbotMessagePushApi.PageResult<QqbotMessagePushApi.MessageSubscriptionView>
|
||||||
|
>('/qqbot/message-push/subscriptions', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates one source-scoped global message subscription.
|
||||||
|
* @param data - Complete subscription name, source, configuration, and enabled state.
|
||||||
|
* @returns The persisted subscription view.
|
||||||
|
*/
|
||||||
|
export function createMessageSubscription(
|
||||||
|
data: QqbotMessagePushApi.MessageSubscriptionInput,
|
||||||
|
) {
|
||||||
|
return requestClient.post<QqbotMessagePushApi.MessageSubscriptionView>(
|
||||||
|
'/qqbot/message-push/subscriptions',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces one global subscription without coercing its string ID.
|
||||||
|
* @param id - Stable subscription ID, encoded before becoming a URL segment.
|
||||||
|
* @param data - Complete replacement subscription input.
|
||||||
|
* @returns The updated subscription view.
|
||||||
|
*/
|
||||||
|
export function updateMessageSubscription(
|
||||||
|
id: string,
|
||||||
|
data: QqbotMessagePushApi.MessageSubscriptionInput,
|
||||||
|
) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.MessageSubscriptionView>(
|
||||||
|
`/qqbot/message-push/subscriptions/${encodeURIComponent(id)}`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes whether a subscription matches future source events.
|
||||||
|
* @param id - Stable subscription ID, encoded before becoming a URL segment.
|
||||||
|
* @param enabled - Requested future enabled state.
|
||||||
|
* @returns The updated subscription view.
|
||||||
|
*/
|
||||||
|
export function setMessageSubscriptionEnabled(id: string, enabled: boolean) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.MessageSubscriptionView>(
|
||||||
|
`/qqbot/message-push/subscriptions/${encodeURIComponent(id)}/enabled`,
|
||||||
|
{ enabled },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft-deletes one global message subscription.
|
||||||
|
* @param id - Stable subscription ID, encoded before becoming a URL segment.
|
||||||
|
* @returns Whether the subscription was deleted.
|
||||||
|
*/
|
||||||
|
export function deleteMessageSubscription(id: string) {
|
||||||
|
return requestClient.delete<boolean>(
|
||||||
|
`/qqbot/message-push/subscriptions/${encodeURIComponent(id)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pages global message templates using independent list filters.
|
||||||
|
* @param params - Pagination, source, name, and enabled-state filters.
|
||||||
|
* @returns A strict `{ items, total }` template page.
|
||||||
|
*/
|
||||||
|
export function getMessageTemplateList(
|
||||||
|
params: QqbotMessagePushApi.MessageTemplateListQuery,
|
||||||
|
) {
|
||||||
|
return requestClient.get<
|
||||||
|
QqbotMessagePushApi.PageResult<QqbotMessagePushApi.MessageTemplateView>
|
||||||
|
>('/qqbot/message-push/templates', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates one source-scoped global message template.
|
||||||
|
* @param data - Complete template content, source, metadata, and enabled state.
|
||||||
|
* @returns The persisted template view.
|
||||||
|
*/
|
||||||
|
export function createMessageTemplate(
|
||||||
|
data: QqbotMessagePushApi.MessageTemplateInput,
|
||||||
|
) {
|
||||||
|
return requestClient.post<QqbotMessagePushApi.MessageTemplateView>(
|
||||||
|
'/qqbot/message-push/templates',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces one global message template without coercing its string ID.
|
||||||
|
* @param id - Stable template ID, encoded before becoming a URL segment.
|
||||||
|
* @param data - Complete replacement template input.
|
||||||
|
* @returns The updated template view.
|
||||||
|
*/
|
||||||
|
export function updateMessageTemplate(
|
||||||
|
id: string,
|
||||||
|
data: QqbotMessagePushApi.MessageTemplateInput,
|
||||||
|
) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.MessageTemplateView>(
|
||||||
|
`/qqbot/message-push/templates/${encodeURIComponent(id)}`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes whether a template can be selected for future bindings and events.
|
||||||
|
* @param id - Stable template ID, encoded before becoming a URL segment.
|
||||||
|
* @param enabled - Requested future enabled state.
|
||||||
|
* @returns The updated template view.
|
||||||
|
*/
|
||||||
|
export function setMessageTemplateEnabled(id: string, enabled: boolean) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.MessageTemplateView>(
|
||||||
|
`/qqbot/message-push/templates/${encodeURIComponent(id)}/enabled`,
|
||||||
|
{ enabled },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft-deletes one global message template when the backend permits it.
|
||||||
|
* @param id - Stable template ID, encoded before becoming a URL segment.
|
||||||
|
* @returns Whether the template was deleted.
|
||||||
|
*/
|
||||||
|
export function deleteMessageTemplate(id: string) {
|
||||||
|
return requestClient.delete<boolean>(
|
||||||
|
`/qqbot/message-push/templates/${encodeURIComponent(id)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a source-scoped message template using server-controlled example data.
|
||||||
|
* @param data - Template content and source identity only.
|
||||||
|
* @returns Safe rendered text and the example variables used.
|
||||||
|
*/
|
||||||
|
export function previewMessageTemplate(
|
||||||
|
data: QqbotMessagePushApi.MessageTemplatePreviewInput,
|
||||||
|
) {
|
||||||
|
return requestClient.post<QqbotMessagePushApi.MessageTemplatePreview>(
|
||||||
|
'/qqbot/message-push/templates/preview',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists message-push bindings belonging to exactly one QQBot account.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @returns Account-scoped binding views with their configured targets.
|
||||||
|
*/
|
||||||
|
export function getAccountMessagePushBindings(selfId: string) {
|
||||||
|
return requestClient.get<
|
||||||
|
QqbotMessagePushApi.QqbotMessagePublishBindingView[]
|
||||||
|
>(`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/bindings`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates one account-scoped binding from a global subscription and template.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @param data - Subscription, template, target, and enabled-state input.
|
||||||
|
* @returns The persisted account binding view.
|
||||||
|
*/
|
||||||
|
export function createAccountMessagePushBinding(
|
||||||
|
selfId: string,
|
||||||
|
data: QqbotMessagePushApi.QqbotMessagePublishBindingInput,
|
||||||
|
) {
|
||||||
|
return requestClient.post<QqbotMessagePushApi.QqbotMessagePublishBindingView>(
|
||||||
|
`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/bindings`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces one binding for exactly one QQBot account.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @param id - Stable binding ID, encoded before becoming a URL segment.
|
||||||
|
* @param data - Subscription, template, target, and enabled-state input.
|
||||||
|
* @returns The updated account binding view.
|
||||||
|
*/
|
||||||
|
export function updateAccountMessagePushBinding(
|
||||||
|
selfId: string,
|
||||||
|
id: string,
|
||||||
|
data: QqbotMessagePushApi.QqbotMessagePublishBindingInput,
|
||||||
|
) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.QqbotMessagePublishBindingView>(
|
||||||
|
`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/bindings/${encodeURIComponent(id)}`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes whether one account binding creates future delivery work.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @param id - Stable binding ID, encoded before becoming a URL segment.
|
||||||
|
* @param enabled - Requested future enabled state.
|
||||||
|
* @returns The updated account binding view.
|
||||||
|
*/
|
||||||
|
export function setAccountMessagePushBindingEnabled(
|
||||||
|
selfId: string,
|
||||||
|
id: string,
|
||||||
|
enabled: boolean,
|
||||||
|
) {
|
||||||
|
return requestClient.put<QqbotMessagePushApi.QqbotMessagePublishBindingView>(
|
||||||
|
`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/bindings/${encodeURIComponent(id)}/enabled`,
|
||||||
|
{ enabled },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft-deletes one binding from exactly one QQBot account.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @param id - Stable binding ID, encoded before becoming a URL segment.
|
||||||
|
* @returns Whether the account binding was deleted.
|
||||||
|
*/
|
||||||
|
export function deleteAccountMessagePushBinding(selfId: string, id: string) {
|
||||||
|
return requestClient.delete<boolean>(
|
||||||
|
`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/bindings/${encodeURIComponent(id)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists group and private-message target choices for exactly one QQBot account.
|
||||||
|
* @param selfId - QQBot self ID kept as a string and encoded in the URL.
|
||||||
|
* @returns Availability state plus currently discoverable string-ID targets.
|
||||||
|
*/
|
||||||
|
export function getAccountMessagePushTargets(selfId: string) {
|
||||||
|
return requestClient.get<QqbotMessagePushApi.QqbotMessagePushTargetOptionsResponse>(
|
||||||
|
`/qqbot/accounts/${encodeURIComponent(selfId)}/message-push/targets`,
|
||||||
|
);
|
||||||
|
}
|
||||||
34
apps/web-antdv-next/src/router/routes/modules/qqbot.spec.ts
Normal file
34
apps/web-antdv-next/src/router/routes/modules/qqbot.spec.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('qqbot routes', () => {
|
||||||
|
it('adds two flat message-push route children without loading future pages', () => {
|
||||||
|
const source = readFileSync(
|
||||||
|
'apps/web-antdv-next/src/router/routes/modules/qqbot.ts',
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [name, path, componentPath, title] of [
|
||||||
|
[
|
||||||
|
'QqBotMessageSubscription',
|
||||||
|
'/qqbot/message-subscription',
|
||||||
|
'#/views/qqbot/message-subscription/list',
|
||||||
|
'消息订阅',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'QqBotMessageTemplate',
|
||||||
|
'/qqbot/message-template',
|
||||||
|
'#/views/qqbot/message-template/list',
|
||||||
|
'消息模板',
|
||||||
|
],
|
||||||
|
] as const) {
|
||||||
|
expect(source).toMatch(
|
||||||
|
new RegExp(
|
||||||
|
String.raw`\{\s*component: \(\) => import\('${componentPath}'\),\s*meta: \{\s*icon: 'lucide:[^']+',\s*title: '${title}',\s*\},\s*name: '${name}',\s*path: '${path}',\s*\}`,
|
||||||
|
's',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -112,6 +112,24 @@ const routes: RouteRecordRaw[] = [
|
|||||||
name: 'QqBotSendLog',
|
name: 'QqBotSendLog',
|
||||||
path: '/qqbot/sendLog',
|
path: '/qqbot/sendLog',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: () => import('#/views/qqbot/message-subscription/list'),
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:bell-ring',
|
||||||
|
title: '消息订阅',
|
||||||
|
},
|
||||||
|
name: 'QqBotMessageSubscription',
|
||||||
|
path: '/qqbot/message-subscription',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: () => import('#/views/qqbot/message-template/list'),
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:message-square-plus',
|
||||||
|
title: '消息模板',
|
||||||
|
},
|
||||||
|
name: 'QqBotMessageTemplate',
|
||||||
|
path: '/qqbot/message-template',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: () => import('#/views/qqbot/permission/list'),
|
component: () => import('#/views/qqbot/permission/list'),
|
||||||
meta: {
|
meta: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user