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