diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 461fe61b..912f7e13 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -33,7 +33,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
- go-version: 1.26.0
+ go-version: 1.26.1
check-latest: true
- name: Checkout code
uses: actions/checkout@v6
@@ -63,13 +63,13 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
+ uses: docker/setup-buildx-action@v4
- name: Writing certs to file
run: |
echo "${{ secrets.TTL_KEY }}" > shared_key.pem
echo "${{ secrets.TTL_CERT }}" > shared_cert.pem
- name: Build
- uses: docker/bake-action@v6
+ uses: docker/bake-action@v7
with:
source: .
load: true
@@ -93,14 +93,14 @@ jobs:
packages: write
steps:
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
+ uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to DockerHub
- uses: docker/login-action@v3.7.0
+ uses: docker/login-action@v4.0.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
- uses: docker/login-action@v3.7.0
+ uses: docker/login-action@v4.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -109,7 +109,7 @@ jobs:
uses: actions/checkout@v6
- name: Docker meta
id: meta
- uses: docker/metadata-action@v5
+ uses: docker/metadata-action@v6
with:
images: |
amir20/dozzle
@@ -124,7 +124,7 @@ jobs:
echo "${{ secrets.TTL_KEY }}" > shared_key.pem
echo "${{ secrets.TTL_CERT }}" > shared_cert.pem
- name: Build and push
- uses: docker/build-push-action@v6.19.2
+ uses: docker/build-push-action@v7.0.0
with:
push: true
context: .
diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
index 082c636d..8cd85711 100644
--- a/.github/workflows/dev.yml
+++ b/.github/workflows/dev.yml
@@ -18,14 +18,14 @@ jobs:
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == 'amir20/dozzle') }}
steps:
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
+ uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to DockerHub
- uses: docker/login-action@v3.7.0
+ uses: docker/login-action@v4.0.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
- uses: docker/login-action@v3.7.0
+ uses: docker/login-action@v4.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v6
- name: Docker meta
id: meta
- uses: docker/metadata-action@v5
+ uses: docker/metadata-action@v6
with:
images: |
amir20/dozzle
@@ -44,7 +44,7 @@ jobs:
echo "${{ secrets.TTL_KEY }}" > shared_key.pem
echo "${{ secrets.TTL_CERT }}" > shared_cert.pem
- name: Build and push
- uses: docker/build-push-action@v6.19.2
+ uses: docker/build-push-action@v7.0.0
with:
context: .
push: true
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ceb3fb65..126b4590 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -58,7 +58,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
- go-version: "1.26.0"
+ go-version: "1.26.1"
check-latest: true
- name: Checkout code
uses: actions/checkout@v6
@@ -81,12 +81,12 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
- go-version: "1.26.0"
+ go-version: "1.26.1"
check-latest: true
- name: Generate dependencies
run: make fake_assets shared_key.pem shared_cert.pem
- name: Stactic checker
- uses: dominikh/staticcheck-action@v1.4.0
+ uses: dominikh/staticcheck-action@v1.4.1
with:
install-go: false
int-test:
@@ -111,11 +111,11 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
+ uses: docker/setup-buildx-action@v4
- name: Generate certs
run: make shared_key.pem shared_cert.pem
- name: Build
- uses: docker/bake-action@v6
+ uses: docker/bake-action@v7
with:
source: .
load: true
diff --git a/Dockerfile b/Dockerfile
index cbf2e7dd..2d8ff178 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Build assets
-FROM --platform=$BUILDPLATFORM node:25.7.0-alpine AS node
+FROM --platform=$BUILDPLATFORM node:25.8.0-alpine AS node
RUN npm install -g --force corepack && corepack enable
diff --git a/assets/components.d.ts b/assets/components.d.ts
index e3fdba68..8572cf58 100644
--- a/assets/components.d.ts
+++ b/assets/components.d.ts
@@ -15,8 +15,10 @@ declare module 'vue' {
AlertForm: typeof import('./components/Notification/AlertForm.vue')['default']
Announcements: typeof import('./components/Announcements.vue')['default']
BarChart: typeof import('./components/BarChart.vue')['default']
+ 'Carbon:add': typeof import('~icons/carbon/add')['default']
'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default']
'Carbon:circleSolid': typeof import('~icons/carbon/circle-solid')['default']
+ 'Carbon:close': typeof import('~icons/carbon/close')['default']
'Carbon:information': typeof import('~icons/carbon/information')['default']
'Carbon:logoKubernetes': typeof import('~icons/carbon/logo-kubernetes')['default']
'Carbon:macShift': typeof import('~icons/carbon/mac-shift')['default']
@@ -59,7 +61,6 @@ declare module 'vue' {
HostList: typeof import('./components/HostList.vue')['default']
HostLog: typeof import('./components/HostViewer/HostLog.vue')['default']
HostMenu: typeof import('./components/HostMenu.vue')['default']
- 'Ic:baselineFiberNew': typeof import('~icons/ic/baseline-fiber-new')['default']
'Ic:sharpKeyboardReturn': typeof import('~icons/ic/sharp-keyboard-return')['default']
IndeterminateBar: typeof import('./components/common/IndeterminateBar.vue')['default']
'Ion:ellipsisVertical': typeof import('~icons/ion/ellipsis-vertical')['default']
@@ -107,7 +108,6 @@ declare module 'vue' {
'Mdi:cloudOutline': typeof import('~icons/mdi/cloud-outline')['default']
'Mdi:cog': typeof import('~icons/mdi/cog')['default']
'Mdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
- 'Mdi:creation': typeof import('~icons/mdi/creation')['default']
'Mdi:docker': typeof import('~icons/mdi/docker')['default']
'Mdi:gauge': typeof import('~icons/mdi/gauge')['default']
'Mdi:github': typeof import('~icons/mdi/github')['default']
diff --git a/assets/components/Notification/WebhookDestinationForm.vue b/assets/components/Notification/WebhookDestinationForm.vue
index 5e58d02d..57cce12b 100644
--- a/assets/components/Notification/WebhookDestinationForm.vue
+++ b/assets/components/Notification/WebhookDestinationForm.vue
@@ -57,6 +57,43 @@
>
+
+
+
{{ error }}
@@ -110,6 +147,12 @@ useFocus(nameInput, { initialValue: true });
const webhookUrl = ref(destination?.url ?? "");
const payloadFormat = ref
(isEditing ? "custom" : "slack");
const template = ref(isEditing ? (destination?.template ?? "") : PAYLOAD_TEMPLATES[payloadFormat.value]);
+let headerKeyCounter = 0;
+const headers = ref<{ name: string; value: string; key: number }[]>(
+ destination?.headers
+ ? Object.entries(destination.headers).map(([name, value]) => ({ name, value, key: headerKeyCounter++ }))
+ : [],
+);
const isTesting = ref(false);
const isSaving = ref(false);
const error = ref(null);
@@ -143,6 +186,12 @@ onScopeDispose(() => {
templateEditorView?.destroy();
});
+function headersToRecord(): Record | undefined {
+ const filtered = headers.value.filter((h) => h.name.trim() && h.value.trim());
+ if (filtered.length === 0) return undefined;
+ return Object.fromEntries(filtered.map((h) => [h.name.trim(), h.value.trim()]));
+}
+
const canTest = computed(() => webhookUrl.value.trim().length > 0);
const isValidUrl = computed(() => {
@@ -174,6 +223,7 @@ async function testDestination() {
body: JSON.stringify({
url: webhookUrl.value.trim(),
template: template.value.trim() || undefined,
+ headers: headersToRecord(),
}),
});
@@ -198,6 +248,7 @@ async function saveDestination() {
type: "webhook",
url: webhookUrl.value.trim(),
template: template.value.trim() || undefined,
+ headers: headersToRecord(),
};
const url = isEditing
diff --git a/assets/types/notifications.ts b/assets/types/notifications.ts
index 8283272d..37c7381d 100644
--- a/assets/types/notifications.ts
+++ b/assets/types/notifications.ts
@@ -19,6 +19,7 @@ export interface Dispatcher {
type: string;
url?: string;
template?: string;
+ headers?: Record;
prefix?: string;
expiresAt?: string;
}
diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md
index b1b722e2..09af81eb 100644
--- a/docs/guide/getting-started.md
+++ b/docs/guide/getting-started.md
@@ -16,7 +16,7 @@ The easiest way to set up Dozzle is to use the CLI and mount `docker.sock` file.
::: code-group
```sh
-docker run -d -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle:latest
+docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v dozzle_data:/data -p 8080:8080 amir20/dozzle:latest
```
```yaml [docker-compose.yml]
@@ -26,6 +26,7 @@ services:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
+ - dozzle_data:/data
ports:
- 8080:8080
environment:
@@ -37,6 +38,8 @@ services:
#
# Uncomment to enable authentication. See https://dozzle.dev/guide/authentication
# - DOZZLE_AUTH_PROVIDER=simple
+volumes:
+ dozzle_data:
```
:::
@@ -45,6 +48,9 @@ services:
> Dozzle supports actions, such as stopping, starting, and restarting containers, or attaching to container shells. But they are disabled by default for security reasons. To enable them, uncomment the corresponding environment variables.
> Dozzle also supports connecting to remote agents to monitor multiple Docker hosts. See [agent](/guide/agent) to learn more.
+> [!IMPORTANT]
+> Dozzle stores notification settings and other data in `/data` inside the container. To persist these settings across container restarts, you need to mount a volume to `/data`. Without this mount, notification settings will be lost when the container is recreated. See the Docker Compose example above for the recommended volume configuration.
+
## Docker Swarm
Dozzle supports running in Swarm mode by deploying it on every node. To run Dozzle in Swarm mode, you can use the following configuration:
diff --git a/docs/guide/k8s.md b/docs/guide/k8s.md
index 9fa405a8..9a2712be 100644
--- a/docs/guide/k8s.md
+++ b/docs/guide/k8s.md
@@ -84,6 +84,8 @@ spec:
```
This configuration creates a service account, a cluster role, and a cluster role binding to allow Dozzle to access the necessary Kubernetes resources. It also creates a deployment for Dozzle and exposes it via a service.
+> [!WARNING]
+> When deploying this with any GitOps tool (like Flux CD or Argo CD) in a specific namespace apart from `default`, make sure to change the **namespace** in the **ClusterRoleBinding Subject**
All other features are supported as well, including authentication, filtering, and more. You can use the same environment variables as you would in Docker to configure Dozzle in Kubernetes.
diff --git a/go.mod b/go.mod
index 90cf93b1..41e15465 100644
--- a/go.mod
+++ b/go.mod
@@ -36,8 +36,8 @@ require (
github.com/yuin/goldmark v1.7.16
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.48.0
- golang.org/x/sync v0.19.0
- google.golang.org/grpc v1.79.1
+ golang.org/x/sync v0.20.0
+ google.golang.org/grpc v1.79.2
google.golang.org/protobuf v1.36.11
k8s.io/api v0.35.2
k8s.io/apimachinery v0.35.2
@@ -138,7 +138,7 @@ require (
sigs.k8s.io/yaml v1.6.0 // indirect
)
-go 1.26.0
+go 1.26.1
tool (
github.com/air-verse/air
diff --git a/go.sum b/go.sum
index 56447c55..9ab1d437 100644
--- a/go.sum
+++ b/go.sum
@@ -446,6 +446,8 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -516,6 +518,8 @@ google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
+google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
+google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0 h1:6Al3kEFFP9VJhRz3DID6quisgPnTeZVr4lep9kkxdPA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0/go.mod h1:QLvsjh0OIR0TYBeiu2bkWGTJBUNQ64st52iWj/yA93I=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1/go.mod h1:YNKnb2OAApgYn2oYY47Rn7alMr1zWjb2U8Q0aoGWiNc=
diff --git a/internal/agent/client.go b/internal/agent/client.go
index 4cbf2f4e..d531f7cf 100644
--- a/internal/agent/client.go
+++ b/internal/agent/client.go
@@ -529,6 +529,7 @@ func (c *Client) UpdateNotificationConfig(ctx context.Context, subscriptions []t
Type: d.Type,
Url: d.URL,
Template: d.Template,
+ Headers: d.Headers,
}
}
diff --git a/internal/agent/pb/rpc.pb.go b/internal/agent/pb/rpc.pb.go
index eebdb59d..5f461580 100644
--- a/internal/agent/pb/rpc.pb.go
+++ b/internal/agent/pb/rpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
-// protoc v6.33.4
+// protoc v7.34.0
// source: rpc.proto
package pb
diff --git a/internal/agent/pb/rpc_grpc.pb.go b/internal/agent/pb/rpc_grpc.pb.go
index 7a3f8f20..4eb8dad6 100644
--- a/internal/agent/pb/rpc_grpc.pb.go
+++ b/internal/agent/pb/rpc_grpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
-// - protoc v6.33.4
+// - protoc v7.34.0
// source: rpc.proto
package pb
diff --git a/internal/agent/pb/types.pb.go b/internal/agent/pb/types.pb.go
index c72ae8df..f19c8088 100644
--- a/internal/agent/pb/types.pb.go
+++ b/internal/agent/pb/types.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
-// protoc v6.33.4
+// protoc v7.34.0
// source: types.proto
package pb
@@ -935,6 +935,7 @@ type NotificationDispatcher struct {
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"`
Template string `protobuf:"bytes,5,opt,name=template,proto3" json:"template,omitempty"`
+ Headers map[string]string `protobuf:"bytes,6,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -1004,6 +1005,13 @@ func (x *NotificationDispatcher) GetTemplate() string {
return ""
}
+func (x *NotificationDispatcher) GetHeaders() map[string]string {
+ if x != nil {
+ return x.Headers
+ }
+ return nil
+}
+
var File_types_proto protoreflect.FileDescriptor
const file_types_proto_rawDesc = "" +
@@ -1092,13 +1100,17 @@ const file_types_proto_rawDesc = "" +
"\x13containerExpression\x18\x06 \x01(\tR\x13containerExpression\x12*\n" +
"\x10metricExpression\x18\a \x01(\tR\x10metricExpression\x12\x1a\n" +
"\bcooldown\x18\b \x01(\x05R\bcooldown\x12\"\n" +
- "\fsampleWindow\x18\t \x01(\x05R\fsampleWindow\"~\n" +
+ "\fsampleWindow\x18\t \x01(\x05R\fsampleWindow\"\x83\x02\n" +
"\x16NotificationDispatcher\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
"\x04type\x18\x03 \x01(\tR\x04type\x12\x10\n" +
"\x03url\x18\x04 \x01(\tR\x03url\x12\x1a\n" +
- "\btemplate\x18\x05 \x01(\tR\btemplate*3\n" +
+ "\btemplate\x18\x05 \x01(\tR\btemplate\x12G\n" +
+ "\aheaders\x18\x06 \x03(\v2-.protobuf.NotificationDispatcher.HeadersEntryR\aheaders\x1a:\n" +
+ "\fHeadersEntry\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
+ "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01*3\n" +
"\x0fContainerAction\x12\t\n" +
"\x05Start\x10\x00\x12\b\n" +
"\x04Stop\x10\x01\x12\v\n" +
@@ -1117,7 +1129,7 @@ func file_types_proto_rawDescGZIP() []byte {
}
var file_types_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_types_proto_goTypes = []any{
(ContainerAction)(0), // 0: protobuf.ContainerAction
(*Container)(nil), // 1: protobuf.Container
@@ -1133,25 +1145,27 @@ var file_types_proto_goTypes = []any{
(*NotificationDispatcher)(nil), // 11: protobuf.NotificationDispatcher
nil, // 12: protobuf.Container.LabelsEntry
nil, // 13: protobuf.Host.LabelsEntry
- (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
- (*anypb.Any)(nil), // 15: google.protobuf.Any
+ nil, // 14: protobuf.NotificationDispatcher.HeadersEntry
+ (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
+ (*anypb.Any)(nil), // 16: google.protobuf.Any
}
var file_types_proto_depIdxs = []int32{
- 14, // 0: protobuf.Container.created:type_name -> google.protobuf.Timestamp
- 14, // 1: protobuf.Container.started:type_name -> google.protobuf.Timestamp
+ 15, // 0: protobuf.Container.created:type_name -> google.protobuf.Timestamp
+ 15, // 1: protobuf.Container.started:type_name -> google.protobuf.Timestamp
12, // 2: protobuf.Container.labels:type_name -> protobuf.Container.LabelsEntry
2, // 3: protobuf.Container.stats:type_name -> protobuf.ContainerStat
- 14, // 4: protobuf.Container.finished:type_name -> google.protobuf.Timestamp
- 15, // 5: protobuf.LogEvent.message:type_name -> google.protobuf.Any
- 14, // 6: protobuf.LogEvent.timestamp:type_name -> google.protobuf.Timestamp
+ 15, // 4: protobuf.Container.finished:type_name -> google.protobuf.Timestamp
+ 16, // 5: protobuf.LogEvent.message:type_name -> google.protobuf.Any
+ 15, // 6: protobuf.LogEvent.timestamp:type_name -> google.protobuf.Timestamp
3, // 7: protobuf.GroupMessage.fragments:type_name -> protobuf.LogFragment
- 14, // 8: protobuf.ContainerEvent.timestamp:type_name -> google.protobuf.Timestamp
+ 15, // 8: protobuf.ContainerEvent.timestamp:type_name -> google.protobuf.Timestamp
13, // 9: protobuf.Host.labels:type_name -> protobuf.Host.LabelsEntry
- 10, // [10:10] is the sub-list for method output_type
- 10, // [10:10] is the sub-list for method input_type
- 10, // [10:10] is the sub-list for extension type_name
- 10, // [10:10] is the sub-list for extension extendee
- 0, // [0:10] is the sub-list for field type_name
+ 14, // 10: protobuf.NotificationDispatcher.headers:type_name -> protobuf.NotificationDispatcher.HeadersEntry
+ 11, // [11:11] is the sub-list for method output_type
+ 11, // [11:11] is the sub-list for method input_type
+ 11, // [11:11] is the sub-list for extension type_name
+ 11, // [11:11] is the sub-list for extension extendee
+ 0, // [0:11] is the sub-list for field type_name
}
func init() { file_types_proto_init() }
@@ -1165,7 +1179,7 @@ func file_types_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_types_proto_rawDesc), len(file_types_proto_rawDesc)),
NumEnums: 1,
- NumMessages: 13,
+ NumMessages: 14,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/internal/agent/server.go b/internal/agent/server.go
index f987f31b..79fd60f1 100644
--- a/internal/agent/server.go
+++ b/internal/agent/server.go
@@ -429,6 +429,7 @@ func (s *server) UpdateNotificationConfig(ctx context.Context, req *pb.UpdateNot
Type: d.Type,
URL: d.Url,
Template: d.Template,
+ Headers: d.Headers,
}
}
diff --git a/internal/container/event_generator.go b/internal/container/event_generator.go
index d1baf677..14792451 100644
--- a/internal/container/event_generator.go
+++ b/internal/container/event_generator.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"hash/fnv"
+ "math"
"strings"
"sync"
"time"
@@ -86,6 +87,9 @@ func (g *EventGenerator) flushGroup(pendingGroup []*LogEvent) bool {
func (g *EventGenerator) processBuffer() {
var pendingGroup []*LogEvent
+ seenFirst := false
+ var lastOrphanTimestamp int64
+ orphanCount := 0
loop:
for {
@@ -95,8 +99,9 @@ loop:
break loop
}
- // Complex logs are emitted immediately
+ // Complex logs are emitted immediately and always mark seenFirst
if !current.IsSimple() {
+ seenFirst = true
if !g.flushGroup(pendingGroup) {
break loop
}
@@ -107,6 +112,24 @@ loop:
continue
}
+ // Skip leading simple events without a level that look like orphaned
+ // continuation lines from a group already emitted in a prior fetch.
+ // Only skip if they have a timestamp and are close in time, matching
+ // the group continuation criteria.
+ if !seenFirst && !current.HasLevel() && current.Timestamp > 0 {
+ if lastOrphanTimestamp == 0 || math.Abs(float64(lastOrphanTimestamp-current.Timestamp)) < maxGroupTimeDelta {
+ lastOrphanTimestamp = current.Timestamp
+ orphanCount++
+ continue
+ }
+ }
+ if !seenFirst {
+ if orphanCount > 0 {
+ log.Debug().Int("count", orphanCount).Str("container", g.containerID).Msg("skipped orphaned continuation lines")
+ }
+ seenFirst = true
+ }
+
// Simple log - peek ahead to decide grouping
next := g.peek()
@@ -125,7 +148,7 @@ loop:
pendingGroup = append(pendingGroup, current)
- if next == nil || !next.IsSimple() || !canContinueGroup(pendingGroup[0], next) {
+ if next == nil || !next.IsSimple() || !canContinueGroup(pendingGroup[len(pendingGroup)-1], next) {
if !g.flushGroup(pendingGroup) {
break loop
}
@@ -154,32 +177,12 @@ func (g *EventGenerator) nextEvent() *LogEvent {
// canStartGroup checks if current can start a group with next
func canStartGroup(current, next *LogEvent) bool {
- // Current must have a known level
- if !current.HasLevel() {
- return false
- }
- // Next must not have its own level (continuation)
- if next.HasLevel() {
- return false
- }
- // Must be close in time
- if !current.IsCloseToTime(next) {
- return false
- }
- return true
+ return current.HasLevel() && canContinueGroup(current, next)
}
-// canContinueGroup checks if next can be added to a group started by first
-func canContinueGroup(first, next *LogEvent) bool {
- // Next must not have its own level (continuation)
- if next.HasLevel() {
- return false
- }
- // Must be close in time to the group leader
- if !first.IsCloseToTime(next) {
- return false
- }
- return true
+// canContinueGroup checks if next can be appended after prev in a group
+func canContinueGroup(prev, next *LogEvent) bool {
+ return !next.HasLevel() && prev.IsCloseToTime(next)
}
func (g *EventGenerator) consumeReader() {
@@ -220,9 +223,8 @@ func createEvent(message string, streamType StdType) *LogEvent {
h := fnv.New32a()
h.Write([]byte(message))
logEvent := &LogEvent{Id: h.Sum32(), Message: message, Stream: streamType.String(), Type: LogTypeSingle}
- if index := strings.IndexAny(message, " "); index != -1 {
- logId := message[:index]
- if timestamp, err := time.Parse(time.RFC3339Nano, logId); err == nil {
+ if index := strings.IndexByte(message, ' '); index != -1 {
+ if timestamp, err := time.Parse(time.RFC3339Nano, message[:index]); err == nil {
logEvent.Timestamp = timestamp.UnixMilli()
message = strings.TrimSuffix(message[index+1:], "\n")
logEvent.Message = message
diff --git a/internal/container/event_generator_test.go b/internal/container/event_generator_test.go
index 6a56b543..b4b3690a 100644
--- a/internal/container/event_generator_test.go
+++ b/internal/container/event_generator_test.go
@@ -240,6 +240,114 @@ func TestEventGenerator_MixedLogs(t *testing.T) {
assert.Equal(t, LogTypeSingle, event2.Type)
}
+func TestEventGenerator_SkipsLeadingOrphanedContinuationLines(t *testing.T) {
+ // Simulate a pagination boundary where orphaned continuation lines
+ // (no level, with timestamp) appear before the first real log entry.
+ // These should be skipped as they belong to a group from a prior fetch.
+ baseTime := "2020-05-13T18:55:37.772853839Z"
+ messages := []string{
+ baseTime + " at line 42", // orphan continuation (no level)
+ baseTime + " in function foo", // orphan continuation (no level)
+ baseTime + " ERROR: Next error", // real entry with level
+ baseTime + " at line 99", // continuation of the real entry
+ }
+
+ reader := &mockLogReader{
+ messages: messages,
+ types: []StdType{STDERR, STDERR, STDERR, STDERR},
+ }
+
+ g := NewEventGenerator(context.Background(), reader, Container{Tty: false})
+ event := <-g.Events
+
+ require.NotNil(t, event, "Expected event to not be nil")
+ assert.Equal(t, LogTypeGroup, event.Type)
+
+ fragments, ok := event.Message.([]LogFragment)
+ require.True(t, ok, "Expected Message to be []LogFragment")
+ assert.Len(t, fragments, 2)
+ assert.Equal(t, "ERROR: Next error", fragments[0].Message)
+ assert.Equal(t, "at line 99", fragments[1].Message)
+}
+
+func TestEventGenerator_DoesNotSkipLeadingLevellessLogsWithTimestampGap(t *testing.T) {
+ // Leading lines without levels but far apart in time should NOT be skipped.
+ // They are standalone logs, not orphaned group continuations.
+ messages := []string{
+ "2020-05-13T18:55:37.000Z some log without level",
+ "2020-05-13T18:55:38.000Z another log without level",
+ }
+
+ reader := &mockLogReader{
+ messages: messages,
+ types: []StdType{STDOUT, STDOUT},
+ }
+
+ g := NewEventGenerator(context.Background(), reader, Container{Tty: false})
+
+ // First line is skipped (potential orphan), but the second is too far in time
+ // so it should be emitted.
+ event1 := <-g.Events
+ require.NotNil(t, event1, "Expected event to not be nil")
+ assert.Equal(t, LogTypeSingle, event1.Type)
+ assert.Equal(t, "another log without level", event1.Message)
+}
+
+func TestEventGenerator_AllOrphanedLinesProducesNoEvents(t *testing.T) {
+ // If all lines are orphaned continuations, no events should be emitted.
+ baseTime := "2020-05-13T18:55:37.772853839Z"
+ messages := []string{
+ baseTime + " at line 42",
+ baseTime + " in function foo",
+ baseTime + " in function bar",
+ }
+
+ reader := &mockLogReader{
+ messages: messages,
+ types: []StdType{STDERR, STDERR, STDERR},
+ }
+
+ g := NewEventGenerator(context.Background(), reader, Container{Tty: false})
+ event, ok := <-g.Events
+
+ assert.False(t, ok, "Expected channel to be closed with no events")
+ assert.Nil(t, event)
+}
+
+func TestEventGenerator_OrphanedLinesFollowedByComplexLog(t *testing.T) {
+ // Orphaned continuation lines should be skipped even when followed by a complex log.
+ baseTime := "2020-05-13T18:55:37.772853839Z"
+ messages := []string{
+ baseTime + " at line 42",
+ baseTime + " in function foo",
+ baseTime + " {\"level\": \"info\", \"message\": \"test\"}",
+ }
+
+ reader := &mockLogReader{
+ messages: messages,
+ types: []StdType{STDERR, STDERR, STDOUT},
+ }
+
+ g := NewEventGenerator(context.Background(), reader, Container{Tty: false})
+ event := <-g.Events
+
+ require.NotNil(t, event, "Expected event to not be nil")
+ assert.Equal(t, LogTypeComplex, event.Type)
+}
+
+func TestEventGenerator_DoesNotSkipLeadingLinesWithoutTimestamp(t *testing.T) {
+ // Lines without timestamps (e.g., tty/raw input) should not be skipped
+ // even if they lack a level.
+ input := "some raw output"
+
+ g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: true})
+ event := <-g.Events
+
+ require.NotNil(t, event, "Expected event to not be nil")
+ assert.Equal(t, input, event.Message)
+ assert.Equal(t, LogTypeSingle, event.Type)
+}
+
func TestEventGenerator_NoGroupingWhenTimestampGap(t *testing.T) {
// Messages with different timestamps (too far apart to group)
messages := []string{
diff --git a/internal/container/types.go b/internal/container/types.go
index ebc1c93e..7893ecad 100644
--- a/internal/container/types.go
+++ b/internal/container/types.go
@@ -199,8 +199,13 @@ func (l *LogEvent) IsSimple() bool {
return l.Type == LogTypeSingle || l.Type == LogTypeGroup
}
+// maxGroupTimeDelta is the maximum time difference (in milliseconds) between
+// consecutive log lines that can be grouped together. Docker can introduce
+// up to ~30ms of jitter between related log lines (e.g., a stack trace).
+const maxGroupTimeDelta = 50
+
func (l *LogEvent) IsCloseToTime(other *LogEvent) bool {
- return math.Abs(float64(l.Timestamp-other.Timestamp)) < 10
+ return math.Abs(float64(l.Timestamp-other.Timestamp)) < maxGroupTimeDelta
}
func (l *LogEvent) MessageId() int64 {
diff --git a/internal/notification/config.go b/internal/notification/config.go
index 20a31502..7e8dc96c 100644
--- a/internal/notification/config.go
+++ b/internal/notification/config.go
@@ -59,6 +59,7 @@ func (m *Manager) LoadConfig(r io.Reader) error {
Type: d.Type,
URL: d.URL,
Template: d.Template,
+ Headers: d.Headers,
APIKey: d.APIKey,
Prefix: d.Prefix,
ExpiresAt: d.ExpiresAt,
@@ -116,6 +117,7 @@ func (m *Manager) HandleNotificationConfig(subscriptions []types.SubscriptionCon
Type: dc.Type,
URL: dc.URL,
Template: dc.Template,
+ Headers: dc.Headers,
APIKey: dc.APIKey,
Prefix: dc.Prefix,
ExpiresAt: dc.ExpiresAt,
@@ -137,7 +139,7 @@ func (m *Manager) HandleNotificationConfig(subscriptions []types.SubscriptionCon
func createDispatcher(config DispatcherConfig) (dispatcher.Dispatcher, error) {
switch config.Type {
case "webhook":
- return dispatcher.NewWebhookDispatcher(config.Name, config.URL, config.Template)
+ return dispatcher.NewWebhookDispatcher(config.Name, config.URL, config.Template, config.Headers)
case "cloud":
return dispatcher.NewCloudDispatcher(config.Name, config.APIKey, config.Prefix, config.ExpiresAt)
default:
diff --git a/internal/notification/dispatcher/webhook.go b/internal/notification/dispatcher/webhook.go
index 670cea9f..97506cf8 100644
--- a/internal/notification/dispatcher/webhook.go
+++ b/internal/notification/dispatcher/webhook.go
@@ -24,16 +24,18 @@ type WebhookDispatcher struct {
URL string
Template *template.Template
TemplateText string // Original template string for serialization
+ Headers map[string]string
client *http.Client
}
// NewWebhookDispatcher creates a new webhook dispatcher
// If templateStr is empty, the notification will be marshaled as JSON directly
-func NewWebhookDispatcher(name, url, templateStr string) (*WebhookDispatcher, error) {
+func NewWebhookDispatcher(name, url, templateStr string, headers map[string]string) (*WebhookDispatcher, error) {
w := &WebhookDispatcher{
Name: name,
URL: url,
TemplateText: templateStr,
+ Headers: headers,
client: &http.Client{
Timeout: 10 * time.Second,
},
@@ -88,6 +90,9 @@ func (w *WebhookDispatcher) SendTest(ctx context.Context, notification types.Not
return TestResult{Success: false, Error: fmt.Sprintf("failed to create request: %v", err)}
}
+ for k, v := range w.Headers {
+ req.Header.Set(k, v)
+ }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", UserAgent)
diff --git a/internal/notification/manager.go b/internal/notification/manager.go
index 6ba165e9..b92b956e 100644
--- a/internal/notification/manager.go
+++ b/internal/notification/manager.go
@@ -152,8 +152,13 @@ func (m *Manager) UpdateSubscription(id int, updates map[string]any) error {
SampleWindow: sub.SampleWindow,
MetricCooldowns: sub.MetricCooldowns,
MetricSampleBuffers: sub.MetricSampleBuffers,
+ TriggeredContainerIDs: sub.TriggeredContainerIDs,
}
+ // Preserve runtime stats (atomics can't be copied in struct literal)
+ updated.TriggerCount.Store(sub.TriggerCount.Load())
+ updated.LastTriggeredAt.Store(sub.LastTriggeredAt.Load())
+
// Apply updates to the clone
for key, value := range updates {
switch key {
@@ -305,6 +310,7 @@ func (m *Manager) Dispatchers() []DispatcherConfig {
Type: "webhook",
URL: v.URL,
Template: v.TemplateText,
+ Headers: v.Headers,
})
case *dispatcher.CloudDispatcher:
result = append(result, DispatcherConfig{
diff --git a/internal/notification/types.go b/internal/notification/types.go
index e186a880..3318fad7 100644
--- a/internal/notification/types.go
+++ b/internal/notification/types.go
@@ -160,14 +160,15 @@ func (s *Subscription) CompileExpressions() error {
// DispatcherConfig represents a dispatcher configuration
type DispatcherConfig struct {
- ID int `json:"id" yaml:"id"`
- Name string `json:"name" yaml:"name"`
- Type string `json:"type" yaml:"type"` // "webhook", "cloud"
- URL string `json:"url,omitempty" yaml:"url,omitempty"`
- Template string `json:"template,omitempty" yaml:"template,omitempty"` // Go template for custom payload format
- APIKey string `json:"apiKey,omitempty" yaml:"apiKey,omitempty"` // API key for cloud dispatcher
- Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` // API key prefix for cloud dispatcher
- ExpiresAt *time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty"`
+ ID int `json:"id" yaml:"id"`
+ Name string `json:"name" yaml:"name"`
+ Type string `json:"type" yaml:"type"` // "webhook", "cloud"
+ URL string `json:"url,omitempty" yaml:"url,omitempty"`
+ Template string `json:"template,omitempty" yaml:"template,omitempty"` // Go template for custom payload format
+ Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` // Custom HTTP headers
+ APIKey string `json:"apiKey,omitempty" yaml:"apiKey,omitempty"` // API key for cloud dispatcher
+ Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` // API key prefix for cloud dispatcher
+ ExpiresAt *time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty"`
}
// Config represents the persisted notification configuration
diff --git a/internal/web/download.go b/internal/web/download.go
index 967f5e68..e0423292 100644
--- a/internal/web/download.go
+++ b/internal/web/download.go
@@ -137,7 +137,11 @@ func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
for _, c := range containers {
// Create new file in zip for this container's logs
fileName := fmt.Sprintf("%s-%s.log", c.containerService.Container.Name, nowFmt)
- f, err := zw.Create(fileName)
+ f, err := zw.CreateHeader(&zip.FileHeader{
+ Name: fileName,
+ Modified: now,
+ Method: zip.Deflate,
+ })
if err != nil {
log.Error().Err(err).Msgf("error creating zip entry for container %s", c.id)
return
diff --git a/internal/web/notifications.go b/internal/web/notifications.go
index 689eba87..38263775 100644
--- a/internal/web/notifications.go
+++ b/internal/web/notifications.go
@@ -37,13 +37,14 @@ type NotificationRuleResponse struct {
}
type DispatcherResponse struct {
- ID int `json:"id"`
- Name string `json:"name"`
- Type string `json:"type"`
- URL *string `json:"url,omitempty"`
- Template *string `json:"template,omitempty"`
- Prefix *string `json:"prefix,omitempty"`
- ExpiresAt *time.Time `json:"expiresAt,omitempty"`
+ ID int `json:"id"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ URL *string `json:"url,omitempty"`
+ Template *string `json:"template,omitempty"`
+ Headers map[string]string `json:"headers,omitempty"`
+ Prefix *string `json:"prefix,omitempty"`
+ ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
type NotificationRuleInput struct {
@@ -69,10 +70,11 @@ type NotificationRuleUpdateInput struct {
}
type DispatcherInput struct {
- Name string `json:"name"`
- Type string `json:"type"`
- URL *string `json:"url,omitempty"`
- Template *string `json:"template,omitempty"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ URL *string `json:"url,omitempty"`
+ Template *string `json:"template,omitempty"`
+ Headers map[string]string `json:"headers,omitempty"`
}
type PreviewInput struct {
@@ -92,8 +94,9 @@ type PreviewResult struct {
}
type TestWebhookInput struct {
- URL string `json:"url"`
- Template *string `json:"template,omitempty"`
+ URL string `json:"url"`
+ Template *string `json:"template,omitempty"`
+ Headers map[string]string `json:"headers,omitempty"`
}
type TestWebhookResult struct {
@@ -142,6 +145,10 @@ func dispatcherConfigToResponse(d *notification.DispatcherConfig) *DispatcherRes
if d.Template != "" {
template = &d.Template
}
+ var headers map[string]string
+ if len(d.Headers) > 0 {
+ headers = d.Headers
+ }
var prefix *string
if d.Prefix != "" {
prefix = &d.Prefix
@@ -152,6 +159,7 @@ func dispatcherConfigToResponse(d *notification.DispatcherConfig) *DispatcherRes
Type: d.Type,
URL: url,
Template: template,
+ Headers: headers,
Prefix: prefix,
ExpiresAt: d.ExpiresAt,
}
@@ -369,7 +377,7 @@ func (h *handler) createDispatcher(w http.ResponseWriter, r *http.Request) {
if input.Template != nil {
templateStr = *input.Template
}
- webhook, err := dispatcher.NewWebhookDispatcher(input.Name, url, templateStr)
+ webhook, err := dispatcher.NewWebhookDispatcher(input.Name, url, templateStr, input.Headers)
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
@@ -382,13 +390,17 @@ func (h *handler) createDispatcher(w http.ResponseWriter, r *http.Request) {
id := h.hostService.AddDispatcher(d)
- writeJSON(w, http.StatusCreated, &DispatcherResponse{
+ resp := &DispatcherResponse{
ID: id,
Name: input.Name,
Type: input.Type,
URL: input.URL,
Template: input.Template,
- })
+ }
+ if len(input.Headers) > 0 {
+ resp.Headers = input.Headers
+ }
+ writeJSON(w, http.StatusCreated, resp)
}
func (h *handler) updateDispatcher(w http.ResponseWriter, r *http.Request) {
@@ -415,7 +427,7 @@ func (h *handler) updateDispatcher(w http.ResponseWriter, r *http.Request) {
if input.Template != nil {
templateStr = *input.Template
}
- webhook, err := dispatcher.NewWebhookDispatcher(input.Name, url, templateStr)
+ webhook, err := dispatcher.NewWebhookDispatcher(input.Name, url, templateStr, input.Headers)
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
@@ -428,13 +440,17 @@ func (h *handler) updateDispatcher(w http.ResponseWriter, r *http.Request) {
h.hostService.UpdateDispatcher(id, d)
- writeJSON(w, http.StatusOK, &DispatcherResponse{
+ resp := &DispatcherResponse{
ID: id,
Name: input.Name,
Type: input.Type,
URL: input.URL,
Template: input.Template,
- })
+ }
+ if len(input.Headers) > 0 {
+ resp.Headers = input.Headers
+ }
+ writeJSON(w, http.StatusOK, resp)
}
func (h *handler) deleteDispatcher(w http.ResponseWriter, r *http.Request) {
@@ -596,7 +612,7 @@ func (h *handler) testWebhook(w http.ResponseWriter, r *http.Request) {
templateStr = *input.Template
}
- webhook, err := dispatcher.NewWebhookDispatcher("test", input.URL, templateStr)
+ webhook, err := dispatcher.NewWebhookDispatcher("test", input.URL, templateStr, input.Headers)
if err != nil {
errStr := err.Error()
writeJSON(w, http.StatusOK, &TestWebhookResult{
diff --git a/locales/da.yml b/locales/da.yml
index 4eca22a8..84a380c3 100644
--- a/locales/da.yml
+++ b/locales/da.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Brugerdefineret
template: Skabelon
template-hint: Go skabelon syntaks
+ headers: Brugerdefinerede Headers
+ headers-hint: Valgfrie HTTP-headers (f.eks. Authorization)
+ header-name: Header-navn
+ header-value: Header-værdi
+ add-header: Tilføj Header
test: Test
test-success: Test succesfuld
cancel: Annuller
diff --git a/locales/de.yml b/locales/de.yml
index 08126762..58b7e326 100644
--- a/locales/de.yml
+++ b/locales/de.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Benutzerdefiniert
template: Vorlage
template-hint: Go-Template-Syntax
+ headers: Benutzerdefinierte Header
+ headers-hint: Optionale HTTP-Header (z.B. Authorization)
+ header-name: Header-Name
+ header-value: Header-Wert
+ add-header: Header hinzufügen
test: Testen
test-success: Test erfolgreich
cancel: Abbrechen
diff --git a/locales/en.yml b/locales/en.yml
index ef9c76f5..bfce26a6 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -249,6 +249,11 @@ notifications:
format-custom: Custom
template: Template
template-hint: Go template syntax
+ headers: Custom Headers
+ headers-hint: Optional HTTP headers (e.g., Authorization)
+ header-name: Header name
+ header-value: Header value
+ add-header: Add Header
test: Test
test-success: Test successful
cancel: Cancel
diff --git a/locales/es.yml b/locales/es.yml
index 7b0cbb4d..3f4edfd2 100644
--- a/locales/es.yml
+++ b/locales/es.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Personalizado
template: Plantilla
template-hint: Sintaxis de plantilla Go
+ headers: Encabezados Personalizados
+ headers-hint: Encabezados HTTP opcionales (ej. Authorization)
+ header-name: Nombre del encabezado
+ header-value: Valor del encabezado
+ add-header: Añadir Encabezado
test: Probar
test-success: Prueba exitosa
cancel: Cancelar
diff --git a/locales/fr.yml b/locales/fr.yml
index ccd1cb7f..9e60cc9f 100644
--- a/locales/fr.yml
+++ b/locales/fr.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Personnalisé
template: Modèle
template-hint: Syntaxe de modèle Go
+ headers: En-têtes Personnalisés
+ headers-hint: En-têtes HTTP optionnels (ex. Authorization)
+ header-name: Nom de l'en-tête
+ header-value: Valeur de l'en-tête
+ add-header: Ajouter un En-tête
test: Tester
test-success: Test réussi
cancel: Annuler
diff --git a/locales/id.yml b/locales/id.yml
index 949da6b6..e87a833c 100644
--- a/locales/id.yml
+++ b/locales/id.yml
@@ -252,6 +252,11 @@ notifications:
format-custom: Kustom
template: Template
template-hint: Sintaks template Go
+ headers: Header Kustom
+ headers-hint: Header HTTP opsional (mis. Authorization)
+ header-name: Nama header
+ header-value: Nilai header
+ add-header: Tambah Header
test: Test
test-success: Test berhasil
cancel: Batal
diff --git a/locales/it.yml b/locales/it.yml
index 803723b0..ff3e5582 100644
--- a/locales/it.yml
+++ b/locales/it.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Personalizzato
template: Template
template-hint: Sintassi template Go
+ headers: Header Personalizzati
+ headers-hint: Header HTTP opzionali (es. Authorization)
+ header-name: Nome header
+ header-value: Valore header
+ add-header: Aggiungi Header
test: Test
test-success: Test riuscito
cancel: Annulla
diff --git a/locales/ko.yml b/locales/ko.yml
index 5da7bb84..e96cc775 100644
--- a/locales/ko.yml
+++ b/locales/ko.yml
@@ -243,6 +243,11 @@ notifications:
format-custom: 사용자 정의
template: 템플릿
template-hint: Go 템플릿 구문
+ headers: 사용자 정의 헤더
+ headers-hint: "선택적 HTTP 헤더 (예: Authorization)"
+ header-name: 헤더 이름
+ header-value: 헤더 값
+ add-header: 헤더 추가
test: 테스트
test-success: 테스트 성공
cancel: 취소
diff --git a/locales/nl.yml b/locales/nl.yml
index 0ae17ec7..5bb91588 100644
--- a/locales/nl.yml
+++ b/locales/nl.yml
@@ -241,6 +241,11 @@ notifications:
format-custom: Aangepast
template: Sjabloon
template-hint: Go-sjabloonsyntaxis
+ headers: Aangepaste Headers
+ headers-hint: Optionele HTTP-headers (bijv. Authorization)
+ header-name: Headernaam
+ header-value: Headerwaarde
+ add-header: Header toevoegen
test: Testen
test-success: Test geslaagd
cancel: Annuleren
diff --git a/locales/pl.yml b/locales/pl.yml
index 484bf9f3..e4a87862 100644
--- a/locales/pl.yml
+++ b/locales/pl.yml
@@ -247,6 +247,11 @@ notifications:
format-custom: Własny
template: Szablon
template-hint: Składnia szablonu Go
+ headers: Niestandardowe Nagłówki
+ headers-hint: Opcjonalne nagłówki HTTP (np. Authorization)
+ header-name: Nazwa nagłówka
+ header-value: Wartość nagłówka
+ add-header: Dodaj Nagłówek
test: Test
test-success: Test zakończony pomyślnie
cancel: Anuluj
diff --git a/locales/pr.yml b/locales/pr.yml
index a83f76ff..4479e5f7 100644
--- a/locales/pr.yml
+++ b/locales/pr.yml
@@ -249,6 +249,11 @@ notifications:
format-custom: Personalizado
template: Modelo
template-hint: Sintaxe de modelo Go
+ headers: Cabeçalhos Personalizados
+ headers-hint: Cabeçalhos HTTP opcionais (ex. Authorization)
+ header-name: Nome do cabeçalho
+ header-value: Valor do cabeçalho
+ add-header: Adicionar Cabeçalho
test: Testar
test-success: Teste bem-sucedido
cancel: Cancelar
diff --git a/locales/pt.yml b/locales/pt.yml
index 30fda788..8bafcb61 100644
--- a/locales/pt.yml
+++ b/locales/pt.yml
@@ -239,6 +239,11 @@ notifications:
format-custom: Personalizado
template: Template
template-hint: Sintaxe de template Go
+ headers: Cabeçalhos Personalizados
+ headers-hint: Cabeçalhos HTTP opcionais (ex. Authorization)
+ header-name: Nome do cabeçalho
+ header-value: Valor do cabeçalho
+ add-header: Adicionar Cabeçalho
test: Testar
test-success: Teste bem-sucedido
cancel: Cancelar
diff --git a/locales/ru.yml b/locales/ru.yml
index 0fdc5749..b12458ea 100644
--- a/locales/ru.yml
+++ b/locales/ru.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Пользовательский
template: Шаблон
template-hint: Синтаксис шаблона Go
+ headers: Пользовательские Заголовки
+ headers-hint: Необязательные HTTP-заголовки (напр. Authorization)
+ header-name: Имя заголовка
+ header-value: Значение заголовка
+ add-header: Добавить Заголовок
test: Тест
test-success: Тест успешен
cancel: Отмена
diff --git a/locales/sl.yml b/locales/sl.yml
index bd47db7c..974de745 100644
--- a/locales/sl.yml
+++ b/locales/sl.yml
@@ -245,6 +245,11 @@ notifications:
format-custom: Po meri
template: Predloga
template-hint: Sintaksa predloge Go
+ headers: Prilagojene Glave
+ headers-hint: Neobvezne HTTP glave (npr. Authorization)
+ header-name: Ime glave
+ header-value: Vrednost glave
+ add-header: Dodaj Glavo
test: Test
test-success: Test uspešen
cancel: Prekliči
diff --git a/locales/tr.yml b/locales/tr.yml
index 9e7ea670..430c41df 100644
--- a/locales/tr.yml
+++ b/locales/tr.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: Özel
template: Şablon
template-hint: Go şablon söz dizimi
+ headers: Özel Başlıklar
+ headers-hint: İsteğe bağlı HTTP başlıkları (ör. Authorization)
+ header-name: Başlık adı
+ header-value: Başlık değeri
+ add-header: Başlık Ekle
test: Test
test-success: Test başarılı
cancel: İptal
diff --git a/locales/zh-tw.yml b/locales/zh-tw.yml
index 18b067e2..36be3c8d 100644
--- a/locales/zh-tw.yml
+++ b/locales/zh-tw.yml
@@ -243,6 +243,11 @@ notifications:
format-custom: 自訂
template: 範本
template-hint: Go 範本語法
+ headers: 自訂標頭
+ headers-hint: 選填的 HTTP 標頭(例如 Authorization)
+ header-name: 標頭名稱
+ header-value: 標頭值
+ add-header: 新增標頭
test: 測試
test-success: 測試成功
cancel: 取消
diff --git a/locales/zh.yml b/locales/zh.yml
index 552ff5cc..171a42e2 100644
--- a/locales/zh.yml
+++ b/locales/zh.yml
@@ -240,6 +240,11 @@ notifications:
format-custom: 自定义
template: 模板
template-hint: Go 模板语法
+ headers: 自定义请求头
+ headers-hint: 可选的 HTTP 请求头(如 Authorization)
+ header-name: 请求头名称
+ header-value: 请求头值
+ add-header: 添加请求头
test: 测试
test-success: 测试成功
cancel: 取消
diff --git a/package.json b/package.json
index afa9f4f2..70f71b73 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "dozzle",
- "version": "10.0.7",
+ "version": "10.1.0",
"description": "Realtime log viewer for docker containers.",
"homepage": "https://github.com/amir20/dozzle#readme",
"bugs": {
"url": "https://github.com/amir20/dozzle/issues"
},
- "packageManager": "pnpm@10.30.3",
+ "packageManager": "pnpm@10.31.0",
"type": "module",
"repository": {
"type": "git",
@@ -40,7 +40,7 @@
"@iconify-json/carbon": "^1.2.19",
"@iconify-json/cil": "^1.2.3",
"@iconify-json/ic": "^1.2.4",
- "@iconify-json/material-symbols": "^1.2.59",
+ "@iconify-json/material-symbols": "^1.2.60",
"@iconify-json/mdi": "^1.2.3",
"@iconify-json/mdi-light": "^1.2.2",
"@iconify-json/octicon": "^1.2.21",
@@ -73,22 +73,22 @@
"unplugin-vue-macros": "^2.14.5",
"vite": "7.3.1",
"vite-plugin-vue-layouts": "^0.11.0",
- "vite-svg-loader": "^5.1.0",
+ "vite-svg-loader": "^5.1.1",
"vitepress": "1.6.4",
"vue": "^3.5.29",
- "vue-i18n": "^11.2.8",
+ "vue-i18n": "^11.3.0",
"vue-router": "^5.0.3"
},
"devDependencies": {
"@apache-arrow/esnext-esm": "^21.1.0",
"@iconify-json/ion": "^1.2.6",
- "@iconify-json/material-symbols-light": "^1.2.59",
+ "@iconify-json/material-symbols-light": "^1.2.60",
"@iconify-json/ri": "^1.2.10",
"@iconify-json/svg-spinners": "^1.2.4",
"@pinia/testing": "^1.0.3",
"@playwright/test": "^1.58.2",
"@types/lodash.debounce": "^4.0.9",
- "@types/node": "^25.3.3",
+ "@types/node": "^25.3.5",
"@vitejs/plugin-vue": "6.0.4",
"@vue/compiler-sfc": "^3.5.29",
"@vue/test-utils": "^2.4.6",
@@ -97,7 +97,7 @@
"concurrently": "^9.2.1",
"eventsourcemock": "^2.0.0",
"jsdom": "^28.1.0",
- "lint-staged": "^16.3.1",
+ "lint-staged": "^16.3.2",
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2",
"simple-git-hooks": "^2.13.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 79f3b62c..edca4b75 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,8 +45,8 @@ importers:
specifier: ^1.2.4
version: 1.2.4
'@iconify-json/material-symbols':
- specifier: ^1.2.59
- version: 1.2.59
+ specifier: ^1.2.60
+ version: 1.2.60
'@iconify-json/mdi':
specifier: ^1.2.3
version: 1.2.3
@@ -61,7 +61,7 @@ importers:
version: 1.2.2
'@intlify/unplugin-vue-i18n':
specifier: ^11.0.7
- version: 11.0.7(@vue/compiler-dom@3.5.29)(eslint@9.19.0(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
+ version: 11.0.7(@vue/compiler-dom@3.5.29)(eslint@9.19.0(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
'@lezer/highlight':
specifier: ^1.2.3
version: 1.2.3
@@ -70,7 +70,7 @@ importers:
version: 0.5.19(tailwindcss@4.2.1)
'@tailwindcss/vite':
specifier: 4.2.1
- version: 4.2.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
+ version: 4.2.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vueuse/components':
specifier: ^14.2.1
version: 14.2.1(vue@3.5.29(typescript@5.9.3))
@@ -136,25 +136,25 @@ importers:
version: 31.0.0(vue@3.5.29(typescript@5.9.3))
unplugin-vue-macros:
specifier: ^2.14.5
- version: 2.14.5(@vueuse/core@14.2.1(vue@3.5.29(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))
+ version: 2.14.5(@vueuse/core@14.2.1(vue@3.5.29(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))
vite:
specifier: 7.3.1
- version: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite-plugin-vue-layouts:
specifier: ^0.11.0
- version: 0.11.0(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
+ version: 0.11.0(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
vite-svg-loader:
- specifier: ^5.1.0
- version: 5.1.0(vue@3.5.29(typescript@5.9.3))
+ specifier: ^5.1.1
+ version: 5.1.1(vue@3.5.29(typescript@5.9.3))
vitepress:
specifier: 1.6.4
- version: 1.6.4(@algolia/client-search@5.23.4)(@types/node@25.3.3)(fuse.js@7.1.0)(lightningcss@1.31.1)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(sortablejs@1.15.7)(terser@5.39.0)(typescript@5.9.3)
+ version: 1.6.4(@algolia/client-search@5.23.4)(@types/node@25.3.5)(fuse.js@7.1.0)(lightningcss@1.31.1)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(sortablejs@1.15.7)(terser@5.39.0)(typescript@5.9.3)
vue:
specifier: ^3.5.29
version: 3.5.29(typescript@5.9.3)
vue-i18n:
- specifier: ^11.2.8
- version: 11.2.8(vue@3.5.29(typescript@5.9.3))
+ specifier: ^11.3.0
+ version: 11.3.0(vue@3.5.29(typescript@5.9.3))
vue-router:
specifier: ^5.0.3
version: 5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
@@ -166,8 +166,8 @@ importers:
specifier: ^1.2.6
version: 1.2.6
'@iconify-json/material-symbols-light':
- specifier: ^1.2.59
- version: 1.2.59
+ specifier: ^1.2.60
+ version: 1.2.60
'@iconify-json/ri':
specifier: ^1.2.10
version: 1.2.10
@@ -184,11 +184,11 @@ importers:
specifier: ^4.0.9
version: 4.0.9
'@types/node':
- specifier: ^25.3.3
- version: 25.3.3
+ specifier: ^25.3.5
+ version: 25.3.5
'@vitejs/plugin-vue':
specifier: 6.0.4
- version: 6.0.4(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))
+ version: 6.0.4(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))
'@vue/compiler-sfc':
specifier: ^3.5.29
version: 3.5.29
@@ -211,8 +211,8 @@ importers:
specifier: ^28.1.0
version: 28.1.0
lint-staged:
- specifier: ^16.3.1
- version: 16.3.1
+ specifier: ^16.3.2
+ version: 16.3.2
prettier:
specifier: ^3.8.1
version: 3.8.1
@@ -224,13 +224,13 @@ importers:
version: 2.13.1
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@25.3.3)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.3.5)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
vitest:
specifier: ^4.0.18
- version: 4.0.18(@types/node@25.3.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ version: 4.0.18(@types/node@25.3.5)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue-component-type-helpers:
specifier: 3.2.5
version: 3.2.5
@@ -1003,11 +1003,11 @@ packages:
'@iconify-json/ion@1.2.6':
resolution: {integrity: sha512-JftEXKfjvJNn3SrGeSBrG/waRkjeTpLdMLNLwpAX4NgI14QgJoAeXEh2iZjNPqioAkeIgErX4Bi6mnFwpjk3BQ==}
- '@iconify-json/material-symbols-light@1.2.59':
- resolution: {integrity: sha512-0dO9tGoHI9aV8Y9J0vI5oH2AsFiN8GrWoCECUqokBqoH/8Y8lpTGzDs2vW1qomNQmiMrqxxvz+u/wkQAVGX2OA==}
+ '@iconify-json/material-symbols-light@1.2.60':
+ resolution: {integrity: sha512-rBdUMsTK9v8BPI5zM9/2eFwX0lHCYyki4FfcaHCnMDk0BcnfvGueEqFP1TyfUGk4peQpmGHR44ZJ0jW2bPAxBA==}
- '@iconify-json/material-symbols@1.2.59':
- resolution: {integrity: sha512-difuedW4jZyiDDu2SySjPCr+UsM65PRkEg3zUuKz0YRGMkNyBsk3R6j9s/ofP1NaKr2y32XxrzTawmnJZbzOAw==}
+ '@iconify-json/material-symbols@1.2.60':
+ resolution: {integrity: sha512-bmVrnz/9KJNh/j+nO3pyxzo0h8iENIKxv5RASDURhjKdPI53rRcKN7E2Gmwi25k9CBJ+IC6L9l0AGgngeqyRuw==}
'@iconify-json/mdi-light@1.2.2':
resolution: {integrity: sha512-86UV9uyNve8zRFWiPrOrrDp9GDzsZM7plYV/on4VjgLLqXlyriuy541eHZB7LIOzTUyIPVli7QiUpBbTtBhsFw==}
@@ -1048,18 +1048,30 @@ packages:
vue-i18n:
optional: true
- '@intlify/core-base@11.2.8':
- resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==}
+ '@intlify/core-base@11.3.0':
+ resolution: {integrity: sha512-NNX5jIwF4TJBe7RtSKDMOA6JD9mp2mRcBHAwt2X+Q8PvnZub0yj5YYXlFu2AcESdgQpEv/5Yx2uOCV/yh7YkZg==}
+ engines: {node: '>= 16'}
+
+ '@intlify/devtools-types@11.3.0':
+ resolution: {integrity: sha512-G9CNL4WpANWVdUjubOIIS7/D2j/0j+1KJmhBJxHilWNKr9mmt3IjFV3Hq4JoBP23uOoC5ynxz/FHZ42M+YxfGw==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.2.8':
resolution: {integrity: sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==}
engines: {node: '>= 16'}
+ '@intlify/message-compiler@11.3.0':
+ resolution: {integrity: sha512-RAJp3TMsqohg/Wa7bVF3cChRhecSYBLrTCQSj7j0UtWVFLP+6iEJoE2zb7GU5fp+fmG5kCbUdzhmlAUCWXiUJw==}
+ engines: {node: '>= 16'}
+
'@intlify/shared@11.2.8':
resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==}
engines: {node: '>= 16'}
+ '@intlify/shared@11.3.0':
+ resolution: {integrity: sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ==}
+ engines: {node: '>= 16'}
+
'@intlify/unplugin-vue-i18n@11.0.7':
resolution: {integrity: sha512-wswKprS1D8VfnxxVhKxug5wa3MbDSOcCoXOBjnzhMK+6NfP6h6UI8pFqSBIvcW8nPDuzweTc0Sk3PeBCcubfoQ==}
engines: {node: '>= 20'}
@@ -1623,10 +1635,6 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
- '@trysound/sax@0.2.0':
- resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
- engines: {node: '>=10.13.0'}
-
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@@ -1690,8 +1698,8 @@ packages:
'@types/node@24.10.9':
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
- '@types/node@25.3.3':
- resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==}
+ '@types/node@25.3.5':
+ resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -3170,8 +3178,8 @@ packages:
resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==}
engines: {node: '>= 12.0.0'}
- lint-staged@16.3.1:
- resolution: {integrity: sha512-bqvvquXzFBAlSbluugR4KXAe4XnO/QZcKVszpkBtqLWa2KEiVy8n6Xp38OeUbv/gOJOX4Vo9u5pFt/ADvbm42Q==}
+ lint-staged@16.3.2:
+ resolution: {integrity: sha512-xKqhC2AeXLwiAHXguxBjuChoTTWFC6Pees0SHPwOpwlvI3BH7ZADFPddAdN3pgo3aiKgPUx/bxE78JfUnxQnlg==}
engines: {node: '>=20.17'}
hasBin: true
@@ -3614,6 +3622,10 @@ packages:
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+ sax@1.5.0:
+ resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==}
+ engines: {node: '>=11.0.0'}
+
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
@@ -3768,8 +3780,8 @@ packages:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
- svgo@3.3.2:
- resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
+ svgo@3.3.3:
+ resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -4052,8 +4064,8 @@ packages:
vue: ^3.2.4
vue-router: ^4.0.11
- vite-svg-loader@5.1.0:
- resolution: {integrity: sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==}
+ vite-svg-loader@5.1.1:
+ resolution: {integrity: sha512-RPzcXA/EpKJA0585x58DBgs7my2VfeJ+j2j1EoHY4Zh82Y7hV4cR1fElgy2aZi85+QSrcLLoTStQ5uZjD68u+Q==}
peerDependencies:
vue: '>=3.2.13'
@@ -4183,8 +4195,8 @@ packages:
vue-component-type-helpers@3.2.5:
resolution: {integrity: sha512-tkvNr+bU8+xD/onAThIe7CHFvOJ/BO6XCOrxMzeytJq40nTfpGDJuVjyCM8ccGZKfAbGk2YfuZyDMXM56qheZQ==}
- vue-i18n@11.2.8:
- resolution: {integrity: sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==}
+ vue-i18n@11.3.0:
+ resolution: {integrity: sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA==}
engines: {node: '>= 16'}
peerDependencies:
vue: ^3.0.0
@@ -4931,11 +4943,11 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/material-symbols-light@1.2.59':
+ '@iconify-json/material-symbols-light@1.2.60':
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/material-symbols@1.2.59':
+ '@iconify-json/material-symbols@1.2.60':
dependencies:
'@iconify/types': 2.0.0
@@ -4975,7 +4987,7 @@ snapshots:
'@iconify/types': 2.0.0
mlly: 1.8.0
- '@intlify/bundle-utils@11.0.7(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))':
+ '@intlify/bundle-utils@11.0.7(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))':
dependencies:
'@intlify/message-compiler': 11.2.8
'@intlify/shared': 11.2.8
@@ -4987,26 +4999,39 @@ snapshots:
source-map-js: 1.2.1
yaml-eslint-parser: 1.3.0
optionalDependencies:
- vue-i18n: 11.2.8(vue@3.5.29(typescript@5.9.3))
+ vue-i18n: 11.3.0(vue@3.5.29(typescript@5.9.3))
- '@intlify/core-base@11.2.8':
+ '@intlify/core-base@11.3.0':
dependencies:
- '@intlify/message-compiler': 11.2.8
- '@intlify/shared': 11.2.8
+ '@intlify/devtools-types': 11.3.0
+ '@intlify/message-compiler': 11.3.0
+ '@intlify/shared': 11.3.0
+
+ '@intlify/devtools-types@11.3.0':
+ dependencies:
+ '@intlify/core-base': 11.3.0
+ '@intlify/shared': 11.3.0
'@intlify/message-compiler@11.2.8':
dependencies:
'@intlify/shared': 11.2.8
source-map-js: 1.2.1
+ '@intlify/message-compiler@11.3.0':
+ dependencies:
+ '@intlify/shared': 11.3.0
+ source-map-js: 1.2.1
+
'@intlify/shared@11.2.8': {}
- '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.29)(eslint@9.19.0(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))':
+ '@intlify/shared@11.3.0': {}
+
+ '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.29)(eslint@9.19.0(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.19.0(jiti@2.6.1))
- '@intlify/bundle-utils': 11.0.7(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))
+ '@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))
'@intlify/shared': 11.2.8
- '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.29)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
+ '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.29)(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.53.3)
'@typescript-eslint/scope-manager': 8.42.0
'@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.3)
@@ -5017,7 +5042,7 @@ snapshots:
unplugin: 2.3.11
vue: 3.5.29(typescript@5.9.3)
optionalDependencies:
- vue-i18n: 11.2.8(vue@3.5.29(typescript@5.9.3))
+ vue-i18n: 11.3.0(vue@3.5.29(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
@@ -5025,14 +5050,14 @@ snapshots:
- supports-color
- typescript
- '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.29)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))':
+ '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.29)(vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@babel/parser': 7.29.0
optionalDependencies:
'@intlify/shared': 11.2.8
'@vue/compiler-dom': 3.5.29
vue: 3.5.29(typescript@5.9.3)
- vue-i18n: 11.2.8(vue@3.5.29(typescript@5.9.3))
+ vue-i18n: 11.3.0(vue@3.5.29(typescript@5.9.3))
'@isaacs/cliui@8.0.2':
dependencies:
@@ -5430,14 +5455,12 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.2.1
- '@tailwindcss/vite@4.2.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
+ '@tailwindcss/vite@4.2.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
'@tailwindcss/node': 4.2.1
'@tailwindcss/oxide': 4.2.1
tailwindcss: 4.2.1
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
-
- '@trysound/sax@0.2.0': {}
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
'@tsconfig/node10@1.0.11': {}
@@ -5500,7 +5523,7 @@ snapshots:
dependencies:
undici-types: 7.16.0
- '@types/node@25.3.3':
+ '@types/node@25.3.5':
dependencies:
undici-types: 7.18.2
@@ -5551,15 +5574,15 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
- '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.3.3)(lightningcss@1.31.1)(terser@5.39.0))(vue@3.5.29(typescript@5.9.3))':
+ '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.3.5)(lightningcss@1.31.1)(terser@5.39.0))(vue@3.5.29(typescript@5.9.3))':
dependencies:
- vite: 5.4.21(@types/node@25.3.3)(lightningcss@1.31.1)(terser@5.39.0)
+ vite: 5.4.21(@types/node@25.3.5)(lightningcss@1.31.1)(terser@5.39.0)
vue: 3.5.29(typescript@5.9.3)
- '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))':
+ '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue: 3.5.29(typescript@5.9.3)
'@vitest/expect@4.0.18':
@@ -5571,13 +5594,13 @@ snapshots:
chai: 6.2.1
tinyrainbow: 3.0.3
- '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
+ '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
'@vitest/spy': 4.0.18
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
'@vitest/pretty-format@4.0.18':
dependencies:
@@ -5739,12 +5762,12 @@ snapshots:
transitivePeerDependencies:
- vue
- '@vue-macros/devtools@0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
+ '@vue-macros/devtools@0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
sirv: 3.0.1
vue: 3.5.29(typescript@5.9.3)
optionalDependencies:
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
transitivePeerDependencies:
- typescript
@@ -7150,7 +7173,7 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.31.1
lightningcss-win32-x64-msvc: 1.31.1
- lint-staged@16.3.1:
+ lint-staged@16.3.2:
dependencies:
commander: 14.0.3
listr2: 9.0.5
@@ -7583,6 +7606,8 @@ snapshots:
dependencies:
tslib: 2.8.1
+ sax@1.5.0: {}
+
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
@@ -7744,15 +7769,15 @@ snapshots:
dependencies:
has-flag: 4.0.0
- svgo@3.3.2:
+ svgo@3.3.3:
dependencies:
- '@trysound/sax': 0.2.0
commander: 7.2.0
css-select: 5.1.0
css-tree: 2.3.1
css-what: 6.1.0
csso: 5.0.5
picocolors: 1.1.1
+ sax: 1.5.0
symbol-tree@3.2.4: {}
@@ -7829,14 +7854,14 @@ snapshots:
transitivePeerDependencies:
- typescript
- ts-node@10.9.2(@types/node@25.3.3)(typescript@5.9.3):
+ ts-node@10.9.2(@types/node@25.3.5)(typescript@5.9.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
acorn: 8.14.1
acorn-walk: 8.3.4
arg: 4.1.3
@@ -7935,12 +7960,12 @@ snapshots:
optionalDependencies:
'@vueuse/core': 14.2.1(vue@3.5.29(typescript@5.9.3))
- unplugin-combine@1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)):
+ unplugin-combine@1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)):
optionalDependencies:
esbuild: 0.27.1
rollup: 4.53.3
unplugin: 1.16.1
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
unplugin-icons@23.0.1(@vue/compiler-sfc@3.5.29):
dependencies:
@@ -7983,7 +8008,7 @@ snapshots:
transitivePeerDependencies:
- vue
- unplugin-vue-macros@2.14.5(@vueuse/core@14.2.1(vue@3.5.29(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3)):
+ unplugin-vue-macros@2.14.5(@vueuse/core@14.2.1(vue@3.5.29(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 1.11.4(vue@3.5.29(typescript@5.9.3))
'@vue-macros/boolean-prop': 0.5.5(vue@3.5.29(typescript@5.9.3))
@@ -7998,7 +8023,7 @@ snapshots:
'@vue-macros/define-render': 1.6.6(vue@3.5.29(typescript@5.9.3))
'@vue-macros/define-slots': 1.2.6(vue@3.5.29(typescript@5.9.3))
'@vue-macros/define-stylex': 0.2.3(vue@3.5.29(typescript@5.9.3))
- '@vue-macros/devtools': 0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
+ '@vue-macros/devtools': 0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vue-macros/export-expose': 0.3.5(vue@3.5.29(typescript@5.9.3))
'@vue-macros/export-props': 0.6.5(vue@3.5.29(typescript@5.9.3))
'@vue-macros/export-render': 0.3.5(vue@3.5.29(typescript@5.9.3))
@@ -8015,7 +8040,7 @@ snapshots:
'@vue-macros/short-vmodel': 1.5.5(vue@3.5.29(typescript@5.9.3))
'@vue-macros/volar': 0.30.15(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))
unplugin: 1.16.1
- unplugin-combine: 1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
+ unplugin-combine: 1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
unplugin-vue-define-options: 1.5.5(vue@3.5.29(typescript@5.9.3))
vue: 3.5.29(typescript@5.9.3)
transitivePeerDependencies:
@@ -8071,33 +8096,36 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite-plugin-vue-layouts@0.11.0(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)):
+ vite-plugin-vue-layouts@0.11.0(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)):
dependencies:
debug: 4.4.0
fast-glob: 3.3.3
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue: 3.5.29(typescript@5.9.3)
vue-router: 5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
transitivePeerDependencies:
- supports-color
- vite-svg-loader@5.1.0(vue@3.5.29(typescript@5.9.3)):
+ vite-svg-loader@5.1.1(vue@3.5.29(typescript@5.9.3)):
dependencies:
- svgo: 3.3.2
+ debug: 4.4.3
+ svgo: 3.3.3
vue: 3.5.29(typescript@5.9.3)
+ transitivePeerDependencies:
+ - supports-color
- vite@5.4.21(@types/node@25.3.3)(lightningcss@1.31.1)(terser@5.39.0):
+ vite@5.4.21(@types/node@25.3.5)(lightningcss@1.31.1)(terser@5.39.0):
dependencies:
esbuild: 0.25.10
postcss: 8.5.6
rollup: 4.52.4
optionalDependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
fsevents: 2.3.3
lightningcss: 1.31.1
terser: 5.39.0
- vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
+ vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
dependencies:
esbuild: 0.27.1
fdir: 6.5.0(picomatch@4.0.3)
@@ -8106,7 +8134,7 @@ snapshots:
rollup: 4.53.3
tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.31.1
@@ -8114,7 +8142,7 @@ snapshots:
tsx: 4.19.2
yaml: 2.8.2
- vitepress@1.6.4(@algolia/client-search@5.23.4)(@types/node@25.3.3)(fuse.js@7.1.0)(lightningcss@1.31.1)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(sortablejs@1.15.7)(terser@5.39.0)(typescript@5.9.3):
+ vitepress@1.6.4(@algolia/client-search@5.23.4)(@types/node@25.3.5)(fuse.js@7.1.0)(lightningcss@1.31.1)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(sortablejs@1.15.7)(terser@5.39.0)(typescript@5.9.3):
dependencies:
'@docsearch/css': 3.8.2
'@docsearch/js': 3.8.2(@algolia/client-search@5.23.4)(react@18.3.1)(search-insights@2.17.3)
@@ -8123,7 +8151,7 @@ snapshots:
'@shikijs/transformers': 2.5.0
'@shikijs/types': 2.5.0
'@types/markdown-it': 14.1.2
- '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.3.3)(lightningcss@1.31.1)(terser@5.39.0))(vue@3.5.29(typescript@5.9.3))
+ '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.3.5)(lightningcss@1.31.1)(terser@5.39.0))(vue@3.5.29(typescript@5.9.3))
'@vue/devtools-api': 7.7.2
'@vue/shared': 3.5.18
'@vueuse/core': 12.8.2(typescript@5.9.3)
@@ -8132,7 +8160,7 @@ snapshots:
mark.js: 8.11.1
minisearch: 7.1.2
shiki: 2.5.0
- vite: 5.4.21(@types/node@25.3.3)(lightningcss@1.31.1)(terser@5.39.0)
+ vite: 5.4.21(@types/node@25.3.5)(lightningcss@1.31.1)(terser@5.39.0)
vue: 3.5.29(typescript@5.9.3)
optionalDependencies:
postcss: 8.5.6
@@ -8163,10 +8191,10 @@ snapshots:
- typescript
- universal-cookie
- vitest@4.0.18(@types/node@25.3.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
+ vitest@4.0.18(@types/node@25.3.5)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
dependencies:
'@vitest/expect': 4.0.18
- '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
+ '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vitest/pretty-format': 4.0.18
'@vitest/runner': 4.0.18
'@vitest/snapshot': 4.0.18
@@ -8183,10 +8211,10 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
jsdom: 28.1.0
transitivePeerDependencies:
- jiti
@@ -8207,10 +8235,11 @@ snapshots:
vue-component-type-helpers@3.2.5: {}
- vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)):
+ vue-i18n@11.3.0(vue@3.5.29(typescript@5.9.3)):
dependencies:
- '@intlify/core-base': 11.2.8
- '@intlify/shared': 11.2.8
+ '@intlify/core-base': 11.3.0
+ '@intlify/devtools-types': 11.3.0
+ '@intlify/shared': 11.3.0
'@vue/devtools-api': 6.6.4
vue: 3.5.29(typescript@5.9.3)
diff --git a/protos/types.proto b/protos/types.proto
index b3d30795..1bc0a631 100644
--- a/protos/types.proto
+++ b/protos/types.proto
@@ -111,4 +111,5 @@ message NotificationDispatcher {
string type = 3;
string url = 4;
string template = 5;
+ map headers = 6;
}
diff --git a/types/notification.go b/types/notification.go
index 2f27ce42..f3a8b27c 100644
--- a/types/notification.go
+++ b/types/notification.go
@@ -71,6 +71,7 @@ type DispatcherConfig struct {
Type string
URL string
Template string
+ Headers map[string]string
APIKey string
Prefix string
ExpiresAt *time.Time