From 6d8f3005b0e2a86173637b0d104dcf61300051bf Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Fri, 10 Apr 2026 13:46:40 -0700 Subject: [PATCH] feat: improve Dozzle Cloud discoverability (#4609) Co-authored-by: Claude Sonnet 4.6 --- assets/auto-imports.d.ts | 2 + assets/components.d.ts | 4 +- assets/components/CloudPopover.vue | 141 ++++++++++ assets/components/CloudSettingsCard.vue | 151 +++++++++++ assets/components/Links.vue | 2 + .../Notification/CloudDestinationForm.vue | 30 +-- .../Notification/DestinationCard.vue | 2 +- .../Notification/DestinationForm.vue | 72 +++-- assets/composable/alertForm.ts | 6 +- assets/composable/cloudConfig.ts | 56 ++++ assets/pages/notifications.vue | 57 ++-- assets/pages/settings.vue | 7 + assets/types/notifications.ts | 12 + internal/agent/client.go | 20 +- internal/agent/client_test.go | 4 + internal/agent/pb/rpc.pb.go | 251 ++++++++++++------ internal/agent/pb/rpc_grpc.pb.go | 38 +++ internal/agent/pb/types.pb.go | 111 +++++--- internal/agent/server.go | 30 ++- internal/migration/cloud_config.go | 124 +++++++++ internal/notification/cloud_config.go | 33 +++ internal/notification/config.go | 56 ++-- internal/notification/log_listener.go | 2 +- internal/notification/manager.go | 51 +++- internal/notification/processing.go | 6 +- internal/notification/types.go | 16 +- internal/support/cli/agent_command.go | 56 ++++ internal/support/container/agent_service.go | 4 + internal/support/docker/multi_host_service.go | 177 ++++++++++-- internal/support/k8s/k8s_cluster_service.go | 12 + internal/web/cloud.go | 72 +++-- internal/web/notifications.go | 30 +-- internal/web/routes.go | 5 + locales/da.yml | 21 +- locales/de.yml | 21 +- locales/en.yml | 23 +- locales/es.yml | 21 +- locales/fr.yml | 21 +- locales/id.yml | 21 +- locales/it.yml | 21 +- locales/ko.yml | 21 +- locales/nl.yml | 21 +- locales/pl.yml | 21 +- locales/pr.yml | 21 +- locales/pt.yml | 21 +- locales/ru.yml | 21 +- locales/sl.yml | 21 +- locales/tr.yml | 21 +- locales/zh-tw.yml | 21 +- locales/zh.yml | 21 +- main.go | 6 +- protos/rpc.proto | 7 + protos/types.proto | 11 +- types/notification.go | 20 +- 54 files changed, 1659 insertions(+), 384 deletions(-) create mode 100644 assets/components/CloudPopover.vue create mode 100644 assets/components/CloudSettingsCard.vue create mode 100644 assets/composable/cloudConfig.ts create mode 100644 internal/migration/cloud_config.go create mode 100644 internal/notification/cloud_config.go diff --git a/assets/auto-imports.d.ts b/assets/auto-imports.d.ts index 1846b0e9..7a06a0dd 100644 --- a/assets/auto-imports.d.ts +++ b/assets/auto-imports.d.ts @@ -205,6 +205,7 @@ declare global { const useClipboard: typeof import('@vueuse/core').useClipboard const useClipboardItems: typeof import('@vueuse/core').useClipboardItems const useCloned: typeof import('@vueuse/core').useCloned + const useCloudConfig: typeof import('./composable/cloudConfig').useCloudConfig const useColorMode: typeof import('@vueuse/core').useColorMode const useConfirmDialog: typeof import('@vueuse/core').useConfirmDialog const useContainerActions: typeof import('./composable/containerActions').useContainerActions @@ -634,6 +635,7 @@ declare module 'vue' { readonly useClipboard: UnwrapRef readonly useClipboardItems: UnwrapRef readonly useCloned: UnwrapRef + readonly useCloudConfig: UnwrapRef readonly useColorMode: UnwrapRef readonly useConfirmDialog: UnwrapRef readonly useContainerActions: UnwrapRef diff --git a/assets/components.d.ts b/assets/components.d.ts index ee23ed9d..d83c235c 100644 --- a/assets/components.d.ts +++ b/assets/components.d.ts @@ -36,6 +36,8 @@ declare module 'vue' { 'Cil:columns': typeof import('~icons/cil/columns')['default'] 'Cil:xCircle': typeof import('~icons/cil/x-circle')['default'] CloudDestinationForm: typeof import('./components/Notification/CloudDestinationForm.vue')['default'] + CloudPopover: typeof import('./components/CloudPopover.vue')['default'] + CloudSettingsCard: typeof import('./components/CloudSettingsCard.vue')['default'] ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default'] ContainerActionsToolbar: typeof import('./components/ContainerViewer/ContainerActionsToolbar.vue')['default'] ContainerDropdown: typeof import('./components/ContainerDropdown.vue')['default'] @@ -102,7 +104,6 @@ declare module 'vue' { 'Mdi:chartBar': typeof import('~icons/mdi/chart-bar')['default'] 'Mdi:chartLine': typeof import('~icons/mdi/chart-line')['default'] 'Mdi:check': typeof import('~icons/mdi/check')['default'] - 'Mdi:checkCircle': typeof import('~icons/mdi/check-circle')['default'] 'Mdi:chevronDoubleDown': typeof import('~icons/mdi/chevron-double-down')['default'] 'Mdi:chevronDown': typeof import('~icons/mdi/chevron-down')['default'] 'Mdi:chevronLeft': typeof import('~icons/mdi/chevron-left')['default'] @@ -121,6 +122,7 @@ declare module 'vue' { 'Mdi:keyboardEsc': typeof import('~icons/mdi/keyboard-esc')['default'] 'Mdi:lightningBolt': typeof import('~icons/mdi/lightning-bolt')['default'] 'Mdi:linkVariant': typeof import('~icons/mdi/link-variant')['default'] + 'Mdi:linkVariantOff': typeof import('~icons/mdi/link-variant-off')['default'] 'Mdi:magnify': typeof import('~icons/mdi/magnify')['default'] 'Mdi:pencilOutline': typeof import('~icons/mdi/pencil-outline')['default'] 'Mdi:plus': typeof import('~icons/mdi/plus')['default'] diff --git a/assets/components/CloudPopover.vue b/assets/components/CloudPopover.vue new file mode 100644 index 00000000..b0a90a54 --- /dev/null +++ b/assets/components/CloudPopover.vue @@ -0,0 +1,141 @@ + + + diff --git a/assets/components/CloudSettingsCard.vue b/assets/components/CloudSettingsCard.vue new file mode 100644 index 00000000..332088f4 --- /dev/null +++ b/assets/components/CloudSettingsCard.vue @@ -0,0 +1,151 @@ + + + diff --git a/assets/components/Links.vue b/assets/components/Links.vue index e9fc6e2b..6d97270e 100644 --- a/assets/components/Links.vue +++ b/assets/components/Links.vue @@ -12,6 +12,8 @@ + + (); const callbackUrl = `${window.location.origin}${withBase("/")}`; -const cloudLinkUrl = `${__CLOUD_URL__}/link?appUrl=${encodeURIComponent(callbackUrl)}`; +const cloudLinkUrl = `${__CLOUD_URL__}/link?appUrl=${encodeURIComponent(callbackUrl)}&from=notifications`; const cloudSettingsUrl = `${__CLOUD_URL__}/settings`; -// Cloud status -interface CloudStatus { - user: { email: string; name: string }; - plan: { name: string; events_per_month: number; retention_days: number }; - usage: { events_used: number; events_limit: number; period: string }; -} - -const cloudStatus = ref(null); -const cloudStatusError = ref(false); -const isLoadingCloudStatus = ref(false); +const { cloudStatus, cloudStatusError, isLoadingCloudStatus, fetchCloudStatus } = useCloudConfig(); const usagePercent = computed(() => { if (!cloudStatus.value) return 0; return (cloudStatus.value.usage.events_used / cloudStatus.value.usage.events_limit) * 100; }); -async function fetchCloudStatus() { - isLoadingCloudStatus.value = true; - cloudStatusError.value = false; - try { - const res = await fetch(withBase("/api/cloud/status")); - if (!res.ok) { - cloudStatusError.value = true; - return; - } - cloudStatus.value = await res.json(); - } catch { - cloudStatusError.value = true; - } finally { - isLoadingCloudStatus.value = false; - } -} - if (destination?.prefix) { fetchCloudStatus(); } diff --git a/assets/components/Notification/DestinationCard.vue b/assets/components/Notification/DestinationCard.vue index 5e863d62..0bd4aa03 100644 --- a/assets/components/Notification/DestinationCard.vue +++ b/assets/components/Notification/DestinationCard.vue @@ -27,7 +27,7 @@
  • {{ $t("notifications.destination.edit") }}
  • -
  • +
  • {{ $t("notifications.destination.delete") }}
  • diff --git a/assets/components/Notification/DestinationForm.vue b/assets/components/Notification/DestinationForm.vue index 7a13fb2b..5677898b 100644 --- a/assets/components/Notification/DestinationForm.vue +++ b/assets/components/Notification/DestinationForm.vue @@ -2,30 +2,33 @@

    - {{ - isEditing - ? $t("notifications.destination-form.edit-title") - : $t("notifications.destination-form.create-title") - }} + +

    -

    {{ $t("notifications.destination-form.description") }}

    +

    + + +

    - -
    - -
    -
    {{ $t("notifications.cloud-link-success.title") }}
    -
    {{ $t("notifications.cloud-link-success.message") }}
    -
    -
    - - -
    + +
    {{ $t("notifications.destination-form.type") }}