43 changed files with 1593 additions and 39 deletions
@ -0,0 +1,44 @@ |
|||||
|
# Whether to open mock |
||||
|
VITE_USE_MOCK=false |
||||
|
|
||||
|
# public path |
||||
|
VITE_PUBLIC_PATH=/ |
||||
|
|
||||
|
# Delete console |
||||
|
VITE_DROP_CONSOLE=true |
||||
|
|
||||
|
# Whether to enable gzip or brotli compression |
||||
|
# Optional: gzip | brotli | none |
||||
|
# If you need multiple forms, you can use `,` to separate |
||||
|
VITE_BUILD_COMPRESS='none' |
||||
|
|
||||
|
# Whether to delete origin files when using compress, default false |
||||
|
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE=false |
||||
|
|
||||
|
# Basic interface address SPA |
||||
|
VITE_GLOB_API_URL=/api |
||||
|
|
||||
|
# File upload address, optional |
||||
|
# It can be forwarded by nginx or write the actual address directly |
||||
|
VITE_GLOB_UPLOAD_URL=/upload |
||||
|
|
||||
|
# Interface prefix |
||||
|
VITE_GLOB_API_URL_PREFIX= |
||||
|
|
||||
|
# Whether to enable image compression |
||||
|
VITE_USE_IMAGEMIN=true |
||||
|
|
||||
|
# use pwa |
||||
|
VITE_USE_PWA=false |
||||
|
|
||||
|
# Is it compatible with older browsers |
||||
|
VITE_LEGACY=false |
||||
|
|
||||
|
# Multi-tenancy key |
||||
|
VITE_GLOB_MULTITENANCY_KEY='__tenant' |
||||
|
|
||||
|
# STS Connect |
||||
|
VITE_GLOB_AUTHORITY='http://127.0.0.1:44385' |
||||
|
VITE_GLOB_CLIENT_ID='vue-admin-element' |
||||
|
VITE_GLOB_CLIENT_SECRET='1q2w3e*' |
||||
|
|
||||
@ -0,0 +1,30 @@ |
|||||
|
import { PagedAndSortedResultRequestDto } from '../../model/baseModel'; |
||||
|
import { HttpStatusCode } from '/@/enums/httpEnum'; |
||||
|
|
||||
|
export interface WebhookEvent { |
||||
|
tenantId?: string; |
||||
|
webhookName: string; |
||||
|
data: string; |
||||
|
creationTime: Date; |
||||
|
} |
||||
|
|
||||
|
export interface WebhookSendAttempt { |
||||
|
id: string; |
||||
|
tenantId?: string; |
||||
|
webhookEventId: string; |
||||
|
webhookSubscriptionId: string; |
||||
|
response: string; |
||||
|
responseStatusCode?: HttpStatusCode; |
||||
|
creationTime: Date; |
||||
|
lastModificationTime?: Date; |
||||
|
webhookEvent: WebhookEvent; |
||||
|
} |
||||
|
|
||||
|
export interface WebhookSendAttemptGetListInput extends PagedAndSortedResultRequestDto { |
||||
|
filter?: string; |
||||
|
webhookEventId?: string; |
||||
|
subscriptionId?: string; |
||||
|
responseStatusCode?: HttpStatusCode; |
||||
|
beginCreationTime?: Date; |
||||
|
endCreationTime?: Date; |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
import { CreationAuditedEntityDto, PagedAndSortedResultRequestDto } from '../../model/baseModel'; |
||||
|
|
||||
|
export interface WebhookSubscription extends CreationAuditedEntityDto { |
||||
|
id: string; |
||||
|
tenantId?: string; |
||||
|
webhookUri: string; |
||||
|
secret: string; |
||||
|
isActive: boolean; |
||||
|
webhooks: string[]; |
||||
|
headers: { [key: string]: string }; |
||||
|
} |
||||
|
|
||||
|
export interface WebhookSubscriptionCreateOrUpdate { |
||||
|
webhookUri: string; |
||||
|
secret: string; |
||||
|
isActive: boolean; |
||||
|
webhooks: string[]; |
||||
|
headers: { [key: string]: string }; |
||||
|
} |
||||
|
|
||||
|
export type CreateWebhookSubscription = WebhookSubscriptionCreateOrUpdate; |
||||
|
|
||||
|
export type UpdateWebhookSubscription = WebhookSubscriptionCreateOrUpdate; |
||||
|
|
||||
|
export interface WebhookAvailable { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
description: string; |
||||
|
} |
||||
|
|
||||
|
export interface WebhookAvailableGroup { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
webhooks: WebhookAvailable[]; |
||||
|
} |
||||
|
|
||||
|
export interface WebhookSubscriptionGetListInput extends PagedAndSortedResultRequestDto { |
||||
|
filter?: string; |
||||
|
tenantId?: string; |
||||
|
webhookUri?: string; |
||||
|
secret?: string; |
||||
|
isActive?: boolean; |
||||
|
webhooks?: string; |
||||
|
beginCreationTime?: Date; |
||||
|
endCreationTime?: Date; |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
import { defAbpHttp } from '/@/utils/http/abp'; |
||||
|
import { PagedResultDto } from '../model/baseModel'; |
||||
|
import { WebhookSendAttempt, WebhookSendAttemptGetListInput } from './model/sendAttemptsModel'; |
||||
|
|
||||
|
const remoteServiceName = 'WebhooksManagement'; |
||||
|
const controllerName = 'WebhookSendRecord'; |
||||
|
|
||||
|
export const getById = (id: string) => { |
||||
|
return defAbpHttp.request<WebhookSendAttempt>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'GetAsync', |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const deleteById = (id: string) => { |
||||
|
return defAbpHttp.request<WebhookSendAttempt>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'DeleteAsync', |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export const getList = (input: WebhookSendAttemptGetListInput) => { |
||||
|
return defAbpHttp.request<PagedResultDto<WebhookSendAttempt>>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'GetListAsync', |
||||
|
params: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const resend = (id: string) => { |
||||
|
return defAbpHttp.request<void>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'ResendAsync', |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
import { defAbpHttp } from '/@/utils/http/abp'; |
||||
|
import { |
||||
|
WebhookSubscription, |
||||
|
WebhookAvailableGroup, |
||||
|
CreateWebhookSubscription, |
||||
|
UpdateWebhookSubscription, |
||||
|
WebhookSubscriptionGetListInput, |
||||
|
} from './model/subscriptionsModel'; |
||||
|
import { ListResultDto, PagedResultDto } from '../model/baseModel'; |
||||
|
|
||||
|
const remoteServiceName = 'WebhooksManagement'; |
||||
|
const controllerName = 'WebhookSubscription'; |
||||
|
|
||||
|
export const create = (input: CreateWebhookSubscription) => { |
||||
|
return defAbpHttp.request<WebhookSubscription>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'CreateAsync', |
||||
|
data: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const update = (id: string, input: UpdateWebhookSubscription) => { |
||||
|
return defAbpHttp.request<WebhookSubscription>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'UpdateAsync', |
||||
|
data: input, |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const getById = (id: string) => { |
||||
|
return defAbpHttp.request<WebhookSubscription>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'GetAsync', |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const deleteById = (id: string) => { |
||||
|
return defAbpHttp.request<WebhookSubscription>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'DeleteAsync', |
||||
|
params: { |
||||
|
id: id, |
||||
|
}, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const getList = (input: WebhookSubscriptionGetListInput) => { |
||||
|
return defAbpHttp.request<PagedResultDto<WebhookSubscription>>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'GetListAsync', |
||||
|
params: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const getAllAvailableWebhooks = () => { |
||||
|
return defAbpHttp.request<ListResultDto<WebhookAvailableGroup>>({ |
||||
|
service: remoteServiceName, |
||||
|
controller: controllerName, |
||||
|
action: 'GetAllAvailableWebhooksAsync', |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,163 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
@register="registerModal" |
||||
|
:width="900" |
||||
|
:height="500" |
||||
|
:title="L('SendAttempts')" |
||||
|
:mask-closable="false" |
||||
|
> |
||||
|
<Form |
||||
|
ref="formElRef" |
||||
|
:colon="true" |
||||
|
label-align="left" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
:model="modelRef" |
||||
|
> |
||||
|
<Tabs v-model:activeKey="activeKey"> |
||||
|
<TabPane key="basic" :tab="L('BasicInfo')"> |
||||
|
<FormItem :label="L('DisplayName:CreationTime')"> |
||||
|
<Input readonly :value="getDateTime(modelRef.creationTime)" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:ResponseStatusCode')"> |
||||
|
<Tag v-if="modelRef.responseStatusCode" :color="getHttpStatusColor(modelRef.responseStatusCode)">{{ httpStatusCodeMap[modelRef.responseStatusCode] }}</Tag> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:Response')"> |
||||
|
<CodeEditor readonly style="height: 300px;" :mode="MODE.HTML" v-model:value="modelRef.response" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
|
||||
|
<TabPane v-if="modelRef.webhookEvent" key="event" :tab="L('WebhookEvent')"> |
||||
|
<FormItem :label="L('DisplayName:WebhookEventId')"> |
||||
|
<Input readonly :value="modelRef.webhookEventId" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:WebhookName')"> |
||||
|
<Input readonly :value="modelRef.webhookEvent.webhookName" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:CreationTime')"> |
||||
|
<Input readonly :value="getDateTime(modelRef.webhookEvent.creationTime)" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:Data')"> |
||||
|
<CodeEditor readonly style="height: 300px;" :mode="MODE.JSON" v-model:value="modelRef.webhookEvent.data" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
|
||||
|
<TabPane v-if="subscriptionRef.id" key="subscription" :tab="L('Subscriptions')"> |
||||
|
<FormItem :label="L('DisplayName:SubscriptionId')"> |
||||
|
<Input readonly :value="modelRef.webhookSubscriptionId" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:IsActive')"> |
||||
|
<Checkbox disabled v-model:checked="subscriptionRef.isActive">{{ L('DisplayName:IsActive') }}</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:WebhookUri')"> |
||||
|
<Input readonly :value="subscriptionRef.webhookUri" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:Secret')"> |
||||
|
<Input readonly :value="subscriptionRef.secret" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:CreationTime')"> |
||||
|
<Input readonly :value="getDateTime(subscriptionRef.creationTime)" /> |
||||
|
</FormItem> |
||||
|
<FormItem :label="L('DisplayName:Webhooks')"> |
||||
|
<TextArea readonly :value="getWebhooks(subscriptionRef.webhooks)" :auto-size="{ minRows: 5, maxRows: 10 }" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="headers" :label="L('DisplayName:Headers')"> |
||||
|
<CodeEditor readonly style="height: 300px;" :mode="MODE.JSON" v-model:value="subscriptionRef.headers" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
</Tabs> |
||||
|
</Form> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { ref, computed } from 'vue'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { |
||||
|
Checkbox, |
||||
|
Form, |
||||
|
Tabs, |
||||
|
Tag, |
||||
|
Input, |
||||
|
} from 'ant-design-vue'; |
||||
|
import { CodeEditor, MODE } from '/@/components/CodeEditor'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { getById } from '/@/api/webhooks/send-attempts'; |
||||
|
import { getById as getSubscription } from '/@/api/webhooks/subscriptions'; |
||||
|
import { WebhookSendAttempt } from '/@/api/webhooks/model/sendAttemptsModel'; |
||||
|
import { WebhookSubscription } from '/@/api/webhooks/model/subscriptionsModel'; |
||||
|
import { httpStatusCodeMap, getHttpStatusColor } from '../../typing'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const TabPane = Tabs.TabPane; |
||||
|
const TextArea = Input.TextArea; |
||||
|
|
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
const formElRef = ref<any>(); |
||||
|
const activeKey = ref('basic'); |
||||
|
const modelRef = ref<WebhookSendAttempt>(getDefaultModel()); |
||||
|
const subscriptionRef = ref<WebhookSubscription>(getDefaultSubscription()); |
||||
|
const [registerModal] = useModalInner((model) => { |
||||
|
activeKey.value = 'basic'; |
||||
|
fetchModel(model.id); |
||||
|
}); |
||||
|
const getDateTime = computed(() => { |
||||
|
return (date?: Date) => { |
||||
|
return date ? formatToDateTime(date) : ''; |
||||
|
} |
||||
|
}); |
||||
|
const getWebhooks = computed(() => { |
||||
|
return (webhooks: string[]) => { |
||||
|
return webhooks.reduce((hook, p) => hook + p + '\n', ''); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
function fetchModel(id: string) { |
||||
|
if (!id) { |
||||
|
modelRef.value = getDefaultModel(); |
||||
|
return; |
||||
|
} |
||||
|
getById(id).then((res) => { |
||||
|
modelRef.value = res; |
||||
|
fetchSubscription(res.webhookSubscriptionId); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function fetchSubscription(id: string) { |
||||
|
getSubscription(id).then((res) => { |
||||
|
subscriptionRef.value = res; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function getDefaultModel() : WebhookSendAttempt { |
||||
|
return { |
||||
|
id: '', |
||||
|
webhookEventId: '', |
||||
|
webhookSubscriptionId: '', |
||||
|
webhookEvent: { |
||||
|
tenantId: undefined, |
||||
|
webhookName: '', |
||||
|
data: '{}', |
||||
|
creationTime: new Date(), |
||||
|
}, |
||||
|
response: '', |
||||
|
responseStatusCode: undefined, |
||||
|
creationTime: new Date(), |
||||
|
lastModificationTime: undefined, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function getDefaultSubscription() : WebhookSubscription { |
||||
|
return { |
||||
|
id: '', |
||||
|
webhooks: [], |
||||
|
webhookUri: '', |
||||
|
headers: {}, |
||||
|
secret: '', |
||||
|
isActive: true, |
||||
|
creatorId: '', |
||||
|
creationTime: new Date(), |
||||
|
}; |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,97 @@ |
|||||
|
<template> |
||||
|
<div class="content"> |
||||
|
<BasicTable @register="registerTable"> |
||||
|
<template #code="{ record }"> |
||||
|
<Tag :color="getHttpStatusColor(record.responseStatusCode)">{{ httpStatusCodeMap[record.responseStatusCode] }}</Tag> |
||||
|
</template> |
||||
|
<template #action="{ record }"> |
||||
|
<TableAction |
||||
|
:stop-button-propagation="true" |
||||
|
:actions="[ |
||||
|
{ |
||||
|
auth: 'AbpWebhooks.SendAttempts', |
||||
|
label: L('Edit'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleEdit.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
auth: 'AbpWebhooks.SendAttempts.Delete', |
||||
|
color: 'error', |
||||
|
label: L('Delete'), |
||||
|
icon: 'ant-design:delete-outlined', |
||||
|
onClick: handleDelete.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
:dropDownActions="[ |
||||
|
{ |
||||
|
auth: 'AbpWebhooks.SendAttempts.Resend', |
||||
|
label: L('Resend'), |
||||
|
ifShow: [JobStatus.Running, JobStatus.FailedRetry].includes(record.status), |
||||
|
onClick: handlePause.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
/> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
<SendAttemptModal @register="registerModal" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { Switch, Tag } from 'ant-design-vue'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import { BasicTable, TableAction, useTable } from '/@/components/Table'; |
||||
|
import { formatPagedRequest } from '/@/utils/http/abp/helper'; |
||||
|
import { getDataColumns } from '../datas/TableData'; |
||||
|
import { getSearchFormSchemas } from '../datas/ModalData'; |
||||
|
import { httpStatusCodeMap, getHttpStatusColor } from '../../typing'; |
||||
|
import { getList } from '/@/api/webhooks/send-attempts'; |
||||
|
import SendAttemptModal from './SendAttemptModal.vue'; |
||||
|
|
||||
|
const { createConfirm } = useMessage(); |
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
const [registerModal, { openModal }] = useModal(); |
||||
|
const [registerTable, { reload }] = useTable({ |
||||
|
rowKey: 'id', |
||||
|
title: L('SendAttempts'), |
||||
|
columns: getDataColumns(), |
||||
|
api: getList, |
||||
|
beforeFetch: formatPagedRequest, |
||||
|
pagination: true, |
||||
|
striped: false, |
||||
|
useSearchForm: true, |
||||
|
showTableSetting: true, |
||||
|
bordered: true, |
||||
|
showIndexColumn: false, |
||||
|
canResize: false, |
||||
|
immediate: true, |
||||
|
clickToRowSelect: false, |
||||
|
formConfig: getSearchFormSchemas(), |
||||
|
actionColumn: { |
||||
|
width: 220, |
||||
|
title: L('Actions'), |
||||
|
dataIndex: 'action', |
||||
|
slots: { customRender: 'action' }, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
function handleEdit(record) { |
||||
|
openModal(true, record); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('ItemWillBeDeletedMessage'), |
||||
|
okCancel: true, |
||||
|
onOk: () => { |
||||
|
deleteById(record.id).then(() => { |
||||
|
reload(); |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,84 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { FormProps } from '/@/components/Form'; |
||||
|
import { getList as getTenants } from '/@/api/saas/tenant'; |
||||
|
import { getList as getSubscriptions } from '/@/api/webhooks/subscriptions'; |
||||
|
import { httpStatusOptions } from '../../typing'; |
||||
|
|
||||
|
const { L } = useLocalization('WebhooksManagement', 'AbpUi'); |
||||
|
|
||||
|
export function getSearchFormSchemas(): Partial<FormProps> { |
||||
|
return { |
||||
|
labelWidth: 100, |
||||
|
schemas: [ |
||||
|
{ |
||||
|
field: 'tenantId', |
||||
|
component: 'ApiSelect', |
||||
|
label: L('DisplayName:TenantId'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
api: getTenants, |
||||
|
params: { |
||||
|
skipCount: 0, |
||||
|
maxResultCount: 100, |
||||
|
}, |
||||
|
resultField: 'items', |
||||
|
labelField: 'name', |
||||
|
valueField: 'id', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'subscriptionId', |
||||
|
component: 'ApiSelect', |
||||
|
label: L('DisplayName:Subscription'), |
||||
|
colProps: { span: 12 }, |
||||
|
componentProps: { |
||||
|
api: getSubscriptions, |
||||
|
params: { |
||||
|
skipCount: 0, |
||||
|
maxResultCount: 100, |
||||
|
}, |
||||
|
resultField: 'items', |
||||
|
labelField: 'webhookUri', |
||||
|
valueField: 'id', |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
field: 'responseStatusCode', |
||||
|
component: 'Select', |
||||
|
label: L('DisplayName:ResponseStatusCode'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
options: httpStatusOptions, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'beginCreationTime', |
||||
|
component: 'DatePicker', |
||||
|
label: L('DisplayName:BeginCreationTime'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
style: { |
||||
|
width: '100%', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'endCreationTime', |
||||
|
component: 'DatePicker', |
||||
|
label: L('DisplayName:EndCreationTime'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
style: { |
||||
|
width: '100%', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 12 }, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { BasicColumn } from '/@/components/Table'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
|
||||
|
export function getDataColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
title: 'id', |
||||
|
dataIndex: 'id', |
||||
|
width: 1, |
||||
|
ifShow: false, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:TenantId'), |
||||
|
dataIndex: 'tenantId', |
||||
|
align: 'left', |
||||
|
width: 150, |
||||
|
sorter: true, |
||||
|
fixed: 'left', |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:ResponseStatusCode'), |
||||
|
dataIndex: 'responseStatusCode', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
sorter: true, |
||||
|
slots: { |
||||
|
customRender: 'code', |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:CreationTime'), |
||||
|
dataIndex: 'creationTime', |
||||
|
align: 'left', |
||||
|
width: 150, |
||||
|
sorter: true, |
||||
|
format: (text) => { |
||||
|
return formatToDateTime(text); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Response'), |
||||
|
dataIndex: 'response', |
||||
|
align: 'left', |
||||
|
width: 'auto', |
||||
|
sorter: true, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<SendAttemptTable /> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
|
||||
|
import SendAttemptTable from './components/SendAttemptTable.vue'; |
||||
|
export default defineComponent({ |
||||
|
name: 'SendAttempts', |
||||
|
components: { |
||||
|
SendAttemptTable, |
||||
|
}, |
||||
|
setup() {}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,179 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
@register="registerModal" |
||||
|
:width="800" |
||||
|
:height="400" |
||||
|
:title="modalTitle" |
||||
|
:mask-closable="false" |
||||
|
@ok="handleSubmit" |
||||
|
> |
||||
|
<Form |
||||
|
ref="formElRef" |
||||
|
label-align="left" |
||||
|
layout="horizontal" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
:model="modelRef" |
||||
|
:rules="modelRules" |
||||
|
> |
||||
|
<FormItem name="tenantId" :label="L('DisplayName:TenantId')"> |
||||
|
<Select v-model:value="modelRef.tenantId"> |
||||
|
<SelectOption |
||||
|
v-for="tenant in tenantsRef" |
||||
|
:key="tenant.id" |
||||
|
:value="tenant.id" |
||||
|
>{{ tenant.name }}</SelectOption> |
||||
|
</Select> |
||||
|
</FormItem> |
||||
|
<FormItem name="isActive" :label="L('DisplayName:IsActive')"> |
||||
|
<Checkbox v-model:checked="modelRef.isActive">{{ L('DisplayName:IsActive') }}</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem name="webhookUri" required :label="L('DisplayName:WebhookUri')"> |
||||
|
<Input v-model:value="modelRef.webhookUri" autocomplete="off" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="secret" required :label="L('DisplayName:Secret')"> |
||||
|
<Input v-model:value="modelRef.secret" autocomplete="off" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="webhooks" :label="L('DisplayName:Webhooks')"> |
||||
|
<Select v-model:value="modelRef.webhooks" mode="multiple"> |
||||
|
<SelectGroup v-for="group in webhooksGroupRef" :key="group.name" :label="group.displayName"> |
||||
|
<SelectOption |
||||
|
v-for="option in group.webhooks" |
||||
|
:key="option.name" |
||||
|
:value="option.name" |
||||
|
>{{ option.displayName }}</SelectOption> |
||||
|
</SelectGroup> |
||||
|
</Select> |
||||
|
</FormItem> |
||||
|
<FormItem name="headers" :label="L('DisplayName:Headers')"> |
||||
|
<CodeEditor style="height: 300px;" :mode="MODE.JSON" v-model:value="modelRef.headers" /> |
||||
|
</FormItem> |
||||
|
</Form> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { computed, ref, reactive, unref, onMounted, nextTick } from 'vue'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useValidation } from '/@/hooks/abp/useValidation'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { |
||||
|
Checkbox, |
||||
|
Form, |
||||
|
Select, |
||||
|
Input, |
||||
|
} from 'ant-design-vue'; |
||||
|
import { CodeEditor, MODE } from '/@/components/CodeEditor'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { Tenant } from '/@/api/saas/model/tenantModel'; |
||||
|
import { getList as getTenants } from '/@/api/saas/tenant'; |
||||
|
import { getById, create, update, getAllAvailableWebhooks } from '/@/api/webhooks/subscriptions'; |
||||
|
import { WebhookSubscription, WebhookAvailableGroup } from '/@/api/webhooks/model/subscriptionsModel'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const SelectGroup = Select.OptGroup; |
||||
|
const SelectOption = Select.Option; |
||||
|
|
||||
|
const emit = defineEmits(['change', 'register']); |
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
const { ruleCreator } = useValidation(); |
||||
|
const { createMessage } = useMessage(); |
||||
|
const formElRef = ref<any>(); |
||||
|
const tenantsRef = ref<Tenant[]>([]); |
||||
|
const webhooksGroupRef = ref<WebhookAvailableGroup[]>([]); |
||||
|
const modelRef = ref<WebhookSubscription>(getDefaultModel()); |
||||
|
const [registerModal, { closeModal, changeOkLoading }] = useModalInner((model) => { |
||||
|
fetchModel(model.id); |
||||
|
nextTick(() => { |
||||
|
const formEl = unref(formElRef); |
||||
|
formEl?.clearValidate(); |
||||
|
}); |
||||
|
}); |
||||
|
const isEditModal = computed(() => { |
||||
|
if (modelRef.value.id) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}); |
||||
|
const modalTitle = computed(() => { |
||||
|
if (!isEditModal.value) { |
||||
|
return L('Subscriptions:AddNew'); |
||||
|
} |
||||
|
return L('Subscriptions:Edit'); |
||||
|
}); |
||||
|
const modelRules = reactive({ |
||||
|
webhookUri: ruleCreator.fieldRequired({ |
||||
|
name: 'WebhookUri', |
||||
|
resourceName: 'WebhooksManagement', |
||||
|
prefix: 'DisplayName', |
||||
|
}), |
||||
|
secret: ruleCreator.fieldRequired({ |
||||
|
name: 'Secret', |
||||
|
resourceName: 'WebhooksManagement', |
||||
|
prefix: 'DisplayName', |
||||
|
}), |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
fetchTenants(); |
||||
|
fetchAvailableWebhooks(); |
||||
|
}); |
||||
|
|
||||
|
function fetchAvailableWebhooks() { |
||||
|
getAllAvailableWebhooks().then((res) => { |
||||
|
webhooksGroupRef.value = res.items; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function fetchTenants() { |
||||
|
getTenants({ |
||||
|
skipCount: 0, |
||||
|
maxResultCount: 100, |
||||
|
sorting: undefined, |
||||
|
}).then((res) => { |
||||
|
tenantsRef.value = res.items; |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function fetchModel(id: string) { |
||||
|
if (!id) { |
||||
|
modelRef.value = getDefaultModel(); |
||||
|
return; |
||||
|
} |
||||
|
getById(id).then((res) => { |
||||
|
modelRef.value = res; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
const formEl = unref(formElRef); |
||||
|
formEl?.validate().then(() => { |
||||
|
changeOkLoading(true); |
||||
|
const model = unref(modelRef); |
||||
|
const api = isEditModal.value |
||||
|
? update(model.id, Object.assign(model)) |
||||
|
: create(Object.assign(model)); |
||||
|
api.then(() => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
formEl?.resetFields(); |
||||
|
closeModal(); |
||||
|
emit('change'); |
||||
|
}).finally(() => { |
||||
|
changeOkLoading(false); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function getDefaultModel() : WebhookSubscription { |
||||
|
return { |
||||
|
id: '', |
||||
|
webhooks: [], |
||||
|
webhookUri: '', |
||||
|
headers: {}, |
||||
|
secret: '', |
||||
|
isActive: true, |
||||
|
creatorId: '', |
||||
|
creationTime: new Date(), |
||||
|
}; |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,103 @@ |
|||||
|
<template> |
||||
|
<div class="content"> |
||||
|
<BasicTable @register="registerTable"> |
||||
|
<template #toolbar> |
||||
|
<a-button |
||||
|
v-auth="['AbpWebhooks.Subscriptions.Create']" |
||||
|
type="primary" |
||||
|
@click="handleAddNew" |
||||
|
>{{ L('Subscriptions:AddNew') }}</a-button |
||||
|
> |
||||
|
</template> |
||||
|
<template #active="{ record }"> |
||||
|
<Switch :checked="record.isActive" disabled /> |
||||
|
</template> |
||||
|
<template #webhooks="{ record }"> |
||||
|
<Tag v-for="hook in record.webhooks" color="blue">{{ hook }}</Tag> |
||||
|
</template> |
||||
|
<template #action="{ record }"> |
||||
|
<TableAction |
||||
|
:stop-button-propagation="true" |
||||
|
:actions="[ |
||||
|
{ |
||||
|
auth: 'AbpWebhooks.Subscriptions.Update', |
||||
|
label: L('Edit'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleEdit.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
auth: 'AbpWebhooks.Subscriptions.Delete', |
||||
|
color: 'error', |
||||
|
label: L('Delete'), |
||||
|
icon: 'ant-design:delete-outlined', |
||||
|
onClick: handleDelete.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
/> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
<SubscriptionModal @register="registerModal" @change="reload" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { Switch, Tag } from 'ant-design-vue'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import { BasicTable, TableAction, useTable } from '/@/components/Table'; |
||||
|
import { formatPagedRequest } from '/@/utils/http/abp/helper'; |
||||
|
import { getDataColumns } from '../datas/TableData'; |
||||
|
import { getSearchFormSchemas } from '../datas/ModalData'; |
||||
|
import { deleteById, getList } from '/@/api/webhooks/subscriptions'; |
||||
|
import SubscriptionModal from './SubscriptionModal.vue'; |
||||
|
|
||||
|
const { createConfirm } = useMessage(); |
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
const [registerModal, { openModal }] = useModal(); |
||||
|
const [registerTable, { reload }] = useTable({ |
||||
|
rowKey: 'id', |
||||
|
title: L('Subscriptions'), |
||||
|
columns: getDataColumns(), |
||||
|
api: getList, |
||||
|
beforeFetch: formatPagedRequest, |
||||
|
pagination: true, |
||||
|
striped: false, |
||||
|
useSearchForm: true, |
||||
|
showTableSetting: true, |
||||
|
bordered: true, |
||||
|
showIndexColumn: false, |
||||
|
canResize: false, |
||||
|
immediate: true, |
||||
|
clickToRowSelect: false, |
||||
|
formConfig: getSearchFormSchemas(), |
||||
|
actionColumn: { |
||||
|
width: 220, |
||||
|
title: L('Actions'), |
||||
|
dataIndex: 'action', |
||||
|
slots: { customRender: 'action' }, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
openModal(true, { id: null }); |
||||
|
} |
||||
|
|
||||
|
function handleEdit(record) { |
||||
|
openModal(true, record); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('ItemWillBeDeletedMessage'), |
||||
|
okCancel: true, |
||||
|
onOk: () => { |
||||
|
deleteById(record.id).then(() => { |
||||
|
reload(); |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,108 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { FormProps } from '/@/components/Form'; |
||||
|
import { getList } from '/@/api/saas/tenant'; |
||||
|
import { getAllAvailableWebhooks } from '/@/api/webhooks/subscriptions'; |
||||
|
|
||||
|
const { L } = useLocalization('WebhooksManagement', 'AbpUi'); |
||||
|
|
||||
|
function getAllAvailables(): Promise<any> { |
||||
|
return getAllAvailableWebhooks().then((res) => { |
||||
|
return res.items.map((group) => { |
||||
|
return { |
||||
|
label: group.displayName, |
||||
|
value: group.name, |
||||
|
options: group.webhooks.map((p) => { |
||||
|
return { |
||||
|
label: p.displayName, |
||||
|
value: p.name, |
||||
|
} |
||||
|
}), |
||||
|
} |
||||
|
}) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function getSearchFormSchemas(): Partial<FormProps> { |
||||
|
return { |
||||
|
labelWidth: 100, |
||||
|
schemas: [ |
||||
|
{ |
||||
|
field: 'tenantId', |
||||
|
component: 'ApiSelect', |
||||
|
label: L('DisplayName:TenantId'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
api: getList, |
||||
|
params: { |
||||
|
skipCount: 0, |
||||
|
maxResultCount: 1000, |
||||
|
}, |
||||
|
resultField: 'items', |
||||
|
labelField: 'name', |
||||
|
valueField: 'id', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'webhookUri', |
||||
|
component: 'Input', |
||||
|
label: L('DisplayName:WebhookUri'), |
||||
|
colProps: { span: 12 }, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'secret', |
||||
|
component: 'Input', |
||||
|
label: L('DisplayName:Secret'), |
||||
|
colProps: { span: 6 }, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'isActive', |
||||
|
component: 'Checkbox', |
||||
|
label: L('DisplayName:IsActive'), |
||||
|
colProps: { span: 6 }, |
||||
|
defaultValue: true, |
||||
|
renderComponentContent: L('DisplayName:IsActive'), |
||||
|
}, |
||||
|
{ |
||||
|
field: 'webhooks', |
||||
|
component: 'ApiSelect', |
||||
|
label: L('DisplayName:Webhooks'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
api: () => getAllAvailables(), |
||||
|
showSearch: true, |
||||
|
filterOption: (onputValue: string, option: any) => { |
||||
|
return option.label.includes(onputValue); |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'beginCreationTime', |
||||
|
component: 'DatePicker', |
||||
|
label: L('DisplayName:BeginCreationTime'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
style: { |
||||
|
width: '100%', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'endCreationTime', |
||||
|
component: 'DatePicker', |
||||
|
label: L('DisplayName:EndCreationTime'), |
||||
|
colProps: { span: 6 }, |
||||
|
componentProps: { |
||||
|
style: { |
||||
|
width: '100%', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 24 }, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { BasicColumn } from '/@/components/Table'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const { L } = useLocalization('WebhooksManagement'); |
||||
|
|
||||
|
export function getDataColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
title: 'id', |
||||
|
dataIndex: 'id', |
||||
|
width: 1, |
||||
|
ifShow: false, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:TenantId'), |
||||
|
dataIndex: 'tenantId', |
||||
|
align: 'left', |
||||
|
width: 150, |
||||
|
sorter: true, |
||||
|
fixed: 'left', |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:WebhookUri'), |
||||
|
dataIndex: 'webhookUri', |
||||
|
align: 'left', |
||||
|
width: 300, |
||||
|
sorter: true, |
||||
|
fixed: 'left', |
||||
|
slots: { |
||||
|
customRender: 'name', |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:IsActive'), |
||||
|
dataIndex: 'isActive', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
sorter: true, |
||||
|
slots: { |
||||
|
customRender: 'active', |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:CreationTime'), |
||||
|
dataIndex: 'creationTime', |
||||
|
align: 'left', |
||||
|
width: 150, |
||||
|
sorter: true, |
||||
|
format: (text) => { |
||||
|
return formatToDateTime(text); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Webhooks'), |
||||
|
dataIndex: 'webhooks', |
||||
|
align: 'left', |
||||
|
width: 200, |
||||
|
sorter: true, |
||||
|
slots: { |
||||
|
customRender: 'webhooks', |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<SubscriptionTable /> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
|
||||
|
import SubscriptionTable from './components/SubscriptionTable.vue'; |
||||
|
export default defineComponent({ |
||||
|
name: 'Subscriptions', |
||||
|
components: { |
||||
|
SubscriptionTable, |
||||
|
}, |
||||
|
setup() {}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,76 @@ |
|||||
|
import { HttpStatusCode } from '/@/enums/httpEnum'; |
||||
|
|
||||
|
export const httpStatusCodeMap = { |
||||
|
[HttpStatusCode.Continue]: '100 - Continue', |
||||
|
[HttpStatusCode.SwitchingProtocols]: '101 - Switching Protocols', |
||||
|
[HttpStatusCode.OK]: '200 - OK', |
||||
|
[HttpStatusCode.Created]: '201 - Created', |
||||
|
[HttpStatusCode.Accepted]: '202 - Accepted', |
||||
|
[HttpStatusCode.NonAuthoritativeInformation]: '203 - Non Authoritative Information', |
||||
|
[HttpStatusCode.NoContent]: '204 - No Content', |
||||
|
[HttpStatusCode.ResetContent]: '205 - Reset Content', |
||||
|
[HttpStatusCode.PartialContent]: '206 - Partial Content', |
||||
|
[HttpStatusCode.Ambiguous]: '300 - Ambiguous', |
||||
|
[HttpStatusCode.MultipleChoices]: '300 - Multiple Choices', |
||||
|
[HttpStatusCode.Moved]: '301 - Moved', |
||||
|
[HttpStatusCode.MovedPermanently]: '301 - Moved Permanently', |
||||
|
[HttpStatusCode.Found]: '302 - Found', |
||||
|
[HttpStatusCode.Redirect]: '302 - Redirect', |
||||
|
[HttpStatusCode.RedirectMethod]: '303 - Redirect Method', |
||||
|
[HttpStatusCode.SeeOther]: '303 - See Other', |
||||
|
[HttpStatusCode.NotModified]: '304 - Not Modified', |
||||
|
[HttpStatusCode.UseProxy]: '305 - Use Proxy', |
||||
|
[HttpStatusCode.Unused]: '306 - Unused', |
||||
|
[HttpStatusCode.RedirectKeepVerb]: '307 - Redirect Keep Verb', |
||||
|
[HttpStatusCode.TemporaryRedirect]: '307 - Temporary Redirect', |
||||
|
[HttpStatusCode.BadRequest]: '400 - Bad Request', |
||||
|
[HttpStatusCode.Unauthorized]: '401 - Unauthorized', |
||||
|
[HttpStatusCode.PaymentRequired]: '402 - Payment Required', |
||||
|
[HttpStatusCode.Forbidden]: '403 - Forbidden', |
||||
|
[HttpStatusCode.NotFound]: '404 - Not Found', |
||||
|
[HttpStatusCode.MethodNotAllowed]: '405 - Method Not Allowed', |
||||
|
[HttpStatusCode.NotAcceptable]: '406 - Not Acceptable', |
||||
|
[HttpStatusCode.ProxyAuthenticationRequired]: '407 - Proxy Authentication Required', |
||||
|
[HttpStatusCode.RequestTimeout]: '408 - Request Timeout', |
||||
|
[HttpStatusCode.Conflict]: '409 - Conflict', |
||||
|
[HttpStatusCode.Gone]: '410 - Gone', |
||||
|
[HttpStatusCode.LengthRequired]: '411 - Length Required', |
||||
|
[HttpStatusCode.PreconditionFailed]: '412 - Precondition Failed', |
||||
|
[HttpStatusCode.RequestEntityTooLarge]: '413 - Request Entity Too Large', |
||||
|
[HttpStatusCode.RequestUriTooLong]: '414 - Request Uri Too Long', |
||||
|
[HttpStatusCode.UnsupportedMediaType]: '415 - Unsupported Media Type', |
||||
|
[HttpStatusCode.RequestedRangeNotSatisfiable]: '416 - Requested Range Not Satisfiable', |
||||
|
[HttpStatusCode.ExpectationFailed]: '417 - Expectation Failed', |
||||
|
[HttpStatusCode.UpgradeRequired]: '426 - Upgrade Required', |
||||
|
[HttpStatusCode.InternalServerError]: '500 - Internal Server Error', |
||||
|
[HttpStatusCode.NotImplemented]: '501 - Not Implemented', |
||||
|
[HttpStatusCode.BadGateway]: '502 - Bad Gateway', |
||||
|
[HttpStatusCode.ServiceUnavailable]: '503 - Service Unavailable', |
||||
|
[HttpStatusCode.GatewayTimeout]: '504 - Gateway Timeout', |
||||
|
[HttpStatusCode.HttpVersionNotSupported]: '505 - Http Version Not Supported', |
||||
|
} |
||||
|
|
||||
|
export const httpStatusOptions = Object.keys(httpStatusCodeMap).map((key) => { |
||||
|
return { |
||||
|
label: httpStatusCodeMap[key], |
||||
|
value: key, |
||||
|
}; |
||||
|
}) |
||||
|
|
||||
|
export function getHttpStatusColor(statusCode: HttpStatusCode) { |
||||
|
if (statusCode < 200) { |
||||
|
return 'default'; |
||||
|
} |
||||
|
if (statusCode >= 200 && statusCode < 300) { |
||||
|
return 'success'; |
||||
|
} |
||||
|
if (statusCode >= 300 && statusCode < 400) { |
||||
|
return 'processing'; |
||||
|
} |
||||
|
if (statusCode >= 400 && statusCode < 500) { |
||||
|
return 'warning'; |
||||
|
} |
||||
|
if (statusCode >= 500) { |
||||
|
return 'error'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using LINGYUN.Abp.MultiTenancy; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Saas.Tenants; |
||||
|
public class ConnectionStringInvalidator : |
||||
|
IDistributedEventHandler<ConnectionStringCreatedEventData>, |
||||
|
IDistributedEventHandler<ConnectionStringDeletedEventData>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
protected IDistributedCache<TenantCacheItem> Cache { get; } |
||||
|
|
||||
|
public ConnectionStringInvalidator(IDistributedCache<TenantCacheItem> cache) |
||||
|
{ |
||||
|
Cache = cache; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task HandleEventAsync(ConnectionStringCreatedEventData eventData) |
||||
|
{ |
||||
|
await Cache.RemoveAsync( |
||||
|
TenantCacheItem.CalculateCacheKey( |
||||
|
eventData.TenantId, |
||||
|
eventData.TenantName), |
||||
|
considerUow: true); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task HandleEventAsync(ConnectionStringDeletedEventData eventData) |
||||
|
{ |
||||
|
await Cache.RemoveAsync( |
||||
|
TenantCacheItem.CalculateCacheKey( |
||||
|
eventData.TenantId, |
||||
|
eventData.TenantName), |
||||
|
considerUow: true); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
{ |
||||
|
"Routes": [ |
||||
|
{ |
||||
|
"DownstreamPathTemplate": "/api/abp/api-definition", |
||||
|
"DownstreamScheme": "http", |
||||
|
"DownstreamHostAndPorts": [ |
||||
|
{ |
||||
|
"Host": "127.0.0.1", |
||||
|
"Port": 30045 |
||||
|
} |
||||
|
], |
||||
|
"UpstreamPathTemplate": "/api/abp/webhook/api-definition", |
||||
|
"UpstreamHttpMethod": [ "GET" ], |
||||
|
"LoadBalancerOptions": { |
||||
|
"Type": "RoundRobin" |
||||
|
}, |
||||
|
"RateLimitOptions": {}, |
||||
|
"QoSOptions": { |
||||
|
"ExceptionsAllowedBeforeBreaking": 10, |
||||
|
"DurationOfBreak": 1000, |
||||
|
"TimeoutValue": 10000 |
||||
|
}, |
||||
|
"HttpHandlerOptions": { |
||||
|
"UseTracing": true |
||||
|
}, |
||||
|
"Key": "webhook-api-definition" |
||||
|
}, |
||||
|
{ |
||||
|
"DownstreamPathTemplate": "/api/webhooks/{everything}", |
||||
|
"DownstreamScheme": "http", |
||||
|
"DownstreamHostAndPorts": [ |
||||
|
{ |
||||
|
"Host": "127.0.0.1", |
||||
|
"Port": 30045 |
||||
|
} |
||||
|
], |
||||
|
"UpstreamPathTemplate": "/api/webhooks/{everything}", |
||||
|
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ], |
||||
|
"LoadBalancerOptions": { |
||||
|
"Type": "RoundRobin" |
||||
|
}, |
||||
|
"RateLimitOptions": { |
||||
|
"ClientWhitelist": [], |
||||
|
"EnableRateLimiting": true, |
||||
|
"Period": "1s", |
||||
|
"PeriodTimespan": 1, |
||||
|
"Limit": 5 |
||||
|
}, |
||||
|
"QoSOptions": { |
||||
|
"ExceptionsAllowedBeforeBreaking": 10, |
||||
|
"DurationOfBreak": 1000, |
||||
|
"TimeoutValue": 10000 |
||||
|
}, |
||||
|
"HttpHandlerOptions": { |
||||
|
"UseTracing": true |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
Loading…
Reference in new issue