Compare commits

...

65 Commits

Author SHA1 Message Date
Amir Raminfar 9cd388642c chore: release v5.7.0 2023-12-10 13:19:36 -08:00
Amir Raminfar 3bd97f98b2 fix: disables hover on mobile (#2583) 2023-12-10 13:13:55 -08:00
Akash Ramaswamy d776777b7e feat: Moves Jump-To-Context right (#2584) 2023-12-10 13:13:29 -08:00
Amir Raminfar 8baa5021a6 feat: adds some translations (#2582) 2023-12-09 22:40:11 +00:00
renovate[bot] 65d464541e fix(deps): update all non-major dependencies (#2581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-09 22:06:29 +00:00
Akash Ramaswamy 5d883c0825 fix: actions on log UI issues (#2580) 2023-12-09 14:02:24 -08:00
Akash Ramaswamy e6fa8ced58 feat: actions on log messages (#2572) 2023-12-09 05:56:51 -08:00
Gregory D. Estes cdf9005b6f docs: Update supported-env-vars.md (#2578) 2023-12-08 18:16:12 -08:00
renovate[bot] 89aa10d4b7 fix(deps): update all non-major dependencies (#2577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-08 22:12:09 +00:00
renovate[bot] caa68df169 chore(deps): update actions/setup-go action to v5 (#2574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 11:30:21 -08:00
renovate[bot] e2230d5adc fix(deps): update all non-major dependencies (#2573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 18:32:10 +00:00
Amir Raminfar 2fbb65c921 fix: fixes profile disk configuration when pinned is an empty set (#2570) 2023-12-07 08:05:19 -08:00
Amir Raminfar 185538b794 test: fixes test snapshot and updates vitest (#2569) 2023-12-05 23:00:29 +00:00
renovate[bot] 3bb2a509f2 chore(deps): update actions/configure-pages action to v4 (#2566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 14:58:47 -08:00
renovate[bot] cf43d36500 chore(deps): update actions/deploy-pages action to v3 (#2567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 14:58:36 -08:00
renovate[bot] 7819b3fec3 chore(deps): update all non-major dependencies (#2565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 21:33:35 +00:00
Amir Raminfar 0d6c9305c7 test: fixes snapshots (#2564) 2023-12-05 08:09:02 -08:00
ab623 24f298b94d fix: Updates to sidebar on mobile to align closer to desktop (#2562) 2023-12-05 07:53:10 -08:00
Akash Ramaswamy f240b7011b test: added test cases for container actions (#2559) 2023-12-05 07:48:37 -08:00
renovate[bot] f027c753c1 fix(deps): update all non-major dependencies (#2560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 02:26:54 +00:00
Amir Raminfar 5c5b4bb622 chore: release v5.6.1 2023-12-01 11:27:57 -08:00
Amir Raminfar 7da60edb1a chore(analytics): removes GA and only pings with beacon. also adds support actions (#2557) 2023-12-01 18:26:41 +00:00
ab623 23fbf361ce fix: Icon placement when hovering over containers with health checks. (#2556) 2023-12-01 10:13:18 -08:00
Amir Raminfar 6dab94d3b4 fix: changes --enableactions to --enable-actions (#2554) 2023-12-01 06:32:49 -08:00
Amir Raminfar fd5ed091d5 fix: moves favorite star to the left and healthcheck to the right of container name (#2555) 2023-12-01 06:31:26 -08:00
Akash Ramaswamy 3d198bf6c1 docs: actions feature (#2552) 2023-11-30 16:20:55 -08:00
Amir Raminfar 4806b63ea8 chore: release v5.6.0 2023-11-30 15:19:49 -08:00
renovate[bot] c2ee3d50c9 chore(deps): update all non-major dependencies (#2550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-30 23:07:27 +00:00
Amir Raminfar 10e4a7cfaa fix: disables action routes if not configured (#2549) 2023-11-30 22:28:42 +00:00
Akash Ramaswamy f78534f529 feat: can start and stop containers now using the drop down. This feature is not enabled by default. (#2548) 2023-11-30 14:04:22 -08:00
ab623 050e499f8e fix: Added .gitkeep to dist folder, to enable fresh build with no errors (#2547) 2023-11-29 09:33:36 -08:00
Amir Raminfar 29e1bbc981 chore: release v5.5.0 2023-11-28 15:55:34 -08:00
Amir Raminfar 1fa44cc920 chore: updates modules (#2544) 2023-11-28 15:53:31 -08:00
Amir Raminfar 23aaf99947 chore: minor perf improvement 2023-11-28 14:31:37 -08:00
Amir Raminfar 60650ddc2c feat: syncs all profile localStorage to disk (#2537) 2023-11-27 15:57:54 -08:00
Amir Raminfar b54b419a08 fix: removes pages content from index.html (#2541) 2023-11-26 19:41:24 -08:00
Amir Raminfar 9bc5496b0e fix: imroves margin left on title bar (#2542) 2023-11-26 19:41:11 -08:00
renovate[bot] 2016f8d8b0 fix(deps): update all non-major dependencies (#2538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-27 00:59:58 +00:00
renovate[bot] 72c6382d73 chore(deps): update dependency jsdom to v23 (#2539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-26 16:56:25 -08:00
Amir Raminfar ae72e3f4a6 chore: updates icons (#2540) 2023-11-26 16:55:26 -08:00
Amir Raminfar 1ec25e6a64 chore: release v5.4.5 2023-11-25 13:29:18 -08:00
Amir Raminfar ee4f344346 chore: updates imports 2023-11-25 13:29:07 -08:00
renovate[bot] 06e05bca45 chore(deps): update all non-major dependencies (#2534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-25 21:26:11 +00:00
Amir Raminfar 5635ba4619 fix: improves secondary color (#2533) 2023-11-25 13:22:23 -08:00
Amir Raminfar 32dec4e817 fix: improves primary colors (#2531) 2023-11-25 11:47:37 -08:00
Amir Raminfar 2ed41282a5 fix: uses a single key in localStorage to save all visible JSON keys. (#2530) 2023-11-23 18:37:00 -08:00
Ahmet 26cd16a403 fix: update de.yml to use consistent wording and fix small issues (#2528) 2023-11-22 09:26:31 -08:00
Alban Browaeys b243783f5d fix: make README container host port consistent (#2529) 2023-11-22 05:50:48 -08:00
Amir Raminfar 8ab71e2caa feat: improves search style and adding a slight animation (#2527) 2023-11-22 05:48:26 -08:00
Amir Raminfar 318b07d1a9 feat: when doing modifier+f again in search, hides search and show browser search (#2526) 2023-11-22 05:48:03 -08:00
renovate[bot] 7ff26b822f chore(deps): update all non-major dependencies (#2525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-21 22:28:56 +00:00
Amir Raminfar cacf2cd261 chore: release v5.4.4 2023-11-21 14:19:11 -08:00
Amir Raminfar 47bc4b7fc5 fix: fixes bugs with reporting on addr and base (#2521) 2023-11-21 14:14:54 -08:00
renovate[bot] 38c4d9e4b7 chore(deps): update all non-major dependencies (#2517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 00:54:55 +00:00
Amir Raminfar 1d87d09e89 chore: release v5.4.3 2023-11-19 14:18:50 -08:00
Amir Raminfar 70645be134 fix: fixes security warnings with net package (#2516) 2023-11-19 05:14:37 +00:00
Amir Raminfar c659f103f8 feat: improves analytics by removing google analytics in favor of a simple ping (#2515) 2023-11-19 03:35:24 +00:00
renovate[bot] 58fef4e16a chore(deps): update all non-major dependencies (#2514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 15:57:55 +00:00
Amir Raminfar 70e0c359da fix: hides errors when trying to parse simple numbers as json. fixes #2510 (#2512) 2023-11-18 07:52:27 -08:00
Amir Raminfar 0ad8f32588 chore: moves warn log to debug 2023-11-17 15:43:41 -08:00
Amir Raminfar 07147d1e5c fix: returns afters healthcheck 2023-11-17 15:38:17 -08:00
EDIflyer 02a40fdd8e docs: Add docker-compose.yml option (#2511) 2023-11-17 12:48:43 -08:00
renovate[bot] eb94da7b92 chore(deps): update all non-major dependencies (#2507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 03:27:35 +00:00
Amir Raminfar 660b4c6f51 chore: updates to vite v5 (#2505) 2023-11-16 19:24:05 -08:00
renovate[bot] f08518d442 chore(deps): update all non-major dependencies (#2502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 16:47:45 +00:00
71 changed files with 3092 additions and 1385 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
check-latest: true
@@ -91,7 +91,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.0.0
uses: docker/build-push-action@v5.1.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.0.0
uses: docker/build-push-action@v5.1.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+2 -2
View File
@@ -36,7 +36,7 @@ jobs:
node-version: latest
cache: pnpm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v4
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
@@ -59,4 +59,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v3
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
check-latest: true
View File
+2 -2
View File
@@ -14,7 +14,7 @@ COPY package.json ./
RUN pnpm install --offline --ignore-scripts --no-optional
# Copy assets and translations to build
COPY .* *.config.ts *.config.js ./
COPY .* *.config.ts *.config.js *.config.cts ./
COPY assets ./assets
COPY locales ./locales
COPY public ./public
@@ -22,7 +22,7 @@ COPY public ./public
# Build assets
RUN pnpm build
FROM --platform=$BUILDPLATFORM golang:1.21.4-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.21.5-alpine AS builder
RUN apk add --no-cache ca-certificates && mkdir /dozzle
+2 -2
View File
@@ -45,7 +45,7 @@ Here is the Docker Compose file:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8080:8080
- 8888:8080
For advanced options like [authentication](https://dozzle.dev/guide/authentication), [remote hosts](https://dozzle.dev/guide/remote-hosts) or common [questions](https://dozzle.dev/guide/faq) see documentation at [dozzle.dev](https://dozzle.dev/guide/getting-started).
@@ -76,7 +76,7 @@ If it's not enabled please follow [this tutorial](https://github.com/containers/
Once you have the podman remote socket you can run Dozzle on podman.
```
podman run --volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock -d -p 8080:8080 amir20/dozzle:latest
podman run --volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock -d -p 8888:8080 amir20/dozzle:latest
```
## Security
+11 -19
View File
@@ -31,11 +31,9 @@ declare global {
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGenericProjection: typeof import('@vueuse/math')['createGenericProjection']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createPinia: typeof import('pinia')['createPinia']
const createProjection: typeof import('@vueuse/math')['createProjection']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
@@ -70,9 +68,7 @@ declare global {
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const lightTheme: typeof import('./stores/settings')['lightTheme']
const logicAnd: typeof import('@vueuse/math')['logicAnd']
const logicNot: typeof import('@vueuse/math')['logicNot']
const logicOr: typeof import('@vueuse/math')['logicOr']
const logSearchContext: typeof import('./composable/logSearchContext')['logSearchContext']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
@@ -120,7 +116,6 @@ declare global {
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const releases: typeof import('./stores/releases')['default']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
@@ -160,7 +155,6 @@ declare global {
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useAbs: typeof import('@vueuse/math')['useAbs']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
@@ -178,7 +172,6 @@ declare global {
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useAverage: typeof import('@vueuse/math')['useAverage']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
@@ -186,13 +179,12 @@ declare global {
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useCeil: typeof import('@vueuse/math')['useCeil']
const useClamp: typeof import('@vueuse/math')['useClamp']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useContainerActions: typeof import('./composable/containerActions')['useContainerActions']
const useContainerContext: typeof import('./composable/containerContext')['useContainerContext']
const useContainerStore: typeof import('./stores/container')['useContainerStore']
const useCounter: typeof import('@vueuse/core')['useCounter']
@@ -228,7 +220,6 @@ declare global {
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFloor: typeof import('@vueuse/math')['useFloor']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
@@ -247,16 +238,14 @@ declare global {
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useLogSearchContext: typeof import('./composable/logSearchContext')['useLogSearchContext']
const useLogStream: typeof import('./composable/eventsource')['useLogStream']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMath: typeof import('@vueuse/math')['useMath']
const useMax: typeof import('@vueuse/math')['useMax']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useMin: typeof import('@vueuse/math')['useMin']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
@@ -276,19 +265,17 @@ declare global {
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePrecision: typeof import('@vueuse/math')['usePrecision']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useProjection: typeof import('@vueuse/math')['useProjection']
const useProfileStorage: typeof import('./composable/profileStorage')['useProfileStorage']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useReleases: typeof import('./stores/releases')['useReleases']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRound: typeof import('@vueuse/math')['useRound']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
@@ -308,7 +295,6 @@ declare global {
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSum: typeof import('@vueuse/math')['useSum']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
@@ -329,7 +315,6 @@ declare global {
const useToast: typeof import('./composable/toast')['useToast']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useTrunc: typeof import('@vueuse/math')['useTrunc']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: typeof import('@vueuse/core')['useVModel']
@@ -367,6 +352,7 @@ declare global {
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}
// for vue template auto import
import { UnwrapRef } from 'vue'
@@ -550,6 +536,7 @@ declare module 'vue' {
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useContainerActions: UnwrapRef<typeof import('./composable/containerActions')['useContainerActions']>
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
@@ -603,6 +590,7 @@ declare module 'vue' {
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
@@ -635,6 +623,7 @@ declare module 'vue' {
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly useProfileStorage: UnwrapRef<typeof import('./composable/profileStorage')['useProfileStorage']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
@@ -892,6 +881,7 @@ declare module '@vue/runtime-core' {
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useContainerActions: UnwrapRef<typeof import('./composable/containerActions')['useContainerActions']>
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
@@ -945,6 +935,7 @@ declare module '@vue/runtime-core' {
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
@@ -977,6 +968,7 @@ declare module '@vue/runtime-core' {
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly useProfileStorage: UnwrapRef<typeof import('./composable/profileStorage')['useProfileStorage']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
+9
View File
@@ -10,10 +10,17 @@ declare module 'vue' {
BarChart: typeof import('./components/BarChart.vue')['default']
'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default']
'Carbon:circleSolid': typeof import('~icons/carbon/circle-solid')['default']
'Carbon:copyFile': typeof import('~icons/carbon/copy-file')['default']
'Carbon:information': typeof import('~icons/carbon/information')['default']
'Carbon:macShift': typeof import('~icons/carbon/mac-shift')['default']
'Carbon:play': typeof import('~icons/carbon/play')['default']
'Carbon:restart': typeof import('~icons/carbon/restart')['default']
'Carbon:rowCollapse': typeof import('~icons/carbon/row-collapse')['default']
'Carbon:rowExpand': typeof import('~icons/carbon/row-expand')['default']
'Carbon:searchLocate': typeof import('~icons/carbon/search-locate')['default']
'Carbon:star': typeof import('~icons/carbon/star')['default']
'Carbon:starFilled': typeof import('~icons/carbon/star-filled')['default']
'Carbon:stopFilledAlt': typeof import('~icons/carbon/stop-filled-alt')['default']
'Carbon:warning': typeof import('~icons/carbon/warning')['default']
'Cil:checkCircle': typeof import('~icons/cil/check-circle')['default']
'Cil:circle': typeof import('~icons/cil/circle')['default']
@@ -25,6 +32,7 @@ declare module 'vue' {
ContainerStat: typeof import('./components/LogViewer/ContainerStat.vue')['default']
ContainerTable: typeof import('./components/ContainerTable.vue')['default']
ContainerTitle: typeof import('./components/LogViewer/ContainerTitle.vue')['default']
CopyLogMessage: typeof import('./components/LogViewer/CopyLogMessage.vue')['default']
DateTime: typeof import('./components/common/DateTime.vue')['default']
DistanceTime: typeof import('./components/common/DistanceTime.vue')['default']
DockerEventLogItem: typeof import('./components/LogViewer/DockerEventLogItem.vue')['default']
@@ -42,6 +50,7 @@ declare module 'vue' {
LogDate: typeof import('./components/LogViewer/LogDate.vue')['default']
LogEventSource: typeof import('./components/LogViewer/LogEventSource.vue')['default']
LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default']
LogMessageActions: typeof import('./components/LogViewer/LogMessageActions.vue')['default']
LogStd: typeof import('./components/LogViewer/LogStd.vue')['default']
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
+1 -1
View File
@@ -59,5 +59,5 @@ async function logout() {
}
const { hasUpdate, latest } = useReleases();
const latestTag = useStorage("DOZZLE_LATEST_TAG", config.version);
const latestTag = useProfileStorage("releaseSeen", config.version);
</script>
@@ -1,5 +1,5 @@
<template>
<div class="flex gap-x-2">
<div class="relative flex w-full gap-x-2">
<div v-if="showStd">
<log-std :std="logEntry.std"></log-std>
</div>
@@ -22,6 +22,11 @@
</ul>
<field-list :fields="logEntry.unfilteredMessage" :expanded="expanded" :visible-keys="visibleKeys"></field-list>
</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)"
:log-entry="logEntry"
/>
</div>
</template>
<script lang="ts" setup>
@@ -45,6 +50,7 @@ const validValues = computed(() => {
.text-light {
@apply text-base-content/70;
}
.fields {
&:hover {
&::after {
@@ -1,6 +1,10 @@
<template>
<div class="flex flex-1 items-center gap-2 truncate">
<container-health :health="container.health" v-if="container.health"></container-health>
<label class="swap swap-rotate">
<input type="checkbox" v-model="pinned" />
<carbon:star-filled class="swap-on text-secondary" />
<carbon:star class="swap-off" />
</label>
<div class="inline-flex font-mono text-sm">
<div v-if="config.hosts.length > 1" class="mobile-hidden font-thin">
{{ container.hostLabel }}<span class="mx-2">/</span>
@@ -13,24 +17,20 @@
{{ container.swarmId }}
</div>
</div>
<container-health :health="container.health" v-if="container.health"></container-health>
<tag class="mobile-hidden font-mono" size="small">{{ container.image.replace(/@sha.*/, "") }}</tag>
<label class="swap swap-rotate">
<input type="checkbox" v-model="pinned" />
<carbon:star-filled class="swap-on text-secondary" />
<carbon:star class="swap-off" />
</label>
</div>
</template>
<script lang="ts" setup>
const { container } = useContainerContext();
const pinned = computed({
get: () => pinnedContainers.value.has(container.value.storageKey),
get: () => pinnedContainers.value.has(container.value.name),
set: (value) => {
if (value) {
pinnedContainers.value.add(container.value.storageKey);
pinnedContainers.value.add(container.value.name);
} else {
pinnedContainers.value.delete(container.value.storageKey);
pinnedContainers.value.delete(container.value.name);
}
},
});
@@ -4,7 +4,7 @@
<carbon:circle-solid class="w-2.5 text-red" v-if="streamConfig.stderr" />
<carbon:circle-solid class="w-2.5 text-blue" v-if="streamConfig.stdout" />
</label>
<ul tabindex="0" class="menu dropdown-content rounded-box z-50 w-52 bg-base p-1 shadow">
<ul tabindex="0" class="menu dropdown-content z-50 w-52 rounded-box bg-base p-1 shadow">
<li>
<a @click.prevent="clear()">
<octicon:trash-24 /> {{ $t("toolbar.clear") }}
@@ -65,17 +65,57 @@
{{ $t("toolbar.show", { std: "STDERR" }) }}
</a>
</li>
<!-- Container Actions (Enabled via config) -->
<template v-if="enableActions">
<li class="line"></li>
<li>
<button
@click="stop()"
:disabled="actionStates.stop || actionStates.restart"
v-if="container.state == 'running'"
>
<carbon:stop-filled-alt /> {{ $t("toolbar.stop") }}
</button>
<button
@click="start()"
:disabled="actionStates.start || actionStates.restart"
v-if="container.state != 'running'"
>
<carbon:play /> {{ $t("toolbar.start") }}
</button>
</li>
<li>
<button @click="restart()" :disabled="disableRestart">
<carbon:restart
:class="{
'animate-spin': actionStates.restart,
'text-secondary': actionStates.restart,
}"
/>
{{ $t("toolbar.restart") }}
</button>
</li>
</template>
</ul>
</div>
</template>
<script lang="ts" setup>
const { showSearch } = useSearchFilter();
const { base } = config;
const { base, enableActions } = config;
const clear = defineEmit();
const { container, streamConfig } = useContainerContext();
// container context is provided in the parent component: <LogContainer>
const { actionStates, start, stop, restart } = useContainerActions();
const disableRestart = computed(() => {
return actionStates.stop || actionStates.start || actionStates.restart;
});
</script>
<style scoped lang="postcss">
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<scrollable-view :scrollable="scrollable" v-if="container">
<template #header v-if="showTitle">
<div class="mx-2 flex items-center gap-2">
<div class="ml-4 mr-2 flex items-center gap-2">
<container-title @close="$emit('close')" />
<container-stat class="ml-auto" />
@@ -10,6 +10,7 @@ import { vi, describe, expect, beforeEach, test, afterEach } from "vitest";
import { computed, nextTick } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import { containerContext } from "@/composable/containerContext";
import { createI18n } from "vue-i18n";
vi.mock("@/stores/config", () => ({
__esModule: true,
@@ -68,7 +69,7 @@ describe("<LogEventSource />", () => {
return mount(LogEventSource, {
global: {
plugins: [router, createTestingPinia({ createSpy: vi.fn })],
plugins: [router, createTestingPinia({ createSpy: vi.fn }), createI18n({})],
components: {
LogViewer,
},
@@ -0,0 +1,60 @@
<template>
<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() != ''"
:title="t('log_actions.copy_log')"
>
<span
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
@click="copyLogMessageToClipBoard()"
>
<carbon:copy-file />
</span>
</div>
<div
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
:title="t('log_actions.jump_to_context')"
v-if="isSearching()"
>
<a
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
@click="handleJumpLineSelected($event, logEntry)"
:href="`#${logEntry.id}`"
>
<carbon:search-locate />
</a>
</div>
</div>
</template>
<script lang="ts" setup>
import { LogEntry, JSONObject } from "@/models/LogEntry";
const { message, logEntry } = defineProps<{
message: string;
logEntry: LogEntry<string | JSONObject>;
}>();
const { showToast } = useToast();
const { copy, isSupported, copied } = useClipboard();
const { t } = useI18n();
const { isSearching } = useSearchFilter();
const { handleJumpLineSelected } = useLogSearchContext();
async function copyLogMessageToClipBoard() {
await copy(message);
if (copied.value) {
showToast(
{
title: t("toasts.copied.title"),
message: t("toasts.copied.message"),
type: "info",
},
{ expire: 2000 },
);
}
}
</script>
+5 -19
View File
@@ -5,17 +5,9 @@
:key="item.id"
:data-key="item.id"
:class="{ 'border border-secondary': toRaw(item) === toRaw(lastSelectedItem) }"
class="group/entry"
>
<a
class="jump-context tooltip tooltip-right tooltip-primary"
v-if="isSearching()"
data-tip="Jump to Context"
@click="handleJumpLineSelected($event, item)"
:href="`#${item.id}`"
>
<ic:sharp-find-in-page />
</a>
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys.value" />
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys" />
</li>
</ul>
</template>
@@ -32,22 +24,16 @@ const props = defineProps<{
const { container } = useContainerContext();
let visibleKeys = persistentVisibleKeys(container);
const visibleKeys = persistentVisibleKeys(container);
const { filteredPayload } = useVisibleFilter(visibleKeys);
const { filteredMessages, resetSearch, isSearching } = useSearchFilter();
const { filteredMessages } = useSearchFilter();
const { messages } = toRefs(props);
const visible = filteredPayload(messages);
const filtered = filteredMessages(visible);
let lastSelectedItem: LogEntry<string | JSONObject> | undefined = $ref(undefined);
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
lastSelectedItem = item;
resetSearch();
}
const { lastSelectedItem } = useLogSearchContext();
const routeHash = useRouteHash();
watch(
routeHash,
+10 -2
View File
@@ -1,9 +1,17 @@
<template>
<div class="flex items-start gap-x-2">
<div class="relative flex w-full items-start gap-x-2">
<log-std :std="logEntry.std" v-if="showStd" />
<log-date :date="logEntry.date" v-if="showTimestamp" />
<log-level class="flex" :level="logEntry.level" :position="logEntry.position" />
<div class="whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap" v-html="colorize(logEntry.message)"></div>
<div
class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"
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="logEntry.message"
:log-entry="logEntry"
/>
</div>
</template>
<script lang="ts" setup>
@@ -1,105 +1,124 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<LogEventSource /> > render html correctly > should render dates with 12 hour style 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render dates with 24 hour style 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">This is a message.</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">This is a message.</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with color 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><span style="color:#000">black<span style="color:#AAA">white</span></span></div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with filter 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"2\\" class=\\"\\"><a data-v-2e92daca=\\"\\" class=\\"jump-context tooltip tooltip-right tooltip-primary\\" data-tip=\\"Jump to Context\\" href=\\"#2\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\">
<path fill=\\"currentColor\\" d=\\"M20 19.59V8l-6-6H4v20l15.57-.02l-4.81-4.81c-.8.52-1.74.83-2.76.83c-2.76 0-5-2.24-5-5s2.24-5 5-5s5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3s-3 1.34-3 3z\\"></path>
</svg></a>
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="2" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><mark>test</mark> bar</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<div class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer" title="log_actions.jump_to_context"><a class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700" href="#2"><svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
<path fill="currentColor" d="m30 28.586l-4.688-4.688a8.028 8.028 0 1 0-1.415 1.414L28.586 30zM19 25a6 6 0 1 1 6-6a6.007 6.007 0 0 1-6 6zM2 12h8v2H2zM2 2h16v2H2zm0 5h16v2H2z"></path>
</svg></a></div>
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><mark>test</mark> bar</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with html entities 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > renders correctly 1`] = `
"<div class=\\"flex min-h-[1px] justify-center\\"><span class=\\"loading loading-bars loading-md mt-4 text-primary\\" style=\\"display: none;\\"></span></div>
<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\"></ul>"
"<div class="flex min-h-[1px] justify-center"><span class="loading loading-bars loading-md mt-4 text-primary" style="display: none;"></span></div>
<ul data-v-2e92daca="" class="events group py-4 medium"></ul>"
`;
exports[`<LogEventSource /> > should parse messages 1`] = `
+41 -18
View File
@@ -1,23 +1,20 @@
<template>
<div
class="fixed -right-px -top-px z-10 flex w-96 items-center gap-4 rounded-bl border border-secondary/20 bg-base-darker p-4 shadow"
v-show="showSearch"
v-if="search"
>
<div class="input input-primary flex h-auto items-center">
<mdi:magnify />
<input
class="input input-ghost flex-1"
type="text"
placeholder="Find / RegEx"
ref="input"
v-model="searchFilter"
@keyup.esc="resetSearch()"
/>
<transition name="slide">
<div class="fixed right-px top-12 z-10 flex items-center p-4" v-show="showSearch" v-if="search">
<div class="input input-primary flex h-auto items-center !shadow-lg">
<mdi:magnify />
<input
class="input input-ghost w-72 flex-1"
type="text"
placeholder="Find / RegEx"
ref="input"
v-model="searchFilter"
@keyup.esc="resetSearch()"
/>
<a class="btn btn-circle btn-xs" @click="resetSearch()"> <mdi:close /></a>
</div>
</div>
<a class="btn btn-circle btn-xs" @click="resetSearch()"> <mdi:close /></a>
</div>
</transition>
</template>
<script lang="ts" setup>
@@ -32,5 +29,31 @@ onKeyStroke("f", (e) => {
}
});
onMounted(() => {
onKeyStroke(
"f",
(e) => {
if (e.ctrlKey || e.metaKey) {
e.stopPropagation();
resetSearch();
}
},
{ target: input.value },
);
});
onUnmounted(() => resetSearch());
</script>
<style lang="postcss" scoped>
.slide-enter-active,
.slide-leave-active {
transition: all 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.slide-enter-from,
.slide-leave-to {
transform: translateY(-150px);
opacity: 0;
}
</style>
+3 -4
View File
@@ -36,6 +36,7 @@
<div class="truncate">
{{ item.name }}<span class="font-light opacity-70" v-if="item.isSwarm">{{ item.swarmId }}</span>
</div>
<container-health :health="item.health"></container-health>
<span
class="pin"
@click.stop.prevent="store.appendActiveContainer(item)"
@@ -44,8 +45,6 @@
>
<cil:columns />
</span>
<container-health :health="item.health"></container-health>
</router-link>
<template #content>
<container-popup :container="item"></container-popup>
@@ -93,7 +92,7 @@ const sortedContainers = computed(() =>
const groupedContainers = computed(() =>
sortedContainers.value.reduce(
(acc, item) => {
if (debouncedIds.value.has(item.storageKey)) {
if (debouncedIds.value.has(item.name)) {
acc.pinned.push(item);
} else {
acc.unpinned.push(item);
@@ -145,7 +144,7 @@ const activeContainersById = computed(() =>
@apply text-[0.95rem];
}
.containers a {
@apply auto-cols-[auto_max-content];
@apply auto-cols-[auto_max-content_max-content];
.pin {
display: none;
+6 -2
View File
@@ -20,7 +20,7 @@
</div>
<transition name="fade">
<div v-show="show">
<div v-show="show" class="h-[calc(100vh-60px)] overflow-auto">
<div class="mt-4 flex items-center justify-center gap-2">
<dropdown-menu
v-model="sessionHost"
@@ -39,7 +39,7 @@
<ul class="menu">
<li class="menu-title">{{ $t("label.containers") }}</li>
<li v-for="item in sortedContainers" :key="item.id">
<li v-for="item in sortedContainers" :key="item.id" :class="item.state">
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
active-class="active-primary"
@@ -85,6 +85,10 @@ const sortedContainers = computed(() =>
const hosts = computed(() => config.hosts.map(({ id, name }) => ({ value: id, label: name })));
</script>
<style scoped lang="postcss">
li.exited {
@apply opacity-50;
}
.fade-enter-active,
.fade-leave-active {
@apply transition-opacity;
+45
View File
@@ -0,0 +1,45 @@
type ContainerActions = "start" | "stop" | "restart";
export const useContainerActions = () => {
const { container } = useContainerContext();
const { showToast } = useToast();
const actionStates = reactive({
stop: false,
restart: false,
start: false,
});
async function actionHandler(action: ContainerActions) {
const actionUrl = `/api/actions/${action}/${container.value.host}/${container.value.id}`;
const errors = {
404: "container not found",
500: "unable to complete action",
400: "invalid action",
} as Record<number, string>;
const defaultError = "something went wrong";
const toastTitle = "Action Failed";
actionStates[action] = true;
try {
const response = await fetch(withBase(actionUrl), { method: "POST" });
if (!response.ok) {
const message = errors[response.status] ?? defaultError;
showToast({ type: "error", message, title: toastTitle });
}
} catch (error) {
showToast({ type: "error", message: defaultError, title: toastTitle });
}
actionStates[action] = false;
}
return {
actionStates,
start: () => actionHandler("start"),
stop: () => actionHandler("stop"),
restart: () => actionHandler("restart"),
};
};
+16
View File
@@ -0,0 +1,16 @@
import { JSONObject, LogEntry } from "@/models/LogEntry";
const lastSelectedItem = ref<LogEntry<string | JSONObject> | undefined>(undefined);
export const useLogSearchContext = () => {
const { resetSearch } = useSearchFilter();
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
lastSelectedItem.value = item;
resetSearch();
}
return {
lastSelectedItem,
handleJumpLineSelected,
};
};
+36
View File
@@ -0,0 +1,36 @@
import { Profile } from "@/stores/config";
export function useProfileStorage<K extends keyof Profile>(key: K, defaultValue: NonNullable<Profile[K]>) {
const storageKey = "DOZZLE_" + key.toUpperCase();
const storage = useStorage<NonNullable<Profile[K]>>(storageKey, defaultValue, undefined, {
writeDefaults: false,
mergeDefaults: true,
});
if (config.profile?.[key]) {
if (storage.value instanceof Set && config.profile[key] instanceof Array) {
storage.value = new Set([...(config.profile[key] as Iterable<any>)]) as unknown as NonNullable<Profile[K]>;
} else if (config.profile[key] instanceof Array) {
storage.value = config.profile[key] as NonNullable<Profile[K]>;
} else if (config.profile[key] instanceof Object) {
Object.assign(storage.value, config.profile[key]);
} else {
storage.value = config.profile[key] as NonNullable<Profile[K]>;
}
}
if (config.user) {
watch(
storage,
(value) => {
fetch(withBase("/api/profile"), {
method: "PATCH",
body: JSON.stringify({ [key]: value }, (_, value) => (value instanceof Set ? [...value] : value)),
});
},
{ deep: true },
);
}
return storage;
}
+13 -4
View File
@@ -7,9 +7,18 @@ if (config.hosts.length === 1 && !sessionHost.value) {
sessionHost.value = config.hosts[0].id;
}
export function persistentVisibleKeys(container: Ref<Container>) {
return computed(() => useStorage(container.value.storageKey, []));
export function persistentVisibleKeys(container: Ref<Container>): Ref<string[][]> {
const storage = useProfileStorage("visibleKeys", {});
return computed(() => {
if (!(container.value.storageKey in storage.value)) {
// Returning a temporary ref here to avoid writing an empty array to storage
const visibleKeys = ref<string[][]>([]);
watchOnce(visibleKeys, () => (storage.value[container.value.storageKey] = visibleKeys.value), { deep: true });
return visibleKeys.value;
}
return storage.value[container.value.storageKey];
});
}
const DOZZLE_PINNED_CONTAINERS = "DOZZLE_PINNED_CONTAINERS";
export const pinnedContainers = useStorage(DOZZLE_PINNED_CONTAINERS, new Set<string>());
export const pinnedContainers = useProfileStorage("pinned", new Set<string>());
+3 -3
View File
@@ -1,12 +1,12 @@
import { ComplexLogEntry, type JSONObject, type LogEntry } from "@/models/LogEntry";
import type { ComputedRef, Ref } from "vue";
import type { Ref } from "vue";
export function useVisibleFilter(visibleKeys: ComputedRef<Ref<string[][]>>) {
export function useVisibleFilter(visibleKeys: Ref<string[][]>) {
function filteredPayload(messages: Ref<LogEntry<string | JSONObject>[]>) {
return computed(() => {
return messages.value.map((d) => {
if (d instanceof ComplexLogEntry) {
return ComplexLogEntry.fromLogEvent(d, visibleKeys.value);
return ComplexLogEntry.fromLogEvent(d, visibleKeys);
} else {
return d;
}
+3 -3
View File
@@ -5,8 +5,8 @@
--base-color: 97% 0 0;
--base-darker-color: 90% 0 0;
--base-content-color: 33% 0 0;
--primary-color: 76.96% 0.143 176.65;
--secondary-color: 76.04% 0.168 64.07;
--primary-color: 79.96% 0.143 176.65;
--secondary-color: 82.67% 0.15380739746702807 77.82;
}
@define-mixin dark {
@@ -14,7 +14,7 @@
--base-color: 18% 0 0;
--base-darker-color: 11% 0 0;
--base-content-color: 89.23% 0 0;
--primary-color: 76.96% 0.143 176.65;
--primary-color: 70.96% 0.143 176.65;
--secondary-color: 91.36% 0.11098 90.1243;
}
+10 -2
View File
@@ -2,7 +2,7 @@ import { type Settings } from "@/stores/settings";
const text = document.querySelector("script#config__json")?.textContent || "{}";
interface Config {
export interface Config {
version: string;
base: string;
authorizationNeeded: boolean;
@@ -11,16 +11,24 @@ interface Config {
hostname: string;
hosts: { name: string; id: string }[];
authProvider: "simple" | "none" | "forward-proxy";
enableActions: boolean;
user?: {
username: string;
email: string;
name: string;
avatar: string;
};
serverSettings?: Settings;
profile?: Profile;
pages?: { id: string; title: string }[];
}
export interface Profile {
settings?: Settings;
pinned?: Set<string>;
visibleKeys?: { [key: string]: string[][] };
releaseSeen?: string;
}
const pageConfig = JSON.parse(text);
const config: Config = {
+1 -12
View File
@@ -1,5 +1,4 @@
import { toRefs } from "@vueuse/core";
const DOZZLE_SETTINGS_KEY = "DOZZLE_SETTINGS";
export type Settings = {
search: boolean;
@@ -30,17 +29,7 @@ export const DEFAULT_SETTINGS: Settings = {
automaticRedirect: true,
};
export const settings = useStorage(DOZZLE_SETTINGS_KEY, DEFAULT_SETTINGS);
settings.value = { ...DEFAULT_SETTINGS, ...settings.value, ...config.serverSettings };
if (config.user) {
watch(settings, (value) => {
fetch(withBase("/api/profile/settings"), {
method: "PUT",
body: JSON.stringify(value),
});
});
}
export const settings = useProfileStorage("settings", DEFAULT_SETTINGS);
export const {
collapseNav,
View File
+1 -1
View File
@@ -61,7 +61,7 @@ services:
playwright:
container_name: playwright
image: mcr.microsoft.com/playwright:v1.39.0-jammy
image: mcr.microsoft.com/playwright:v1.40.1-jammy
working_dir: /app
volumes:
- .:/app
+1
View File
@@ -68,6 +68,7 @@ export default defineConfig({
{ text: "Healthcheck", link: "/guide/healthcheck" },
{ text: "Hostname", link: "/guide/hostname" },
{ text: "Remote Hosts", link: "/guide/remote-hosts" },
{ text: "Container Actions", link: "/guide/actions" },
{ text: "Custom Documentation", link: "/guide/custom-documentation" },
{ text: "Supported Env Vars", link: "/guide/supported-env-vars" },
{ text: "Logging Files on Disk", link: "/guide/log-files-on-disk" },
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+32
View File
@@ -0,0 +1,32 @@
---
title: Container Actions
---
# Using Container Actions
Dozzle now supports **Container Actions**, which allows you to `start`, `stop` and `restart` container from within the UI in the dropdown menu.
<img title="Container Actions" alt="Container Acions Menu UI" width="250" src="/.vitepress/theme/media/dozzle-ui-actions.png">
This feature is **disabled** by default, which can be enabled by setting environment variable`DOZZLE_ENABLE_ACTIONS` to `true`
::: code-group
```sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --enable-actions
```
```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_ENABLE_ACTIONS: true
```
:::
+23 -1
View File
@@ -12,10 +12,32 @@ However, there are workarounds to be able to still access files using mounts.
## Mounting Local Log Files with Alpine
Dozzle reads any output stream. This can be used in combination with alpine to `tail` a mounted file. An example of this is:
Dozzle reads any output stream. This can be used in combination with alpine to `tail` a mounted file. An example of this is as follows:
::: code-group
```sh
docker run -v /var/log/system.log:/var/log/test.log alpine tail -f /var/log/test.log
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle-from-file:
container_name: dozzle-from-file
image: alpine
volumes:
- /var/log/system.log:/var/log/stream.log
command:
- tail
- -f
- /var/log/stream.log
network_mode: none
restart: unless-stopped
```
:::
In the above example `/var/log/system.log` is mounted from the host and used with `tail -f` to follow the file. `tail` is smart to follow log rotations. During my testing, using alpine only uses about `300KB` of memory.
The second tab shows a `docker-compose` file which is useful if you want the log stream to survive server reboot.
+14 -13
View File
@@ -6,16 +6,17 @@ title: Environment variables and configuration
Configurations can be done with flags or environment variables. The table below outlines all supported options and their respective env vars.
| Flag | Env Variable | Default |
| ---------------- | ---------------------- | ------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
| Flag | Env Variable | Default |
| ------------------ | ----------------------- | ------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | `""` |
| `--enable-actions` | `DOZZLE_ENABLE_ACTIONS` | false |
+4 -4
View File
@@ -3,11 +3,11 @@
"type": "module",
"name": "docs",
"devDependencies": {
"@unocss/preset-typography": "^0.57.3",
"@unocss/reset": "^0.57.3",
"@unocss/transformer-directives": "^0.57.3",
"@unocss/preset-typography": "^0.58.0",
"@unocss/reset": "^0.58.0",
"@unocss/transformer-directives": "^0.58.0",
"dozzle": "workspace:*",
"sitemap": "^7.1.1",
"unocss": "^0.57.3"
"unocss": "^0.58.0"
}
}
+594 -130
View File
@@ -6,14 +6,14 @@ settings:
devDependencies:
'@unocss/preset-typography':
specifier: ^0.57.3
version: 0.57.3
specifier: ^0.58.0
version: 0.58.0
'@unocss/reset':
specifier: ^0.57.3
version: 0.57.3
specifier: ^0.58.0
version: 0.58.0
'@unocss/transformer-directives':
specifier: ^0.57.3
version: 0.57.3
specifier: ^0.58.0
version: 0.58.0
dozzle:
specifier: workspace:*
version: link:..
@@ -21,8 +21,8 @@ devDependencies:
specifier: ^7.1.1
version: 7.1.1
unocss:
specifier: ^0.57.3
version: 0.57.3(postcss@8.4.31)(vite@4.5.0)
specifier: ^0.58.0
version: 0.58.0(postcss@8.4.31)(vite@4.5.0)
packages:
@@ -45,6 +45,333 @@ packages:
resolution: {integrity: sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==}
dev: true
/@babel/code-frame@7.23.5:
resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.23.4
chalk: 2.4.2
dev: true
/@babel/compat-data@7.23.3:
resolution: {integrity: sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/core@7.23.5:
resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.1
'@babel/code-frame': 7.23.5
'@babel/generator': 7.23.5
'@babel/helper-compilation-targets': 7.22.15
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
'@babel/helpers': 7.23.5
'@babel/parser': 7.23.5
'@babel/template': 7.22.15
'@babel/traverse': 7.23.5
'@babel/types': 7.23.5
convert-source-map: 2.0.0
debug: 4.3.4
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
dev: true
/@babel/generator@7.23.5:
resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.5
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.20
jsesc: 2.5.2
dev: true
/@babel/helper-annotate-as-pure@7.22.5:
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.3
dev: true
/@babel/helper-compilation-targets@7.22.15:
resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/compat-data': 7.23.3
'@babel/helper-validator-option': 7.22.15
browserslist: 4.22.1
lru-cache: 5.1.1
semver: 6.3.1
dev: true
/@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.5):
resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.5
'@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.5)
'@babel/helper-skip-transparent-expression-wrappers': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
semver: 6.3.1
dev: true
/@babel/helper-environment-visitor@7.22.20:
resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-function-name@7.23.0:
resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.22.15
'@babel/types': 7.23.5
dev: true
/@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.5
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.3
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.5
dev: true
/@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-module-imports': 7.22.15
'@babel/helper-simple-access': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
'@babel/helper-validator-identifier': 7.22.20
dev: true
/@babel/helper-optimise-call-expression@7.22.5:
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.3
dev: true
/@babel/helper-plugin-utils@7.22.5:
resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5):
resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-environment-visitor': 7.22.20
'@babel/helper-member-expression-to-functions': 7.23.0
'@babel/helper-optimise-call-expression': 7.22.5
dev: true
/@babel/helper-simple-access@7.22.5:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.23.5
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.3
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.5
dev: true
/@babel/helper-string-parser@7.22.5:
resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-string-parser@7.23.4:
resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-identifier@7.22.20:
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-option@7.22.15:
resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helpers@7.23.5:
resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.22.15
'@babel/traverse': 7.23.5
'@babel/types': 7.23.5
transitivePeerDependencies:
- supports-color
dev: true
/@babel/highlight@7.23.4:
resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.22.20
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
/@babel/parser@7.23.5:
resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.23.5
dev: true
/@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
'@babel/helper-plugin-utils': 7.22.5
'@babel/helper-simple-access': 7.22.5
dev: true
/@babel/plugin-transform-typescript@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-ogV0yWnq38CFwH20l2Afz0dfKuZBx9o/Y2Rmh5vuSS0YD1hswgEgTfyTzuSrT2q9btmHRSqYoSfwFUVaC1M1Jw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-annotate-as-pure': 7.22.5
'@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5)
'@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5)
dev: true
/@babel/preset-typescript@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5
'@babel/helper-validator-option': 7.22.15
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5)
'@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
'@babel/plugin-transform-typescript': 7.23.3(@babel/core@7.23.5)
dev: true
/@babel/template@7.22.15:
resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.23.5
'@babel/parser': 7.23.5
'@babel/types': 7.23.5
dev: true
/@babel/traverse@7.23.5:
resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.23.5
'@babel/generator': 7.23.5
'@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.5
'@babel/types': 7.23.5
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types@7.23.3:
resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.22.5
'@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
dev: true
/@babel/types@7.23.5:
resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
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
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
@@ -247,8 +574,8 @@ packages:
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
dev: true
/@iconify/utils@2.1.11:
resolution: {integrity: sha512-M/w3PkN8zQYXi8N6qK/KhnYMfEbbb6Sk8RZVn8g+Pmmu5ybw177RpsaGwpziyHeUsu4etrexYSWq3rwnIqzYCg==}
/@iconify/utils@2.1.12:
resolution: {integrity: sha512-7vf3Uk6H7TKX4QMs2gbg5KR1X9J0NJzKSRNWhMZ+PWN92l0t6Q3tj2ZxLDG07rC3ppWBtTtA4FPmkQphuEmdsg==}
dependencies:
'@antfu/install-pkg': 0.1.1
'@antfu/utils': 0.7.6
@@ -315,8 +642,8 @@ packages:
resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==}
dev: true
/@rollup/pluginutils@5.0.5:
resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==}
/@rollup/pluginutils@5.1.0:
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -343,32 +670,32 @@ packages:
'@types/node': 17.0.45
dev: true
/@unocss/astro@0.57.3(vite@4.5.0):
resolution: {integrity: sha512-Kwu/k8iGNVrMtOuzJ7jKOvjYZFZz3recSxd7ceDp5Hi5SMsmjvXXHzkQ1Iypj1g0nczWcX4U+krROr2EH0GlnA==}
/@unocss/astro@0.58.0(vite@4.5.0):
resolution: {integrity: sha512-df+tEFO5eKXjQOwSWQhS9IdjD0sfLHLtn8U09sEKR2Nmh5CvpwyBxmvLQgOCilPou7ehmyKfsyGRLZg7IMp+Ew==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
peerDependenciesMeta:
vite:
optional: true
dependencies:
'@unocss/core': 0.57.3
'@unocss/reset': 0.57.3
'@unocss/vite': 0.57.3(vite@4.5.0)
'@unocss/core': 0.58.0
'@unocss/reset': 0.58.0
'@unocss/vite': 0.58.0(vite@4.5.0)
vite: 4.5.0
transitivePeerDependencies:
- rollup
dev: true
/@unocss/cli@0.57.3:
resolution: {integrity: sha512-F5k0IjkbHFlZDcGAUr7UTa2xehxobfqWzooDL0tU9PtvAk6S4Edf5Iq0HymAcVK1k9yO17i7Pvg6dw7gOM0TIg==}
/@unocss/cli@0.58.0:
resolution: {integrity: sha512-rhsrDBxAVueygMcAbMkbuvsHbBL2rG6N96LllYwHn16FLgOE3Sf4JW1/LlNjQje3BtwMMtbSCCAeu2SryFhzbw==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@ampproject/remapping': 2.2.1
'@rollup/pluginutils': 5.0.5
'@unocss/config': 0.57.3
'@unocss/core': 0.57.3
'@unocss/preset-uno': 0.57.3
'@rollup/pluginutils': 5.1.0
'@unocss/config': 0.58.0
'@unocss/core': 0.58.0
'@unocss/preset-uno': 0.58.0
cac: 6.7.14
chokidar: 3.5.3
colorette: 2.0.20
@@ -381,169 +708,174 @@ packages:
- rollup
dev: true
/@unocss/config@0.57.3:
resolution: {integrity: sha512-jrjvmcrrdiHHLqp6LBpHWs7VAnumFK3fEdMH7celRe+99CTOvRn73caUThyD2Ftt8rDIMejmteR1hqVBH51kug==}
/@unocss/config@0.58.0:
resolution: {integrity: sha512-WQD29gCZ7cajnMzezD1PRW0qQSxo/C6PX9ktygwhdinFx9nXuLZnKFOz65TiI8y48e53g1i7ivvgY3m4Sq5mIg==}
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
unconfig: 0.3.11
dev: true
/@unocss/core@0.57.3:
resolution: {integrity: sha512-o6snDo5vwAenIqA+wjjI6BUsftJXXSqrPHYqplb+QX5bLfxW/OU1xhBRlnhiP0BOGGZXqgGEETU1ym8MM9bLwA==}
/@unocss/core@0.58.0:
resolution: {integrity: sha512-KhABQXGE2AgtO9vE28d+HnciuyGDcuygsnQdUwlzUuR4K05OSw2kRE9emRN4HaMycD+gA/zDbQrJxTXb6mQUiA==}
dev: true
/@unocss/extractor-arbitrary-variants@0.57.3:
resolution: {integrity: sha512-OmF+2TjJ97i7KOCR8wPgZK/pkp8Rcfo4tzqT/4jBUIi7rfDGZx/eE3aZKFpZSZlUuTH5cdReaKxymXQmJ4dibA==}
/@unocss/extractor-arbitrary-variants@0.58.0:
resolution: {integrity: sha512-s9wK2UugJM0WK1HpgPz2kTbpeyQc46zais+nauN/ykVX6NMq8PtGzSWszzf+0aIbtWAQGiqAfiYNTpf09tJHfg==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/inspector@0.57.3:
resolution: {integrity: sha512-Oj5cUbuwx+4/rckW3mfpdKMWzhOOSehXChzuJ7x7tMDDB5ywdHwnDsxtK07Y+5UwKHC322T3I3VtLolOfsdlCA==}
/@unocss/inspector@0.58.0:
resolution: {integrity: sha512-ZC4QauFGdh3/VkzW/FqkO2R03JEbzGNuX0DK03pwas8/jFIGh8pPldesj8GEKm1YWr1emx9cw7JUnhR8XSUBlA==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/core': 0.58.0
'@unocss/rule-utils': 0.58.0
gzip-size: 6.0.0
sirv: 2.0.3
dev: true
/@unocss/postcss@0.57.3(postcss@8.4.31):
resolution: {integrity: sha512-rYXQ2/iXeF59/g8xbvoyYJ9EClQCBcWj2oeJCt85dykOYyQJCWJT+LoYF0s/kvg7m+x5ovdNQfXtAACLYBqh9g==}
/@unocss/postcss@0.58.0(postcss@8.4.31):
resolution: {integrity: sha512-2hAwLbfUFqysi8FN1cn3xkHy5GhLMlYy6W4NrAZ2ws7F2MKpsCT2xCj7dT5cI2tW8ulD2YoVbKH15dBhNsMNUA==}
engines: {node: '>=14'}
peerDependencies:
postcss: ^8.4.21
dependencies:
'@unocss/config': 0.57.3
'@unocss/core': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/config': 0.58.0
'@unocss/core': 0.58.0
'@unocss/rule-utils': 0.58.0
css-tree: 2.3.1
fast-glob: 3.3.2
magic-string: 0.30.5
postcss: 8.4.31
dev: true
/@unocss/preset-attributify@0.57.3:
resolution: {integrity: sha512-leX9jxM2PnqvZn42thDb2rPdE0nq6WtIr98pvdnkRZKt5gLwtOJCANXH/gVP6tPdRRf6FiZstA8jvAxVGL1sIA==}
/@unocss/preset-attributify@0.58.0:
resolution: {integrity: sha512-Ew78noYes12K9gk4dF36MkjpiIqTi1XVqcniiAzxCkzuctxN4B57vW3LVTwjInGmWNNKWN3UNR4q1o0VxH4xJg==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/preset-icons@0.57.3:
resolution: {integrity: sha512-cG7gaFQzSidHS+nHPV9HEB3aaUVs/PjZywxMl5jwkJIWHuFMU/SQZXMorH6avU2jH8PoYkRZfjLdRWA+h/+fPA==}
/@unocss/preset-icons@0.58.0:
resolution: {integrity: sha512-niT32avw+8l+L40LGhrmX6qDV9Z8/gOn4xjjRhLZZouKni3CJOpz9taILyF4xp1nak5nxGT4wa0tuC/htvOF5A==}
dependencies:
'@iconify/utils': 2.1.11
'@unocss/core': 0.57.3
'@iconify/utils': 2.1.12
'@unocss/core': 0.58.0
ofetch: 1.3.3
transitivePeerDependencies:
- supports-color
dev: true
/@unocss/preset-mini@0.57.3:
resolution: {integrity: sha512-2KFxbbxRqhc+0fyWNYSiRGGr+3jp4jEQIRnjT8sv5uAMo1OaUmUTwz2qzYhSc3sCM8ZEofblZY2BOcqJwZ5yxA==}
/@unocss/preset-mini@0.58.0:
resolution: {integrity: sha512-oMliJZVTN3ecAvf52yN+MyJszaJOZoKwMMbUAFqVis62MaqRzZ8mSw12QFLFyX2pltulDFpMBTAKro+hP0wXEg==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/extractor-arbitrary-variants': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/core': 0.58.0
'@unocss/extractor-arbitrary-variants': 0.58.0
'@unocss/rule-utils': 0.58.0
dev: true
/@unocss/preset-tagify@0.57.3:
resolution: {integrity: sha512-GXs5Hu6XtHcIRLexPghHkfb6ekSodh4Xs8895xrvP5H7Tm/+wEIHALXkW762Rujl1Rtq+xzxAeCoeFxW+y9eLw==}
/@unocss/preset-tagify@0.58.0:
resolution: {integrity: sha512-I+dzfs/bofiGb2AUxkhcTDhB+r2+/3SO81PFwf3Ae7afnzhA2SLsKAkEqO8YN3M3mwZL7IfXn6vpsWeEAlk/yw==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/preset-typography@0.57.3:
resolution: {integrity: sha512-C/pIfRY56wxBuV4bTIeZMZYMmYc0gD8DU+sJSPWiZJP1JHiLc3FzSnc51BYcT/Dqdx0fDWhJyP2qqo9000VFKQ==}
/@unocss/preset-typography@0.58.0:
resolution: {integrity: sha512-8qo+Z1CJtXFMDbAvtizUTRLuLxCIzytgYU0GmuRkfc2iwASSDNDsvh8nAYQfWpyAEOV7QEHtS9c9xL4b0c89FA==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/preset-mini': 0.57.3
'@unocss/core': 0.58.0
'@unocss/preset-mini': 0.58.0
dev: true
/@unocss/preset-uno@0.57.3:
resolution: {integrity: sha512-dLZrFc6GrE5J0zAZMFXk/c4WKq7fmU0jCgHvbDXLGdKdJ7zpByslhc2YTPqkLW40F6+73SCN7DlARInSh2fa4g==}
/@unocss/preset-uno@0.58.0:
resolution: {integrity: sha512-DpgfjtvSgsWeyZH+jQHc1k5IReiZNb7oGpHVnfF6SlHETTnMHSeNetxkPQWYrqJLPI6llNLPTdTa5j47NtmOiA==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/preset-mini': 0.57.3
'@unocss/preset-wind': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/core': 0.58.0
'@unocss/preset-mini': 0.58.0
'@unocss/preset-wind': 0.58.0
'@unocss/rule-utils': 0.58.0
dev: true
/@unocss/preset-web-fonts@0.57.3:
resolution: {integrity: sha512-W/voQjgo98oj/D/oGrhL4xAS0XsR6fF9yULu3xf4nWrUkdkZq/64/rOM5uLBgUFSmkulW524Dsjd1INYmPzz8w==}
/@unocss/preset-web-fonts@0.58.0:
resolution: {integrity: sha512-QarDDEUlexQ2IIn23pE1eHDskG2Tz+JjCe+FAN0DoNLLhvUUWSB4cQIMFWP6dSMJ047Blj9IpgAl9dERICW1qQ==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
ofetch: 1.3.3
dev: true
/@unocss/preset-wind@0.57.3:
resolution: {integrity: sha512-LymBZtNK86qEpLpbH5eOAiHNFvkIAjfL+Jlok5xI/yO/GCqjnTiw1QAxu2vxLUnQlqlvu7IykOx+Hk1nNvkSaA==}
/@unocss/preset-wind@0.58.0:
resolution: {integrity: sha512-2zgaIy9RAGie9CsUYCkYRDSERBi8kG6Q/mQLgNfP9HMz5IThlnDHFWF/hLAVD51xQUg9gH8qWBR9kN/1ioT5Tw==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/preset-mini': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/core': 0.58.0
'@unocss/preset-mini': 0.58.0
'@unocss/rule-utils': 0.58.0
dev: true
/@unocss/reset@0.57.3:
resolution: {integrity: sha512-E6Q8jucQlVLOM+d+F5DKGi/8GVc8KDwAQnbcpbrGL/1iix4IM3emRkPmujgTLWS+HIRRcWcEvT6sNwnd9r6H2A==}
/@unocss/reset@0.58.0:
resolution: {integrity: sha512-UVZ5kz37JGbwAA06k/gjKYcekcTwi6oIhev1EpTtCvHLL6XYcYqcwb/u4Wjzprd3L3lxDGYXvGdjREGm2u7vbQ==}
dev: true
/@unocss/rule-utils@0.57.3:
resolution: {integrity: sha512-koWXeRJHFt/SJdb3m7s/2+NrBCcUtl67SX7rSHJc99Z+dwZgIsPUfdfRKM4mZD43MayybvDrd1Wue2LNQg5R/g==}
/@unocss/rule-utils@0.58.0:
resolution: {integrity: sha512-LBJ9dJ/j5UIMzJF7pmIig55MtJAYtG+tn/zQRveZuPRVahzP+KqwlyB7u3uCUnQhdgo/MJODMcqyr0jl6+kTuA==}
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
magic-string: 0.30.5
dev: true
/@unocss/scope@0.57.3:
resolution: {integrity: sha512-hL0Gjd5getA6ziiOvu1M2Jw5e+FnD9rzu+t+4SnxWcpP+bZtu+LBrt5FeqrizwUHfE/723iuEvg16W5hjhGLQA==}
/@unocss/scope@0.58.0:
resolution: {integrity: sha512-XgUXZJvbxWSRC/DNOWI5DYdR6Nd6IZxsE5ls3AFA5msgtk5OH4YNQELLMabQw7xbRbU/fftlRJa3vncSfOyl6w==}
dev: true
/@unocss/transformer-attributify-jsx-babel@0.57.3:
resolution: {integrity: sha512-b5esljHAz274tv0sXe8GmHew7FXzwkRQrod6NdR9pyFlPQ9gn7gxi0MIsvIV0U8PdSz3HOHOuT0tU/zphjaJDA==}
/@unocss/transformer-attributify-jsx-babel@0.58.0:
resolution: {integrity: sha512-ckDq/q476x2yikjS8usmSUGuakqMQrg2pm8sdBINTPdJxGc7kJRvI5UDnzRw4W9hE5IH+E4gg0XfCtFad0O3eg==}
dependencies:
'@unocss/core': 0.57.3
'@babel/core': 7.23.5
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5)
'@babel/preset-typescript': 7.23.3(@babel/core@7.23.5)
'@unocss/core': 0.58.0
transitivePeerDependencies:
- supports-color
dev: true
/@unocss/transformer-attributify-jsx@0.57.3:
resolution: {integrity: sha512-OgejFNN7AcCgudh/HGe2BS00TbRv7Bi+siWeUS7AEGEG+p7cQZn92XljCFZGqIyvgfVWypb6/xve9H4oY3/E+w==}
/@unocss/transformer-attributify-jsx@0.58.0:
resolution: {integrity: sha512-QDdBEFDE7ntfCH7+8zHRW72MIQ9NH3uYGUE7lYgr5Ap8qzBHCxMT1kUrY6gwuoo3U4dMu2wruglYRHD88hvGkw==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/transformer-compile-class@0.57.3:
resolution: {integrity: sha512-DqmRTQujqAdk4uSrqy+t9xSVmKM9E3yW9PCwDxI1evva0/qTFexzjoR42glq8x7LSn0ZmFyflXcQoeXmwjsBrg==}
/@unocss/transformer-compile-class@0.58.0:
resolution: {integrity: sha512-/BysfTg2q9sGPfiRHqWw/bT60/gjpBGBRVkIFsG4WVT2pgf3BfQUPu5FumSvZSRd0rA/pR57Lp6ZREAdj6+q+A==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/transformer-directives@0.57.3:
resolution: {integrity: sha512-GXedqnb0PB+XkjdfNEInuLxxLjPbaCQLCUhSvTcw67+kalGgX2Mn/hYwpSHmDMLe+Uld5n0vcJRu2a8chBdAwA==}
/@unocss/transformer-directives@0.58.0:
resolution: {integrity: sha512-sU2U/aIykRkGGbA4Qo9Z5XE/KqWf7KhBwC1m8pUoqjawsZex4aVnQgXzDPfcjtmy6pElwK0z2U5DnO+OK9vCgQ==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/rule-utils': 0.57.3
'@unocss/core': 0.58.0
'@unocss/rule-utils': 0.58.0
css-tree: 2.3.1
dev: true
/@unocss/transformer-variant-group@0.57.3:
resolution: {integrity: sha512-PpKtnwyb4ncjDhsTm/PtiL0RUdmaee+07W0AzSEz29IFFwyrueIP6WHmD6agKmgDPoaw5Ywebt6DdkSbnfYHzw==}
/@unocss/transformer-variant-group@0.58.0:
resolution: {integrity: sha512-O2n8uVIpNic57rrkaaQ8jnC1WJ9N6FkoqxatRDXZ368aJ1CJNya0ZcVUL6lGGND0bOLXen4WmEN62ZxEWTqdkA==}
dependencies:
'@unocss/core': 0.57.3
'@unocss/core': 0.58.0
dev: true
/@unocss/vite@0.57.3(vite@4.5.0):
resolution: {integrity: sha512-SX2wtxRFLka0CgMwqokKuhaBUptj8vcpmLObVRRgV+7dSdx6GMbZcjZfQfibMKhJY3d5iSAylcfyW2JqTX2F+g==}
/@unocss/vite@0.58.0(vite@4.5.0):
resolution: {integrity: sha512-OCUOLMSOBEtXOEyBbAvMI3/xdR175BWRzmvV9Wc34ANZclEvCdVH8+WU725ibjY4VT0gVIuX68b13fhXdHV41A==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
dependencies:
'@ampproject/remapping': 2.2.1
'@rollup/pluginutils': 5.0.5
'@unocss/config': 0.57.3
'@unocss/core': 0.57.3
'@unocss/inspector': 0.57.3
'@unocss/scope': 0.57.3
'@unocss/transformer-directives': 0.57.3
'@rollup/pluginutils': 5.1.0
'@unocss/config': 0.58.0
'@unocss/core': 0.58.0
'@unocss/inspector': 0.58.0
'@unocss/scope': 0.58.0
'@unocss/transformer-directives': 0.58.0
chokidar: 3.5.3
fast-glob: 3.3.2
magic-string: 0.30.5
@@ -558,6 +890,13 @@ packages:
hasBin: true
dev: true
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: true
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
@@ -582,11 +921,35 @@ packages:
fill-range: 7.0.1
dev: true
/browserslist@4.22.1:
resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001563
electron-to-chromium: 1.4.588
node-releases: 2.0.13
update-browserslist-db: 1.0.13(browserslist@4.22.1)
dev: true
/cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
dev: true
/caniuse-lite@1.0.30001563:
resolution: {integrity: sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==}
dev: true
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: true
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -602,6 +965,16 @@ packages:
fsevents: 2.3.3
dev: true
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: true
/color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: true
/colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
dev: true
@@ -611,6 +984,10 @@ packages:
engines: {node: ^14.18.0 || >=16.10.0}
dev: true
/convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
dev: true
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -652,6 +1029,10 @@ packages:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
dev: true
/electron-to-chromium@1.4.588:
resolution: {integrity: sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w==}
dev: true
/esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
@@ -682,6 +1063,16 @@ packages:
'@esbuild/win32-x64': 0.18.20
dev: true
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
dev: true
/escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: true
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
@@ -741,6 +1132,11 @@ packages:
dev: true
optional: true
/gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
dev: true
/get-stream@6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
@@ -753,6 +1149,11 @@ packages:
is-glob: 4.0.3
dev: true
/globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: true
/gzip-size@6.0.0:
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
engines: {node: '>=10'}
@@ -760,6 +1161,11 @@ packages:
duplexer: 0.1.2
dev: true
/has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: true
/human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -803,6 +1209,22 @@ packages:
hasBin: true
dev: true
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
/jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: true
/json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
dev: true
/jsonc-parser@3.2.0:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
@@ -823,6 +1245,12 @@ packages:
p-locate: 5.0.0
dev: true
/lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
yallist: 3.1.1
dev: true
/magic-string@0.30.5:
resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
engines: {node: '>=12'}
@@ -884,6 +1312,10 @@ packages:
resolution: {integrity: sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==}
dev: true
/node-releases@2.0.13:
resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
dev: true
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -1003,6 +1435,11 @@ packages:
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
dev: true
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
dev: true
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -1049,6 +1486,18 @@ packages:
engines: {node: '>=6'}
dev: true
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: true
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: true
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -1074,38 +1523,38 @@ packages:
mlly: 1.4.2
dev: true
/unocss@0.57.3(postcss@8.4.31)(vite@4.5.0):
resolution: {integrity: sha512-reIvKa1sG9bwRZ6oGwj8p2XZSmT5On/NEisqkxsk1vTV5ZHIagbilG3aNMb5vNcI7MhRb4dy0Z4cvyNGd3194Q==}
/unocss@0.58.0(postcss@8.4.31)(vite@4.5.0):
resolution: {integrity: sha512-MSPRHxBqWN+1AHGV+J5uUy4//e6ZBK6O+ISzD0qrXcCD/GNtxk1+lYjOK2ltkUiKX539+/KF91vNxzhhwEf+xA==}
engines: {node: '>=14'}
peerDependencies:
'@unocss/webpack': 0.57.3
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
'@unocss/webpack': 0.58.0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
peerDependenciesMeta:
'@unocss/webpack':
optional: true
vite:
optional: true
dependencies:
'@unocss/astro': 0.57.3(vite@4.5.0)
'@unocss/cli': 0.57.3
'@unocss/core': 0.57.3
'@unocss/extractor-arbitrary-variants': 0.57.3
'@unocss/postcss': 0.57.3(postcss@8.4.31)
'@unocss/preset-attributify': 0.57.3
'@unocss/preset-icons': 0.57.3
'@unocss/preset-mini': 0.57.3
'@unocss/preset-tagify': 0.57.3
'@unocss/preset-typography': 0.57.3
'@unocss/preset-uno': 0.57.3
'@unocss/preset-web-fonts': 0.57.3
'@unocss/preset-wind': 0.57.3
'@unocss/reset': 0.57.3
'@unocss/transformer-attributify-jsx': 0.57.3
'@unocss/transformer-attributify-jsx-babel': 0.57.3
'@unocss/transformer-compile-class': 0.57.3
'@unocss/transformer-directives': 0.57.3
'@unocss/transformer-variant-group': 0.57.3
'@unocss/vite': 0.57.3(vite@4.5.0)
'@unocss/astro': 0.58.0(vite@4.5.0)
'@unocss/cli': 0.58.0
'@unocss/core': 0.58.0
'@unocss/extractor-arbitrary-variants': 0.58.0
'@unocss/postcss': 0.58.0(postcss@8.4.31)
'@unocss/preset-attributify': 0.58.0
'@unocss/preset-icons': 0.58.0
'@unocss/preset-mini': 0.58.0
'@unocss/preset-tagify': 0.58.0
'@unocss/preset-typography': 0.58.0
'@unocss/preset-uno': 0.58.0
'@unocss/preset-web-fonts': 0.58.0
'@unocss/preset-wind': 0.58.0
'@unocss/reset': 0.58.0
'@unocss/transformer-attributify-jsx': 0.58.0
'@unocss/transformer-attributify-jsx-babel': 0.58.0
'@unocss/transformer-compile-class': 0.58.0
'@unocss/transformer-directives': 0.58.0
'@unocss/transformer-variant-group': 0.58.0
'@unocss/vite': 0.58.0(vite@4.5.0)
vite: 4.5.0
transitivePeerDependencies:
- postcss
@@ -1113,6 +1562,17 @@ packages:
- supports-color
dev: true
/update-browserslist-db@1.0.13(browserslist@4.22.1):
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.22.1
escalade: 3.1.1
picocolors: 1.0.0
dev: true
/vite@4.5.0:
resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -1156,6 +1616,10 @@ packages:
isexe: 2.0.0
dev: true
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true
/yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

+16 -15
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/distribution v2.8.2+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -18,47 +18,48 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.10.0
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.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.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.10
github.com/go-chi/jwtauth/v5 v5.1.1
github.com/go-chi/jwtauth/v5 v5.3.0
github.com/yuin/goldmark v1.6.0
github.com/yuin/goldmark-meta v1.1.0
)
require (
github.com/alexflint/go-scalar v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/andybalholm/cascadia v1.3.2 // 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/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.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/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.11 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.17 // indirect
github.com/lestrrat-go/option v1.0.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
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
golang.org/x/tools v0.12.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.15.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
)
go 1.21.4
go 1.21.5
+51 -28
View File
@@ -49,8 +49,9 @@ github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258m
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 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
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=
github.com/beme/abide v0.0.0-20190723115211-635a09831760/go.mod h1:6+8gCKsZnxzhGTmKRh4BSkLos9CbWRJNcrp55We4SqQ=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -68,8 +69,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
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/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
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/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -88,6 +91,10 @@ github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.1.1 h1:Pjixqu5YkjE9sCLpzE01L0Q4sQzJIPdo7uz9r8ftp/c=
github.com/go-chi/jwtauth/v5 v5.1.1/go.mod h1:CYP1WSbzD4MPuKCr537EM3kfFhSQgpUEtMJFuYJjqWU=
github.com/go-chi/jwtauth/v5 v5.2.0 h1:rw2wRNY6QHxyjYhoZYrQ4IeXVpPeun9nCZ9DBItDFPc=
github.com/go-chi/jwtauth/v5 v5.2.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY=
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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -133,6 +140,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -151,12 +160,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -174,16 +179,18 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
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/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.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7FuKLQ=
github.com/lestrrat-go/jwx/v2 v2.0.11/go.mod h1:ZtPtMFlrfDrH2Y0iwfa3dRFn8VzwBrB+cyrm3IBWdDg=
github.com/lestrrat-go/jwx/v2 v2.0.16 h1:TuH3dBkYTy2giQg/9D8f20znS3JtMRuQJ372boS3lWk=
github.com/lestrrat-go/jwx/v2 v2.0.16/go.mod h1:jBHyESp4e7QxfERM0UKkQ80/94paqNIEcdEfiUYz5zE=
github.com/lestrrat-go/jwx/v2 v2.0.17 h1:+WavkdKVWO90ECnIzUetOnjY+kcqqw4WXEUmil7sMCE=
github.com/lestrrat-go/jwx/v2 v2.0.17/go.mod h1:G8randPHLGAqhcNCqtt6/V/7E6fvJRl3Sf9z777eTQ0=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
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=
@@ -213,6 +220,8 @@ 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.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
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=
@@ -250,9 +259,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -288,8 +299,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -326,9 +337,12 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -350,8 +364,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -393,13 +407,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.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/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -410,13 +431,15 @@ 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=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -469,8 +492,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
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=
@@ -570,8 +593,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
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=
+40
View File
@@ -0,0 +1,40 @@
package analytics
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httputil"
log "github.com/sirupsen/logrus"
)
func SendBeacon(e BeaconEvent) error {
jsonValue, err := json.Marshal(e)
if err != nil {
return err
}
req, err := http.NewRequest("POST", "https://b.dozzle.dev/event", bytes.NewBuffer(jsonValue))
if err != nil {
return err
}
response, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode/100 != 2 {
dump, err := httputil.DumpResponse(response, true)
if err != nil {
return err
}
log.Debugf("%v", string(dump))
return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status)
}
return nil
}
+15
View File
@@ -16,3 +16,18 @@ type RequestEvent struct {
TotalContainers int `json:"totalContainers"`
RunningContainers int `json:"runningContainers"`
}
type BeaconEvent struct {
Name string `json:"name"`
Version string `json:"version"`
Browser string `json:"browser"`
AuthProvider string `json:"authProvider"`
FilterLength int `json:"filterLength"`
Clients int `json:"clients"`
HasDocumentation bool `json:"hasDocumentation"`
HasCustomAddress bool `json:"hasCustomAddress"`
HasCustomBase bool `json:"hasCustomBase"`
HasHostname bool `json:"hasHostname"`
RunningContainers int `json:"runningContainers"`
HasActions bool `json:"hasActions"`
}
+17
View File
@@ -13,6 +13,7 @@ import (
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
@@ -49,6 +50,9 @@ type DockerCLI interface {
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
ContainerStats(ctx context.Context, containerID string, stream bool) (types.ContainerStats, error)
Ping(ctx context.Context) (types.Ping, error)
ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error
ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error
ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error
}
type Client struct {
@@ -145,6 +149,19 @@ func (d *Client) FindContainer(id string) (Container, error) {
return container, nil
}
func (d *Client) ContainerActions(action string, containerID string) error {
switch action {
case "start":
return d.cli.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{})
case "stop":
return d.cli.ContainerStop(context.Background(), containerID, container.StopOptions{})
case "restart":
return d.cli.ContainerRestart(context.Background(), containerID, container.StopOptions{})
default:
return fmt.Errorf("unknown action: %s", action)
}
}
func (d *Client) ListContainers() ([]Container, error) {
containerListOptions := types.ContainerListOptions{
Filters: d.filters,
+111
View File
@@ -50,6 +50,42 @@ func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, st
return types.ContainerStats{}, nil
}
func (m *mockedProxy) ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error {
args := m.Called(ctx, containerID, options)
err := args.Get(0)
if err != nil {
return args.Error(0)
}
return nil
}
func (m *mockedProxy) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
args := m.Called(ctx, containerID, options)
err := args.Get(0)
if err != nil {
return args.Error(0)
}
return nil
}
func (m *mockedProxy) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
args := m.Called(ctx, containerID, options)
err := args.Get(0)
if err != nil {
return args.Error(0)
}
return nil
}
func Test_dockerClient_ListContainers_null(t *testing.T) {
proxy := new(mockedProxy)
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
@@ -202,3 +238,78 @@ func Test_dockerClient_FindContainer_error(t *testing.T) {
proxy.AssertExpectations(t)
}
func Test_dockerClient_ContainerActions_happy(t *testing.T) {
containers := []types.Container{
{
ID: "abcdefghijklmnopqrst",
Names: []string{"/z_test_container"},
},
{
ID: "1234567890_abcxyzdef",
Names: []string{"/a_test_container"},
},
}
proxy := new(mockedProxy)
client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
proxy.On("ContainerStart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
proxy.On("ContainerStop", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
proxy.On("ContainerRestart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
container, err := client.FindContainer("abcdefghijkl")
require.NoError(t, err, "error should not be thrown")
assert.Equal(t, container, Container{
ID: "abcdefghijkl",
Name: "z_test_container",
Names: []string{"/z_test_container"},
Host: "localhost",
Tty: false,
})
actions := []string{"start", "stop", "restart"}
for _, action := range actions {
err := client.ContainerActions(action, container.ID)
require.NoError(t, err, "error should not be thrown")
assert.Equal(t, err, nil)
}
proxy.AssertExpectations(t)
}
func Test_dockerClient_ContainerActions_error(t *testing.T) {
containers := []types.Container{
{
ID: "abcdefghijklmnopqrst",
Names: []string{"/z_test_container"},
},
{
ID: "1234567890_abcxyzdef",
Names: []string{"/a_test_container"},
},
}
proxy := new(mockedProxy)
client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
proxy.On("ContainerStart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
proxy.On("ContainerStop", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
proxy.On("ContainerRestart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
container, err := client.FindContainer("random-id")
require.Error(t, err, "error should be thrown")
actions := []string{"start", "stop", "restart"}
for _, action := range actions {
err := client.ContainerActions(action, container.ID)
require.Error(t, err, "error should be thrown")
assert.Error(t, err, "error should have been returned")
}
proxy.AssertExpectations(t)
}
+7 -1
View File
@@ -5,6 +5,7 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"hash/fnv"
"io"
@@ -164,7 +165,12 @@ func createEvent(message string, streamType StdType) *LogEvent {
if json.Valid([]byte(message)) {
var data map[string]interface{}
if err := json.Unmarshal([]byte(message), &data); err != nil {
log.Warnf("unable to parse json logs - error was \"%v\" while trying unmarshal \"%v\"", err.Error(), message)
var jsonErr *json.UnmarshalTypeError
if errors.As(err, &jsonErr) {
if jsonErr.Value == "string" {
log.Warnf("unable to parse json logs - error was \"%v\" while trying unmarshal \"%v\"", err.Error(), message)
}
}
} else {
logEvent.Message = data
}
+9
View File
@@ -117,6 +117,15 @@ func Test_createEvent(t *testing.T) {
Message: "{\"key\"}",
},
},
{
name: "invalid json message",
args: args{
message: "2020-05-13T18:55:37.772853839Z 123",
},
want: &LogEvent{
Message: "123",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+2 -1
View File
@@ -2,10 +2,11 @@ package healthcheck
import (
"fmt"
log "github.com/sirupsen/logrus"
"net/http"
"os"
"strings"
log "github.com/sirupsen/logrus"
)
func HttpRequest(addr string, base string) error {
@@ -3,6 +3,8 @@ package profile
import (
"encoding/json"
"errors"
"io"
"sync"
"os"
"path/filepath"
@@ -11,6 +13,12 @@ import (
log "github.com/sirupsen/logrus"
)
const (
profileFilename = "profile.json"
)
var missingProfileErr = errors.New("Profile file does not exist")
type Settings struct {
Search bool `json:"search"`
MenuWidth float32 `json:"menuWidth"`
@@ -26,7 +34,15 @@ type Settings struct {
HourStyle string `json:"hourStyle,omitempty"`
}
var data_path string
type Profile struct {
Settings *Settings `json:"settings,omitempty"`
Pinned []string `json:"pinned"`
VisibleKeys map[string][][]string `json:"visibleKeys,omitempty"`
ReleaseSeen string `json:"releaseSeen,omitempty"`
}
var dataPath string
var mux = &sync.Mutex{}
func init() {
path, err := filepath.Abs("./data")
@@ -40,28 +56,40 @@ func init() {
return
}
}
data_path = path
dataPath = path
}
func SaveUserSettings(user auth.User, settings Settings) error {
path := filepath.Join(data_path, user.Username)
func UpdateFromReader(user auth.User, reader io.Reader) error {
mux.Lock()
defer mux.Unlock()
existingProfile, err := Load(user)
if err != nil && err != missingProfileErr {
return err
}
// Create user directory if it doesn't exist
if err := json.NewDecoder(reader).Decode(&existingProfile); err != nil {
return err
}
return Save(user, existingProfile)
}
func Save(user auth.User, profile Profile) error {
path := filepath.Join(dataPath, user.Username)
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.Mkdir(path, 0755); err != nil {
return err
}
}
settings_path := filepath.Join(path, "settings.json")
data, err := json.MarshalIndent(settings, "", " ")
filePath := filepath.Join(path, profileFilename)
data, err := json.MarshalIndent(profile, "", " ")
if err != nil {
return err
}
f, err := os.Create(settings_path)
f, err := os.Create(filePath)
if err != nil {
return err
}
@@ -76,24 +104,28 @@ func SaveUserSettings(user auth.User, settings Settings) error {
return f.Sync()
}
func LoadUserSettings(user auth.User) (Settings, error) {
path := filepath.Join(data_path, user.Username)
settings_path := filepath.Join(path, "settings.json")
func Load(user auth.User) (Profile, error) {
path := filepath.Join(dataPath, user.Username)
profilePath := filepath.Join(path, profileFilename)
if _, err := os.Stat(settings_path); os.IsNotExist(err) {
return Settings{}, errors.New("Settings file does not exist")
if _, err := os.Stat(profilePath); os.IsNotExist(err) {
return Profile{}, missingProfileErr
}
f, err := os.Open(settings_path)
f, err := os.Open(profilePath)
if err != nil {
return Settings{}, err
return Profile{}, err
}
defer f.Close()
var settings Settings
if err := json.NewDecoder(f).Decode(&settings); err != nil {
return Settings{}, err
var profile Profile
if err := json.NewDecoder(f).Decode(&profile); err != nil {
return Profile{}, err
}
return settings, nil
if profile.Pinned == nil {
profile.Pinned = []string{}
}
return profile, nil
}
+40
View File
@@ -0,0 +1,40 @@
package web
import (
"net/http"
"github.com/go-chi/chi/v5"
log "github.com/sirupsen/logrus"
)
func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) {
action := chi.URLParam(r, "action")
id := chi.URLParam(r, "id")
log.Debugf("container action: %s, container id: %s", action, id)
client := h.clientFromRequest(r)
if client == nil {
log.Errorf("no client found for host %v", r.URL)
w.WriteHeader(http.StatusBadRequest)
return
}
container, err := client.FindContainer(id)
if err != nil {
log.Error(err)
w.WriteHeader(http.StatusNotFound)
return
}
err = client.ContainerActions(action, container.ID)
if err != nil {
log.Errorf("error while trying to perform action: %s", action)
w.WriteHeader(http.StatusInternalServerError)
return
}
log.Infof("container action performed: %s; container id: %s", action, id)
w.WriteHeader(http.StatusOK)
}
+25
View File
@@ -8,6 +8,8 @@ import (
"net/http"
"sync"
"github.com/amir20/dozzle/internal/analytics"
"github.com/amir20/dozzle/internal/content"
"github.com/amir20/dozzle/internal/docker"
log "github.com/sirupsen/logrus"
@@ -31,6 +33,20 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
events := make(chan docker.ContainerEvent)
stats := make(chan docker.ContainerStat)
pages, _ := content.ReadAll()
b := analytics.BeaconEvent{
Name: "events",
Version: h.config.Version,
Browser: r.Header.Get("User-Agent"),
AuthProvider: string(h.config.Authorization.Provider),
HasHostname: h.config.Hostname != "",
HasCustomBase: h.config.Base != "/",
HasCustomAddress: h.config.Addr != ":8080",
Clients: len(h.clients),
HasDocumentation: len(pages) > 0,
HasActions: h.config.EnableActions,
}
{
wg := sync.WaitGroup{}
wg.Add(len(h.clients))
@@ -69,9 +85,18 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
log.Errorf("error writing containers to event stream: %v", err)
}
b.RunningContainers = len(allContainers)
f.Flush()
}
if !h.config.NoAnalytics {
go func() {
if err := analytics.SendBeacon(b); err != nil {
log.Debugf("error sending beacon: %v", err)
}
}()
}
for {
select {
case stat := <-stats:
+9 -8
View File
@@ -51,24 +51,25 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
"hostname": h.config.Hostname,
"hosts": hosts,
"authProvider": h.config.Authorization.Provider,
"enableActions": h.config.EnableActions,
}
pages, err := content.ReadAll()
if err != nil {
log.Errorf("error reading content: %v", err)
} else if len(pages) > 0 {
for _, page := range pages {
page.Content = ""
for i, _ := range pages {
pages[i].Content = ""
}
config["pages"] = pages
}
user := auth.UserFromContext(req.Context())
if user != nil {
if settings, err := profile.LoadUserSettings(*user); err == nil {
config["serverSettings"] = settings
if profile, err := profile.Load(*user); err == nil {
config["profile"] = profile
} else {
config["serverSettings"] = struct{}{}
config["profile"] = struct{}{}
}
config["user"] = user
} else if h.config.Authorization.Provider == FORWARD_PROXY {
@@ -125,19 +126,19 @@ func (h *handler) readManifest() map[string]interface{} {
if h.config.Dev {
return map[string]interface{}{}
} else {
file, err := h.content.Open("manifest.json")
file, err := h.content.Open(".vite/manifest.json")
if err != nil {
// this should only happen during test. In production, the file is embedded in the binary and checked in main.go
return map[string]interface{}{}
}
bytes, err := io.ReadAll(file)
if err != nil {
log.Fatalf("Could not read manifest.json: %v", err)
log.Fatalf("Could not read .vite/manifest.json: %v", err)
}
var manifest map[string]interface{}
err = json.Unmarshal(bytes, &manifest)
if err != nil {
log.Fatalf("Could not parse manifest.json: %v", err)
log.Fatalf("Could not parse .vite/manifest.json: %v", err)
}
return manifest
}
+2 -1
View File
@@ -83,6 +83,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
}
g := docker.NewEventGenerator(reader, container.Tty)
encoder := json.NewEncoder(w)
loop:
for {
@@ -91,7 +92,7 @@ loop:
if !ok {
break loop
}
if err := json.NewEncoder(w).Encode(event); err != nil {
if err := encoder.Encode(event); err != nil {
log.Errorf("json encoding error while streaming %v", err.Error())
}
}
+2 -9
View File
@@ -1,7 +1,6 @@
package web
import (
"encoding/json"
"net/http"
"github.com/amir20/dozzle/internal/auth"
@@ -9,20 +8,14 @@ import (
log "github.com/sirupsen/logrus"
)
func (h *handler) saveSettings(w http.ResponseWriter, r *http.Request) {
var settings profile.Settings
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
func (h *handler) updateProfile(w http.ResponseWriter, r *http.Request) {
user := auth.UserFromContext(r.Context())
if user == nil {
http.Error(w, "Unable to find user", http.StatusInternalServerError)
return
}
if err := profile.SaveUserSettings(*user, settings); err != nil {
if err := profile.UpdateFromReader(*user, r.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Errorf("Unable to save user settings: %s", err)
return
+6 -1
View File
@@ -35,6 +35,7 @@ type Config struct {
NoAnalytics bool
Dev bool
Authorization Authorization
EnableActions bool
}
type Authorization struct {
@@ -63,6 +64,7 @@ type DockerClient interface {
ContainerStats(context.Context, string, chan<- docker.ContainerStat) error
Ping(context.Context) (types.Ping, error)
Host() *docker.Host
ContainerActions(action string, containerID string) error
}
func CreateServer(clients map[string]DockerClient, content fs.FS, config Config) *http.Server {
@@ -104,8 +106,11 @@ func createRouter(h *handler) *chi.Mux {
r.Get("/api/logs/download/{host}/{id}", h.downloadLogs)
r.Get("/api/logs/{host}/{id}", h.fetchLogsBetweenDates)
r.Get("/api/events/stream", h.streamEvents)
if h.config.EnableActions {
r.Post("/api/actions/{action}/{host}/{id}", h.containerActions)
}
r.Get("/api/releases", h.releases)
r.Put("/api/profile/settings", h.saveSettings)
r.Patch("/api/profile", h.updateProfile)
r.Get("/api/content/{id}", h.staticContent)
r.Get("/logout", h.clearSession) // TODO remove this
r.Get("/version", h.version)
+91
View File
@@ -0,0 +1,91 @@
package web
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/amir20/dozzle/internal/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func get_mocked_client() *MockedClient {
mockedClient := new(MockedClient)
container := docker.Container{ID: "123"}
mockedClient.On("FindContainer", "123").Return(container, nil)
mockedClient.On("FindContainer", "456").Return(docker.Container{}, errors.New("container not found"))
mockedClient.On("ContainerActions", "start", container.ID).Return(nil)
mockedClient.On("ContainerActions", "stop", container.ID).Return(nil)
mockedClient.On("ContainerActions", "restart", container.ID).Return(nil)
mockedClient.On("ContainerActions", "something-else", container.ID).Return(errors.New("unknown action"))
mockedClient.On("ContainerActions", "start", mock.Anything).Return(errors.New("container not found"))
return mockedClient
}
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)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
}
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)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
}
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)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 500)
}
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)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 404)
}
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)
require.NoError(t, err, "Request should not return an error.")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, rr.Code, 200)
}
+5
View File
@@ -25,6 +25,11 @@ func (m *MockedClient) FindContainer(id string) (docker.Container, error) {
return args.Get(0).(docker.Container), args.Error(1)
}
func (m *MockedClient) ContainerActions(action string, containerID string) error {
args := m.Called(action, containerID)
return args.Error(0)
}
func (m *MockedClient) ListContainers() ([]docker.Container, error) {
args := m.Called()
return args.Get(0).([]docker.Container), args.Error(1)
+27 -12
View File
@@ -6,8 +6,8 @@ toolbar:
show-all: Zeige alle Streams
label:
containers: Container
total-containers: Gesamt Container
running: Laufend
total-containers: Gesamte Container
running: Laufende
total-cpu-usage: Gesamte CPU Auslastung
total-mem-usage: Gesamte Speicher Auslastung
dozzle-version: Dozzle Version
@@ -18,8 +18,9 @@ label:
container-name: Container Name
status: Status
created: Erstellt
avg-cpu: Avg CPU (%)
avg-mem: Avg MEM (%)
avg-cpu: Avg. CPU (%)
avg-mem: Avg. MEM (%)
pinned: Angepinnt
tooltip:
search: Suche Container (⌘ + k, ⌃k)
pin-column: Als Spalte anheften
@@ -31,23 +32,23 @@ error:
events-stream:
title: Unerwarteter Fehler
message: >-
Dozzle UI konnte keine Verbindung zur API herstellen. Bitte überprüfen Sie
Ihre Netzwerkeinstellungen. Wenn Sie einen Reverse-Proxy verwenden,
stellen Sie bitte sicher, dass er ordnungsgemäß konfiguriert ist.
Dozzle UI konnte keine Verbindung zur API herstellen. Bitte überprüfe
Deine Netzwerkeinstellungen. Wenn Du einen Reverse-Proxy verwendest,
stelle bitte sicher, dass er ordnungsgemäß konfiguriert ist.
events-timeout:
title: Etwas stimmt nicht
message: >-
Dozzle UI hat beim Verbinden mit der API ein Timeout. Bitte überprüfen Sie
die Netzwerkverbindung und versuchen Sie es erneut.
Dozzle UI hat beim Verbinden mit der API ein Timeout. Bitte überprüfe
die Netzwerkverbindung und versuche es erneut.
alert:
redirected:
title: Zu neuem Container umgeleitet
message: Dozzle hat Sie automatisch zu neuem Container {containerId} umgeleitet.
message: Dozzle hat dich automatisch zu dem Container {containerId} umgeleitet.
similar-container-found:
title: Ähnlicher Container gefunden
message: >-
Dozzle hat einen ähnlichen Container {containerId} gefunden, der auf dem
gleichen Host ausgeführt wird. Möchten Sie zu ihm wechseln?
gleichen Host ausgeführt wird. Möchtest Du zu ihm wechseln?
title:
page-not-found: Seite nicht gefunden
login: Authentifizierung erforderlich
@@ -66,7 +67,7 @@ settings:
soft-wrap: Zeilenumbruch
12-24-format: >-
Standardmäßig verwendet Dozzle die Spracheinstellungen des Browsers, um die
Zeit anzuzeigen. Sie können die Zeit auf 12 oder 24 Stunden umstellen.
Zeit anzuzeigen. Du kannst die Zeit auf 12 oder 24 Stunden umstellen.
font-size: Schriftgröße für Logs
color-scheme: Farbschema
options: Optionen
@@ -78,3 +79,17 @@ settings:
Eine neue Version ist verfügbar! Aktualisiere auf <a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
show-std: Zeige stdout und stderr Labels
automatic-redirect: Automatische Weiterleitung zu neuen Containern mit demselben Namen
releases:
features: Ein neues Feature | {count} Features
bugFixes: Ein Bug Fix | {count} Fixes
breaking: Ein Breaking Change | {count} Breaking Changes
three_parts: "{first}, {second} und {third}"
two_parts: "{first} und {second}"
latest: Latest
no_releases: Du hast die aktuellste Version
log_actions:
copy_log: Log kopieren
toasts:
copied:
title: Kopiert
message: Log in Zwischenablage kopiert
+10
View File
@@ -4,6 +4,9 @@ toolbar:
search: Search
show: Show only {std}
show-all: Show all streams
stop: Stop
start: Start
restart: Restart
label:
containers: Containers
total-containers: Total Containers
@@ -88,3 +91,10 @@ releases:
two_parts: "{first} with {second}"
latest: Latest
no_releases: You have the latest version
log_actions:
copy_log: Copy log
jump_to_context: Jump to context
toasts:
copied:
title: Copied
message: Log copied to clipboard
+14
View File
@@ -76,3 +76,17 @@ settings:
<a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
show-std: Mostrar etiquetas de salida estándar y salida de error estándar
automatic-redirect: Redireccionar automáticamente a nuevos contenedores con el mismo nombre
releases:
features: Características
bugFixes: Correcciones de errores
breaking: Cambios importantes
three_parts: "{features}, {bugFixes} y {breaking}"
two_parts: "{features} y {bugFixes}"
latest: Último
no_releases: No hay lanzamientos
log_actions:
copy_log: Copiar registro
toasts:
copied:
title: Copiado
message: Registro copiado al portapapeles
+2
View File
@@ -74,3 +74,5 @@ settings:
新版本可用!更新到 <a href="{href}" rel="noreferrer noopener">{nextVersion}</a>。
show-std: 显示stdout和stderr标签
automatic-redirect: 自动重定向到同名的新容器
log_actions:
copy_log: 复制日志
+12 -20
View File
@@ -53,6 +53,7 @@ type args struct {
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running."`
RemoteHost []string `arg:"env:DOZZLE_REMOTE_HOST,--remote-host,separate" help:"list of hosts to connect remotely"`
AuthProvider string `arg:"--auth-provider,env:DOZZLE_AUTH_PROVIDER" default:"none" help:"sets the auth provider to use. Currently only forward-proxy is supported."`
EnableActions bool `arg:"--enable-actions,env:DOZZLE_ENABLE_ACTIONS" default:"false" help:"enables essential actions on containers from the web interface."`
}
type HealthcheckCmd struct {
@@ -62,7 +63,7 @@ func (args) Version() string {
return version
}
//go:embed dist
//go:embed all:dist
var content embed.FS
func main() {
@@ -72,6 +73,7 @@ func main() {
if err := healthcheck.HttpRequest(args.Addr, args.Base); err != nil {
log.Fatal(err)
}
return
}
if args.AuthProvider != "none" && args.AuthProvider != "forward-proxy" && args.AuthProvider != "simple" {
@@ -115,24 +117,13 @@ func doStartEvent(arg args) {
log.Debug("Analytics disabled.")
return
}
host, err := os.Hostname()
if err != nil {
log.Debug(err)
return
event := analytics.BeaconEvent{
Name: "start",
Version: version,
}
event := analytics.StartEvent{
ClientId: host,
Version: version,
FilterLength: len(arg.Filter),
CustomAddress: arg.Addr != ":8080",
CustomBase: arg.Base != "/",
RemoteHostLength: len(arg.RemoteHost),
Protected: arg.Username != "",
HasHostname: arg.Hostname != "",
}
if err := analytics.SendStartEvent(event); err != nil {
if err := analytics.SendBeacon(event); err != nil {
log.Debug(err)
}
}
@@ -211,6 +202,7 @@ func createServer(args args, clients map[string]web.DockerClient) *http.Server {
Provider: provider,
Authorizer: authorizer,
},
EnableActions: args.EnableActions,
}
assets, err := fs.Sub(content, "dist")
@@ -229,8 +221,8 @@ func createServer(args args, clients map[string]web.DockerClient) *http.Server {
}
if !dev {
if _, err := assets.Open("manifest.json"); err != nil {
log.Fatal("manifest.json not found")
if _, err := assets.Open(".vite/manifest.json"); err != nil {
log.Fatal(".vite/manifest.json not found")
}
if _, err := assets.Open("index.html"); err != nil {
log.Fatal("index.html not found")
@@ -246,7 +238,7 @@ func createLocalClient(args args, localClientFactory func(map[string][]string) (
if err == nil {
_, err := dockerClient.ListContainers()
if err != nil {
log.Warnf("Could not connect to local Docker Engine: %s", err)
log.Debugf("Could not connect to local Docker Engine: %s", err)
} else {
log.Debugf("Connected to local Docker Engine")
return dockerClient
+43 -42
View File
@@ -1,12 +1,13 @@
{
"name": "dozzle",
"version": "5.4.2",
"description": "Realtime log viewer for docker containers. ",
"version": "5.7.0",
"description": "Realtime log viewer for docker containers.",
"homepage": "https://github.com/amir20/dozzle#readme",
"bugs": {
"url": "https://github.com/amir20/dozzle/issues"
},
"packageManager": "pnpm@8.10.4",
"packageManager": "pnpm@8.12.0",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/amir20/dozzle.git"
@@ -27,19 +28,19 @@
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.21",
"@iconify-json/cil": "^1.1.5",
"@iconify-json/mdi": "^1.1.55",
"@iconify-json/mdi-light": "^1.1.7",
"@iconify-json/octicon": "^1.1.49",
"@iconify-json/ph": "^1.1.6",
"@iconify-json/carbon": "^1.1.25",
"@iconify-json/cil": "^1.1.7",
"@iconify-json/mdi": "^1.1.60",
"@iconify-json/mdi-light": "^1.1.9",
"@iconify-json/octicon": "^1.1.51",
"@iconify-json/ph": "^1.1.8",
"@intlify/unplugin-vue-i18n": "^1.5.0",
"@tailwindcss/typography": "^0.5.10",
"@vueuse/components": "^10.6.1",
"@vueuse/core": "^10.6.1",
"@vueuse/integrations": "^10.6.1",
"@vueuse/math": "^10.6.1",
"@vueuse/router": "^10.6.1",
"@vueuse/components": "^10.7.0",
"@vueuse/core": "^10.7.0",
"@vueuse/integrations": "^10.7.0",
"@vueuse/math": "^10.7.0",
"@vueuse/router": "^10.7.0",
"ansi-to-html": "^0.7.2",
"autoprefixer": "^10.4.16",
"d3-array": "^3.2.4",
@@ -48,58 +49,58 @@
"d3-selection": "^3.0.0",
"d3-shape": "^3.2.0",
"d3-transition": "^3.0.1",
"daisyui": "^4.0.4",
"daisyui": "^4.4.19",
"date-fns": "^2.30.0",
"entities": "^4.5.0",
"fuse.js": "^7.0.0",
"lodash.debounce": "^4.0.8",
"pinia": "^2.1.7",
"postcss": "^8.4.31",
"postcss": "^8.4.32",
"postcss-mixins": "^9.0.4",
"splitpanes": "^3.1.5",
"tailwindcss": "^3.3.5",
"unplugin-auto-import": "^0.16.7",
"unplugin-icons": "^0.17.4",
"unplugin-vue-components": "^0.25.2",
"unplugin-vue-macros": "^2.6.2",
"vite": "4.5.0",
"vite-plugin-pages": "^0.31.0",
"vite-plugin-vue-layouts": "^0.8.0",
"vue": "^3.3.8",
"vue-i18n": "^9.6.5",
"tailwindcss": "^3.3.6",
"unplugin-auto-import": "^0.17.2",
"unplugin-icons": "^0.18.1",
"unplugin-vue-components": "^0.26.0",
"unplugin-vue-macros": "^2.7.0",
"vite": "5.0.7",
"vite-plugin-pages": "^0.32.0",
"vite-plugin-vue-layouts": "^0.9.1",
"vue": "^3.3.11",
"vue-i18n": "^9.8.0",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@iconify-json/ic": "^1.1.14",
"@iconify-json/ic": "^1.1.16",
"@pinia/testing": "^0.1.3",
"@playwright/test": "^1.39.0",
"@playwright/test": "^1.40.1",
"@types/d3-array": "^3.2.1",
"@types/d3-ease": "^3.0.2",
"@types/d3-scale": "^4.0.8",
"@types/d3-selection": "^3.0.10",
"@types/d3-shape": "^3.1.5",
"@types/d3-shape": "^3.1.6",
"@types/d3-transition": "^3.0.8",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^20.9.0",
"@types/semver": "^7.5.5",
"@vitejs/plugin-vue": "4.4.1",
"@vue/compiler-sfc": "^3.3.8",
"@vue/test-utils": "^2.4.1",
"@types/node": "^20.10.4",
"@types/semver": "^7.5.6",
"@vitejs/plugin-vue": "4.5.2",
"@vue/compiler-sfc": "^3.3.11",
"@vue/test-utils": "^2.4.3",
"bumpp": "^9.2.0",
"c8": "^8.0.1",
"concurrently": "^8.2.2",
"eventsourcemock": "^2.0.0",
"jest-serializer-vue": "^3.1.0",
"jsdom": "^22.1.0",
"lint-staged": "^15.1.0",
"jsdom": "^23.0.1",
"lint-staged": "^15.2.0",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7",
"prettier-plugin-tailwindcss": "^0.5.9",
"simple-git-hooks": "^2.9.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"vitepress": "1.0.0-rc.25",
"vitest": "^0.34.6",
"vue-tsc": "^1.8.22"
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vitepress": "1.0.0-rc.31",
"vitest": "^1.0.4",
"vue-tsc": "^1.8.25"
},
"lint-staged": {
"*.{js,vue,css,ts,html,md}": [
+1396 -912
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -15,9 +15,9 @@
{{- if .Dev}}
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/assets/main.ts"></script>
{{- else }} {{ $js := index .Manifest "assets/main.ts" "file" }} {{ $css := index .Manifest "assets/main.css" "file"
{{- else }} {{ $js := index .Manifest "assets/main.ts" "file" }} {{ $css := index .Manifest "assets/main.ts" "css"
}}
<link rel="stylesheet" href="{{ .Base }}/{{ $css }}" />
<link rel="stylesheet" href="{{ .Base }}/{{ index $css 0}}" />
<script type="module" src="{{ .Base }}/{{ $js }}"></script>
{{- end }}
<meta name="description" content="Dozzle is a log viewer for Docker" />
+3
View File
@@ -3,6 +3,9 @@ import DaisyUI from "daisyui";
import Typography from "@tailwindcss/typography";
export default {
future: {
hoverOnlyWhenSupported: true,
},
content: ["./assets/**/*.{vue,js,ts}", "./public/index.html"],
theme: {
extend: {