Skip to content

Commit f62ab0e

Browse files
committed
feat: update webhook settings with secret management
- Bump version to 1.1.5 in package.json - Add API endpoint to retrieve webhook secrets based on target (inbound/outbound) - Implement AppToaster component for consistent toast notifications across admin pages - Update AdminLayout to include AppToaster - Enhance webhook settings UI with reveal/hide functionality for secrets - Add new translations for webhook secret management in English and Turkish - Refactor settings page hooks to manage secret visibility and dirty state - Update OpenAPI documentation to reflect new webhook secret retrieval endpoint - Modify WebhookService to include method for fetching stored secrets
1 parent e176473 commit f62ab0e

23 files changed

Lines changed: 350 additions & 149 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "booking-calendar",
3-
"version": "1.1.4",
3+
"version": "1.1.5",
44
"private": true,
55
"scripts": {
66
"dev:server": "bun run --watch src/server/index.ts",

src/client/src/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ export const api = {
504504
"/admin/settings/webhook",
505505
).then((r) => r.data),
506506

507+
getWebhookSecret: (target: "outbound" | "inbound") =>
508+
request<{ success: boolean; data: { secret: string } }>(
509+
`/admin/settings/webhook/secret?target=${target}`,
510+
).then((r) => r.data.secret),
511+
507512
setWebhookSettings: (input: {
508513
outbound?: {
509514
enabled?: boolean;

src/client/src/components/AdminLayout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useState } from "react";
1616
import { Outlet, useLocation, useNavigate } from "react-router-dom";
1717
import { useAuth } from "../context/AuthContext";
1818
import { useI18n } from "../context/I18nContext";
19+
import AppToaster from "./AppToaster";
1920

2021
export default function AdminLayout() {
2122
const [css] = useStyletron();
@@ -53,6 +54,8 @@ export default function AdminLayout() {
5354
backgroundColor: "var(--color-bg-primary)",
5455
})}
5556
>
57+
<AppToaster />
58+
5659
{/* Sidebar */}
5760
<nav
5861
className={css({
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PLACEMENT, ToasterContainer } from "baseui/toast";
2+
3+
const TOAST_AUTO_HIDE_DURATION = 5000;
4+
5+
export default function AppToaster() {
6+
return (
7+
<ToasterContainer
8+
placement={PLACEMENT.bottomRight}
9+
autoHideDuration={TOAST_AUTO_HIDE_DURATION}
10+
/>
11+
);
12+
}

src/client/src/i18n/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,10 @@
261261
"webhookSecretHint": "Signature header format: sha256=<hmac>. Minimum 16 characters.",
262262
"webhookSecretPlaceholder": "Paste a long random secret",
263263
"webhookGenerateSecret": "Generate Random Secret",
264+
"webhookRevealSecret": "Reveal",
265+
"webhookHideSecret": "Hide",
264266
"webhookSecretConfigured": "A secret is already configured. Leave empty to keep it unchanged.",
267+
"webhookSecretHiddenHint": "Stored secrets are hidden after refresh for security. Enter a new value only if you want to replace it.",
265268
"webhookSave": "Save Webhook Settings",
266269
"webhookSaved": "Webhook settings saved",
267270
"webhookSendTest": "Send Test Event",
@@ -276,6 +279,7 @@
276279
"webhookInboundSecretHint": "Use the same sha256=<hmac> header format based on timestamp.body. Minimum 16 characters.",
277280
"webhookInboundSecretPlaceholder": "Paste the secret used by the external command sender",
278281
"webhookInboundSecretConfigured": "An inbound secret is already configured. Leave empty to keep it unchanged.",
282+
"webhookInboundSecretHiddenHint": "Stored inbound secrets are hidden after refresh for security. Enter a new value only if you want to replace it.",
279283
"webhookInboundScopes": "Allowed Scopes",
280284
"webhookInboundScopesHint": "Only the checked action groups can be executed by inbound webhook commands.",
281285
"version": "Version",

src/client/src/i18n/schema.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,18 @@
993993
"webhookGenerateSecret": {
994994
"type": "string"
995995
},
996+
"webhookRevealSecret": {
997+
"type": "string"
998+
},
999+
"webhookHideSecret": {
1000+
"type": "string"
1001+
},
9961002
"webhookSecretConfigured": {
9971003
"type": "string"
9981004
},
1005+
"webhookSecretHiddenHint": {
1006+
"type": "string"
1007+
},
9991008
"webhookSave": {
10001009
"type": "string"
10011010
},
@@ -1038,6 +1047,9 @@
10381047
"webhookInboundSecretConfigured": {
10391048
"type": "string"
10401049
},
1050+
"webhookInboundSecretHiddenHint": {
1051+
"type": "string"
1052+
},
10411053
"webhookInboundScopes": {
10421054
"type": "string"
10431055
},
@@ -1211,7 +1223,10 @@
12111223
"webhookSecretHint",
12121224
"webhookSecretPlaceholder",
12131225
"webhookGenerateSecret",
1226+
"webhookRevealSecret",
1227+
"webhookHideSecret",
12141228
"webhookSecretConfigured",
1229+
"webhookSecretHiddenHint",
12151230
"webhookSave",
12161231
"webhookSaved",
12171232
"webhookSendTest",
@@ -1226,6 +1241,7 @@
12261241
"webhookInboundSecretHint",
12271242
"webhookInboundSecretPlaceholder",
12281243
"webhookInboundSecretConfigured",
1244+
"webhookInboundSecretHiddenHint",
12291245
"webhookInboundScopes",
12301246
"webhookInboundScopesHint",
12311247
"version",

src/client/src/i18n/tr.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,10 @@
261261
"webhookSecretHint": "Imza basligi formati: sha256=<hmac>. Minimum 16 karakter.",
262262
"webhookSecretPlaceholder": "Uzun ve rastgele bir gizli anahtar girin",
263263
"webhookGenerateSecret": "Rastgele Gizli Anahtar Uret",
264+
"webhookRevealSecret": "Goster",
265+
"webhookHideSecret": "Gizle",
264266
"webhookSecretConfigured": "Bir gizli anahtar zaten kayıtlı. Aynı kalması için boş bırakın.",
267+
"webhookSecretHiddenHint": "Kayitli secret guvenlik nedeniyle sayfa yenilendikten sonra gosterilmez. Yalnizca degistirmek istiyorsaniz yeni bir deger girin.",
265268
"webhookSave": "Webhook Ayarlarını Kaydet",
266269
"webhookSaved": "Webhook ayarları kaydedildi",
267270
"webhookSendTest": "Test Olayı Gönder",
@@ -276,6 +279,7 @@
276279
"webhookInboundSecretHint": "timestamp.body tabanlı sha256=<hmac> başlık formatını kullanın. Minimum 16 karakter.",
277280
"webhookInboundSecretPlaceholder": "Harici komut göndericisinin kullanacağı gizli anahtarı girin",
278281
"webhookInboundSecretConfigured": "Bir inbound secret zaten kayıtlı. Aynı kalması için boş bırakın.",
282+
"webhookInboundSecretHiddenHint": "Kayitli inbound secret guvenlik nedeniyle sayfa yenilendikten sonra gosterilmez. Yalnizca degistirmek istiyorsaniz yeni bir deger girin.",
279283
"webhookInboundScopes": "İzinli Scope'lar",
280284
"webhookInboundScopesHint": "Yalnızca işaretlenen aksiyon grupları inbound webhook komutları tarafından çalıştırılabilir.",
281285
"version": "Sürüm",

src/client/src/pages/AppointmentDetailPage/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { useStyletron } from "baseui";
22
import { Button, KIND, SIZE } from "baseui/button";
3-
import { PLACEMENT, ToasterContainer, toaster } from "baseui/toast";
3+
import { toaster } from "baseui/toast";
44
import { Copy } from "lucide-react";
55
import { useCallback, useEffect, useMemo, useState } from "react";
66
import { useParams } from "react-router-dom";
77
import { type ApiAppointment, api } from "../../api";
8+
import AppToaster from "../../components/AppToaster";
89
import ConfirmationDialog from "../../components/ConfirmationDialog";
910
import { useI18n } from "../../context/I18nContext";
1011

@@ -119,7 +120,7 @@ export default function AppointmentDetailPage() {
119120
justifyContent: "center",
120121
})}
121122
>
122-
<ToasterContainer placement={PLACEMENT.bottomRight} />
123+
<AppToaster />
123124

124125
<div
125126
className={css({

src/client/src/pages/AppointmentsPage/index.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useStyletron } from "baseui";
2-
import { PLACEMENT, ToasterContainer } from "baseui/toast";
32
import ListFiltersBar from "../../components/ListFilters/ListFiltersBar";
43
import ListFiltersFeedback from "../../components/ListFilters/ListFiltersFeedback";
54
import PageLoadingSpinner from "../../components/PageLoadingSpinner";
@@ -47,8 +46,6 @@ export default function AppointmentsPage() {
4746

4847
return (
4948
<div>
50-
<ToasterContainer placement={PLACEMENT.bottomRight} />
51-
5249
<AppointmentsHeader
5350
title={t("appointments.title")}
5451
description={t("appointments.description")}

src/client/src/pages/BookingPage/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useStyletron } from "baseui";
2-
import { PLACEMENT, ToasterContainer } from "baseui/toast";
32
import { useEffect } from "react";
43
import { useNavigate, useParams } from "react-router-dom";
4+
import AppToaster from "../../components/AppToaster";
55
import { useI18n } from "../../context/I18nContext";
66
import BookingFormSection from "./components/BookingFormSection";
77
import BookingHeader from "./components/BookingHeader";
@@ -79,7 +79,7 @@ export default function BookingPage() {
7979
justifyContent: "center",
8080
})}
8181
>
82-
<ToasterContainer placement={PLACEMENT.bottomRight} />
82+
<AppToaster />
8383

8484
<div
8585
className={css({

0 commit comments

Comments
 (0)