mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
feat(console): add webhook logs page (#3862)
This commit is contained in:
parent
b92508db3a
commit
af42e87bc0
41 changed files with 247 additions and 47 deletions
|
@ -1,25 +1,27 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Select from '@/components/Select';
|
||||
import Select, { type Option } from '@/components/Select';
|
||||
import { logEventTitle } from '@/consts/logs';
|
||||
|
||||
type Props = {
|
||||
value?: string;
|
||||
onChange: (value?: string) => void;
|
||||
options?: Array<Option<string>>;
|
||||
};
|
||||
|
||||
function EventSelector({ value, onChange }: Props) {
|
||||
const defaultEventOptions = Object.entries(logEventTitle).map(([value, title]) => ({
|
||||
value,
|
||||
title: title ?? value,
|
||||
}));
|
||||
|
||||
function EventSelector({ value, onChange, options }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const options = Object.entries(logEventTitle).map(([value, title]) => ({
|
||||
value,
|
||||
title: title ?? value,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Select
|
||||
isClearable
|
||||
value={value}
|
||||
options={options}
|
||||
options={options ?? defaultEventOptions}
|
||||
placeholder={t('logs.event')}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
|
||||
tbody {
|
||||
tr {
|
||||
cursor: default;
|
||||
|
||||
td {
|
||||
font: var(--font-body-2);
|
||||
border-top: 1px solid var(--color-divider);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font: var(--font-body-2);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ export enum ConnectorsTabs {
|
|||
|
||||
export enum WebhookDetailsTabs {
|
||||
Settings = 'settings',
|
||||
RecentRequests = 'recent-requests',
|
||||
}
|
||||
|
||||
export enum SignInExperiencePage {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import { HookEvent } from '@logto/schemas';
|
||||
import { HookEvent, type LogKey } from '@logto/schemas';
|
||||
import { yes } from '@silverhand/essentials';
|
||||
|
||||
type HookEventLabel = {
|
||||
|
@ -12,4 +12,14 @@ export const hookEventLabel = Object.freeze({
|
|||
[HookEvent.PostSignIn]: 'webhooks.events.post_sign_in',
|
||||
}) satisfies HookEventLabel;
|
||||
|
||||
type HookEventLogKey = {
|
||||
[key in HookEvent]: LogKey;
|
||||
};
|
||||
|
||||
export const hookEventLogKey = Object.freeze({
|
||||
[HookEvent.PostRegister]: 'TriggerHook.PostRegister',
|
||||
[HookEvent.PostResetPassword]: 'TriggerHook.PostResetPassword',
|
||||
[HookEvent.PostSignIn]: 'TriggerHook.PostSignIn',
|
||||
}) satisfies HookEventLogKey;
|
||||
|
||||
export const isHookFeatureEnabled = yes(process.env.HOOK_FEATURE_ENABLED);
|
||||
|
|
|
@ -40,6 +40,7 @@ import UserRoles from '@/pages/UserDetails/UserRoles';
|
|||
import UserSettings from '@/pages/UserDetails/UserSettings';
|
||||
import Users from '@/pages/Users';
|
||||
import WebhookDetails from '@/pages/WebhookDetails';
|
||||
import WebhookLogs from '@/pages/WebhookDetails/WebhookLogs';
|
||||
import WebhookSettings from '@/pages/WebhookDetails/WebhookSettings';
|
||||
import Webhooks from '@/pages/Webhooks';
|
||||
|
||||
|
@ -96,7 +97,12 @@ function ConsoleContent() {
|
|||
<Route path=":id" element={<WebhookDetails />}>
|
||||
<Route index element={<Navigate replace to={WebhookDetailsTabs.Settings} />} />
|
||||
<Route path={WebhookDetailsTabs.Settings} element={<WebhookSettings />} />
|
||||
<Route path={WebhookDetailsTabs.RecentRequests} element={<WebhookLogs />} />
|
||||
</Route>
|
||||
<Route
|
||||
path={`:hookId/${WebhookDetailsTabs.RecentRequests}/:logId`}
|
||||
element={<AuditLogDetails />}
|
||||
/>
|
||||
</Route>
|
||||
)}
|
||||
<Route path="users">
|
||||
|
@ -108,7 +114,10 @@ function ConsoleContent() {
|
|||
<Route path={UserDetailsTabs.Roles} element={<UserRoles />} />
|
||||
<Route path={UserDetailsTabs.Logs} element={<UserLogs />} />
|
||||
</Route>
|
||||
<Route path={`:id/${UserDetailsTabs.Logs}/:logId`} element={<AuditLogDetails />} />
|
||||
<Route
|
||||
path={`:userId/${UserDetailsTabs.Logs}/:logId`}
|
||||
element={<AuditLogDetails />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="audit-logs">
|
||||
<Route index element={<AuditLogs />} />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import type { User, Log } from '@logto/schemas';
|
||||
import type { User, Log, Hook } from '@logto/schemas';
|
||||
import { demoAppApplicationId } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
@ -16,6 +17,7 @@ import TabNav, { TabNavItem } from '@/components/TabNav';
|
|||
import UserName from '@/components/UserName';
|
||||
import { logEventTitle } from '@/consts/logs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import { getUserTitle } from '@/utils/user';
|
||||
|
||||
import EventIcon from './components/EventIcon';
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -27,18 +29,31 @@ const getDetailsTabNavLink = (logId: string, userId?: string) =>
|
|||
userId ? `/users/${userId}/logs/${logId}` : `/audit-logs/${logId}`;
|
||||
|
||||
function AuditLogDetails() {
|
||||
const { id, logId } = useParams();
|
||||
const { userId, hookId, logId } = useParams();
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data, error, mutate } = useSWR<Log, RequestError>(logId && `api/logs/${logId}`);
|
||||
const { data: userData } = useSWR<User, RequestError>(id && `api/users/${id}`);
|
||||
const { data: userData } = useSWR<User, RequestError>(userId && `api/users/${userId}`);
|
||||
const { data: hookData } = useSWR<Hook, RequestError>(hookId && `api/hooks/${hookId}`);
|
||||
|
||||
const isLoading = !data && !error;
|
||||
|
||||
const backLink = getAuditLogDetailsRelatedResourceLink(pathname);
|
||||
const backLinkTitle = id
|
||||
? t('log_details.back_to_user', { name: userData?.name ?? t('users.unnamed') })
|
||||
: t('log_details.back_to_logs');
|
||||
const backLinkTitle =
|
||||
conditional(
|
||||
userId &&
|
||||
t('log_details.back_to', {
|
||||
name: conditional(userData && getUserTitle(userData)) ?? t('users.unnamed'),
|
||||
})
|
||||
) ??
|
||||
conditional(
|
||||
hookId &&
|
||||
t('log_details.back_to', {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
name: hookData?.name || t('users.unnamed'),
|
||||
})
|
||||
) ??
|
||||
t('log_details.back_to_logs');
|
||||
|
||||
if (!logId) {
|
||||
return null;
|
||||
|
@ -105,7 +120,7 @@ function AuditLogDetails() {
|
|||
</div>
|
||||
</Card>
|
||||
<TabNav>
|
||||
<TabNavItem href={getDetailsTabNavLink(logId, id)}>
|
||||
<TabNavItem href={getDetailsTabNavLink(logId, userId)}>
|
||||
{t('log_details.tab_details')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.logs {
|
||||
flex: 1;
|
||||
margin-bottom: _.unit(6);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
color: var(--color-text-secondary);
|
||||
font: var(--font-body-2);
|
||||
}
|
||||
|
||||
.eventSelector {
|
||||
width: 300px;
|
||||
margin-left: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
126
packages/console/src/pages/WebhookDetails/WebhookLogs/index.tsx
Normal file
126
packages/console/src/pages/WebhookDetails/WebhookLogs/index.tsx
Normal file
|
@ -0,0 +1,126 @@
|
|||
import { type Log, HookEvent } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate, useOutletContext } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
import { z } from 'zod';
|
||||
|
||||
import EventSelector from '@/components/AuditLogTable/components/EventSelector';
|
||||
import DynamicT from '@/components/DynamicT';
|
||||
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
|
||||
import Table from '@/components/Table';
|
||||
import Tag from '@/components/Tag';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import { hookEventLabel, hookEventLogKey } from '@/consts/webhooks';
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import { buildUrl } from '@/utils/url';
|
||||
|
||||
import { type WebhookDetailsOutletContext } from '../types';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const hooLogEventOptions = Object.values(HookEvent).map((event) => ({
|
||||
title: <DynamicT forKey={hookEventLabel[event]} />,
|
||||
value: hookEventLogKey[event],
|
||||
}));
|
||||
|
||||
function WebhookLogs() {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
hook: { id },
|
||||
} = useOutletContext<WebhookDetailsOutletContext>();
|
||||
|
||||
const pageSize = defaultPageSize;
|
||||
const [{ page, event }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
event: '',
|
||||
startTimeExclusive: '',
|
||||
});
|
||||
|
||||
const url = buildUrl(`api/hooks/${id}/recent-logs`, {
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
...conditional(event && { logKey: event }),
|
||||
});
|
||||
|
||||
const { data, error, mutate } = useSWR<[Log[], number], RequestError>(url);
|
||||
const isLoading = !data && !error;
|
||||
const [logs, totalCount] = data ?? [];
|
||||
|
||||
return (
|
||||
<Table
|
||||
className={styles.logs}
|
||||
rowGroups={[{ key: 'logs', data: logs }]}
|
||||
rowIndexKey="id"
|
||||
rowClickHandler={({ id }) => {
|
||||
navigate(`${pathname}/${id}`);
|
||||
}}
|
||||
filter={
|
||||
<div className={styles.filter}>
|
||||
<div className={styles.title}>{t('logs.filter_by')}</div>
|
||||
<div className={styles.eventSelector}>
|
||||
<EventSelector
|
||||
value={event}
|
||||
options={hooLogEventOptions}
|
||||
onChange={(event) => {
|
||||
updateSearchParameters({ event, page: undefined });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
title: 'Status',
|
||||
dataIndex: 'status',
|
||||
colSpan: 5,
|
||||
render: ({ payload }) => {
|
||||
const result = z
|
||||
.object({ response: z.object({ statusCode: z.number().optional() }) })
|
||||
.optional()
|
||||
.safeParse(payload);
|
||||
const statusCode = result.success ? result.data?.response.statusCode : undefined;
|
||||
const isError = !statusCode || statusCode >= 400;
|
||||
return (
|
||||
<Tag type="result" status={isError ? 'error' : 'success'}>
|
||||
{statusCode ?? 'Request error'}
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('logs.event'),
|
||||
dataIndex: 'event',
|
||||
colSpan: 6,
|
||||
render: ({ key }) => {
|
||||
const event = Object.values(HookEvent).find((event) => hookEventLogKey[event] === key);
|
||||
return conditional(event && t(hookEventLabel[event])) ?? '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('logs.time'),
|
||||
dataIndex: 'time',
|
||||
colSpan: 5,
|
||||
render: ({ createdAt }) => new Date(createdAt).toLocaleString(),
|
||||
},
|
||||
]}
|
||||
placeholder={<EmptyDataPlaceholder />}
|
||||
pagination={{
|
||||
page,
|
||||
totalCount,
|
||||
pageSize,
|
||||
onChange: (page) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
errorMessage={error?.body?.message ?? error?.message}
|
||||
onRetry={async () => mutate(undefined, true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default WebhookLogs;
|
|
@ -1,5 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.containsTableLayout {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: _.unit(6);
|
||||
display: flex;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import { type Hook } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -32,6 +33,7 @@ import { type WebhookDetailsOutletContext } from './types';
|
|||
function WebhookDetails() {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { pathname } = useLocation();
|
||||
const isPageHasTable = pathname.endsWith(WebhookDetailsTabs.RecentRequests);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const { data, error, mutate } = useSWR<Hook, RequestError>(id && `api/hooks/${id}`);
|
||||
|
@ -93,6 +95,7 @@ function WebhookDetails() {
|
|||
<DetailsPage
|
||||
backLink="/webhooks"
|
||||
backLinkTitle="webhook_details.back_to_webhooks"
|
||||
className={classNames(isPageHasTable && styles.containsTableLayout)}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
onRetry={mutate}
|
||||
|
@ -178,6 +181,9 @@ function WebhookDetails() {
|
|||
<TabNavItem href={`/webhooks/${data.id}/${WebhookDetailsTabs.Settings}`}>
|
||||
<DynamicT forKey="webhook_details.settings_tab" />
|
||||
</TabNavItem>
|
||||
<TabNavItem href={`/webhooks/${data.id}/${WebhookDetailsTabs.RecentRequests}`}>
|
||||
<DynamicT forKey="webhook_details.recent_requests_tab" />
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<Outlet
|
||||
context={
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Audit Log Details',
|
||||
back_to_logs: 'Zurück zu Audit Logs',
|
||||
back_to_user: 'Zurück zu {{name}}',
|
||||
back_to: 'Zurück zu {{name}}',
|
||||
success: 'Erfolgreich',
|
||||
failed: 'Fehlgeschlagen',
|
||||
event_key: 'Event Schlüssel',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Sie entfernen diesen Webhook. Nach dem Löschen wird keine HTTP-Anforderung an die Endpunkt-URL gesendet.',
|
||||
deleted: 'Der Webhook wurde erfolgreich gelöscht.',
|
||||
settings_tab: 'Einstellungen',
|
||||
recent_requests_tab: 'Letzte Anforderungen',
|
||||
recent_requests_tab: 'Letzte Anforderungen (24h)',
|
||||
settings: {
|
||||
settings: 'Einstellungen',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Audit Log details',
|
||||
back_to_logs: 'Back to Audit Logs',
|
||||
back_to_user: 'Back to {{name}}',
|
||||
back_to: 'Back to {{name}}',
|
||||
success: 'Success',
|
||||
failed: 'Failed',
|
||||
event_key: 'Event Key',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'You are removing this webhook. After deleting it will not send HTTP request to endpoint URL.',
|
||||
deleted: 'The webhook has been successfully deleted.',
|
||||
settings_tab: 'Settings',
|
||||
recent_requests_tab: 'Recent requests',
|
||||
recent_requests_tab: 'Recent requests (24h)',
|
||||
settings: {
|
||||
settings: 'Settings',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Detalle del registro de auditoría',
|
||||
back_to_logs: 'Volver a los registros de auditoría',
|
||||
back_to_user: 'Volver a {{name}}',
|
||||
back_to: 'Volver a {{name}}',
|
||||
success: 'Éxito',
|
||||
failed: 'Fallido',
|
||||
event_key: 'Clave del evento',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Está eliminando este webhook. Después de eliminarlo, no se enviará ninguna solicitud HTTP a la URL de extremo.',
|
||||
deleted: 'El webhook se ha eliminado correctamente.',
|
||||
settings_tab: 'Configuración',
|
||||
recent_requests_tab: 'Solicitudes recientes',
|
||||
recent_requests_tab: 'Solicitudes recientes (24 h)',
|
||||
settings: {
|
||||
settings: 'Configuración',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: "Détails du journal d'audit",
|
||||
back_to_logs: "Retour aux journaux d'audit",
|
||||
back_to_user: 'Retour à {{name}}',
|
||||
back_to: 'Retour à {{name}}',
|
||||
success: 'Succès',
|
||||
failed: 'Échoué',
|
||||
event_key: "Clé d'événement",
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
"Vous êtes en train de supprimer ce webhook. Après suppression, il n'enverra plus de requête HTTP à l'endpoint URL.",
|
||||
deleted: 'Le webhook a été supprimé avec succès.',
|
||||
settings_tab: 'Paramètres',
|
||||
recent_requests_tab: 'Demandes récentes',
|
||||
recent_requests_tab: 'Demandes récentes (24 h)',
|
||||
settings: {
|
||||
settings: 'Paramètres',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Dettagli registro di verifica',
|
||||
back_to_logs: 'Torna ai log di verifica',
|
||||
back_to_user: 'Torna a {{name}}',
|
||||
back_to: 'Torna a {{name}}',
|
||||
success: 'Successo',
|
||||
failed: 'Fallito',
|
||||
event_key: 'Chiave evento',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
"Stai rimuovendo questo webhook. Dopo la cancellazione non invierà una richiesta HTTP all'URL dell'endpoint.",
|
||||
deleted: 'Il webhook è stato eliminato con successo.',
|
||||
settings_tab: 'Impostazioni',
|
||||
recent_requests_tab: 'Richieste recenti',
|
||||
recent_requests_tab: 'Richieste recenti (24h)',
|
||||
settings: {
|
||||
settings: 'Impostazioni',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: '監査ログの詳細',
|
||||
back_to_logs: '監査ログに戻る',
|
||||
back_to_user: '{{name}}に戻る',
|
||||
back_to: '{{name}}に戻る',
|
||||
success: '成功',
|
||||
failed: '失敗',
|
||||
event_key: 'イベントキー',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'このWebhookを削除しています。削除した後はエンドポイントURLにHTTPリクエストが送信されなくなります。',
|
||||
deleted: 'Webhookは削除されました。',
|
||||
settings_tab: '設定',
|
||||
recent_requests_tab: '最近のリクエスト',
|
||||
recent_requests_tab: '最近のリクエスト(24時間)',
|
||||
settings: {
|
||||
settings: '設定',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: '감사 로그 세부 정보',
|
||||
back_to_logs: '감사 기록으로 돌아가기',
|
||||
back_to_user: '{{name}}으로 돌아가기',
|
||||
back_to: '{{name}}으로 돌아가기',
|
||||
success: '성공',
|
||||
failed: '실패',
|
||||
event_key: '이벤트 키',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'이 webhook을 삭제하고 있습니다. 삭제 후, 엔드포인트 URL에 HTTP request를 보내지 않습니다.',
|
||||
deleted: 'Webhook이 성공적으로 삭제되었습니다.',
|
||||
settings_tab: '설정',
|
||||
recent_requests_tab: '최근 요청',
|
||||
recent_requests_tab: '최근 요청 (24시간)',
|
||||
settings: {
|
||||
settings: '설정',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Szczegóły dziennika audytu',
|
||||
back_to_logs: 'Powróć do dziennika audytu',
|
||||
back_to_user: 'Powróć do {{name}}',
|
||||
back_to: 'Powróć do {{name}}',
|
||||
success: 'Sukces',
|
||||
failed: 'Niepowodzenie',
|
||||
event_key: 'Klucz zdarzenia',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Usuwasz ten webhook. Po jego usunięciu nie będzie wysyłany żaden żądanie HTTP do adresu URL końcowego.',
|
||||
deleted: 'Webhook został pomyślnie usunięty.',
|
||||
settings_tab: 'Ustawienia',
|
||||
recent_requests_tab: 'Najnowsze żądania',
|
||||
recent_requests_tab: 'Najnowsze żądania (24h)',
|
||||
settings: {
|
||||
settings: 'Ustawienia',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Detalhes do registro de auditoria',
|
||||
back_to_logs: 'Voltar para registros',
|
||||
back_to_user: 'Voltar para {{name}}',
|
||||
back_to: 'Voltar para {{name}}',
|
||||
success: 'Sucesso',
|
||||
failed: 'Falhou',
|
||||
event_key: 'Chave do evento',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Você está removendo este webhook. Depois de excluí-lo, não enviará solicitação HTTP para o URL do endpoint.',
|
||||
deleted: 'O webhook foi excluído com sucesso.',
|
||||
settings_tab: 'Configurações',
|
||||
recent_requests_tab: 'Solicitações recentes',
|
||||
recent_requests_tab: 'Solicitações recentes (24h)',
|
||||
settings: {
|
||||
settings: 'Configurações',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Detalhes do registo de auditoria',
|
||||
back_to_logs: 'Voltar aos registos de auditoria',
|
||||
back_to_user: 'Voltar para {{name}}',
|
||||
back_to: 'Voltar para {{name}}',
|
||||
success: 'Sucesso',
|
||||
failed: 'Falha',
|
||||
event_key: 'Chave do Evento',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Você está removendo este webhook. Depois de excluí-lo, ele não enviará solicitação HTTP para o URL do endpoint.',
|
||||
deleted: 'O webhook foi excluído com sucesso.',
|
||||
settings_tab: 'Configurações',
|
||||
recent_requests_tab: 'Solicitações recentes',
|
||||
recent_requests_tab: 'Solicitações recentes (24h)',
|
||||
settings: {
|
||||
settings: 'Configurações',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Детали журнала аудита',
|
||||
back_to_logs: 'Назад к журналу аудита',
|
||||
back_to_user: 'Назад к {{name}}',
|
||||
back_to: 'Назад к {{name}}',
|
||||
success: 'Успешно',
|
||||
failed: 'Не удалось',
|
||||
event_key: 'Ключ события',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Вы удаляете этот вебхук. После удаления он не будет отправлять HTTP-запрос на URL-адрес точки доступа.',
|
||||
deleted: 'Вебхук был успешно удален.',
|
||||
settings_tab: 'Настройки',
|
||||
recent_requests_tab: 'Недавние запросы',
|
||||
recent_requests_tab: 'Недавние запросы (за 24ч)',
|
||||
settings: {
|
||||
settings: 'Настройки',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: 'Denetim Kaydı Detayları',
|
||||
back_to_logs: 'Denetim Kayıtlarına Geri Dön',
|
||||
back_to_user: "{{name}}'in Kayıtlarına Geri Dön",
|
||||
back_to: "{{name}}'in Kayıtlarına Geri Dön",
|
||||
success: 'Başarılı',
|
||||
failed: 'Başarısız',
|
||||
event_key: 'Olay Anahtarı',
|
||||
|
|
|
@ -15,7 +15,7 @@ const webhook_details = {
|
|||
'Bu webhook’u kaldırıyorsunuz. Silindikten sonra, HTTP isteği uç nokta URL’ye gönderilmeyecektir.',
|
||||
deleted: 'Webhook başarıyla silindi.',
|
||||
settings_tab: 'Ayarlar',
|
||||
recent_requests_tab: 'Son istekler',
|
||||
recent_requests_tab: 'Son istekler (24s)',
|
||||
settings: {
|
||||
settings: 'Ayarlar',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: '审计日志详情',
|
||||
back_to_logs: '返回审计日志',
|
||||
back_to_user: '返回 {{name}}',
|
||||
back_to: '返回 {{name}}',
|
||||
success: '成功',
|
||||
failed: '失败',
|
||||
event_key: '事件 Key',
|
||||
|
|
|
@ -13,7 +13,7 @@ const webhook_details = {
|
|||
deletion_reminder: '您正在删除此 Webhook。删除后,将不会向端点 URL 发送 HTTP 请求。',
|
||||
deleted: 'Webhook 已成功删除。',
|
||||
settings_tab: '设置',
|
||||
recent_requests_tab: '最近请求',
|
||||
recent_requests_tab: '最近请求(24小时)',
|
||||
settings: {
|
||||
settings: '设置',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: '審計日誌詳情',
|
||||
back_to_logs: '返回審計日誌',
|
||||
back_to_user: '返回 {{name}}',
|
||||
back_to: '返回 {{name}}',
|
||||
success: '成功',
|
||||
failed: '失敗',
|
||||
event_key: '事件 Key',
|
||||
|
|
|
@ -13,7 +13,7 @@ const webhook_details = {
|
|||
deletion_reminder: '您正在刪除此 webhook。刪除後,不會對端點 URL 發送 HTTP 請求。',
|
||||
deleted: 'Webhook 已成功刪除。',
|
||||
settings_tab: '設置',
|
||||
recent_requests_tab: '最新請求',
|
||||
recent_requests_tab: '最新請求(24h)',
|
||||
settings: {
|
||||
settings: '設置',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const log_details = {
|
||||
page_title: '審計日誌詳情',
|
||||
back_to_logs: '返回審計日誌',
|
||||
back_to_user: '返回 {{name}}',
|
||||
back_to: '返回 {{name}}',
|
||||
success: '成功',
|
||||
failed: '失敗',
|
||||
event_key: '事件 Key',
|
||||
|
|
|
@ -13,7 +13,7 @@ const webhook_details = {
|
|||
deletion_reminder: '您正在移除此 Webhook。刪除後,將不會向端點 URL 發送 HTTP 請求。',
|
||||
deleted: 'Webhook 已成功刪除。',
|
||||
settings_tab: '設置',
|
||||
recent_requests_tab: '最近的請求',
|
||||
recent_requests_tab: '最近的請求 (24h)',
|
||||
settings: {
|
||||
settings: '設置',
|
||||
settings_description:
|
||||
|
|
Loading…
Add table
Reference in a new issue