Compare commits

...

92 Commits

Author SHA1 Message Date
Amir Raminfar 9e99e043cd chore: release v6.4.4 2024-04-09 14:00:14 -07:00
Amir Raminfar 9aaae8ee91 fix: fixes actions with new routes. fixes #2883 (#2884) 2024-04-09 20:59:38 +00:00
Amir Raminfar 5110456537 chore: updates packages (#2882) 2024-04-09 10:59:46 -07:00
Amir Raminfar 6fdd5b788f docs: adds filter documentation 2024-04-09 09:18:25 -07:00
Amir Raminfar 2b6fb8556f chore: release v6.4.3 2024-04-08 14:03:18 -07:00
Amir Raminfar af7ed8b424 chore: fixes interval healthcheck (#2881) 2024-04-08 14:02:59 -07:00
renovate[bot] c63dc9dc13 chore(deps): update docker/setup-buildx-action action to v3.3.0 (#2880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-08 19:04:00 +00:00
Amir Raminfar 83f488ecef fix: tries to reconnect of remote host disconnects (#2876) 2024-04-08 11:59:03 -07:00
renovate[bot] 3d2036c97a chore(deps): update all non-major dependencies (#2878)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-08 00:23:36 +00:00
renovate[bot] f3d49607a3 chore(deps): update all non-major dependencies (#2875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 18:06:51 +00:00
Amir Raminfar 9b641a99da fix: fixes background color with ansi colors. fixes #2873 (#2874) 2024-04-05 10:56:05 -07:00
renovate[bot] d09e179445 fix(deps): update all non-major dependencies (#2872)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-04 15:48:53 +00:00
renovate[bot] daffeb17f8 chore(deps): update all non-major dependencies (#2871)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-03 20:30:30 +00:00
Amir Raminfar 3c7df537ec chore: changes APIs to be more restful and prepare for services (#2869) 2024-04-02 10:01:02 -07:00
renovate[bot] 376318bbbf chore(deps): update all non-major dependencies (#2868)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 16:26:38 +00:00
renovate[bot] c448636641 chore(deps): update actions/configure-pages action to v5 (#2864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-30 07:39:13 -07:00
renovate[bot] 30323bf3af chore(deps): update dependency @types/node to ^20.12.2 (#2863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-30 14:35:02 +00:00
renovate[bot] 296ed7945b chore(deps): update all non-major dependencies (#2860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-29 15:00:33 +00:00
Amir Raminfar f343e12979 chore: enables static checker (#2859) 2024-03-28 10:35:10 -07:00
renovate[bot] b3b01d96a8 chore(deps): update all non-major dependencies (#2858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-28 15:24:17 +00:00
renovate[bot] 783b7f0307 fix(deps): update all non-major dependencies (#2857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-27 18:46:25 +00:00
Amir Raminfar cc26b1be81 chore: release v6.4.2 2024-03-26 09:07:37 -07:00
renovate[bot] 263ad3f235 chore(deps): update all non-major dependencies (#2855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 15:50:54 +00:00
Amir Raminfar 9c1b967502 feat: improves icons placement for expanding json (#2854) 2024-03-26 15:46:05 +00:00
renovate[bot] 21c775d40b fix(deps): update dependency @intlify/unplugin-vue-i18n to v4 (#2853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 08:43:20 -07:00
renovate[bot] 943d0a87a1 chore(deps): update all non-major dependencies (#2852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 14:35:49 +00:00
Amir Raminfar a08e85cf7b chore: cleans up tests (#2850) 2024-03-25 15:33:16 -07:00
Amir Raminfar 3a2546f919 feat: hides per page dropdown when total number of containers is less than a page (#2849) 2024-03-25 08:09:10 -07:00
renovate[bot] d55e5f1c53 fix(deps): update dependency unplugin-vue-macros to ^2.7.13 (#2848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 03:50:06 +00:00
Amir Raminfar 0639320dca chore: updates total test for collector (#2847) 2024-03-25 01:45:09 +00:00
renovate[bot] 293f6a075b fix(deps): update all non-major dependencies (#2846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 01:32:55 +00:00
Amir Raminfar ef0938d6b8 chore: adds tests (#2845) 2024-03-23 21:32:17 -07:00
renovate[bot] 192d3783af fix(deps): update all non-major dependencies (#2843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-22 19:11:29 +00:00
renovate[bot] 2d7c806390 fix(deps): update module github.com/docker/docker to v26 (#2841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-21 09:16:51 -07:00
renovate[bot] e643099f34 fix(deps): update all non-major dependencies (#2840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-21 16:14:48 +00:00
Amir Raminfar 821c3e90f2 chore: release v6.4.1 2024-03-21 09:09:25 -07:00
Amir Raminfar b72ee29aa3 fix: this fixes a critical bug where multiple stat collectors were competing and never stopping. see #2838 (#2839) 2024-03-21 16:09:01 +00:00
Amir Raminfar 83ef59cd88 chore: release v6.4.0 2024-03-20 11:37:27 -07:00
Amir Raminfar 0ca6624238 feat: deactivates collection of stats after 6 hours of idle (#2830) 2024-03-20 10:53:52 -07:00
renovate[bot] f9aabf138c fix(deps): update all non-major dependencies (#2837)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-20 16:12:39 +00:00
renovate[bot] 7ed2c50ba7 chore(deps): update all non-major dependencies (#2836)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 22:14:23 +00:00
Amir Raminfar 993b3fd2f6 fix: fixes parsing of logfmt logs with a regex. fixes #2834 (#2835) 2024-03-19 14:36:39 -07:00
Amir Raminfar 9bf6cd8821 chore: code fix 2024-03-19 14:27:57 -07:00
renovate[bot] cb99bcdc09 fix(deps): update dependency date-fns to ^3.6.0 (#2832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-18 03:52:02 +00:00
renovate[bot] a8e4267c67 chore(deps): update all non-major dependencies (#2831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-18 00:57:39 +00:00
renovate[bot] c0a80c5591 chore(deps): update all non-major dependencies (#2827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-15 16:42:44 +00:00
Amir Raminfar 7f38bdaf4b chore: code clean up 2024-03-14 12:22:32 -07:00
Amir Raminfar c2fce52e1c perf: adds caching and brotli (#2826) 2024-03-14 12:11:36 -07:00
Amir Raminfar 09a9886423 perf: makes messages for copy and paste lazy for minor perf improvement 2024-03-14 08:29:16 -07:00
Amir Raminfar e7cabc8857 chore: release v6.3.1 2024-03-13 08:37:55 -07:00
Amir Raminfar 7e3d501f0f fix: fixes global regex with g flag. fixes #2822 (#2825) 2024-03-13 15:36:55 +00:00
Amir Raminfar c2dbcd546c chore: release v6.3.0 2024-03-12 12:20:41 -07:00
Amir Raminfar ec2be9c0f2 feat: brings colors back and fixes #2639 (#2823) 2024-03-12 19:15:18 +00:00
Amir Raminfar 32ed13d7d7 fix: uses data attributes for auto theme and removes postcss-mixins (#2820) 2024-03-11 16:57:47 +00:00
renovate[bot] f5b4cfd55a fix(deps): update all non-major dependencies (#2819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-11 16:44:58 +00:00
renovate[bot] 4c07a7210b fix(deps): update all non-major dependencies (#2818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-11 01:05:59 +00:00
Fabeuss 34d8ef6913 feat: Adds french (#2815) 2024-03-10 06:32:41 -07:00
Amir Raminfar 7fbac3b118 chore: fix types 2024-03-08 11:41:46 -08:00
renovate[bot] 4e59cf3937 fix(deps): update dependency @intlify/unplugin-vue-i18n to v3 (#2813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 15:01:40 -08:00
renovate[bot] e02378f93f chore(deps): update all non-major dependencies (#2812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 22:04:10 +00:00
Nikolaos Pothitos 32b8b02607 docs: add missing base slash and remove non-existent nginx configuration (#2809) 2024-03-05 16:10:01 -08:00
renovate[bot] 721e8e9e36 chore(deps): update all non-major dependencies (#2808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 22:11:40 +00:00
Amir Raminfar 8055512bc4 chore: release v6.2.7 2024-03-05 10:46:33 -08:00
Amir Raminfar 61f26b7bc3 fix: redirects login page to previous page after successful login. see #2806 (#2807) 2024-03-05 18:42:46 +00:00
renovate[bot] e6528a7fc1 fix(deps): update dependency postcss-mixins to v10 (#2805)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 10:37:46 -08:00
renovate[bot] 378de1a12b fix(deps): update all non-major dependencies (#2804)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 23:23:22 +00:00
renovate[bot] c803fe3119 chore(deps): update dependency vue-tsc to v2 (#2803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 08:43:58 -08:00
renovate[bot] 0e1541b78d chore(deps): update all non-major dependencies (#2802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-03 22:07:55 +00:00
Amir Raminfar 1d14c6c6e7 chore: release v6.2.6 2024-03-01 13:09:02 -08:00
Amir Raminfar b78b70b233 feat: adds hideMenu as a query parameter (#2796) 2024-02-29 08:11:02 -08:00
renovate[bot] f9465a2d66 chore(deps): update all non-major dependencies (#2797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-28 18:30:49 +00:00
renovate[bot] a5da85d603 chore(deps): update all non-major dependencies to ^3.4.20 (#2795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 03:40:30 +00:00
renovate[bot] 2140fe3d37 fix(deps): update module github.com/puzpuzpuz/xsync/v3 to v3.1.0 (#2794)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-25 21:24:11 +00:00
renovate[bot] ff2e766324 chore(deps): update all non-major dependencies (#2791)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-24 16:21:17 +00:00
Amir Raminfar 336e8acd0b fix: improves fuzzy by using key down instead of up (#2790) 2024-02-23 21:09:14 +00:00
renovate[bot] a6c2c7dbbc fix(deps): update all non-major dependencies (#2789)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-21 17:38:43 +00:00
Amir Raminfar 8d844b4302 chore: release v6.2.5 2024-02-20 09:15:38 -08:00
Amir Raminfar ac66969126 chore: fixes types 2024-02-20 09:15:17 -08:00
renovate[bot] b896e16941 fix(deps): update all non-major dependencies (#2788)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-20 16:57:21 +00:00
Amir Raminfar 5b1b2463b0 fix: fixes missing nano seconds for parsing between logs. see #2783 (#2785) 2024-02-20 08:50:00 -08:00
Amir Raminfar 07d0c7fac5 fix: fixes changing items per page. fixes #2784 (#2786) 2024-02-19 23:12:16 +00:00
renovate[bot] d2d573c399 fix(deps): update all non-major dependencies (#2782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 01:57:52 +00:00
renovate[bot] 54ad9d094b chore(deps): update all non-major dependencies (#2781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 21:51:56 +00:00
renovate[bot] afb9bc971c fix(deps): update all non-major dependencies (#2778)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 16:42:38 +00:00
renovate[bot] 3e0d010390 chore(deps): update all non-major dependencies (#2777)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 16:39:46 +00:00
renovate[bot] 76e5b960aa fix(deps): update all non-major dependencies (#2776)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-12 20:16:36 +00:00
Amir Raminfar 152bdb8c8f feat: adds italian locale (#2775) 2024-02-12 15:53:06 +00:00
Amir Raminfar 77e49d132f chore: removes unused packages (#2772) 2024-02-11 13:43:55 -08:00
Amir Raminfar 3fe087935c chore: release v6.2.4 2024-02-10 09:46:49 -08:00
renovate[bot] b47cf7940a chore(deps): update all non-major dependencies (#2771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-10 17:16:41 +00:00
Amir Raminfar 420b398e50 chore: uses 1.22 for testing (#2770) 2024-02-10 17:13:19 +00:00
Ivan 91d79c99ea feat: add Content-Encoding for downloading logs (#2769) 2024-02-10 06:02:10 -08:00
62 changed files with 2656 additions and 2147 deletions
+4 -4
View File
@@ -31,7 +31,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
go-version: 1.22.x
check-latest: true
- name: Checkout code
uses: actions/checkout@v4
@@ -84,14 +84,14 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0
- name: Login to DockerHub
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v5.3.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+3 -3
View File
@@ -18,14 +18,14 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0
- name: Login to DockerHub
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v5.3.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
node-version: latest
cache: pnpm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@v5
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
+5 -5
View File
@@ -54,16 +54,16 @@ jobs:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
go-version: 1.22.x
check-latest: true
- name: Checkout code
uses: actions/checkout@v4
- name: Run Go Tests with Coverage
run: make test SKIP_ASSET=1
# - name: Stactic checker
# uses: dominikh/staticcheck-action@v1.3.0
# with:
# install-go: false
- name: Stactic checker
uses: dominikh/staticcheck-action@v1.3.1
with:
install-go: false
int-test:
name: Integration Tests
timeout-minutes: 60
+1 -1
View File
@@ -22,7 +22,7 @@ COPY public ./public
# Build assets
RUN pnpm build
FROM --platform=$BUILDPLATFORM golang:1.22.0-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.22.2-alpine AS builder
RUN apk add --no-cache ca-certificates && mkdir /dozzle
+5 -9
View File
@@ -3,6 +3,7 @@
</template>
<script lang="ts" setup>
const mode = useColorMode();
watchEffect(() => {
if (smallerScrollbars.value) {
document.documentElement.classList.add("has-custom-scrollbars");
@@ -10,16 +11,11 @@ watchEffect(() => {
document.documentElement.classList.remove("has-custom-scrollbars");
}
switch (lightTheme.value) {
case "dark":
document.documentElement.setAttribute("data-theme", "dark");
break;
case "light":
document.documentElement.setAttribute("data-theme", "light");
break;
default:
document.documentElement.removeAttribute("data-theme");
let theme = lightTheme.value;
if (theme === "auto") {
theme = mode.value;
}
document.documentElement.setAttribute("data-theme", theme);
});
</script>
<style lang="postcss">
+3
View File
@@ -230,6 +230,7 @@ declare global {
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useHead: typeof import('@vueuse/head')['useHead']
const useHosts: typeof import('./stores/hosts')['useHosts']
const useI18n: typeof import('vue-i18n')['useI18n']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
@@ -587,6 +588,7 @@ declare module 'vue' {
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
readonly useHead: UnwrapRef<typeof import('@vueuse/head')['useHead']>
readonly useHosts: UnwrapRef<typeof import('./stores/hosts')['useHosts']>
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
@@ -937,6 +939,7 @@ declare module '@vue/runtime-core' {
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
readonly useHead: UnwrapRef<typeof import('@vueuse/head')['useHead']>
readonly useHosts: UnwrapRef<typeof import('./stores/hosts')['useHosts']>
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
+2
View File
@@ -52,6 +52,8 @@ declare module 'vue' {
LogStd: typeof import('./components/LogViewer/LogStd.vue')['default']
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
'MaterialSymbols:collapseAllRounded': typeof import('~icons/material-symbols/collapse-all-rounded')['default']
'MaterialSymbols:expandAllRounded': typeof import('~icons/material-symbols/expand-all-rounded')['default']
'Mdi:announcement': typeof import('~icons/mdi/announcement')['default']
'Mdi:arrowUp': typeof import('~icons/mdi/arrow-up')['default']
'Mdi:check': typeof import('~icons/mdi/check')['default']
+2 -2
View File
@@ -1,12 +1,11 @@
!
<template>
<div class="text-right">
<div class="text-right" v-if="containers.length > pageSizes[0]">
Show per page
<dropdown-menu
class="dropdown-left btn-xs md:btn-sm"
v-model="perPage"
:options="pageSizes.map((i) => ({ label: i.toLocaleString(), value: i }))"
v-if="containers.length > 0"
/>
</div>
<table class="table table-lg bg-base">
@@ -129,6 +128,7 @@ const sortedContainers = computedWithControl(
const totalPages = computed(() => Math.ceil(sortedContainers.value.length / perPage.value));
const isPaginated = computed(() => totalPages.value > 1);
const currentPage = ref(1);
watch(perPage, () => (currentPage.value = 1));
const paginated = computed(() => {
const start = (currentPage.value - 1) * perPage.value;
const end = start + perPage.value;
+1 -1
View File
@@ -73,7 +73,7 @@ describe("<FuzzySearchModal />", () => {
test("choose baz", async () => {
const wrapper = createFuzzySearchModal();
await wrapper.find("input").setValue("baz");
await wrapper.find("input").trigger("keyup.enter");
await wrapper.find("input").trigger("keydown.enter");
expect(useRouter().push).toHaveBeenCalledWith({ name: "container-id", params: { id: "567" } });
});
});
+9 -5
View File
@@ -4,12 +4,12 @@
<mdi:magnify class="flex size-8" />
<input
tabindex="0"
class="input input-ghost input-lg flex-1 px-1"
class="input input-lg input-ghost flex-1 px-1"
ref="input"
@keyup.down="selectedIndex = Math.min(selectedIndex + 1, data.length - 1)"
@keyup.up="selectedIndex = Math.max(selectedIndex - 1, 0)"
@keyup.enter.exact="selected(data[selectedIndex].item)"
@keyup.alt.enter="addColumn(data[selectedIndex])"
@keydown.down="selectedIndex = Math.min(selectedIndex + 1, data.length - 1)"
@keydown.up="selectedIndex = Math.max(selectedIndex - 1, 0)"
@keydown.enter.exact="selected(data[selectedIndex].item)"
@keydown.alt.enter="addColumn(data[selectedIndex])"
v-model="query"
:placeholder="$t('placeholder.search-containers')"
/>
@@ -148,4 +148,8 @@ function matchedName({ item, matches = [] }: { item: { name: string }; matches?:
:deep(mark) {
@apply bg-transparent text-inherit underline underline-offset-2;
}
.menu a {
@apply transition-none duration-0;
}
</style>
+11 -16
View File
@@ -1,5 +1,10 @@
<template>
<div class="relative flex w-full gap-x-2">
<div class="group/item relative flex w-full gap-x-2" @click="expandToggle()">
<label class="swap swap-rotate invisible absolute -left-4 top-0.5 size-4 group-hover/item:visible">
<input type="checkbox" v-model="expanded" @click="expandToggle()" />
<material-symbols:expand-all-rounded class="swap-off text-secondary" />
<material-symbols:collapse-all-rounded class="swap-on text-secondary" />
</label>
<div v-if="showStd">
<log-std :std="logEntry.std"></log-std>
</div>
@@ -10,8 +15,8 @@
<log-level :level="logEntry.level"></log-level>
</div>
<div>
<ul class="fields cursor-pointer space-x-4" :class="{ expanded }" @click="expandToggle()">
<li v-for="(value, name) in validValues" class="inline-block">
<ul class="fields cursor-pointer space-x-4" :class="{ expanded }">
<li v-for="(value, name) in validValues">
<span class="text-light">{{ name }}=</span><span class="font-bold" v-if="value === null">&lt;null&gt;</span>
<template v-else-if="Array.isArray(value)">
<span class="font-bold" v-html="markSearch(JSON.stringify(value))"> </span>
@@ -24,7 +29,7 @@
</div>
<log-message-actions
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
:message="JSON.stringify(logEntry.message)"
:message="() => JSON.stringify(logEntry.message)"
:log-entry="logEntry"
/>
</div>
@@ -52,18 +57,8 @@ const validValues = computed(() => {
}
.fields {
&:hover {
&::after {
content: "expand json";
@apply ml-2 inline-block text-secondary;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
}
&.expanded:hover {
&::after {
content: "collapse json";
}
li {
@apply inline-flex;
}
}
</style>
+6 -6
View File
@@ -11,9 +11,9 @@
></field-list>
</template>
<template v-else-if="Array.isArray(value)">
<a @click="toggleField(name)" class="link-primary cursor-pointer">
{{ hasField(name) ? "remove" : "add" }}&nbsp;</a
>
<a @click.stop="toggleField(name)" class="link-primary mr-2 cursor-pointer">
{{ hasField(name) ? "remove" : "add" }}
</a>
<span class="text-light">{{ name }}=</span>[
<span class="font-bold" v-for="(item, index) in value">
<span v-html="JSON.stringify(item)"></span><span v-if="index !== value.length - 1">,</span>
@@ -21,9 +21,9 @@
]
</template>
<template v-else>
<a @click="toggleField(name)" class="link-primary cursor-pointer">
{{ hasField(name) ? "remove" : "add" }}&nbsp;</a
>
<a @click.stop="toggleField(name)" class="link-primary mr-2 cursor-pointer">
{{ hasField(name) ? "remove" : "add" }}
</a>
<span class="text-light">{{ name }}=</span><span class="font-bold" v-html="value"></span>
</template>
</li>
@@ -12,7 +12,7 @@
</a>
</li>
<li>
<a :href="`${base}/api/logs/download/${container.host}/${container.id}`" download>
<a :href="`${base}/api/hosts/${container.host}/containers/${container.id}/logs/download`" download>
<octicon:download-24 /> {{ $t("toolbar.download") }}
</a>
</li>
@@ -90,7 +90,7 @@ describe("<LogEventSource />", () => {
});
}
const sourceUrl = "/api/logs/stream/localhost/abc?stdout=1&stderr=1";
const sourceUrl = "/api/hosts/localhost/containers/abc/logs/stream?stdout=1&stderr=1";
test("renders correctly", async () => {
const wrapper = createLogEventSource();
@@ -2,7 +2,7 @@
<div class="flex gap-2">
<div
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
v-if="isSupported && message.trim() != ''"
v-if="isSupported"
:title="t('log_actions.copy_log')"
>
<span
@@ -32,7 +32,7 @@
import { LogEntry, JSONObject } from "@/models/LogEntry";
const { message, logEntry } = defineProps<{
message: string;
message: () => string;
logEntry: LogEntry<string | JSONObject>;
}>();
@@ -44,7 +44,7 @@ const { isSearching } = useSearchFilter();
const { handleJumpLineSelected } = useLogSearchContext();
async function copyLogMessageToClipBoard() {
await copy(message);
await copy(message());
if (copied.value) {
showToast(
+11 -2
View File
@@ -5,11 +5,11 @@
<log-level class="flex" :level="logEntry.level" :position="logEntry.position" />
<div
class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"
v-html="markSearch(logEntry.message)"
v-html="colorize(logEntry.message)"
></div>
<log-message-actions
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
:message="decodeXML(logEntry.message)"
:message="() => decodeXML(stripAnsi(logEntry.message))"
:log-entry="logEntry"
/>
</div>
@@ -17,10 +17,19 @@
<script lang="ts" setup>
import { SimpleLogEntry } from "@/models/LogEntry";
import { decodeXML } from "entities";
import AnsiConvertor from "ansi-to-html";
import stripAnsi from "strip-ansi";
const ansiConvertor = new AnsiConvertor({
escapeXML: false,
fg: "var(--base-content-color)",
bg: "var(--base-color)",
});
defineProps<{
logEntry: SimpleLogEntry;
}>();
const { markSearch } = useSearchFilter();
const colorize = (value: string) => markSearch(ansiConvertor.toHtml(value));
</script>
+6 -5
View File
@@ -20,18 +20,19 @@ let glopbalShow = globalShowPopup();
let show = ref(glopbalShow.value);
let delayedShow = refDebounced(show, 1000);
let content: HTMLElement | null = $ref(null);
let content = ref<HTMLElement>();
const onMouseEnter = (e: Event) => {
show.value = true;
glopbalShow.value = true;
if (e.target && content && e.target instanceof Element) {
if (content.value && e.target instanceof HTMLElement) {
const { left, top, width } = e.target.getBoundingClientRect();
const x = left + width + 10;
const y = top;
content.style.left = `${x}px`;
content.style.top = `${y}px`;
content.value.style.left = `${x}px`;
content.value.style.top = `${y}px`;
}
};
@@ -40,7 +41,7 @@ const onMouseLeave = () => {
glopbalShow.value = false;
};
const el = useCurrentElement();
const el: Ref<HTMLElement> = useCurrentElement();
useEventListener(() => el.value?.nextElementSibling, "mouseenter", onMouseEnter);
useEventListener(() => el.value?.nextElementSibling, "mouseleave", onMouseLeave);
</script>
+4 -12
View File
@@ -10,10 +10,11 @@
</div>
<transition :name="sessionHost ? 'slide-left' : 'slide-right'" mode="out-in">
<ul class="menu p-0" v-if="!sessionHost">
<li v-for="host in config.hosts">
<a @click.prevent="setHost(host.id)">
<li v-for="host in hosts">
<a @click.prevent="setHost(host.id)" :class="{ 'pointer-events-none text-base-content/50': !host.available }">
<ph:computer-tower />
{{ host.name }}
<span class="badge badge-error badge-xs p-1.5" v-if="!host.available">offline</span>
</a>
</li>
</ul>
@@ -68,6 +69,7 @@ import { sessionHost } from "@/composable/storage";
const store = useContainerStore();
const { activeContainers, visibleContainers, ready } = storeToRefs(store);
const { hosts } = useHosts();
function setHost(host: string | null) {
sessionHost.value = host;
@@ -116,16 +118,6 @@ const menuItems = computed(() => {
}
});
const hosts = computed(() =>
config.hosts.reduce(
(acc, item) => {
acc[item.id] = item;
return acc;
},
{} as Record<string, { name: string; id: string }>,
),
);
const activeContainersById = computed(() =>
activeContainers.value.reduce(
(acc, item) => {
+1 -1
View File
@@ -10,7 +10,7 @@ export const useContainerActions = () => {
});
async function actionHandler(action: ContainerActions) {
const actionUrl = `/api/actions/${action}/${container.value.host}/${container.value.id}`;
const actionUrl = `/api/hosts/${container.value.host}/containers/${container.value.id}/actions/${action}`;
const errors = {
404: "container not found",
+6 -2
View File
@@ -85,7 +85,9 @@ export function useLogStream() {
console.debug(`Connecting to ${containerId} with params`, params);
es = new EventSource(
withBase(`/api/logs/stream/${container.value.host}/${containerId}?${new URLSearchParams(params).toString()}`),
withBase(
`/api/hosts/${container.value.host}/containers/${containerId}/logs/stream?${new URLSearchParams(params).toString()}`,
),
);
es.addEventListener("container-stopped", () => {
close();
@@ -118,7 +120,9 @@ export function useLogStream() {
const logs = await (
await fetch(
withBase(`/api/logs/${container.value.host}/${containerId}?${new URLSearchParams(params).toString()}`),
withBase(
`/api/hosts/${container.value.host}/containers/${containerId}/logs?${new URLSearchParams(params).toString()}`,
),
)
).text();
if (logs) {
+1 -1
View File
@@ -25,7 +25,7 @@ function matchRecord(record: Record<string, any>, regex: RegExp): boolean {
export function useSearchFilter() {
const regex = $computed(() => {
const isSmartCase = debouncedSearchFilter.value === debouncedSearchFilter.value.toLowerCase();
return new RegExp(encodeXML(debouncedSearchFilter.value), isSmartCase ? "i" : undefined);
return new RegExp(encodeXML(debouncedSearchFilter.value), isSmartCase ? "ig" : "g");
});
function filteredMessages(messages: Ref<LogEntry<string | JSONObject>[]>) {
+5 -3
View File
@@ -1,8 +1,8 @@
<template>
<div>
<mobile-menu v-if="isMobile" @search="showFuzzySearch"></mobile-menu>
<mobile-menu v-if="isMobile && !forceMenuHidden" @search="showFuzzySearch"></mobile-menu>
<splitpanes @resized="onResized($event)">
<pane min-size="10" :size="menuWidth" v-if="!isMobile && !collapseNav">
<pane min-size="10" :size="menuWidth" v-if="!isMobile && !collapseNav && !forceMenuHidden">
<side-panel @search="showFuzzySearch"></side-panel>
</pane>
<pane min-size="10">
@@ -27,7 +27,7 @@
<label
class="btn btn-circle swap btn-neutral swap-rotate fixed -left-12 bottom-4 w-16 transition-all hover:-left-4"
:class="{ '!-left-6': collapseNav }"
v-if="!isMobile"
v-if="!isMobile && !forceMenuHidden"
>
<input type="checkbox" v-model="collapseNav" />
<mdi:chevron-right class="swap-on" />
@@ -79,6 +79,8 @@ const { toasts, removeToast } = useToast();
const modal = ref<HTMLDialogElement>();
const open = ref(false);
const searchParams = new URLSearchParams(window.location.search);
const forceMenuHidden = ref(searchParams.has("hideMenu"));
watch(open, () => {
if (open.value) {
+14 -30
View File
@@ -1,23 +1,5 @@
@import "splitpanes/dist/splitpanes.css";
@define-mixin light {
--base-lighter-color: 100% 0 0;
--base-color: 97% 0 0;
--base-darker-color: 90% 0 0;
--base-content-color: 33% 0 0;
--primary-color: 79.96% 0.143 176.65;
--secondary-color: 82.67% 0.15380739746702807 77.82;
}
@define-mixin dark {
--base-lighter-color: 25% 0 0;
--base-color: 18% 0 0;
--base-darker-color: 11% 0 0;
--base-content-color: 89.23% 0 0;
--primary-color: 70.96% 0.143 176.65;
--secondary-color: 91.36% 0.11098 90.1243;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -41,21 +23,23 @@
--n: var(--base-lighter-color);
}
html[data-theme="dark"] {
@mixin dark;
}
@media (prefers-color-scheme: dark) {
html {
@mixin dark;
}
--base-lighter-color: 25% 0 0;
--base-color: 18% 0 0;
--base-darker-color: 11% 0 0;
--base-content-color: 89.23% 0 0;
--primary-color: 70.96% 0.143 176.65;
--secondary-color: 91.36% 0.11098 90.1243;
}
html[data-theme="light"] {
@mixin light;
}
@media (prefers-color-scheme: light) {
html {
@mixin light;
}
--base-lighter-color: 100% 0 0;
--base-color: 97% 0 0;
--base-darker-color: 90% 0 0;
--base-content-color: 33% 0 0;
--primary-color: 79.96% 0.143 176.65;
--secondary-color: 82.67% 0.15380739746702807 77.82;
}
@media screen and (max-device-width: 480px) {
body {
-webkit-text-size-adjust: 100%;
+6 -1
View File
@@ -47,6 +47,7 @@ let error = $ref(false);
let username = $ref("");
let password = $ref("");
let form: HTMLFormElement | undefined = $ref();
const params = new URLSearchParams(window.location.search);
async function onLogin() {
const url = config.authProvider === "simple" ? "/api/token" : "/api/validateCredentials";
@@ -57,7 +58,11 @@ async function onLogin() {
if (response.status == 200) {
error = false;
window.location.href = withBase("/");
if (params.has("redirectUrl")) {
window.location.href = withBase(params.get("redirectUrl")!);
} else {
window.location.href = withBase("/");
}
} else {
error = true;
}
+6
View File
@@ -5,6 +5,7 @@ import { Container } from "@/models/Container";
import i18n from "@/modules/i18n";
const { showToast } = useToast();
const { markHostAvailable } = useHosts();
// @ts-ignore
const { t } = i18n.global;
@@ -68,6 +69,11 @@ export const useContainerStore = defineStore("container", () => {
}
});
es.addEventListener("host-unavailable", (e) => {
const hostId = (e as MessageEvent).data;
markHostAvailable(hostId, false);
});
es.addEventListener("container-health", (e) => {
const event = JSON.parse((e as MessageEvent).data) as { actorId: string; health: ContainerHealth };
const container = allContainersById.value[event.actorId];
+25
View File
@@ -0,0 +1,25 @@
type Host = {
name: string;
id: string;
available: boolean;
};
const hosts = computed(() =>
config.hosts.reduce(
(acc, item) => {
acc[item.id] = { ...item, available: true };
return acc;
},
{} as Record<string, Host>,
),
);
const markHostAvailable = (id: string, available: boolean) => {
hosts.value[id].available = available;
};
export function useHosts() {
return {
hosts,
markHostAvailable,
};
}
+9 -2
View File
@@ -44,7 +44,8 @@ services:
build:
context: .
depends_on:
- proxy
proxy:
condition: service_healthy
proxy:
container_name: proxy
@@ -53,12 +54,18 @@ services:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
healthcheck:
test: ["CMD", "nc", "-z", "127.0.0.1", "2375"]
interval: 5s
retries: 5
start_period: 5s
start_interval: 5s
ports:
- 2375:2375
playwright:
container_name: playwright
image: mcr.microsoft.com/playwright:v1.41.2-jammy
image: mcr.microsoft.com/playwright:v1.43.0-jammy
working_dir: /app
volumes:
- .:/app
+1
View File
@@ -69,6 +69,7 @@ export default defineConfig({
{ text: "Hostname", link: "/guide/hostname" },
{ text: "Remote Hosts", link: "/guide/remote-hosts" },
{ text: "Container Actions", link: "/guide/actions" },
{ text: "Filters", link: "/guide/filters" },
{ text: "Supported Env Vars", link: "/guide/supported-env-vars" },
{ text: "Logging Files on Disk", link: "/guide/log-files-on-disk" },
{ text: "Analytics", link: "/guide/analytics" },
+3 -5
View File
@@ -4,12 +4,12 @@ title: Changing Application Base
# Changing Dozzle Base
Dozzle by default mounts to "/". This can be changed with the `--base` flag. For example, if you want to mount to "/foobar" then you can use `--base foobar` or the env variable `DOZZLE_BASE`.
Dozzle by default mounts to "/". This can be changed with the `--base` flag. For example, if you want to mount to "/foobar" then you can use `--base /foobar` or the env variable `DOZZLE_BASE`.
::: code-group
```sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --base foobar
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --base /foobar
```
```yaml [docker-compose.yml]
@@ -22,7 +22,7 @@ services:
ports:
- 8080:8080
environment:
DOZZLE_BASE: foobar
DOZZLE_BASE: /foobar
```
:::
@@ -35,8 +35,6 @@ Here is an example with Nginx and proxy Dozzle with a different base:
```conf
location ^~ /foobar/ {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app dozzle;
set $upstream_port 8080;
set $upstream_proto http;
+30
View File
@@ -0,0 +1,30 @@
---
title: Filter
---
# Filtering Containers
Dozzle supports conditional filtering similar to Docker's [--filter](https://docs.docker.com/reference/cli/docker/container/ls/#filter) with `DOZZLE_FILTER` or `--filter`. Filters are passed directly to Docker to limit what Dozzle can see. For example, filtering by label is supported with `--filter "label=color"`, which is similar to `docker ps` command with `docker ps --filter "label=color"`.
::: code-group
```sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --filter label=color
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8080:8080
environment:
DOZZLE_FILTER: label=color
```
:::
Common filters are `name` or `label` to limit Dozzle's access to containers.
+4 -4
View File
@@ -3,11 +3,11 @@
"type": "module",
"name": "docs",
"devDependencies": {
"@unocss/preset-typography": "^0.58.5",
"@unocss/reset": "^0.58.5",
"@unocss/transformer-directives": "^0.58.5",
"@unocss/preset-typography": "^0.59.0",
"@unocss/reset": "^0.59.0",
"@unocss/transformer-directives": "^0.59.0",
"dozzle": "workspace:*",
"sitemap": "^7.1.1",
"unocss": "^0.58.5"
"unocss": "^0.59.0"
}
}
+268 -276
View File
@@ -6,14 +6,14 @@ settings:
devDependencies:
'@unocss/preset-typography':
specifier: ^0.58.5
version: 0.58.5
specifier: ^0.59.0
version: 0.59.0
'@unocss/reset':
specifier: ^0.58.5
version: 0.58.5
specifier: ^0.59.0
version: 0.59.0
'@unocss/transformer-directives':
specifier: ^0.58.5
version: 0.58.5
specifier: ^0.59.0
version: 0.59.0
dozzle:
specifier: workspace:*
version: link:..
@@ -21,17 +21,17 @@ devDependencies:
specifier: ^7.1.1
version: 7.1.1
unocss:
specifier: ^0.58.5
version: 0.58.5(postcss@8.4.32)(vite@5.0.10)
specifier: ^0.59.0
version: 0.59.0(postcss@8.4.32)(vite@5.0.10)
packages:
/@ampproject/remapping@2.2.1:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
/@ampproject/remapping@2.3.0:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.20
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
dev: true
/@antfu/install-pkg@0.1.1:
@@ -45,12 +45,12 @@ packages:
resolution: {integrity: sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==}
dev: true
/@babel/code-frame@7.23.5:
resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
/@babel/code-frame@7.24.2:
resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.23.4
chalk: 2.4.2
'@babel/highlight': 7.24.2
picocolors: 1.0.0
dev: true
/@babel/compat-data@7.23.5:
@@ -58,20 +58,20 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/core@7.23.9:
resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==}
/@babel/core@7.24.4:
resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.1
'@babel/code-frame': 7.23.5
'@babel/generator': 7.23.6
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.24.2
'@babel/generator': 7.24.4
'@babel/helper-compilation-targets': 7.23.6
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9)
'@babel/helpers': 7.23.9
'@babel/parser': 7.23.9
'@babel/template': 7.23.9
'@babel/traverse': 7.23.9
'@babel/types': 7.23.9
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4)
'@babel/helpers': 7.24.4
'@babel/parser': 7.24.4
'@babel/template': 7.24.0
'@babel/traverse': 7.24.1
'@babel/types': 7.24.0
convert-source-map: 2.0.0
debug: 4.3.4
gensync: 1.0.0-beta.2
@@ -81,13 +81,13 @@ packages:
- supports-color
dev: true
/@babel/generator@7.23.6:
resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
/@babel/generator@7.24.4:
resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.9
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.20
'@babel/types': 7.24.0
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2
dev: true
@@ -95,7 +95,7 @@ packages:
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.6
'@babel/types': 7.24.0
dev: true
/@babel/helper-compilation-targets@7.23.6:
@@ -109,19 +109,19 @@ packages:
semver: 6.3.1
dev: true
/@babel/helper-create-class-features-plugin@7.23.7(@babel/core@7.23.9):
resolution: {integrity: sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==}
/@babel/helper-create-class-features-plugin@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.9
'@babel/core': 7.24.4
'@babel/helper-annotate-as-pure': 7.22.5
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-function-name': 7.23.0
'@babel/helper-member-expression-to-functions': 7.23.0
'@babel/helper-optimise-call-expression': 7.22.5
'@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.9)
'@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.4)
'@babel/helper-skip-transparent-expression-wrappers': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
semver: 6.3.1
@@ -136,38 +136,38 @@ packages:
resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.23.9
'@babel/types': 7.23.9
'@babel/template': 7.24.0
'@babel/types': 7.24.0
dev: true
/@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.9
'@babel/types': 7.24.0
dev: true
/@babel/helper-member-expression-to-functions@7.23.0:
resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.6
'@babel/types': 7.24.0
dev: true
/@babel/helper-module-imports@7.22.15:
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.9
'@babel/types': 7.24.0
dev: true
/@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9):
/@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4):
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.9
'@babel/core': 7.24.4
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-module-imports': 7.22.15
'@babel/helper-simple-access': 7.22.5
@@ -179,21 +179,21 @@ packages:
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.6
'@babel/types': 7.24.0
dev: true
/@babel/helper-plugin-utils@7.22.5:
resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
/@babel/helper-plugin-utils@7.24.0:
resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-replace-supers@7.22.20(@babel/core@7.23.9):
resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
/@babel/helper-replace-supers@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.9
'@babel/core': 7.24.4
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-member-expression-to-functions': 7.23.0
'@babel/helper-optimise-call-expression': 7.22.5
@@ -203,21 +203,21 @@ packages:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.9
'@babel/types': 7.24.0
dev: true
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.6
'@babel/types': 7.24.0
dev: true
/@babel/helper-split-export-declaration@7.22.6:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.9
'@babel/types': 7.24.0
dev: true
/@babel/helper-string-parser@7.23.4:
@@ -235,131 +235,123 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/helpers@7.23.9:
resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==}
/@babel/helpers@7.24.4:
resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.23.9
'@babel/traverse': 7.23.9
'@babel/types': 7.23.9
'@babel/template': 7.24.0
'@babel/traverse': 7.24.1
'@babel/types': 7.24.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/highlight@7.23.4:
resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
/@babel/highlight@7.24.2:
resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.22.20
chalk: 2.4.2
js-tokens: 4.0.0
picocolors: 1.0.0
dev: true
/@babel/parser@7.23.9:
resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
/@babel/parser@7.24.4:
resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.23.9
'@babel/types': 7.24.0
dev: true
/@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
/@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.9
'@babel/helper-plugin-utils': 7.22.5
'@babel/core': 7.24.4
'@babel/helper-plugin-utils': 7.24.0
dev: true
/@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
/@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.9
'@babel/helper-plugin-utils': 7.22.5
'@babel/core': 7.24.4
'@babel/helper-plugin-utils': 7.24.0
dev: true
/@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
/@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.9
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9)
'@babel/helper-plugin-utils': 7.22.5
'@babel/core': 7.24.4
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4)
'@babel/helper-plugin-utils': 7.24.0
'@babel/helper-simple-access': 7.22.5
dev: true
/@babel/plugin-transform-typescript@7.23.6(@babel/core@7.23.9):
resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==}
/@babel/plugin-transform-typescript@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.9
'@babel/core': 7.24.4
'@babel/helper-annotate-as-pure': 7.22.5
'@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.9)
'@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.9)
'@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.4)
'@babel/helper-plugin-utils': 7.24.0
'@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4)
dev: true
/@babel/preset-typescript@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
/@babel/preset-typescript@7.24.1(@babel/core@7.24.4):
resolution: {integrity: sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.9
'@babel/helper-plugin-utils': 7.22.5
'@babel/core': 7.24.4
'@babel/helper-plugin-utils': 7.24.0
'@babel/helper-validator-option': 7.23.5
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.9)
'@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.9)
'@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.9)
'@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4)
'@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.4)
'@babel/plugin-transform-typescript': 7.24.1(@babel/core@7.24.4)
dev: true
/@babel/template@7.23.9:
resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==}
/@babel/template@7.24.0:
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.23.5
'@babel/parser': 7.23.9
'@babel/types': 7.23.9
'@babel/code-frame': 7.24.2
'@babel/parser': 7.24.4
'@babel/types': 7.24.0
dev: true
/@babel/traverse@7.23.9:
resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==}
/@babel/traverse@7.24.1:
resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.23.5
'@babel/generator': 7.23.6
'@babel/code-frame': 7.24.2
'@babel/generator': 7.24.4
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-function-name': 7.23.0
'@babel/helper-hoist-variables': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.9
'@babel/types': 7.23.9
'@babel/parser': 7.24.4
'@babel/types': 7.24.0
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types@7.23.6:
resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.23.4
'@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
dev: true
/@babel/types@7.23.9:
resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==}
/@babel/types@7.24.0:
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.23.4
@@ -592,13 +584,13 @@ packages:
- supports-color
dev: true
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
/@jridgewell/gen-mapping@0.3.5:
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.20
'@jridgewell/trace-mapping': 0.3.25
dev: true
/@jridgewell/resolve-uri@3.1.1:
@@ -606,8 +598,8 @@ packages:
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
/@jridgewell/set-array@1.2.1:
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
dev: true
@@ -615,8 +607,8 @@ packages:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
/@jridgewell/trace-mapping@0.3.20:
resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==}
/@jridgewell/trace-mapping@0.3.25:
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
dependencies:
'@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15
@@ -779,215 +771,215 @@ packages:
'@types/node': 17.0.45
dev: true
/@unocss/astro@0.58.5(vite@5.0.10):
resolution: {integrity: sha512-LtuVnj8oFAK9663OVhQO8KpdJFiOyyPsYfnOZlDCOFK3gHb/2WMrzdBwr1w8LoQF3bDedkFMKirVF7gWjyZiaw==}
/@unocss/astro@0.59.0(vite@5.0.10):
resolution: {integrity: sha512-1M1OeYO7rthZ8z5Xokb0hE89rXk9AIvELDMRJl0TxYJ8xwK824S2LxpyK52i2g3+S2XmJaQMFoRvfLdCNK+dMw==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
peerDependenciesMeta:
vite:
optional: true
dependencies:
'@unocss/core': 0.58.5
'@unocss/reset': 0.58.5
'@unocss/vite': 0.58.5(vite@5.0.10)
'@unocss/core': 0.59.0
'@unocss/reset': 0.59.0
'@unocss/vite': 0.59.0(vite@5.0.10)
vite: 5.0.10
transitivePeerDependencies:
- rollup
dev: true
/@unocss/cli@0.58.5:
resolution: {integrity: sha512-FzVVXO9ghsGtJpu9uR4o7JeM9gUfWNbVZZ/IfH+0WbDJuyx4rO/jwN55z0yA5QDkhvOz9DvzwPCBzLpTJ5q+Lw==}
/@unocss/cli@0.59.0:
resolution: {integrity: sha512-2aWSFJ1SCxJBjRAsRog7JvVFfnB4Dps+ol0IFQw99nxaQx8YNiGKN8/bZjkajr0Leo77eAhKoxh2+LjIjrkr6Q==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@ampproject/remapping': 2.2.1
'@ampproject/remapping': 2.3.0
'@rollup/pluginutils': 5.1.0
'@unocss/config': 0.58.5
'@unocss/core': 0.58.5
'@unocss/preset-uno': 0.58.5
'@unocss/config': 0.59.0
'@unocss/core': 0.59.0
'@unocss/preset-uno': 0.59.0
cac: 6.7.14
chokidar: 3.5.3
chokidar: 3.6.0
colorette: 2.0.20
consola: 3.2.3
fast-glob: 3.3.2
magic-string: 0.30.6
magic-string: 0.30.9
pathe: 1.1.2
perfect-debounce: 1.0.0
transitivePeerDependencies:
- rollup
dev: true
/@unocss/config@0.58.5:
resolution: {integrity: sha512-O1pLSeNXfG11QHaLSVwS9rJKvE4b9304IQ3UvOdbYN+7SAT4YTZ7JDU4ngO1KWyOFBO6RD0WspCR95pgqOqJiQ==}
/@unocss/config@0.59.0:
resolution: {integrity: sha512-uBsLXmT8Dap6YRHSdlsVLYev8L6JVK6vUFcVCe0w7B74TC+GceE13lNOG3Zhz1EVfHaOrUEcYFthDILUkjBR3Q==}
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.58.5
unconfig: 0.3.11
'@unocss/core': 0.59.0
unconfig: 0.3.12
dev: true
/@unocss/core@0.58.5:
resolution: {integrity: sha512-qbPqL+46hf1/UelQOwUwpAuvm6buoss43DPYHOPdfNJ+NTWkSpATQMF0JKT04QE0QRQbHNSHdMe9ariG+IIlCw==}
/@unocss/core@0.59.0:
resolution: {integrity: sha512-9tXL6TV4FRpmFy70dHryA5NHsS7bV/x771LOPnZyiw+TRK4oGVk96IsATOflcn7L3FbXQY1mV+8Uzhkhx2PY0A==}
dev: true
/@unocss/extractor-arbitrary-variants@0.58.5:
resolution: {integrity: sha512-KJQX0OJKzy4YjJo09h2la2Q+cn5IJ1JdyPVJJkzovHnv7jSBWzsfct+bj/6a+SJ4p4JBIqEJz3M/qxHv4EPJyA==}
/@unocss/extractor-arbitrary-variants@0.59.0:
resolution: {integrity: sha512-KYprrpJXgdoJZa4iVejW9uqht4W712Ra0x1gvznPW0BupIPF7vEyNAGvPTnB2y5Wb6OjNMiPbGnqL7BfBoxYSQ==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/inspector@0.58.5:
resolution: {integrity: sha512-cbJlIHEZ14puTtttf7sl+VZFDscV1DJiSseh9sSe0xJ/1NVBT9Bvkm09/1tnpLYAgF5gfa1CaCcjKmURgYzKrA==}
/@unocss/inspector@0.59.0:
resolution: {integrity: sha512-4xmc6HuAU12ED6SuGNr8QaKOHgskKGdLhKDHCQqFgYHw8gZoiBhmLcV4jP3Ef34JeWgQYbJ1cHQcYiHFXBmppQ==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/core': 0.59.0
'@unocss/rule-utils': 0.59.0
gzip-size: 6.0.0
sirv: 2.0.4
dev: true
/@unocss/postcss@0.58.5(postcss@8.4.32):
resolution: {integrity: sha512-m4L2YRdYfT6CV306Kl2VwEwbqa/92EpW4GE2Kqak1RuJyFJXBnWEEMJV4Uy6B1jWKLlCEWkuVUW33JUg7X6BxQ==}
/@unocss/postcss@0.59.0(postcss@8.4.32):
resolution: {integrity: sha512-1+kb6x+5cT5WiokBF8s3NgO1HxaF86vrPx3VAeOMSHTUNhzNvb72e6HBLRUp1Qu6kxbCLDixFpBtNZyH5ueoog==}
engines: {node: '>=14'}
peerDependencies:
postcss: ^8.4.21
dependencies:
'@unocss/config': 0.58.5
'@unocss/core': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/config': 0.59.0
'@unocss/core': 0.59.0
'@unocss/rule-utils': 0.59.0
css-tree: 2.3.1
fast-glob: 3.3.2
magic-string: 0.30.6
magic-string: 0.30.9
postcss: 8.4.32
dev: true
/@unocss/preset-attributify@0.58.5:
resolution: {integrity: sha512-OR4gUHamHCb4/LB/zZHlibaraTyILfFvRIzgmJnEb6lITGApQUl86qaJcTbTyfTfLVRufLG/JVeuz2HLUBPRXw==}
/@unocss/preset-attributify@0.59.0:
resolution: {integrity: sha512-jh1hXJQIygvagDwRuWX5pz3a8/PJGgMimzU6v6yWByOfvGIrLoxUFdJCc9IsvP6K9GUUqSBIyJDPx9364yysgQ==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/preset-icons@0.58.5:
resolution: {integrity: sha512-LDNXavHtWaIvMvBezT9O8yiqHJChVCEfTRO6YFVY0yy+wo5jHiuMh6iKeHVcwbYdn3NqHYmpi7b/hrXPMtODzA==}
/@unocss/preset-icons@0.59.0:
resolution: {integrity: sha512-P0tH0ueZYprU1XGVAJl3nDC9uhPBJ43bnsV098uebBa8G650j7W5kslvSfV3XxG+/iujo2k2EGKxja9/uR4E0g==}
dependencies:
'@iconify/utils': 2.1.22
'@unocss/core': 0.58.5
ofetch: 1.3.3
'@unocss/core': 0.59.0
ofetch: 1.3.4
transitivePeerDependencies:
- supports-color
dev: true
/@unocss/preset-mini@0.58.5:
resolution: {integrity: sha512-WqD31fKUAN28OCUOyi1uremmLk0eTMqtCizjbbXsY/DP6RKYUT7trFAtppTcHWFhSQcknb4FURfAZppACsTVQQ==}
/@unocss/preset-mini@0.59.0:
resolution: {integrity: sha512-UQ4+JyHHbtpfW6XyC1bsnoJx4MMCJKo+Y1n+5fYo9WRkVzZ7IkcZBeHG2wkNWH8TLBmzjZNvyUYEAi5lntDW1A==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/extractor-arbitrary-variants': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/core': 0.59.0
'@unocss/extractor-arbitrary-variants': 0.59.0
'@unocss/rule-utils': 0.59.0
dev: true
/@unocss/preset-tagify@0.58.5:
resolution: {integrity: sha512-UB9IXi8vA/SzmmRLMWR7bzeBpxpiRo7y9xk3ruvDddYlsyiwIeDIMwG23YtcA6q41FDQvkrmvTxUEH9LFlv6aA==}
/@unocss/preset-tagify@0.59.0:
resolution: {integrity: sha512-JsVGUmSusHR6BLacxuGOf0XOqtAzBaoas15RvTklmj0eOzh5ClhsqP9C7guENyCMqmCgg0xz/22u4F9hujP6kg==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/preset-typography@0.58.5:
resolution: {integrity: sha512-rFny4a9yxgY34XOom5euCqQaOLV8PpbTg0Pn+5FelUMG4OfMevTwBCe9JttFJcUc3cNTL2enkzIdMa3l66114g==}
/@unocss/preset-typography@0.59.0:
resolution: {integrity: sha512-HADPOJMeQM2O7eZuGBzKKMKTJw9wdWfJImftFAYC9+p42Kg8FNuLg9E10oZjejS8VDSxzcv9HKuyTXYvvDgAmg==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/preset-mini': 0.58.5
'@unocss/core': 0.59.0
'@unocss/preset-mini': 0.59.0
dev: true
/@unocss/preset-uno@0.58.5:
resolution: {integrity: sha512-vgq/R4f7RDmdROy+pX+PeE38I3SgYKd4LL7Wb1HJUaVwz7PkF0XHCynOTbwrPXnK1kp1cnZYYEww7/RiYp+IQQ==}
/@unocss/preset-uno@0.59.0:
resolution: {integrity: sha512-pNiyWZuByCq8hB14ITaJVtjFu02OYOce/I4dbKQ+SEWd1L3t6miplpKzsE+dptqH0iT33EWfQeXs6fkfjH+EZA==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/preset-mini': 0.58.5
'@unocss/preset-wind': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/core': 0.59.0
'@unocss/preset-mini': 0.59.0
'@unocss/preset-wind': 0.59.0
'@unocss/rule-utils': 0.59.0
dev: true
/@unocss/preset-web-fonts@0.58.5:
resolution: {integrity: sha512-WKZ5raSClFXhqzfAhApef3+fuMq6cjKBxvhJ1FBIxFKcSOvN8e2czty2iGQVl02yMsxBWMv0Bpfm7np+cCoI1w==}
/@unocss/preset-web-fonts@0.59.0:
resolution: {integrity: sha512-LH2BnYmxeVUqX3A4+bEX/EljV/82n5XHDwPk0VI3bJ9GWxnhTxkmagIXldmPFqVs8tIAYSDJbY31MGXR9WnqMA==}
dependencies:
'@unocss/core': 0.58.5
ofetch: 1.3.3
'@unocss/core': 0.59.0
ofetch: 1.3.4
dev: true
/@unocss/preset-wind@0.58.5:
resolution: {integrity: sha512-54RkjLmlqMUlC8o8nDCVzB25D1zzK4eth+/3uQzt739qU0U92NxuZKY21ADj9Rp/mVhKBV5FKuXPjmYc6yTQRQ==}
/@unocss/preset-wind@0.59.0:
resolution: {integrity: sha512-HazRIJDZ5/TZCZ3zC2KAd45UvmizPQi2uF7V3ZUqXQRGtrmZN24RsJkZNa4a3LiY2U0fEhHA7Pm6zPGx/nyeJg==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/preset-mini': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/core': 0.59.0
'@unocss/preset-mini': 0.59.0
'@unocss/rule-utils': 0.59.0
dev: true
/@unocss/reset@0.58.5:
resolution: {integrity: sha512-2wMrkCj3SSb5hrx9TKs5jZa34QIRkHv9FotbJutAPo7o8hx+XXn56ogzdoUrcFPJZJUx2R2nyOVbSlGMIjtFtw==}
/@unocss/reset@0.59.0:
resolution: {integrity: sha512-9f5DYoaqzlaFz2fAzAJOXWZ41Qmgg4u9WdzMQ7SYALeEq61khScaSrf8osNTKFZuRLKPWYwX6JErzdAkfIH/6A==}
dev: true
/@unocss/rule-utils@0.58.5:
resolution: {integrity: sha512-w0sGJoeUGwMWLVFLEE9PDiv/fQcQqZnTIIQLYNCjTdqXDRlwTp9ACW0h47x/hAAIXdOtEOOBuTfjGD79GznUmA==}
/@unocss/rule-utils@0.59.0:
resolution: {integrity: sha512-PDGAcyBFXqB7GHhKGRfajoiepL4A9SM4pyulMMT328H0uHEbwrB+niCVE/hubP2rCOGrrq7JH0nR4ftaC6m8Ow==}
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.58.5
magic-string: 0.30.6
'@unocss/core': 0.59.0
magic-string: 0.30.9
dev: true
/@unocss/scope@0.58.5:
resolution: {integrity: sha512-vSentagAwYTnThGRCjzZ6eNSSRuzdWBl21L1BbvVNM91Ss/FugQnZ1hd0m3TrVvvStYXnFVHMQ/MjCAEJ4cMYg==}
/@unocss/scope@0.59.0:
resolution: {integrity: sha512-6rPic8ed4MlFz9nkPL2GxfkmTEwuu8sAxODEDRB5ws2/JzEiZHFrBd7O/p/OJHvwiizmNJ1Y6i65LAxky3RNSg==}
dev: true
/@unocss/transformer-attributify-jsx-babel@0.58.5:
resolution: {integrity: sha512-IAWSSKN3V0D87DE8bqaaPrZBWOdWQ06QNfi9vRuQJfRWOui87ezi9+NffjcnQw/ap9xMk1O6z74/WOW3zo6uYA==}
/@unocss/transformer-attributify-jsx-babel@0.59.0:
resolution: {integrity: sha512-Q0jllcvNE5WkT3vfKlWl6ALtg6PYm3Pd8F2va4PL9x6LvI0KzPz8wA022z+UqX0lMR84bnfdGN3751zWJecLPg==}
dependencies:
'@babel/core': 7.23.9
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.9)
'@babel/preset-typescript': 7.23.3(@babel/core@7.23.9)
'@unocss/core': 0.58.5
'@babel/core': 7.24.4
'@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4)
'@babel/preset-typescript': 7.24.1(@babel/core@7.24.4)
'@unocss/core': 0.59.0
transitivePeerDependencies:
- supports-color
dev: true
/@unocss/transformer-attributify-jsx@0.58.5:
resolution: {integrity: sha512-sItEALyvAt3PZLd9Q1tlIATjaj3kWbS/qI3otUVsYBdZjP4UudzJ3D1fcWNL2WPlgz8KtlVzRUuxob8TQ4ibZg==}
/@unocss/transformer-attributify-jsx@0.59.0:
resolution: {integrity: sha512-4EoADTT/7H4Cmid11y5HZ4Bgc37qxAPcb1W0DBvXZJT1gh7N+4UFmR6s6s+N15IBUX7QKArUKh/U7ZwL9XZPMQ==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/transformer-compile-class@0.58.5:
resolution: {integrity: sha512-4MaxjaZo1rf5uHvDGa2mbnXxAYVYoj1+oRNpL4fE3FoExS1Ka2CiNGQn/S4bHMF51vmXMSWtOzurJpPD4BaJUQ==}
/@unocss/transformer-compile-class@0.59.0:
resolution: {integrity: sha512-9GXyPl/aYxCoHJbD6u6iVrCcyiy4kBUZlgoOqySUZfFCPhslPPyPUsksZSRebRuA4ys2/XSi6cRVhtAZfAdjBw==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/transformer-directives@0.58.5:
resolution: {integrity: sha512-allspF5TlT1B2bJSZ1houHScXOTaTPlatLiEmgQKzr/m93rCvktokaO5J6qeN2VXQdpTIsxdA5B8//7UkfTuIA==}
/@unocss/transformer-directives@0.59.0:
resolution: {integrity: sha512-YHwUw5ByUlY8v3/85q8qdoOUnPKsiQokEM2u8BxOZ8HFoMYb5xjCNjY0I85unvRaKhh1tnrZWG+tq1510RSpHQ==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/rule-utils': 0.58.5
'@unocss/core': 0.59.0
'@unocss/rule-utils': 0.59.0
css-tree: 2.3.1
dev: true
/@unocss/transformer-variant-group@0.58.5:
resolution: {integrity: sha512-SjUwGzKK5CVqn7Gg+3v3hV47ZUll7GcGu0vR3RCLO4gqEfFlZWMTHml1Sl2sY1WAca2iVcDRu+dp0RLxRG/dUA==}
/@unocss/transformer-variant-group@0.59.0:
resolution: {integrity: sha512-tjzWaMusoGy1uFEZuuzu04SOIooG2RkeX2wtlQpr2hM2Kl8ZG4QW+8nxClwLiLkarurBeFtTNXCn5jjL9MVg3g==}
dependencies:
'@unocss/core': 0.58.5
'@unocss/core': 0.59.0
dev: true
/@unocss/vite@0.58.5(vite@5.0.10):
resolution: {integrity: sha512-p4o1XNX1rvjmoUqSSdua8XyWNg/d+YUChDd2L/xEty+6j2qv0wUaohs3UQ87vWlv632/UmgdX+2MbrgtqthCtw==}
/@unocss/vite@0.59.0(vite@5.0.10):
resolution: {integrity: sha512-AfVw7PWjFb/+chqbGUjy0r/yFIWAkvPFUY5p4wXyLlPpbQjKlC/96Q771paLexvIhAvcYjUCrAgZSEMnk2JrSQ==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
dependencies:
'@ampproject/remapping': 2.2.1
'@ampproject/remapping': 2.3.0
'@rollup/pluginutils': 5.1.0
'@unocss/config': 0.58.5
'@unocss/core': 0.58.5
'@unocss/inspector': 0.58.5
'@unocss/scope': 0.58.5
'@unocss/transformer-directives': 0.58.5
chokidar: 3.5.3
'@unocss/config': 0.59.0
'@unocss/core': 0.59.0
'@unocss/inspector': 0.59.0
'@unocss/scope': 0.59.0
'@unocss/transformer-directives': 0.59.0
chokidar: 3.6.0
fast-glob: 3.3.2
magic-string: 0.30.6
magic-string: 0.30.9
vite: 5.0.10
transitivePeerDependencies:
- rollup
@@ -1059,8 +1051,8 @@ packages:
supports-color: 5.5.0
dev: true
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
/chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
@@ -1126,12 +1118,12 @@ packages:
ms: 2.1.2
dev: true
/defu@6.1.3:
resolution: {integrity: sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ==}
/defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
dev: true
/destr@2.0.2:
resolution: {integrity: sha512-65AlobnZMiCET00KaFFjUefxDX0khFA/E4myqZ7a6Sq1yZtR8+FVIvilVX66vF2uobSumxooYZChiRPCKNqhmg==}
/destr@2.0.3:
resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==}
dev: true
/duplexer@0.1.2:
@@ -1364,8 +1356,8 @@ packages:
yallist: 3.1.1
dev: true
/magic-string@0.30.6:
resolution: {integrity: sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==}
/magic-string@0.30.9:
resolution: {integrity: sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
@@ -1397,22 +1389,22 @@ packages:
engines: {node: '>=6'}
dev: true
/mlly@1.4.2:
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
dependencies:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.3.2
dev: true
/mlly@1.5.0:
resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==}
dependencies:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.3.2
ufo: 1.5.3
dev: true
/mlly@1.6.1:
resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==}
dependencies:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.5.3
dev: true
/mrmime@2.0.0:
@@ -1430,8 +1422,8 @@ packages:
hasBin: true
dev: true
/node-fetch-native@1.6.1:
resolution: {integrity: sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw==}
/node-fetch-native@1.6.4:
resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
dev: true
/node-releases@2.0.14:
@@ -1450,12 +1442,12 @@ packages:
path-key: 3.1.1
dev: true
/ofetch@1.3.3:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
/ofetch@1.3.4:
resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==}
dependencies:
destr: 2.0.2
node-fetch-native: 1.6.1
ufo: 1.3.2
destr: 2.0.3
node-fetch-native: 1.6.4
ufo: 1.5.3
dev: true
/onetime@5.1.2:
@@ -1645,24 +1637,24 @@ packages:
engines: {node: '>=6'}
dev: true
/ufo@1.3.2:
resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
/ufo@1.5.3:
resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
dev: true
/unconfig@0.3.11:
resolution: {integrity: sha512-bV/nqePAKv71v3HdVUn6UefbsDKQWRX+bJIkiSm0+twIds6WiD2bJLWWT3i214+J/B4edufZpG2w7Y63Vbwxow==}
/unconfig@0.3.12:
resolution: {integrity: sha512-oDtfWDC0TMYFuwdt7E7CaqYZGqq1wAiC12PRTFe/93IkgNi+wVlF/LCjcD/bgNkGoopb0RsU363Ge3YXy7NGSw==}
dependencies:
'@antfu/utils': 0.7.7
defu: 6.1.3
defu: 6.1.4
jiti: 1.21.0
mlly: 1.4.2
mlly: 1.6.1
dev: true
/unocss@0.58.5(postcss@8.4.32)(vite@5.0.10):
resolution: {integrity: sha512-0g4P6jLgRRNnhscxw7nQ9RHGrKJ1UPPiHPet+YT3TXUcmy4mTiYgo9+kGQf5bjyrzsELJ10cT6Qz2y6g9Tls4g==}
/unocss@0.59.0(postcss@8.4.32)(vite@5.0.10):
resolution: {integrity: sha512-mCF31cfzonVtCFnkW/D99Xf031vedQ4ysyWCmwFr6kVGISc4a1OVqrmC8nv1Lp+8sPHIY9umtpNLX4Iwli/w5w==}
engines: {node: '>=14'}
peerDependencies:
'@unocss/webpack': 0.58.5
'@unocss/webpack': 0.59.0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
peerDependenciesMeta:
'@unocss/webpack':
@@ -1670,26 +1662,26 @@ packages:
vite:
optional: true
dependencies:
'@unocss/astro': 0.58.5(vite@5.0.10)
'@unocss/cli': 0.58.5
'@unocss/core': 0.58.5
'@unocss/extractor-arbitrary-variants': 0.58.5
'@unocss/postcss': 0.58.5(postcss@8.4.32)
'@unocss/preset-attributify': 0.58.5
'@unocss/preset-icons': 0.58.5
'@unocss/preset-mini': 0.58.5
'@unocss/preset-tagify': 0.58.5
'@unocss/preset-typography': 0.58.5
'@unocss/preset-uno': 0.58.5
'@unocss/preset-web-fonts': 0.58.5
'@unocss/preset-wind': 0.58.5
'@unocss/reset': 0.58.5
'@unocss/transformer-attributify-jsx': 0.58.5
'@unocss/transformer-attributify-jsx-babel': 0.58.5
'@unocss/transformer-compile-class': 0.58.5
'@unocss/transformer-directives': 0.58.5
'@unocss/transformer-variant-group': 0.58.5
'@unocss/vite': 0.58.5(vite@5.0.10)
'@unocss/astro': 0.59.0(vite@5.0.10)
'@unocss/cli': 0.59.0
'@unocss/core': 0.59.0
'@unocss/extractor-arbitrary-variants': 0.59.0
'@unocss/postcss': 0.59.0(postcss@8.4.32)
'@unocss/preset-attributify': 0.59.0
'@unocss/preset-icons': 0.59.0
'@unocss/preset-mini': 0.59.0
'@unocss/preset-tagify': 0.59.0
'@unocss/preset-typography': 0.59.0
'@unocss/preset-uno': 0.59.0
'@unocss/preset-web-fonts': 0.59.0
'@unocss/preset-wind': 0.59.0
'@unocss/reset': 0.59.0
'@unocss/transformer-attributify-jsx': 0.59.0
'@unocss/transformer-attributify-jsx-babel': 0.59.0
'@unocss/transformer-compile-class': 0.59.0
'@unocss/transformer-directives': 0.59.0
'@unocss/transformer-variant-group': 0.59.0
'@unocss/vite': 0.59.0(vite@5.0.10)
vite: 5.0.10
transitivePeerDependencies:
- postcss
+25 -24
View File
@@ -4,7 +4,7 @@ require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/alexflint/go-arg v1.4.3
github.com/beme/abide v0.0.0-20190723115211-635a09831760
github.com/docker/docker v25.0.3+incompatible
github.com/docker/docker v26.0.0+incompatible
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1
@@ -13,24 +13,25 @@ require (
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.11.0
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/go-chi/chi/v5 v5.0.11
github.com/go-chi/jwtauth/v5 v5.3.0
github.com/PuerkitoBio/goquery v1.9.1
github.com/go-chi/chi/v5 v5.0.12
github.com/go-chi/jwtauth/v5 v5.3.1
github.com/go-logfmt/logfmt v0.6.0
github.com/puzpuzpuz/xsync/v3 v3.0.2
github.com/yuin/goldmark v1.7.0
github.com/goccy/go-json v0.10.2
github.com/puzpuzpuz/xsync/v3 v3.1.0
github.com/yuin/goldmark v1.7.1
)
require (
@@ -39,32 +40,32 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/httprc v1.0.5 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/mod v0.14.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
golang.org/x/tools v0.19.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
)
go 1.22.0
go 1.22.2
+47 -56
View File
@@ -2,14 +2,13 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7O
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
@@ -24,14 +23,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA=
github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY=
github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v26.0.0+incompatible h1:Ng2qi+gdKADUa/VM+6b6YaY2nlZhk/lVJiKR/2bMudU=
github.com/docker/docker v26.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -40,10 +35,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.3.0 h1:X7RKGks1lrVeIe2omGyz47pNaNjG2YmwlRN5UKhN8qg=
github.com/go-chi/jwtauth/v5 v5.3.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.3.1 h1:1ePWrjVctvp1tyBq5b/2ER8Th/+RbYc7x4qNsc5rh5A=
github.com/go-chi/jwtauth/v5 v5.3.1/go.mod h1:6Fl2RRmWXs3tJYE1IQGX81FsPoGqDwq9c15j52R5q80=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -76,100 +71,98 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY=
github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU=
github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0=
github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=
github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY=
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -182,22 +175,20 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@@ -212,8 +203,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -231,7 +222,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+18 -27
View File
@@ -61,7 +61,7 @@ type Client interface {
ListContainers() ([]Container, error)
FindContainer(string) (Container, error)
ContainerLogs(context.Context, string, string, StdType) (io.ReadCloser, error)
Events(context.Context, chan<- ContainerEvent) <-chan error
Events(context.Context, chan<- ContainerEvent) error
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, StdType) (io.ReadCloser, error)
ContainerStats(context.Context, string, chan<- ContainerStat) error
Ping(context.Context) (types.Ping, error)
@@ -224,7 +224,6 @@ func (d *_client) ContainerStats(ctx context.Context, id string, stats chan<- Co
return err
}
log.Debugf("starting to stream stats for: %s", id)
defer response.Body.Close()
decoder := json.NewDecoder(response.Body)
var v *types.StatsJSON
@@ -298,35 +297,27 @@ func (d *_client) ContainerLogs(ctx context.Context, id string, since string, st
return reader, nil
}
func (d *_client) Events(ctx context.Context, messages chan<- ContainerEvent) <-chan error {
dockerMessages, errors := d.cli.Events(ctx, types.EventsOptions{})
func (d *_client) Events(ctx context.Context, messages chan<- ContainerEvent) error {
dockerMessages, err := d.cli.Events(ctx, types.EventsOptions{})
go func() {
for {
select {
case <-ctx.Done():
return nil
case err := <-err:
return err
for {
select {
case <-ctx.Done():
return
case err := <-errors:
log.Fatalf("error while listening to docker events: %v. Exiting...", err)
case message, ok := <-dockerMessages:
if !ok {
log.Errorf("docker events channel closed")
return
}
if message.Type == "container" && len(message.Actor.ID) > 0 {
messages <- ContainerEvent{
ActorID: message.Actor.ID[:12],
Name: string(message.Action),
Host: d.host.ID,
}
case message := <-dockerMessages:
if message.Type == "container" && len(message.Actor.ID) > 0 {
messages <- ContainerEvent{
ActorID: message.Actor.ID[:12],
Name: string(message.Action),
Host: d.host.ID,
}
}
}
}()
}
return errors
}
func (d *_client) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) {
@@ -334,8 +325,8 @@ func (d *_client) ContainerLogsBetweenDates(ctx context.Context, id string, from
ShowStdout: stdType&STDOUT != 0,
ShowStderr: stdType&STDERR != 0,
Timestamps: true,
Since: from.Format(time.RFC3339),
Until: to.Format(time.RFC3339),
Since: from.Format(time.RFC3339Nano),
Until: to.Format(time.RFC3339Nano),
}
log.Debugf("fetching logs from Docker with option: %+v", options)
+59 -19
View File
@@ -2,7 +2,9 @@ package docker
import (
"context"
"errors"
"sync"
"sync/atomic"
"github.com/puzpuzpuz/xsync/v3"
log "github.com/sirupsen/logrus"
@@ -14,6 +16,9 @@ type ContainerStore struct {
client Client
statsCollector *StatsCollector
wg sync.WaitGroup
connected atomic.Bool
events chan ContainerEvent
ctx context.Context
}
func NewContainerStore(ctx context.Context, client Client) *ContainerStore {
@@ -23,25 +28,54 @@ func NewContainerStore(ctx context.Context, client Client) *ContainerStore {
subscribers: xsync.NewMapOf[context.Context, chan ContainerEvent](),
statsCollector: NewStatsCollector(client),
wg: sync.WaitGroup{},
events: make(chan ContainerEvent),
ctx: ctx,
}
s.wg.Add(1)
go s.init(ctx)
go s.statsCollector.StartCollecting(ctx)
go s.init()
return s
}
func (s *ContainerStore) List() []Container {
func (s *ContainerStore) checkConnectivity() error {
if s.connected.CompareAndSwap(false, true) {
go func() {
log.Debugf("subscribing to docker events from container store %s", s.client.Host())
err := s.client.Events(s.ctx, s.events)
if !errors.Is(err, context.Canceled) {
log.Errorf("docker store unexpectedly disconnected from docker events from %s with %v", s.client.Host(), err)
}
s.connected.Store(false)
}()
if containers, err := s.client.ListContainers(); err != nil {
return err
} else {
s.containers.Clear()
for _, c := range containers {
s.containers.Store(c.ID, &c)
}
}
}
return nil
}
func (s *ContainerStore) List() ([]Container, error) {
s.wg.Wait()
if err := s.checkConnectivity(); err != nil {
return nil, err
}
containers := make([]Container, 0)
s.containers.Range(func(_ string, c *Container) bool {
containers = append(containers, *c)
return true
})
return containers
return containers, nil
}
func (s *ContainerStore) Client() Client {
@@ -49,34 +83,40 @@ func (s *ContainerStore) Client() Client {
}
func (s *ContainerStore) Subscribe(ctx context.Context, events chan ContainerEvent) {
go func() {
if s.statsCollector.Start(s.ctx) {
log.Debug("clearing container stats as stats collector has been stopped")
s.containers.Range(func(_ string, c *Container) bool {
c.Stats.Clear()
return true
})
}
}()
s.subscribers.Store(ctx, events)
}
func (s *ContainerStore) Unsubscribe(ctx context.Context) {
s.subscribers.Delete(ctx)
s.statsCollector.Stop()
}
func (s *ContainerStore) SubscribeStats(ctx context.Context, stats chan ContainerStat) {
s.statsCollector.Subscribe(ctx, stats)
}
func (s *ContainerStore) init(ctx context.Context) {
events := make(chan ContainerEvent)
s.client.Events(ctx, events)
func (s *ContainerStore) init() {
stats := make(chan ContainerStat)
s.statsCollector.Subscribe(ctx, stats)
s.statsCollector.Subscribe(s.ctx, stats)
if containers, err := s.client.ListContainers(); err == nil {
for _, c := range containers {
s.containers.Store(c.ID, &c)
}
} else {
log.Fatalf("error listing containers: %v", err)
}
s.checkConnectivity()
s.wg.Done()
for {
select {
case event := <-events:
log.Debugf("received event: %+v", event)
case event := <-s.events:
log.Tracef("received event: %+v", event)
switch event.Name {
case "start":
if container, err := s.client.FindContainer(event.ActorID); err == nil {
@@ -116,7 +156,7 @@ func (s *ContainerStore) init(ctx context.Context) {
stat.ID = ""
container.Stats.Push(stat)
}
case <-ctx.Done():
case <-s.ctx.Done():
return
}
}
+28 -14
View File
@@ -4,6 +4,7 @@ import (
"context"
"testing"
"github.com/amir20/dozzle/internal/utils"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/mock"
)
@@ -23,9 +24,9 @@ func (m *mockedClient) FindContainer(id string) (Container, error) {
return args.Get(0).(Container), args.Error(1)
}
func (m *mockedClient) Events(ctx context.Context, events chan<- ContainerEvent) <-chan error {
func (m *mockedClient) Events(ctx context.Context, events chan<- ContainerEvent) error {
args := m.Called(ctx, events)
return args.Get(0).(chan error)
return args.Error(0)
}
func (m *mockedClient) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error {
@@ -33,6 +34,11 @@ func (m *mockedClient) ContainerStats(ctx context.Context, id string, stats chan
return args.Error(0)
}
func (m *mockedClient) Host() *Host {
args := m.Called()
return args.Get(0).(*Host)
}
func TestContainerStore_List(t *testing.T) {
client := new(mockedClient)
@@ -42,12 +48,18 @@ func TestContainerStore_List(t *testing.T) {
Name: "test",
},
}, nil)
client.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(make(chan error))
client.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
ctx := args.Get(0).(context.Context)
<-ctx.Done()
})
client.On("Host").Return(&Host{
ID: "localhost",
})
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
store := NewContainerStore(ctx, client)
containers := store.List()
containers, _ := store.List()
assert.Equal(t, containers[0].ID, "1234")
}
@@ -59,22 +71,24 @@ func TestContainerStore_die(t *testing.T) {
ID: "1234",
Name: "test",
State: "running",
Stats: utils.NewRingBuffer[ContainerStat](300),
},
}, nil)
client.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(make(chan error)).
client.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).
Run(func(args mock.Arguments) {
ctx := args.Get(0).(context.Context)
events := args.Get(1).(chan<- ContainerEvent)
go func() {
events <- ContainerEvent{
Name: "die",
ActorID: "1234",
Host: "localhost",
}
<-ctx.Done()
}()
events <- ContainerEvent{
Name: "die",
ActorID: "1234",
Host: "localhost",
}
<-ctx.Done()
})
client.On("Host").Return(&Host{
ID: "localhost",
})
client.On("ContainerStats", mock.Anything, "1234", mock.AnythingOfType("chan<- docker.ContainerStat")).Return(nil)
@@ -87,6 +101,6 @@ func TestContainerStore_die(t *testing.T) {
store.Subscribe(ctx, events)
<-events
containers := store.List()
containers, _ := store.List()
assert.Equal(t, containers[0].State, "exited")
}
+4 -5
View File
@@ -82,9 +82,7 @@ func (g *EventGenerator) consumeReader() {
message, streamType, readerError := readEvent(g.reader, g.tty)
if message != "" {
logEvent := createEvent(message, streamType)
if _, ok := logEvent.Message.(string); ok {
logEvent.Message = stripANSI(logEvent.Message.(string))
}
logEvent.Level = guessLogLevel(logEvent)
g.buffer <- logEvent
}
@@ -158,7 +156,8 @@ func readEvent(reader *bufio.Reader, tty bool) (string, StdType, error) {
}
}
var validLogFmtKey = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
var validLogFmtMessage = regexp.MustCompile(`([a-zA-Z0-9_.-]+)=(?:(?:"(.*)")|(?:(?:([^\s]+)[\s])))`)
var validLogFmtKey = regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`)
func createEvent(message string, streamType StdType) *LogEvent {
h := fnv.New32a()
@@ -182,7 +181,7 @@ func createEvent(message string, streamType StdType) *LogEvent {
} else {
logEvent.Message = data
}
} else if strings.Contains(message, "=") {
} else if validLogFmtMessage.MatchString(message) {
buffer := bufPool.Get().(*bytes.Buffer)
buffer.Reset()
defer bufPool.Put(buffer)
-1
View File
@@ -152,6 +152,5 @@ func Benchmark_readEvent(b *testing.B) {
for i := 0; i < b.N; i++ {
readEvent(reader, true)
// println(message, stream)
}
}
+4
View File
@@ -19,6 +19,10 @@ type Host struct {
ValidCerts bool `json:"-"`
}
func (h *Host) String() string {
return h.ID
}
func ParseConnection(connection string) (Host, error) {
parts := strings.Split(connection, "|")
if len(parts) > 2 {
+13 -12
View File
@@ -5,30 +5,31 @@ import (
"strings"
)
var KEY_VALUE_REGEX = regexp.MustCompile(`level=(\w+)`)
var LOG_LEVELS = []string{"error", "warn", "warning", "info", "debug", "trace", "fatal"}
var LOG_LEVELS_PLAIN = map[string]*regexp.Regexp{}
var LOG_LEVEL_BRACKET = map[string]*regexp.Regexp{}
var keyValueRegex = regexp.MustCompile(`level=(\w+)`)
var logLevels = []string{"error", "warn", "warning", "info", "debug", "trace", "fatal"}
var plainLevels = map[string]*regexp.Regexp{}
var bracketLevels = map[string]*regexp.Regexp{}
func init() {
for _, level := range LOG_LEVELS {
LOG_LEVELS_PLAIN[level] = regexp.MustCompile("(?i)^" + level + "[^a-z]")
for _, level := range logLevels {
plainLevels[level] = regexp.MustCompile("(?i)^" + level + "[^a-z]")
}
for _, level := range LOG_LEVELS {
LOG_LEVEL_BRACKET[level] = regexp.MustCompile("(?i)\\[ ?" + level + " ?\\]")
for _, level := range logLevels {
bracketLevels[level] = regexp.MustCompile("(?i)\\[ ?" + level + " ?\\]")
}
}
func guessLogLevel(logEvent *LogEvent) string {
switch value := logEvent.Message.(type) {
case string:
for _, level := range LOG_LEVELS {
if LOG_LEVELS_PLAIN[level].MatchString(value) {
value = stripANSI(value)
for _, level := range logLevels {
if plainLevels[level].MatchString(value) {
return level
}
if LOG_LEVEL_BRACKET[level].MatchString(value) {
if bracketLevels[level].MatchString(value) {
return level
}
@@ -37,7 +38,7 @@ func guessLogLevel(logEvent *LogEvent) string {
}
}
if matches := KEY_VALUE_REGEX.FindStringSubmatch(value); matches != nil {
if matches := keyValueRegex.FindStringSubmatch(value); matches != nil {
return matches[1]
}
+85 -26
View File
@@ -4,18 +4,27 @@ import (
"context"
"errors"
"io"
"sync"
"sync/atomic"
"time"
"github.com/puzpuzpuz/xsync/v3"
log "github.com/sirupsen/logrus"
)
type StatsCollector struct {
stream chan ContainerStat
subscribers *xsync.MapOf[context.Context, chan ContainerStat]
client Client
cancelers *xsync.MapOf[string, context.CancelFunc]
stream chan ContainerStat
subscribers *xsync.MapOf[context.Context, chan ContainerStat]
client Client
cancelers *xsync.MapOf[string, context.CancelFunc]
stopper context.CancelFunc
timer *time.Timer
mu sync.Mutex
totalStarted atomic.Int32
}
var timeToStop = 6 * time.Hour
func NewStatsCollector(client Client) *StatsCollector {
return &StatsCollector{
stream: make(chan ContainerStat),
@@ -29,40 +38,89 @@ func (c *StatsCollector) Subscribe(ctx context.Context, stats chan ContainerStat
c.subscribers.Store(ctx, stats)
}
func (sc *StatsCollector) StartCollecting(ctx context.Context) {
func (c *StatsCollector) forceStop() {
c.mu.Lock()
defer c.mu.Unlock()
if c.stopper != nil {
c.stopper()
c.stopper = nil
log.Debug("stopping container stats collector")
}
}
func (c *StatsCollector) Stop() {
c.mu.Lock()
defer c.mu.Unlock()
if c.totalStarted.Add(-1) == 0 {
log.Debugf("scheduled to stop container stats collector %s", c.client.Host())
c.timer = time.AfterFunc(timeToStop, func() {
c.forceStop()
})
}
}
func (c *StatsCollector) reset() {
c.mu.Lock()
defer c.mu.Unlock()
log.Debugf("resetting timer for container stats collector %s", c.client.Host())
if c.timer != nil {
c.timer.Stop()
}
c.timer = nil
}
func streamStats(parent context.Context, sc *StatsCollector, id string) {
ctx, cancel := context.WithCancel(parent)
sc.cancelers.Store(id, cancel)
log.Debugf("starting to stream stats for: %s", id)
if err := sc.client.ContainerStats(ctx, id, sc.stream); err != nil {
log.Debugf("stopping to stream stats for: %s", id)
if !errors.Is(err, context.Canceled) && !errors.Is(err, io.EOF) {
log.Errorf("unexpected error when streaming container stats: %v", err)
}
}
}
// Start starts the stats collector and blocks until it's stopped. It returns true if the collector was stopped, false if it was already running
func (sc *StatsCollector) Start(parentCtx context.Context) bool {
sc.reset()
sc.totalStarted.Add(1)
var ctx context.Context
sc.mu.Lock()
if sc.stopper != nil {
sc.mu.Unlock()
return false
}
ctx, sc.stopper = context.WithCancel(parentCtx)
sc.mu.Unlock()
if containers, err := sc.client.ListContainers(); err == nil {
for _, c := range containers {
if c.State == "running" {
go func(client Client, id string) {
ctx, cancel := context.WithCancel(ctx)
sc.cancelers.Store(id, cancel)
if err := client.ContainerStats(ctx, id, sc.stream); err != nil {
if !errors.Is(err, context.Canceled) && !errors.Is(err, io.EOF) {
log.Errorf("unexpected error when streaming container stats: %v", err)
}
}
}(sc.client, c.ID)
go streamStats(ctx, sc, c.ID)
}
}
} else {
log.Errorf("error while listing containers: %v", err)
}
events := make(chan ContainerEvent)
go func() {
log.Debugf("subscribing to docker events from stats collector %s", sc.client.Host())
err := sc.client.Events(context.Background(), events)
if !errors.Is(err, context.Canceled) {
log.Errorf("stats collector unexpectedly disconnected from docker events from %s with %v", sc.client.Host(), err)
}
sc.forceStop()
}()
go func() {
events := make(chan ContainerEvent)
sc.client.Events(ctx, events)
for event := range events {
switch event.Name {
case "start":
go func(client Client, id string) {
ctx, cancel := context.WithCancel(ctx)
sc.cancelers.Store(id, cancel)
if err := client.ContainerStats(ctx, id, sc.stream); err != nil {
if !errors.Is(err, context.Canceled) && !errors.Is(err, io.EOF) {
log.Errorf("unexpected error when streaming container stats: %v", err)
}
}
}(sc.client, event.ActorID)
go streamStats(ctx, sc, event.ActorID)
case "die":
if cancel, ok := sc.cancelers.LoadAndDelete(event.ActorID); ok {
@@ -75,7 +133,8 @@ func (sc *StatsCollector) StartCollecting(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
log.Info("stopped collecting container stats")
return true
case stat := <-sc.stream:
sc.subscribers.Range(func(c context.Context, stats chan ContainerStat) bool {
select {
+83
View File
@@ -0,0 +1,83 @@
package docker
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func startedCollector(ctx context.Context) *StatsCollector {
client := new(mockedClient)
client.On("ListContainers").Return([]Container{
{
ID: "1234",
Name: "test",
State: "running",
},
}, nil)
client.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).
Return(nil).
Run(func(args mock.Arguments) {
ctx := args.Get(0).(context.Context)
<-ctx.Done()
})
client.On("ContainerStats", mock.Anything, mock.Anything, mock.AnythingOfType("chan<- docker.ContainerStat")).
Return(nil).
Run(func(args mock.Arguments) {
stats := args.Get(2).(chan<- ContainerStat)
stats <- ContainerStat{
ID: "1234",
}
})
client.On("Host").Return(&Host{
ID: "localhost",
})
collector := NewStatsCollector(client)
stats := make(chan ContainerStat)
collector.Subscribe(ctx, stats)
go collector.Start(ctx)
<-stats
return collector
}
func TestCancelers(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
collector := startedCollector(ctx)
_, ok := collector.cancelers.Load("1234")
assert.True(t, ok, "canceler should be stored")
assert.False(t, collector.Start(ctx), "second start should return false")
assert.Equal(t, int32(2), collector.totalStarted.Load(), "total started should be 2")
collector.Stop()
assert.Equal(t, int32(1), collector.totalStarted.Load(), "total started should be 1")
}
func TestSecondStart(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
collector := startedCollector(ctx)
assert.False(t, collector.Start(ctx), "second start should return false")
assert.Equal(t, int32(2), collector.totalStarted.Load(), "total started should be 2")
collector.Stop()
assert.Equal(t, int32(1), collector.totalStarted.Load(), "total started should be 1")
}
func TestStop(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
collector := startedCollector(ctx)
collector.Stop()
assert.Equal(t, int32(0), collector.totalStarted.Load(), "total started should be 1")
}
+7
View File
@@ -31,6 +31,13 @@ func (r *RingBuffer[T]) Push(data T) {
}
}
func (r *RingBuffer[T]) Clear() {
r.mutex.Lock()
defer r.mutex.Unlock()
r.data = r.data[:0]
r.start = 0
}
func (r *RingBuffer[T]) Data() []T {
r.mutex.RLock()
defer r.mutex.RUnlock()
+33
View File
@@ -25,3 +25,36 @@ func TestRingBuffer(t *testing.T) {
t.Errorf("Expected data to be %v, got %v", expectedData, data)
}
}
func TestRingBuffer_MarshalJSON(t *testing.T) {
rb := NewRingBuffer[int](3)
rb.Push(1)
rb.Push(2)
rb.Push(3)
data, err := rb.MarshalJSON()
if err != nil {
t.Errorf("Expected error to be nil, got %v", err)
}
expectedData := []byte("[1,2,3]")
if !reflect.DeepEqual(data, expectedData) {
t.Errorf("Expected data to be %v, got %v", expectedData, data)
}
}
func TestRingBuffer_Clear(t *testing.T) {
rb := NewRingBuffer[int](3)
rb.Push(1)
rb.Push(2)
rb.Push(3)
rb.Clear()
data := rb.Data()
expectedData := []int{}
if !reflect.DeepEqual(data, expectedData) {
t.Errorf("Expected data to be %v, got %v", expectedData, data)
}
}
+2 -2
View File
@@ -37,9 +37,9 @@ HTTP/1.1 307 Temporary Redirect
Connection: close
Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
Content-Type: text/html; charset=utf-8
Location: /login
Location: /login?redirectUrl=/
<a href="/login">Temporary Redirect</a>.
<a href="/login?redirectUrl=/">Temporary Redirect</a>.
/* snapshot: Test_createRoutes_username_password */
HTTP/1.1 307 Temporary Redirect
+10 -10
View File
@@ -34,58 +34,58 @@ func Test_handler_containerActions_stop(t *testing.T) {
mockedClient := get_mocked_client()
handler := createHandler(mockedClient, nil, Config{Base: "/", EnableActions: true, Authorization: Authorization{Provider: NONE}})
req, err := http.NewRequest("POST", "/api/actions/stop/localhost/123", nil)
req, err := http.NewRequest("POST", "/api/hosts/localhost/containers/123/actions/stop", nil)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
assert.Equal(t, 200, rr.Code)
}
func Test_handler_containerActions_restart(t *testing.T) {
mockedClient := get_mocked_client()
handler := createHandler(mockedClient, nil, Config{Base: "/", EnableActions: true, Authorization: Authorization{Provider: NONE}})
req, err := http.NewRequest("POST", "/api/actions/restart/localhost/123", nil)
req, err := http.NewRequest("POST", "/api/hosts/localhost/containers/123/actions/restart", nil)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
assert.Equal(t, 200, rr.Code)
}
func Test_handler_containerActions_unknown_action(t *testing.T) {
mockedClient := get_mocked_client()
handler := createHandler(mockedClient, nil, Config{Base: "/", EnableActions: true, Authorization: Authorization{Provider: NONE}})
req, err := http.NewRequest("POST", "/api/actions/something-else/localhost/123", nil)
req, err := http.NewRequest("POST", "/api/hosts/localhost/containers/123/actions/something-else", nil)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 500)
assert.Equal(t, 500, rr.Code)
}
func Test_handler_containerActions_unknown_container(t *testing.T) {
mockedClient := get_mocked_client()
handler := createHandler(mockedClient, nil, Config{Base: "/", EnableActions: true, Authorization: Authorization{Provider: NONE}})
req, err := http.NewRequest("POST", "/api/actions/start/localhost/456", nil)
req, err := http.NewRequest("POST", "/api/hosts/localhost/containers/456/actions/start", nil)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 404)
assert.Equal(t, 404, rr.Code)
}
func Test_handler_containerActions_start(t *testing.T) {
mockedClient := get_mocked_client()
handler := createHandler(mockedClient, nil, Config{Base: "/", EnableActions: true, Authorization: Authorization{Provider: NONE}})
req, err := http.NewRequest("POST", "/api/actions/start/localhost/123", nil)
req, err := http.NewRequest("POST", "/api/hosts/localhost/containers/123/actions/start", nil)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
assert.Equal(t, 200, rr.Code)
}
+1 -1
View File
@@ -17,7 +17,7 @@ import (
func Test_handler_download_logs(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/download/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/download", nil)
require.NoError(t, err, "NewRequest should not return an error.")
mockedClient := new(MockedClient)
+20 -5
View File
@@ -44,11 +44,25 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
stats := make(chan docker.ContainerStat)
for _, store := range h.stores {
allContainers = append(allContainers, store.List()...)
if containers, err := store.List(); err == nil {
allContainers = append(allContainers, containers...)
} else {
log.Errorf("error listing containers: %v", err)
if _, err := fmt.Fprintf(w, "event: host-unavailable\ndata: %s\n\n", store.Client().Host().ID); err != nil {
log.Errorf("error writing event to event stream: %v", err)
}
}
store.SubscribeStats(ctx, stats)
store.Subscribe(ctx, events)
}
defer func() {
for _, store := range h.stores {
store.Unsubscribe(ctx)
}
}()
if err := sendContainersJSON(allContainers, w); err != nil {
log.Errorf("error writing containers to event stream: %v", err)
}
@@ -80,10 +94,11 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
case "start", "die":
if event.Name == "start" {
log.Debugf("found new container with id: %v", event.ActorID)
containers := h.stores[event.Host].List()
if err := sendContainersJSON(containers, w); err != nil {
log.Errorf("error encoding containers to stream: %v", err)
return
if containers, err := h.stores[event.Host].List(); err == nil {
if err := sendContainersJSON(containers, w); err != nil {
log.Errorf("error encoding containers to stream: %v", err)
return
}
}
}
+19 -17
View File
@@ -21,26 +21,24 @@ func Test_handler_streamEvents_happy(t *testing.T) {
require.NoError(t, err, "NewRequest should not return an error.")
mockedClient := new(MockedClient)
errChannel := make(chan error)
mockedClient.On("ListContainers").Return([]docker.Container{}, nil)
mockedClient.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(errChannel).Run(func(args mock.Arguments) {
mockedClient.On("Events", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
messages := args.Get(1).(chan<- docker.ContainerEvent)
go func() {
time.Sleep(50 * time.Millisecond)
messages <- docker.ContainerEvent{
Name: "start",
ActorID: "1234",
Host: "localhost",
}
messages <- docker.ContainerEvent{
Name: "something-random",
ActorID: "1234",
Host: "localhost",
}
time.Sleep(50 * time.Millisecond)
cancel()
}()
time.Sleep(50 * time.Millisecond)
messages <- docker.ContainerEvent{
Name: "start",
ActorID: "1234",
Host: "localhost",
}
messages <- docker.ContainerEvent{
Name: "something-random",
ActorID: "1234",
Host: "localhost",
}
time.Sleep(50 * time.Millisecond)
cancel()
})
mockedClient.On("FindContainer", "1234").Return(docker.Container{
ID: "1234",
@@ -49,6 +47,10 @@ func Test_handler_streamEvents_happy(t *testing.T) {
Stats: utils.NewRingBuffer[docker.ContainerStat](300), // 300 seconds of stats
}, nil)
mockedClient.On("Host").Return(&docker.Host{
ID: "localhost",
})
clients := map[string]docker.Client{
"localhost": mockedClient,
}
+15 -3
View File
@@ -3,7 +3,10 @@ package web
import (
"html/template"
"io"
"mime"
"path/filepath"
"sort"
"strings"
"github.com/goccy/go-json"
@@ -18,9 +21,18 @@ import (
)
func (h *handler) index(w http.ResponseWriter, req *http.Request) {
_, err := h.content.Open(req.URL.Path)
path := req.URL.Path
_, err := h.content.Open(path)
if err == nil && req.URL.Path != "" && req.URL.Path != "/" {
fileServer.ServeHTTP(w, req)
w.Header().Set("Cache-Control", "max-age=31536000, immutable")
// if brotli is enabled, then just send over the compressed file
if file, err := h.content.Open(path + ".br"); strings.Contains(req.Header.Get("Accept-Encoding"), "br") && err == nil {
w.Header().Set("Content-Encoding", "br")
w.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(path)))
io.Copy(w, file)
} else {
fileServer.ServeHTTP(w, req)
}
} else {
h.executeTemplate(w, req)
}
@@ -66,7 +78,7 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
return
} else if h.config.Authorization.Provider == SIMPLE && req.URL.Path != "login" {
log.Debugf("Redirecting to login page for url /%s", req.URL.String())
http.Redirect(w, req, path.Clean(h.config.Base+"/login"), http.StatusTemporaryRedirect)
http.Redirect(w, req, path.Clean(h.config.Base+"/login")+"?redirectUrl=/"+req.URL.String(), http.StatusTemporaryRedirect)
return
}
+16 -5
View File
@@ -3,6 +3,7 @@ package web
import (
"compress/gzip"
"context"
"strings"
"github.com/goccy/go-json"
@@ -30,12 +31,22 @@ func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
}
now := time.Now()
nowFmt := now.Format("2006-01-02T15-04-05")
contentDisposition := fmt.Sprintf("attachment; filename=%s-%s.log", container.Name, nowFmt)
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Disposition", contentDisposition)
w.Header().Set("Content-Encoding", "gzip")
w.Header().Set("Content-Type", "application/text")
} else {
w.Header().Set("Content-Disposition", contentDisposition+".gz")
w.Header().Set("Content-Type", "application/gzip")
}
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s-%s.log.gz", container.Name, now.Format("2006-01-02T15-04-05")))
w.Header().Set("Content-Type", "application/gzip")
zw := gzip.NewWriter(w)
defer zw.Close()
zw.Name = fmt.Sprintf("%s-%s.log", container.Name, now.Format("2006-01-02T15-04-05"))
zw.Name = fmt.Sprintf("%s-%s.log", container.Name, nowFmt)
zw.Comment = "Logs generated by Dozzle"
zw.ModTime = now
@@ -54,8 +65,8 @@ func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/x-jsonl; charset=UTF-8")
from, _ := time.Parse(time.RFC3339, r.URL.Query().Get("from"))
to, _ := time.Parse(time.RFC3339, r.URL.Query().Get("to"))
from, _ := time.Parse(time.RFC3339Nano, r.URL.Query().Get("from"))
to, _ := time.Parse(time.RFC3339Nano, r.URL.Query().Get("to"))
id := chi.URLParam(r, "id")
var stdTypes docker.StdType
+7 -8
View File
@@ -20,7 +20,7 @@ import (
func Test_handler_streamLogs_happy(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
q := req.URL.Query()
q.Add("stdout", "true")
q.Add("stderr", "true")
@@ -44,7 +44,7 @@ func Test_handler_streamLogs_happy(t *testing.T) {
func Test_handler_streamLogs_happy_with_id(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
q := req.URL.Query()
q.Add("stdout", "true")
q.Add("stderr", "true")
@@ -68,7 +68,7 @@ func Test_handler_streamLogs_happy_with_id(t *testing.T) {
func Test_handler_streamLogs_happy_container_stopped(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
q := req.URL.Query()
q.Add("stdout", "true")
q.Add("stderr", "true")
@@ -89,7 +89,7 @@ func Test_handler_streamLogs_happy_container_stopped(t *testing.T) {
func Test_handler_streamLogs_error_finding_container(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
q := req.URL.Query()
q.Add("stdout", "true")
q.Add("stderr", "true")
@@ -109,7 +109,7 @@ func Test_handler_streamLogs_error_finding_container(t *testing.T) {
func Test_handler_streamLogs_error_reading(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
q := req.URL.Query()
q.Add("stdout", "true")
q.Add("stderr", "true")
@@ -130,7 +130,7 @@ func Test_handler_streamLogs_error_reading(t *testing.T) {
func Test_handler_streamLogs_error_std(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/stream/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs/stream", nil)
require.NoError(t, err, "NewRequest should not return an error.")
@@ -143,10 +143,9 @@ func Test_handler_streamLogs_error_std(t *testing.T) {
mockedClient.AssertExpectations(t)
}
// for /api/logs
func Test_handler_between_dates(t *testing.T) {
id := "123456"
req, err := http.NewRequest("GET", "/api/logs/localhost/"+id, nil)
req, err := http.NewRequest("GET", "/api/hosts/localhost/containers/"+id+"/logs", nil)
require.NoError(t, err, "NewRequest should not return an error.")
from, _ := time.Parse(time.RFC3339, "2018-01-01T00:00:00Z")
+4 -4
View File
@@ -90,12 +90,12 @@ func createRouter(h *handler) *chi.Mux {
if h.config.Authorization.Provider != NONE {
r.Use(auth.RequireAuthentication)
}
r.Get("/api/logs/stream/{host}/{id}", h.streamLogs)
r.Get("/api/logs/download/{host}/{id}", h.downloadLogs)
r.Get("/api/logs/{host}/{id}", h.fetchLogsBetweenDates)
r.Get("/api/hosts/{host}/containers/{id}/logs/stream", h.streamLogs)
r.Get("/api/hosts/{host}/containers/{id}/logs/download", h.downloadLogs)
r.Get("/api/hosts/{host}/containers/{id}/logs", h.fetchLogsBetweenDates)
r.Get("/api/events/stream", h.streamEvents)
if h.config.EnableActions {
r.Post("/api/actions/{action}/{host}/{id}", h.containerActions)
r.Post("/api/hosts/{host}/containers/{id}/actions/{action}", h.containerActions)
}
r.Get("/api/releases", h.releases)
r.Get("/api/profile/avatar", h.avatar)
+2 -2
View File
@@ -40,9 +40,9 @@ func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since strin
return args.Get(0).(io.ReadCloser), args.Error(1)
}
func (m *MockedClient) Events(ctx context.Context, events chan<- docker.ContainerEvent) <-chan error {
func (m *MockedClient) Events(ctx context.Context, events chan<- docker.ContainerEvent) error {
args := m.Called(ctx, events)
return args.Get(0).(chan error)
return args.Error(0)
}
func (m *MockedClient) ContainerStats(context.Context, string, chan<- docker.ContainerStat) error {
+101
View File
@@ -0,0 +1,101 @@
toolbar:
clear: Effacer
download: Téléchargement
search: Chercher
show: Montrer seulement {std}
show-all: Afficher tous les flux
stop: Stop
start: Démarrer
restart: Redémarrer
label:
containers: Conteneurs
running-containers: Conteneurs en execution
all-containers: Tous les conteneurs
total-containers: Total des conteneurs
running: En execution
total-cpu-usage: Util. totale CPU
total-mem-usage: Util. totale Mem
dozzle-version: Version de Dozzle
all: Tout
host: Hôte
password: Mot de passe
username: Nom d'utilisateur
container-name: Nom du conteneur
status: Status
created: Créé
avg-cpu: Moyenne CPU (%)
avg-mem: Moyenne MEM (%)
pinned: Epinglé
tooltip:
search: Recherche de conteneurs (⌘ + k, ⌃k)
pin-column: Epinglé en colonne
error:
page-not-found: Cette page n'existe pas
invalid-auth: Nom d'utilisateur ou mot de passe non valides
logs-skipped: Entrées {total} ignorées
container-not-found: Conteneur non trouvé
events-stream:
title: Erreur inattendue
message: >-
L'interface utilisateur de Dozzle n'a pas pu se connecter à l'API. Veuillez vérifier les paramètres de votre réseau.
Si vous utilisez un proxy inversé, assurez-vous qu'il est configuré correctement.
events-timeout:
title: Quelque chose ne va pas
message: >-
La connexion de l'interface utilisateur de Dozzle à l'API a mis trop de temps. Veuillez vérifier la connexion
et réessayez.
alert:
redirected:
title: Redirigé vers le nouveau conteneur
message: Dozzle vous a automatiquement redirigé vers le nouveau conteneur {containerId}.
similar-container-found:
title: Conteneur similaire trouvé
message: >-
Dozzle a trouvé un conteneur similaire {containerId} qui s'exécute sur le même hôte
et basculera automatiquement vers lui à moins que vous ne cliquiez sur "Annuler".
title:
page-not-found: Page non trouvée
login: Authentification requise
dashboard: 1 conteneur | {count} conteneurs
settings: Paramètres
button:
logout: Déconnexion
login: Connexion
settings: Paramètres
placeholder:
search-containers: Recherche de conteneurs (⌘ + k, ⌃k)
settings:
display: Afficher
locale: Langue de remplacement
small-scrollbars: Utiliser des barres de défilement plus petites
show-timesamps: Afficher les horodatages
soft-wrap: Lignes d'enroulement souples
datetime-format: Remplacer le format de la date et de l'heure
font-size: Taille de police à utiliser pour les journaux
color-scheme: Schéma de couleurs
options: Options
show-stopped-containers: Afficher les conteneurs arrêtés
about: A propos de
search: Activer la recherche avec Dozzle en utilisant
using-version: Vous utilisez Dozzle {version}.
update-available: >-
Nouvelle version disponible! Mise à jour de <a href="{href}" target="_blank"
rel="noreferrer noopener">{nextVersion}</a>.
show-std: Afficher les étiquettes stdout et stderr
automatic-redirect: Redirection automatique vers de nouveaux conteneurs portant le même nom
compact: Activer le mode compact pour les journaux
releases:
features: une nouvelle fonctionnalité | {count} fonctionnalités
bugFixes: correction d'un bug | {count} bugs
breaking: un changement radical | {count} changements radicaux
three_parts: "{first}, {second} et {third}"
two_parts: "{first} avec {second}"
latest: Plus récent
no_releases: Vous avez la dernière version
log_actions:
copy_log: Copier le journal
jump_to_context: Aller au contexte
toasts:
copied:
title: Copié
message: Journal copié dans le presse-papiers
+99
View File
@@ -0,0 +1,99 @@
toolbar:
clear: Pulisci
download: Scarica
search: Cerca
show: Mostra solo {std}
show-all: Mostra tutto
stop: Stop
start: Start
restart: Restart
label:
containers: Container
running-containers: Container in esecuzione
all-containers: Tutti i Container
total-containers: Container Totali
running: In esecuzione
total-cpu-usage: Utilizzo Totale della CPU
total-mem-usage: Utilizzo Totale della Memoria
dozzle-version: Versione Dozzle
all: Tutto
host: Host
password: Password
username: Username
container-name: Nome Container
status: Stato
created: Creato
avg-cpu: CPU (%)
avg-mem: MEM (%)
pinned: Spuntato
tooltip:
search: Ricerca container (⌘ + k, ⌃k)
pin-column: Blocca come colonna
error:
page-not-found: Questa pagina non esiste
invalid-auth: Username o password non valide
logs-skipped: Saltato {total} registrazioni
container-not-found: Container non trovato
events-stream:
title: Errore inaspettato
message: >-
Dozzle UI non è stato in grado di connettersi alle API. Controlla la tua configurazione di rete.
Se stai utilizzando un reverse proxy, assicurati di averlo configurato correttamente.
events-timeout:
title: Qualcosa non è corretto
message: >-
Dozzle UI timed out durante la connessione alle API. Controlla la configurazione di rete e riprova.
alert:
redirected:
title: Reindirizzamento ad un nuovo container
message: Dozzle automaticamente si è reindirizzato ad un nuovo container {containerId}.
similar-container-found:
title: Trovato un Container simile
message: >-
Dozzle ha trovato un Container soimile {containerId} in esecuzione sullo stesso Host and verrà automaticamente agganciato ad esso se non premi 'Cancel'.
title:
page-not-found: Pagina non trovata
login: Autenticazione richiesta
dashboard: 1 container | {count} container
settings: Configurazione
button:
logout: Logout
login: Login
settings: Configurazione
placeholder:
search-containers: Ricerca container (⌘ + k, ⌃k)
settings:
display: Visualizza
locale: Sovrascrivi Linguaggio
small-scrollbars: Usa una scrollbars più piccola
show-timesamps: Visualizza timestamp
soft-wrap: Linee morbide
datetime-format: Sovrascrivi data e formato dell'ora
font-size: Grandezza del Font da usare nei log
color-scheme: Schema colori
options: Opzioni
show-stopped-containers: Visualizza i container fermi
about: Circa
search: Abilita la ricerca attraverso Dozzle
using-version: Stai utilizzando Dozzle {version}.
update-available: >-
Nuova versione disponibile! Aggiorna a <a href="{href}" target="_blank"
rel="noreferrer noopener">{nextVersion}</a>.
show-std: Visualizza stdout e stderr
automatic-redirect: Automaticamente reindirizza al nuovo container con lo stesso nome
compact: Abilita il compact mode per i log
releases:
features: nuova funzione | {count} funzioni
bugFixes: nuovo bug fix | {count} fix
breaking: nuovo breaking change | {count} breaking change
three_parts: "{first}, {second} e {third}"
two_parts: "{first} e {second}"
latest: Ultimo
no_releases: Stai utilizzando l'ultima versione
log_actions:
copy_log: Copia log
jump_to_context: Vai a
toasts:
copied:
title: Copiato
message: Log copiato nella clipboard
+39 -39
View File
@@ -1,12 +1,12 @@
{
"name": "dozzle",
"version": "6.2.3",
"version": "6.4.4",
"description": "Realtime log viewer for docker containers.",
"homepage": "https://github.com/amir20/dozzle#readme",
"bugs": {
"url": "https://github.com/amir20/dozzle/issues"
},
"packageManager": "pnpm@8.15.1",
"packageManager": "pnpm@8.15.6",
"type": "module",
"repository": {
"type": "git",
@@ -28,52 +28,54 @@
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.29",
"@iconify-json/carbon": "^1.1.31",
"@iconify-json/cil": "^1.1.8",
"@iconify-json/ic": "^1.1.17",
"@iconify-json/mdi": "^1.1.64",
"@iconify-json/mdi": "^1.1.66",
"@iconify-json/mdi-light": "^1.1.10",
"@iconify-json/octicon": "^1.1.52",
"@iconify-json/ph": "^1.1.11",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@iconify-json/octicon": "^1.1.53",
"@iconify-json/ph": "^1.1.12",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/typography": "^0.5.10",
"@vueuse/components": "^10.7.2",
"@vueuse/core": "^10.7.2",
"@vueuse/integrations": "^10.7.2",
"@vueuse/math": "^10.7.2",
"@vueuse/router": "^10.7.2",
"autoprefixer": "^10.4.17",
"@tailwindcss/typography": "^0.5.12",
"@vueuse/components": "^10.9.0",
"@vueuse/core": "^10.9.0",
"@vueuse/integrations": "^10.9.0",
"@vueuse/router": "^10.9.0",
"ansi-to-html": "^0.7.2",
"autoprefixer": "^10.4.19",
"d3-array": "^3.2.4",
"d3-ease": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-shape": "^3.2.0",
"d3-transition": "^3.0.1",
"daisyui": "^4.6.2",
"date-fns": "^3.3.1",
"daisyui": "^4.10.1",
"date-fns": "^3.6.0",
"entities": "^4.5.0",
"fuse.js": "^7.0.0",
"lodash.debounce": "^4.0.8",
"pinia": "^2.1.7",
"postcss": "^8.4.35",
"postcss-mixins": "^9.0.4",
"postcss": "^8.4.38",
"splitpanes": "^3.1.5",
"tailwindcss": "^3.4.1",
"strip-ansi": "^7.1.0",
"tailwindcss": "^3.4.3",
"unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0",
"unplugin-vue-macros": "^2.7.10",
"vite": "5.1.0",
"vite-plugin-pages": "^0.32.0",
"unplugin-vue-macros": "^2.9.0",
"vite": "5.2.8",
"vite-plugin-compression2": "^1.0.0",
"vite-plugin-pages": "^0.32.1",
"vite-plugin-vue-layouts": "^0.11.0",
"vue": "^3.4.16",
"vue-i18n": "^9.9.1",
"vue-router": "^4.2.5"
"vue": "^3.4.21",
"vue-i18n": "^9.11.0",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@iconify-json/material-symbols": "^1.1.77",
"@pinia/testing": "^0.1.3",
"@playwright/test": "^1.41.2",
"@playwright/test": "^1.43.0",
"@types/d3-array": "^3.2.1",
"@types/d3-ease": "^3.0.2",
"@types/d3-scale": "^4.0.8",
@@ -81,26 +83,24 @@
"@types/d3-shape": "^3.1.6",
"@types/d3-transition": "^3.0.8",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^20.11.16",
"@types/semver": "^7.5.6",
"@vitejs/plugin-vue": "5.0.3",
"@vue/compiler-sfc": "^3.4.16",
"@vue/test-utils": "^2.4.4",
"bumpp": "^9.3.0",
"@types/node": "^20.12.6",
"@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "^3.4.21",
"@vue/test-utils": "^2.4.5",
"bumpp": "^9.4.0",
"c8": "^9.1.0",
"concurrently": "^8.2.2",
"eventsourcemock": "^2.0.0",
"jest-serializer-vue": "^3.1.0",
"jsdom": "^24.0.0",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"simple-git-hooks": "^2.9.0",
"prettier-plugin-tailwindcss": "^0.5.13",
"simple-git-hooks": "^2.11.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vitepress": "1.0.0-rc.42",
"vitest": "^1.2.2",
"vue-tsc": "^1.8.27"
"typescript": "^5.4.4",
"vitepress": "1.0.2",
"vitest": "^1.4.0",
"vue-tsc": "^2.0.11"
},
"lint-staged": {
"*.{js,vue,css,ts,html,md}": [
+1455 -1464
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1,6 +1,5 @@
module.exports = {
plugins: {
"postcss-mixins": {},
"tailwindcss/nesting": {},
tailwindcss: {},
autoprefixer: {},
+2
View File
@@ -9,6 +9,7 @@ import IconsResolver from "unplugin-icons/resolver";
import Pages from "vite-plugin-pages";
import Layouts from "vite-plugin-vue-layouts";
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
import { compression } from "vite-plugin-compression2";
export default defineConfig(() => ({
resolve: {
@@ -63,6 +64,7 @@ export default defineConfig(() => ({
strictMessage: false,
include: [path.resolve(__dirname, "locales/**")],
}),
compression({ algorithm: "brotliCompress", exclude: [/\.(html)$/] }),
],
server: {
watch: {