Compare commits

...

145 Commits

Author SHA1 Message Date
Amir Raminfar c8565e08c9 Release 4.9.2 2023-04-06 09:53:52 -07:00
Amir Raminfar 63a0d544e4 Adds tooltip to healthcheck. Fixes #2104 (#2110) 2023-04-06 16:50:44 +00:00
renovate[bot] e7591f9a64 Update module github.com/docker/docker to v23.0.3+incompatible [SECURITY] (#2109) 2023-04-04 19:12:37 -07:00
renovate[bot] f242776772 Update golang Docker tag to v1.20.3 (#2108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 13:26:28 -07:00
Amir Raminfar 51008ba604 Updates modules 2023-04-04 13:25:41 -07:00
renovate[bot] 96c68686bf Update pnpm to v8.1.1 (#2106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 13:19:26 -07:00
renovate[bot] 93ddbca73a Update dependency vitepress to v1.0.0-alpha.65 (#2107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 13:19:18 -07:00
Amir Raminfar a30de127ca Fixes spaces 2023-04-04 11:32:58 -07:00
Amir Raminfar 8e5611bc50 Updates docs to have analytics and dashboard 2023-04-03 12:00:15 -07:00
Amir Raminfar 9e2d6a484a Tries to remove unneeded npmrc changes 2023-03-31 08:32:02 -07:00
Amir Raminfar 7be7b17cc1 Uses corepack (#2103)
* Uses corepack

* Adds corepack to Docker
2023-03-30 10:15:18 -07:00
Amir Raminfar a619149412 Adds strict-peer-dependencies 2023-03-29 14:18:56 -07:00
Amir Raminfar 488156df89 Revert netlify 2023-03-29 14:12:54 -07:00
Amir Raminfar 1c068cc6f9 Temprorary fix for pnpm 2023-03-29 14:09:26 -07:00
renovate[bot] c6f2f8ccb2 Update dependency vitepress to v1.0.0-alpha.64 (#2093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-29 12:49:27 -07:00
Amir Raminfar 518c78c3d1 Release 4.9.1 2023-03-29 12:38:13 -07:00
Amir Raminfar fe034d1a38 Removes -- 2023-03-29 12:29:26 -07:00
Amir Raminfar 49e6cd1c9b Changes to 1.20 (#2102) 2023-03-29 12:27:35 -07:00
Amir Raminfar 3e78f81a53 Release 4.9.0 2023-03-29 11:56:00 -07:00
Amir Raminfar e84f0cdb97 Adds typechecking with typescript (#2101)
* Fixes all other vue errors

* Adds typescheck

* Fix pnpm

* Fix pnpm again for cache
2023-03-29 11:53:15 -07:00
Amir Raminfar 804bd5b9e1 Fixes mobile bug 2023-03-29 09:59:21 -07:00
Amir Raminfar 9c9f247e0c cleans up typescript errors 2023-03-29 09:38:02 -07:00
Amir Raminfar db43f7a62d Fixes vue-tsc errors 2023-03-29 09:27:35 -07:00
Amir Raminfar efef1dca6b Fixes component d file 2023-03-29 09:20:47 -07:00
Amir Raminfar 3417ec3e99 Cleans up icons 2023-03-29 09:16:38 -07:00
Amir Raminfar bda832a8a3 Updates modules 2023-03-29 09:01:36 -07:00
renovate[bot] 30e9ab68d7 Update cypress/included Docker tag to v12.9.0 (#2100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-29 08:59:02 -07:00
Amir Raminfar 9ba1a0f76a Adds health status by parsing the label from status (#2099)
* Adds healthcheck to status in menu

* Fixes bug with crated

* Moves colors to vars

* Updates icons
2023-03-29 08:58:12 -07:00
Amir Raminfar 0521f343e3 Also checks for level between [] 2023-03-28 16:04:56 -07:00
renovate[bot] 9ecc21dcb2 Update module github.com/docker/docker to v23.0.2+incompatible (#2098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-28 09:55:33 -07:00
Amir Raminfar d99f4b2766 Updates pnpm 2023-03-28 09:08:38 -07:00
Amir Raminfar 5c83e33ace Updates circles 2023-03-27 19:01:25 -07:00
Amir Raminfar a4ac2c60ac Updates modules 2023-03-27 15:29:13 -07:00
Amir Raminfar bbb7fb8ba9 Updates modules 2023-03-26 09:01:49 -07:00
renovate[bot] 0ebd399768 Update dependency vitepress to v1.0.0-alpha.62 (#2092) 2023-03-25 08:40:57 -07:00
Amir Raminfar 9971b41434 Updates modules 2023-03-24 14:33:31 -07:00
Amir Raminfar 7f4d005a67 Update README.md 2023-03-24 13:43:45 -07:00
Amir Raminfar 2709417ba3 Release 4.8.2 2023-03-23 16:12:38 -07:00
Amir Raminfar c4386d2d84 Updates modules 2023-03-23 12:55:43 -07:00
Amir Raminfar fbcf989885 Updates with dark and light video 2023-03-21 15:25:03 -07:00
Amir Raminfar 07352f6e16 Adds dark and light for browser 2023-03-21 12:41:22 -07:00
Amir Raminfar 6fecfce3c7 Updates color on video 2023-03-21 12:35:32 -07:00
Amir Raminfar 2884ffafeb Adds blur 2023-03-21 12:33:06 -07:00
Amir Raminfar 67ac202b04 Updates videos 2023-03-20 13:08:18 -07:00
Amir Raminfar 839afd8fcf Fixes bug for level 2023-03-20 13:06:31 -07:00
Amir Raminfar 202b07bfb4 Updates logic to find ERROR in logs 2023-03-20 11:13:53 -07:00
renovate[bot] 15bd0999a2 Update dependency vite to v4.2.1 (#2088) 2023-03-20 06:08:57 -07:00
Amir Raminfar 4450c2527f node latest (#2089)
* Fixes tests

* Fixes tests

* Updates tests

* Updates node to latest

* Updates modules
2023-03-16 13:48:42 -07:00
renovate[bot] 614e62b782 Update cypress/included Docker tag to v12.8.1 (#2086) 2023-03-16 13:03:49 -07:00
Amir Raminfar 77cce86e29 Fixes file formatting (#2085)
* Updates prettier ignore

* Updates prettier ignore

* Updates formatting

* Fixes tests

* Fixes tests
2023-03-15 10:46:32 -07:00
renovate[bot] 93dc547b9d Update actions/setup-go action to v4 (#2084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-15 09:14:43 -07:00
renovate[bot] 0681a3c0c6 Update cypress/included Docker tag to v12.8.0 (#2063)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-15 08:50:58 -07:00
renovate[bot] 9bf35e3e1f Update dependency vitepress to v1.0.0-alpha.60 (#2082)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-15 08:50:49 -07:00
Amir Raminfar 087e8694d1 Uses node lts instead (#2083)
* Uses node lts instead

* Uses node 18
2023-03-15 08:39:41 -07:00
renovate[bot] 34b034c822 Update dependency vitepress to v1.0.0-alpha.58 (#2080)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-14 14:43:29 -07:00
Amir Raminfar bd598699e3 Adds docker social link 2023-03-13 13:55:51 -07:00
Amir Raminfar 8867b2aa7c Updates docs 2023-03-13 09:51:53 -07:00
Amir Raminfar 296e8fc50e Fixes docker build 2023-03-13 09:19:35 -07:00
Amir Raminfar da065a1966 Fixes space 2023-03-13 09:15:41 -07:00
Amir Raminfar 5262a54463 Removes husky 2023-03-13 09:12:29 -07:00
Amir Raminfar f37c5d09ed Updates lint stage 2023-03-13 09:09:26 -07:00
Amir Raminfar 298e030db0 Uses simple-git-hooks 2023-03-13 09:08:09 -07:00
Amir Raminfar 6f2ef4499b Updates deps 2023-03-13 08:43:06 -07:00
Amir Raminfar e4d1064843 Updates npmrc 2023-03-13 08:30:24 -07:00
Amir Raminfar 0c1ad7c0c7 Adds include-workspace-root 2023-03-12 13:26:40 -07:00
Amir Raminfar fb9333a11b Fixes workspaces and int tests 2023-03-12 13:23:21 -07:00
Amir Raminfar ed67d7d085 Updates libs for dozzle 2023-03-12 12:46:14 -07:00
Amir Raminfar d34136589b Revert "Update dependency vitepress to v1.0.0-alpha.52 (#2078)" (#2079)
This reverts commit ac7b120176.
2023-03-11 16:45:22 -08:00
Amir Raminfar 5226c92f78 Adds sitemap 2023-03-11 15:18:22 -08:00
Amir Raminfar 9e507b31ec Fixes max width for video 2023-03-11 15:11:38 -08:00
renovate[bot] ac7b120176 Update dependency vitepress to v1.0.0-alpha.52 (#2078)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-11 15:06:31 -08:00
Amir Raminfar c15a680797 Adds more docs 2023-03-10 16:05:48 -08:00
Amir Raminfar 1c07142bce Adds workspace 2023-03-10 14:09:57 -08:00
renovate[bot] c886071559 Update docker/setup-buildx-action action to v2.5.0 (#2075)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-10 12:23:11 -08:00
renovate[bot] 44b6809abe Update dependency vitepress to v1.0.0-alpha.51 (#2077)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-10 12:23:01 -08:00
Amir Raminfar 13db2cfb58 Fixes style on mobile 2023-03-10 12:19:56 -08:00
Amir Raminfar a1af2c60cd vitepress (#2076)
* Uses vitepress

* migrates to vitepress

* Remove api link

* Adds gitignore

* Adds gitignore

* Removes unused colors

* Removes optional for docker
2023-03-10 12:01:26 -08:00
Amir Raminfar 37d322bec8 Adds gtag 2023-03-08 13:22:57 -08:00
Amir Raminfar ccd61ce095 Fixes js warn 2023-03-08 13:06:04 -08:00
Amir Raminfar 01c63a3650 Updates colors and adds button 2023-03-08 13:03:36 -08:00
Amir Raminfar 5a99378845 Updates dockerfile for int test 2023-03-08 10:34:08 -08:00
renovate[bot] dfed83df0b Update pnpm to v7.29.1 (#2068)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-08 10:22:00 -08:00
renovate[bot] 75b0d5c4fb Update module github.com/spf13/afero to v1.9.5 (#2071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-08 10:21:41 -08:00
renovate[bot] d654f3eceb Update golang Docker tag to v1.20.2 (#2072)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-08 10:21:30 -08:00
renovate[bot] 6cdcbd8244 Update dependency @intlify/unplugin-vue-i18n to ^0.9.0 (#2073)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-08 10:21:19 -08:00
Amir Raminfar cb18e42602 Fixes pre 2023-03-06 11:45:13 -08:00
Amir Raminfar 37d78117f1 Fixes menu 2023-03-06 09:58:25 -08:00
Amir Raminfar 0cf4e6d2b6 Creates header 2023-03-06 09:45:32 -08:00
Amir Raminfar ec5196ea6e Removes comma 2023-03-06 09:31:37 -08:00
Amir Raminfar 089c2e9b17 Finishes menu 2023-03-06 09:27:33 -08:00
Amir Raminfar 5f2420960b Makes improves to colors and menu 2023-03-05 14:24:43 -08:00
Amir Raminfar e73ee6735e Adds guide link 2023-03-04 11:17:50 -08:00
Amir Raminfar 03b4c5adb9 Updates link colors 2023-03-03 15:20:11 -08:00
Amir Raminfar b92703c7bb Updates title and adds more content 2023-03-03 13:12:18 -08:00
Amir Raminfar 2bb4d10b66 Adds docs layouts 2023-03-03 09:50:01 -08:00
Amir Raminfar e02b08c21d Removes unused components 2023-03-02 15:57:12 -08:00
Amir Raminfar 539ace566c Adds icons for github and docker 2023-03-01 12:15:04 -08:00
Amir Raminfar a29373d1a2 Adds poster fix 2023-02-28 16:48:42 -08:00
Amir Raminfar 3c0eac4541 Adds poster 2023-02-28 16:32:10 -08:00
Amir Raminfar beee7ea9f8 Fixes grid bug 2023-02-28 15:51:08 -08:00
Amir Raminfar 770416805e Adds color to dozzle 2023-02-28 15:48:06 -08:00
Amir Raminfar c2fcecaec0 Updates modules 2023-02-28 13:45:25 -08:00
Amir Raminfar 3a30bcf937 Fixes grid 2023-02-28 13:42:39 -08:00
Amir Raminfar 54f9a04b9d Fixes grid problem 2023-02-28 13:10:24 -08:00
Amir Raminfar 62b7c38f88 Adds dockerignore 2023-02-28 12:02:31 -08:00
Amir Raminfar b474128a5f Fixes vscode settings 2023-02-28 12:00:07 -08:00
Amir Raminfar cb46130880 Moves netlify again 2023-02-28 11:51:34 -08:00
Amir Raminfar 8cf9fbe77c Moves netlify 2023-02-28 11:48:43 -08:00
Amir Raminfar 9c5da96eb4 Moves netlify 2023-02-28 11:48:43 -08:00
Amir Raminfar 5abf538cb4 Updates docs 2023-02-28 11:48:43 -08:00
renovate[bot] a1d79d4fc6 Update dependency vitest to ^0.29.0 (#2064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-27 10:56:07 -08:00
renovate[bot] 468c8982b0 Update module github.com/stretchr/testify to v1.8.2 (#2065)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-27 10:55:51 -08:00
Amir Raminfar a9b4be43c8 Release 4.8.1 2023-02-26 08:41:39 -08:00
Amir Raminfar 1350895676 Updates logic when no TLS certs are found to use plan HTTP. See #2066 (#2067) 2023-02-25 12:57:35 -08:00
Amir Raminfar 591bee332f Release 4.8.0 2023-02-24 09:51:21 -08:00
Amir Raminfar 3257f4b04f Updates remadme file 2023-02-24 09:46:21 -08:00
Amir Raminfar 872729a93b Adds support for multiple hosts (#2059)
* Adds support for multiple hosts

* Adds UI for drop down

* Adds support for TLS and remove SSH

* Changes dropdown to only show up with >1 hosts

* Fixes js tests

* Fixes go tests

* Fixes download link

* Updates readme

* Removes unused imports

* Fixes spaces
2023-02-24 09:42:58 -08:00
renovate[bot] a7d6a5088a Update dependency vite to v4.1.4 (#2060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-23 15:08:26 -08:00
renovate[bot] ebf832b501 Update module github.com/spf13/afero to v1.9.4 (#2061)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-23 15:08:14 -08:00
renovate[bot] 11fd3e14bb Update dependency unplugin-auto-import to ^0.15.0 (#2062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-23 15:07:32 -08:00
Amir Raminfar eb4df34d42 Updates go modules 2023-02-21 08:17:49 -08:00
Amir Raminfar 6bc6dd5c03 Updates modules 2023-02-21 08:16:52 -08:00
renovate[bot] 460f1167b0 Update dependency vite to v4.1.3 (#2057) 2023-02-20 07:08:27 -08:00
renovate[bot] f04563e868 Update dependency @pinia/testing to ^0.0.15 (#2056) 2023-02-20 07:07:59 -08:00
renovate[bot] c4881cf407 Update cypress/included Docker tag to v12.6.0 (#2048) 2023-02-19 07:05:17 -08:00
renovate[bot] 9945f31e7c Update dependency vite to v4.1.2 (#2052) 2023-02-19 07:05:10 -08:00
renovate[bot] c13fd882ed Update dependency vite-plugin-vue-layouts to ^0.8.0 (#2053) 2023-02-19 06:16:37 -08:00
Amir Raminfar da6257c233 Updates modules 2023-02-16 13:24:36 -08:00
Amir Raminfar 0b5da03ffb Release 4.7.5 2023-02-15 12:25:48 -08:00
renovate[bot] 617fc3d387 Update golang Docker tag to v1.20.1 (#2046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-15 12:24:20 -08:00
Amir Raminfar d82f9e4db2 Updates modules 2023-02-14 14:54:29 -08:00
renovate[bot] efabc926cf Update module github.com/docker/docker to v23.0.1+incompatible (#2043) 2023-02-14 06:17:35 -08:00
renovate[bot] c5505e9ac5 Update dependency unplugin-auto-import to ^0.14.0 (#2040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-09 10:30:21 -08:00
Amir Raminfar bb14f9121e Properly handles casting of string. Fixes #2041 (#2042) 2023-02-09 09:46:46 -08:00
Amir Raminfar 050666a39c Uses 127.0.0.1 instead 2023-02-08 10:24:05 -08:00
renovate[bot] a03d83676f Update docker/setup-buildx-action action to v2.4.1 (#2035) 2023-02-06 06:30:41 -08:00
Amir Raminfar 6d4bff0ce8 Adds coverage to ignore 2023-02-03 08:58:34 -08:00
renovate[bot] 5e1b532f52 Update cypress/included Docker tag to v12.5.1 (#2034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-03 08:38:28 -08:00
Amir Raminfar 4ea19f97da Adds more tests 2023-02-02 12:23:46 -08:00
Amir Raminfar 3420eb968e Updates go modules 2023-02-02 12:08:08 -08:00
Amir Raminfar f9f48c3f81 Updates npm modules and tests 2023-02-02 12:06:18 -08:00
renovate[bot] 5fad7b1efc Update dependency vite to v4.1.1 (#2033) 2023-02-02 07:09:46 -08:00
renovate[bot] 1337ad97f1 Update golang Docker tag to v1.20.0 (#2030) 2023-02-02 07:01:36 -08:00
renovate[bot] 520d6c05b1 Update dependency vite to v4.1.0 (#2031) 2023-02-02 07:01:27 -08:00
renovate[bot] 52bd643b63 Update cypress/included Docker tag to v12.5.0 (#2018) 2023-01-31 16:42:19 -08:00
104 changed files with 5103 additions and 2909 deletions
+1 -1
View File
@@ -5,4 +5,4 @@ node_modules
dist
.git
e2e
docs
+8 -7
View File
@@ -1,10 +1,9 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
title: ""
labels: ""
assignees: amir20
---
**Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -24,9 +24,10 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS, Windows or Mac Os]
- Docker version [e.g. `docker version`'s output]
- Browser & version [e.g. chrome, safari]
- Version [e.g. 1.26.1. Can be found at `<Dozzle-host>/version`]
- OS: [e.g. iOS, Windows or Mac Os]
- Docker version [e.g. `docker version`'s output]
- Browser & version [e.g. chrome, safari]
- Version [e.g. 1.26.1. Can be found at `<Dozzle-host>/version`]
**If applicable include logs with `--level debug` and browser logs**
+1 -2
View File
@@ -1,10 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
title: ""
labels: enhancement
assignees: amir20
---
**Is your feature request related to a problem? Please describe.**
+20 -17
View File
@@ -8,28 +8,30 @@ jobs:
name: JavaScript Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v3
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
name: Install Node
with:
node-version: latest
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
- run: corepack enable
- run: pnpm --version
- uses: actions/setup-node@v3
with:
version: 7.26.0
node-version: latest
cache: "pnpm"
cache-dependency-path: "**/pnpm-lock.yaml"
- name: Install dependencies
run: pnpm install
run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Tests
run: pnpm run test
run: pnpm run test -- --outputTruncateLength=500
go-test:
name: Go Tests
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: 1.19.x
go-version: 1.20.x
- name: Checkout code
uses: actions/checkout@v3
- name: Run Go Tests with Coverage
@@ -55,7 +57,7 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.4.0
uses: docker/setup-buildx-action@v2.5.0
- name: Login to DockerHub
uses: docker/login-action@v2.1.0
with:
@@ -80,17 +82,18 @@ jobs:
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: latest
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
with:
version: 6.20.1
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Release to Github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm run release -- --github.release --no-increment --no-git --ci
run: pnpm run release --github.release --no-increment --no-git --ci
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.4.0
uses: docker/setup-buildx-action@v2.5.0
- name: Login to DockerHub
uses: docker/login-action@v2.1.0
with:
+33 -11
View File
@@ -7,22 +7,44 @@ on:
- master
name: Test
jobs:
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
name: Install Node
with:
node-version: latest
- run: corepack enable
- run: pnpm --version
- uses: actions/setup-node@v3
with:
node-version: latest
cache: "pnpm"
cache-dependency-path: "**/pnpm-lock.yaml"
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Typecheck
run: pnpm run typecheck
npm-test:
name: JavaScript Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v3
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
name: Install Node
with:
node-version: latest
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
- run: corepack enable
- run: pnpm --version
- uses: actions/setup-node@v3
with:
version: 7.26.0
node-version: latest
cache: "pnpm"
cache-dependency-path: "**/pnpm-lock.yaml"
- name: Install dependencies
run: pnpm install
run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Tests
run: pnpm run test -- --outputTruncateLength=500
go-test:
@@ -30,9 +52,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: 1.19.x
go-version: 1.20.x
- name: Checkout code
uses: actions/checkout@v3
- name: Run Go Tests with Coverage
@@ -46,7 +68,7 @@ jobs:
with:
fetch-depth: 2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.4.0
uses: docker/setup-buildx-action@v2.5.0
- name: Login to DockerHub
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork }}
uses: docker/login-action@v2.1.0
+1
View File
@@ -7,3 +7,4 @@ dozzle
coverage
.pnpm-debug.log
.vscode
coverage.out
-1
View File
@@ -1 +0,0 @@
_
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
pnpm lint-staged
+3
View File
@@ -0,0 +1,3 @@
shared-workspace-lockfile=false
include-workspace-root=true
ignore-workspace-root-check=true
+5
View File
@@ -0,0 +1,5 @@
auto-imports.d.ts
components.d.ts
docs/.vitepress/cache
docs/.vitepress/dist
dist
+13 -13
View File
@@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
+5 -6
View File
@@ -1,18 +1,17 @@
# Build assets
FROM --platform=$BUILDPLATFORM node:19-alpine as node
RUN npm install -g pnpm
RUN corepack enable
WORKDIR /build
# Install dependencies from lock file
COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY pnpm-*.yaml ./
RUN pnpm fetch --ignore-scripts --no-optional
# Copy package.json and install dependencies
COPY package.json ./
RUN pnpm install -r --offline --ignore-scripts
RUN pnpm install --offline --ignore-scripts --no-optional
# Copy assets and translations to build
COPY .* vite.config.ts index.html ./
@@ -22,7 +21,7 @@ COPY locales ./locales
# Build assets
RUN pnpm build
FROM --platform=$BUILDPLATFORM golang:1.19.5-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.20.3-alpine AS builder
RUN apk add --no-cache ca-certificates && mkdir /dozzle
+32 -12
View File
@@ -2,7 +2,7 @@
Dozzle is a small lightweight application with a web based interface to monitor Docker logs. It doesnt store any log files. It is for live monitoring of your container logs only.
![Image](https://github.com/amir20/dozzle/blob/master/.github/demo.gif?raw=true)
https://user-images.githubusercontent.com/260667/227634771-9ebbe381-16a8-465a-b28a-450c5cd20c94.mp4
[![Go Report Card](https://goreportcard.com/badge/github.com/amir20/dozzle)](https://goreportcard.com/report/github.com/amir20/dozzle)
[![Docker Pulls](https://img.shields.io/docker/pulls/amir20/dozzle.svg)](https://hub.docker.com/r/amir20/dozzle/)
@@ -37,16 +37,7 @@ The simplest way to use dozzle is to run the docker container. Also, mount the D
Dozzle will be available at [http://localhost:8888/](http://localhost:8888/). You can change `-p 8888:8080` to any port. For example, if you want to view dozzle over port 4040 then you would do `-p 4040:8080`.
### With Docker swarm
docker service create \
--name=dozzle \
--publish=8888:8080 \
--constraint=node.role==manager \
--mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
amir20/dozzle:latest
### With Docker compose
### Connecting with Docker compose
version: "3"
services:
@@ -58,7 +49,35 @@ Dozzle will be available at [http://localhost:8888/](http://localhost:8888/). Yo
ports:
- 9999:8080
### Enabling health check
### Connecting to remote hosts
Dozzle supports connecting to multiple remote hosts via `tcp://` using TLS or without. Appropriate certs need to be mounted for Dozzle to be able to successfully connect. At this point, `ssh://` is not supported because Dozzle docker image does not ship with any ssh clients.
To configure remote hosts, `--remote-host` or `DOZZLE_REMOTE_HOST` need to provided and the `pem` files need to be mounted to `/cert` directory. The `/cert` directory expects to have `/certs/{ca,cert,key}.pem` or `/certs/{host}/{ca,cert,key}.pem` in case of multiple hosts.
Below are examples of using `--remote-host` via CLI:
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/certs:/certs -p 8080:8080 amir20/dozzle --remote-host tcp://167.99.1.1:2376
Multiple `--remote-host` flags can be used to specify multiple hosts.
Or to use compose:
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/certs:/certs
ports:
- 8080:8080
environment:
DOZZLE_REMOTE_HOST: tcp://167.99.1.1:2376,tcp://167.99.1.2:2376
You need to make sure appropriate certs are provided in `/certs/167.99.1.1/{ca,cert,key}.pem` and `/certs/167.99.1.2/{ca,cert,key}.pem` for both hosts to work.
### Adding health check
Dozzle doesn't enable healthcheck by default as it adds extra CPU usage. `healthcheck` can be enabled manually.
@@ -129,6 +148,7 @@ Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
## Troubleshooting and FAQs
+10 -9
View File
@@ -1,17 +1,18 @@
package analytics
type StartEvent struct {
ClientId string `json:"-"`
Version string `json:"version"`
FilterLength int `json:"filterLength"`
CustomAddress bool `json:"customAddress"`
CustomBase bool `json:"customBase"`
Protected bool `json:"protected"`
HasHostname bool `json:"hasHostname"`
ClientId string `json:"-"`
Version string `json:"version"`
FilterLength int `json:"filterLength"`
RemoteHostLength int `json:"remoteHostLength"`
CustomAddress bool `json:"customAddress"`
CustomBase bool `json:"customBase"`
Protected bool `json:"protected"`
HasHostname bool `json:"hasHostname"`
}
type RequestEvent struct {
ClientId string `json:"-"`
TotalContainers int `json:"totalContainers"`
ClientId string `json:"-"`
TotalContainers int `json:"totalContainers"`
RunningContainers int `json:"runningContainers"`
}
+13 -3
View File
@@ -1,4 +1,7 @@
// Generated by 'unplugin-auto-import'
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-auto-import
export {}
declare global {
const $$: typeof import('vue/macros')['$$']
@@ -103,10 +106,10 @@ declare global {
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveDirective: typeof import('vue')['resolveDirective']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const search: typeof import('./composables/settings')['search']
const sessionHost: typeof import('./composables/storage')['sessionHost']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const setTitle: typeof import('./composables/title')['setTitle']
@@ -144,6 +147,7 @@ declare global {
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
@@ -314,6 +318,11 @@ declare global {
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
@@ -420,10 +429,10 @@ declare module 'vue' {
readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly resolveDirective: UnwrapRef<typeof import('vue')['resolveDirective']>
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
readonly search: UnwrapRef<typeof import('./composables/settings')['search']>
readonly sessionHost: UnwrapRef<typeof import('./composables/storage')['sessionHost']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly setTitle: UnwrapRef<typeof import('./composables/title')['setTitle']>
@@ -461,6 +470,7 @@ declare module 'vue' {
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']>
readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']>
readonly useArrayFindLast: UnwrapRef<typeof import('@vueuse/core')['useArrayFindLast']>
readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']>
readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']>
readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']>
+20 -15
View File
@@ -1,5 +1,7 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
@@ -7,9 +9,12 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
CarbonCaretDown: typeof import('~icons/carbon/caret-down')['default']
CilColumns: typeof import('~icons/cil/columns')['default']
CilFindInPage: typeof import('~icons/cil/find-in-page')['default']
'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default']
'Cil:checkCircle': typeof import('~icons/cil/check-circle')['default']
'Cil:circle': typeof import('~icons/cil/circle')['default']
'Cil:columns': typeof import('~icons/cil/columns')['default']
'Cil:findInPage': typeof import('~icons/cil/find-in-page')['default']
'Cil:xCircle': typeof import('~icons/cil/x-circle')['default']
ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default']
ContainerStat: typeof import('./components/LogViewer/ContainerStat.vue')['default']
ContainerTitle: typeof import('./components/LogViewer/ContainerTitle.vue')['default']
@@ -25,17 +30,17 @@ declare module '@vue/runtime-core' {
LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default']
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
MdiDotsVertical: typeof import('~icons/mdi/dots-vertical')['default']
MdiLightChevronDoubleDown: typeof import('~icons/mdi-light/chevron-double-down')['default']
MdiLightChevronLeft: typeof import('~icons/mdi-light/chevron-left')['default']
MdiLightChevronRight: typeof import('~icons/mdi-light/chevron-right')['default']
MdiLightCog: typeof import('~icons/mdi-light/cog')['default']
MdiLightLogout: typeof import('~icons/mdi-light/logout')['default']
MdiLightMagnify: typeof import('~icons/mdi-light/magnify')['default']
'Mdi:dotsVertical': typeof import('~icons/mdi/dots-vertical')['default']
'Mdi:lightChevronDoubleDown': typeof import('~icons/mdi-light/chevron-double-down')['default']
'Mdi:lightChevronLeft': typeof import('~icons/mdi-light/chevron-left')['default']
'Mdi:lightChevronRight': typeof import('~icons/mdi-light/chevron-right')['default']
'Mdi:lightCog': typeof import('~icons/mdi-light/cog')['default']
'Mdi:lightLogout': typeof import('~icons/mdi-light/logout')['default']
'Mdi:lightMagnify': typeof import('~icons/mdi-light/magnify')['default']
MobileMenu: typeof import('./components/MobileMenu.vue')['default']
OcticonContainer24: typeof import('~icons/octicon/container24')['default']
OcticonDownload24: typeof import('~icons/octicon/download24')['default']
OcticonTrash24: typeof import('~icons/octicon/trash24')['default']
'Octicon:container24': typeof import('~icons/octicon/container24')['default']
'Octicon:download24': typeof import('~icons/octicon/download24')['default']
'Octicon:trash24': typeof import('~icons/octicon/trash24')['default']
PastTime: typeof import('./components/PastTime.vue')['default']
RelativeTime: typeof import('./components/RelativeTime.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
+3 -1
View File
@@ -3,7 +3,7 @@
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span class="icon">
<mdi-dots-vertical />
<mdi:dots-vertical />
</span>
</button>
</div>
@@ -26,12 +26,14 @@
& > .icon {
height: 100%;
& > svg {
align-self: flex-start;
height: 0.85em;
}
}
}
.is-top {
& .dropdown-menu {
top: 0;
+6 -4
View File
@@ -14,7 +14,7 @@
<div class="media">
<div class="media-left">
<span class="icon is-small" :class="item.state">
<octicon-container-24 />
<octicon:container-24 />
</span>
</div>
<div class="media-content">
@@ -26,7 +26,7 @@
@click.stop.prevent="addColumn(item)"
:title="$t('tooltip.pin-column')"
>
<cil-columns />
<cil:columns />
</span>
</div>
</div>
@@ -109,8 +109,8 @@ function addColumn(container: Container) {
.panel {
min-height: 200px;
width: auto;
margin-left: 0.25rem!important;
margin-right: 0.25rem!important;
margin-left: 0.25rem !important;
margin-right: 0.25rem !important;
}
}
@@ -130,9 +130,11 @@ function addColumn(container: Container) {
:deep(a.dropdown-item) {
padding-right: 1em;
.media-right {
visibility: hidden;
}
&:hover .media-right {
visibility: visible;
}
@@ -41,6 +41,7 @@ function validValues(obj: Record<string, any>) {
&:hover {
cursor: pointer;
&::after {
content: "expand json";
color: var(--secondary-color);
@@ -58,6 +59,7 @@ function validValues(obj: Record<string, any>) {
li {
display: inline-block;
& + li {
margin-left: 1em;
}
@@ -4,13 +4,13 @@
class="column is-narrow"
:data="memoryData"
label="mem"
:stat-value="formatBytes(container.stat.memoryUsage)"
:stat-value="formatBytes(unref(container.stat).memoryUsage)"
></stat-monitor>
<stat-monitor
class="column is-narrow"
:data="cpuData"
label="load"
:stat-value="container.stat.cpu + '%'"
:stat-value="unref(container.stat).cpu + '%'"
></stat-monitor>
</div>
</template>
+20 -1
View File
@@ -1,6 +1,11 @@
<template>
<div class="columns is-marginless has-text-weight-bold is-family-monospace">
<span class="column is-ellipsis">
<span class="icon is-small health" :health="container.health" v-if="container.health" :title="container.health">
<cil:check-circle v-if="container.health == 'healthy'" />
<cil:x-circle v-else-if="container.health == 'unhealthy'" />
<cil:circle v-else />
</span>
{{ container.name }}
<span class="tag is-dark">{{ container.image.replace(/@sha.*/, "") }}</span>
</span>
@@ -14,4 +19,18 @@ import { type ComputedRef } from "vue";
const container = inject("container") as ComputedRef<Container>;
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.icon {
vertical-align: middle;
}
.health {
&[health="unhealthy"] {
color: var(--red-color);
}
&[health="healthy"] {
color: var(--green-color);
}
}
</style>
@@ -4,7 +4,7 @@
<div class="level is-justify-content-start">
<div class="level-left">
<div class="level-item">
<octicon-trash-24 class="mr-4" />
<octicon:trash-24 class="mr-4" />
</div>
</div>
<div class="level-right">
@@ -12,11 +12,11 @@
</div>
</div>
</a>
<a class="dropdown-item" :href="`${base}/api/logs/download?id=${container.id}`">
<a class="dropdown-item" :href="`${base}/api/logs/download?id=${container.id}&host=${sessionHost}`">
<div class="level is-justify-content-start">
<div class="level-left">
<div class="level-item">
<octicon-download-24 class="mr-4" />
<octicon:download-24 class="mr-4" />
</div>
</div>
<div class="level-right">
@@ -29,7 +29,7 @@
<div class="level is-justify-content-start">
<div class="level-left">
<div class="level-item">
<mdi-light-magnify class="mr-4" />
<mdi:light-magnify class="mr-4" />
</div>
</div>
<div class="level-right">
@@ -58,6 +58,7 @@ function onClearClicked() {
button.delete {
background-color: var(--scheme-main-ter);
opacity: 0.6;
&:after,
&:before {
background-color: var(--text-color);
@@ -48,7 +48,7 @@ describe("<LogEventSource />", () => {
) {
settings.value.hourStyle = hourStyle;
search.searchFilter.value = searchFilter;
if(searchFilter){
if (searchFilter) {
search.showSearch.value = true;
}
@@ -91,22 +91,22 @@ describe("<LogEventSource />", () => {
test("should connect to EventSource", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
expect(sources["/api/logs/stream?id=abc&lastEventId="].readyState).toBe(1);
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
expect(sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].readyState).toBe(1);
wrapper.unmount();
});
test("should close EventSource", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
wrapper.unmount();
expect(sources["/api/logs/stream?id=abc&lastEventId="].readyState).toBe(2);
expect(sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].readyState).toBe(2);
});
test("should parse messages", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`,
});
@@ -121,8 +121,8 @@ describe("<LogEventSource />", () => {
describe("render html correctly", () => {
test("should render messages", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`,
});
@@ -134,8 +134,8 @@ describe("<LogEventSource />", () => {
test("should render messages with color", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: '{"ts":1560336942459,"m":"\\u001b[30mblack\\u001b[37mwhite", "id":1}',
});
@@ -147,8 +147,8 @@ describe("<LogEventSource />", () => {
test("should render messages with html entities", async () => {
const wrapper = createLogEventSource();
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"<test>foo bar</test>", "id":1}`,
});
@@ -160,8 +160,8 @@ describe("<LogEventSource />", () => {
test("should render dates with 12 hour style", async () => {
const wrapper = createLogEventSource({ hourStyle: "12" });
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"<test>foo bar</test>", "id":1}`,
});
@@ -173,8 +173,8 @@ describe("<LogEventSource />", () => {
test("should render dates with 24 hour style", async () => {
const wrapper = createLogEventSource({ hourStyle: "24" });
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"<test>foo bar</test>", "id":1}`,
});
@@ -186,11 +186,11 @@ describe("<LogEventSource />", () => {
test("should render messages with filter", async () => {
const wrapper = createLogEventSource({ searchFilter: "test" });
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen();
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"foo bar", "id":1}`,
});
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({
data: `{"ts":1560336942459, "m":"test bar", "id":2}`,
});
+4 -4
View File
@@ -43,20 +43,20 @@ div {
&.debug,
&.trace {
background-color: #9c27b0;
background-color: var(--purple-color);
}
&.info {
background-color: #00b5ad;
background-color: var(--green-color);
}
&.error,
&.fatal {
background-color: #f44336;
background-color: var(--red-color);
}
&.warn {
background-color: #ff9800;
background-color: var(--orange-color);
}
}
</style>
+3 -1
View File
@@ -12,7 +12,7 @@
<div class="level is-justify-content-start">
<div class="level-left">
<div class="level-item">
<cil-find-in-page class="mr-4" />
<cil:find-in-page class="mr-4" />
</div>
</div>
<div class="level-right">
@@ -72,10 +72,12 @@ watch(
display: flex;
word-wrap: break-word;
padding: 0.2em 1em;
&:last-child {
scroll-snap-align: end;
scroll-margin-block-end: 5rem;
}
&:nth-child(odd) {
background-color: rgba(125, 125, 125, 0.08);
}
+1 -1
View File
@@ -15,7 +15,7 @@
<script lang="ts" setup>
const { data, label, statValue } = defineProps<{ data: Point<unknown>[]; label: string; statValue: string | number }>();
let selectedPoint: Point<unknown> = $ref();
let selectedPoint: Point<unknown> | undefined = $ref();
function onSelectedPoint(point: Point<unknown>) {
selectedPoint = point;
@@ -1,231 +1,231 @@
// Vitest Snapshot v1
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<LogEventSource /> > render html correctly > should render dates with 12 hour style 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"1\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\" style=\\"display: none;\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#1\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42AM</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render dates with 24 hour style 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"1\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\" style=\\"display: none;\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#1\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"1\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\" style=\\"display: none;\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#1\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42AM</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\">This is a message.</div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\">This is a message.</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with color 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"1\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\" style=\\"display: none;\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#1\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42AM</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with filter 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"2\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#2\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"2\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#2\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42AM</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\"><mark>test</mark> bar</div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><mark>test</mark> bar</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with html entities 1`] = `
"<ul class=\\"events medium\\" data-v-2e92daca=\\"\\">
<li data-key=\\"1\\" class=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"line-options\\" data-v-2e92daca=\\"\\" style=\\"display: none;\\">
<div class=\\"dropdown is-hoverable is-last is-top minimal\\" data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"dropdown-trigger\\" data-v-539164cb=\\"\\"><button class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\" data-v-539164cb=\\"\\"><span class=\\"icon\\" data-v-539164cb=\\"\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\" data-v-539164cb=\\"\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\" data-v-539164cb=\\"\\">
<div class=\\"dropdown-content\\" data-v-539164cb=\\"\\"><a class=\\"dropdown-item\\" href=\\"#1\\" data-v-2e92daca=\\"\\">
<div class=\\"level is-justify-content-start\\" data-v-2e92daca=\\"\\">
<div class=\\"level-left\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\"><svg viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\" data-v-2e92daca=\\"\\">
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
</svg></div>
</div>
<div class=\\"level-right\\" data-v-2e92daca=\\"\\">
<div class=\\"level-item\\" data-v-2e92daca=\\"\\">Jump to Context</div>
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
</div>
</div>
</a></div>
</div>
</div>
</div>
<div class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\" data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\">
<div class=\\"column is-narrow\\" data-v-a49e52d4=\\"\\">
<div class=\\"date\\" data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42AM</time></div>
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
</div>
<div class=\\"column is-narrow is-flex\\" data-v-a49e52d4=\\"\\">
<div class=\\"\\" data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\"></div>
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
</div>
<div class=\\"text column\\" data-v-a49e52d4=\\"\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div data-v-a49e52d4=\\"\\" class=\\"text column\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > renders correctly 1`] = `
"<div class=\\"infinte-loader\\" data-v-1cd63c6e=\\"\\">
<div class=\\"spinner\\" data-v-1cd63c6e=\\"\\" style=\\"display: none;\\">
<div class=\\"bounce1\\" data-v-1cd63c6e=\\"\\"></div>
<div class=\\"bounce2\\" data-v-1cd63c6e=\\"\\"></div>
<div class=\\"bounce3\\" data-v-1cd63c6e=\\"\\"></div>
"<div data-v-1cd63c6e=\\"\\" class=\\"infinte-loader\\">
<div data-v-1cd63c6e=\\"\\" class=\\"spinner\\" style=\\"display: none;\\">
<div data-v-1cd63c6e=\\"\\" class=\\"bounce1\\"></div>
<div data-v-1cd63c6e=\\"\\" class=\\"bounce2\\"></div>
<div data-v-1cd63c6e=\\"\\" class=\\"bounce3\\"></div>
</div>
</div>
<ul class=\\"events medium\\" data-v-2e92daca=\\"\\"></ul>"
<ul data-v-2e92daca=\\"\\" class=\\"events medium\\"></ul>"
`;
exports[`<LogEventSource /> > should parse messages 1`] = `
+5 -5
View File
@@ -8,8 +8,8 @@
</svg>
</router-link>
</div>
<div class="column ml-4 is-family-monospace is-ellipsis" v-if="$route.name == 'container'">
{{ allContainersById[route.params.id].name }}
<div class="column ml-4 is-family-monospace is-ellipsis" v-if="$route.name == 'container-id'">
{{ allContainersById[route.params.id as string].name }}
</div>
<div class="column is-narrow push-right">
@@ -29,7 +29,7 @@
<div>
<button class="button is-small is-rounded" @click="$emit('search')" :title="$t('tooltip.search')">
<span class="icon">
<mdi-light-magnify />
<mdi:light-magnify />
</span>
</button>
</div>
@@ -38,7 +38,7 @@
<div>
<router-link :to="{ name: 'settings' }" active-class="is-active" class="button is-small is-rounded">
<span class="icon">
<mdi-light-cog />
<mdi:light-cog />
</span>
</router-link>
</div>
@@ -47,7 +47,7 @@
<div>
<a class="button is-small is-rounded" :href="`${base}/logout`" :title="$t('button.logout')">
<span class="icon">
<mdi-light-logout />
<mdi:light-logout />
</span>
</a>
</div>
+8 -2
View File
@@ -22,7 +22,7 @@
@click="scrollToBottom()"
v-show="paused"
>
<mdi-light-chevron-double-down />
<mdi:light-chevron-double-down />
</button>
</transition>
</div>
@@ -45,7 +45,8 @@ const mutationObserver = new MutationObserver((e) => {
scrollToBottom();
} else {
const record = e[e.length - 1];
if (record.target.children[record.target.children.length - 1] == record.addedNodes[record.addedNodes.length - 1]) {
const children = (record.target as HTMLElement).children;
if (children[children.length - 1] == record.addedNodes[record.addedNodes.length - 1]) {
hasMore = true;
}
}
@@ -97,6 +98,7 @@ section {
.is-scrollbar-progress {
text-align: right;
margin-right: 110px;
.scroll-progress {
position: fixed;
top: 60px;
@@ -111,6 +113,7 @@ section {
.is-scrollbar-notification {
text-align: right;
margin-right: 65px;
button {
position: fixed;
bottom: 30px;
@@ -138,9 +141,11 @@ section {
100% {
transform: translateY(0);
}
40% {
transform: translateY(-30px);
}
60% {
transform: translateY(-15px);
}
@@ -150,6 +155,7 @@ section {
.fade-leave-active {
transition: opacity 0.15s ease-in;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
+3 -1
View File
@@ -11,7 +11,7 @@
@keyup.esc="resetSearch()"
/>
<span class="icon is-left">
<mdi-light-magnify />
<mdi:light-magnify />
</span>
</p>
</div>
@@ -52,6 +52,7 @@ onUnmounted(() => resetSearch());
margin-left: 1em;
background-color: var(--scheme-main-ter);
opacity: 0.6;
&:after,
&:before {
background-color: var(--text-color);
@@ -68,6 +69,7 @@ onUnmounted(() => resetSearch());
.input {
color: var(--body-color);
&::placeholder {
color: var(--border-color);
}
+59 -23
View File
@@ -8,31 +8,48 @@
<use href="#logo"></use>
</svg>
</router-link>
<small class="subtitle is-6 is-block mb-4" v-if="hostname">
{{ hostname }}
</small>
</h1>
<div v-if="config.hosts.length > 1" class="mb-3">
<o-dropdown v-model="sessionHost" aria-role="list">
<template #trigger>
<o-button variant="primary" type="button" size="small">
<span>{{ sessionHost }}</span>
<span class="icon">
<carbon:caret-down />
</span>
</o-button>
</template>
<o-dropdown-item :value="value" aria-role="listitem" v-for="value in config.hosts" :key="value">
<span>{{ value }}</span>
</o-dropdown-item>
</o-dropdown>
</div>
</div>
</div>
<div class="columns is-marginless">
<div class="column is-narrow py-0 pl-0 pr-1">
<button class="button is-rounded is-small" @click="$emit('search')" :title="$t('tooltip.search')">
<span class="icon">
<mdi-light-magnify />
<mdi:light-magnify />
</span>
</button>
</div>
<div class="column is-narrow py-0" :class="secured ? 'pl-0 pr-1' : 'px-0'">
<router-link :to="{ name: 'settings' }" active-class="is-active" class="button is-rounded is-small">
<span class="icon">
<mdi-light-cog />
<mdi:light-cog />
</span>
</router-link>
</div>
<div class="column is-narrow py-0 px-0" v-if="secured">
<a class="button is-rounded is-small" :href="`${base}/logout`" :title="$t('button.logout')">
<span class="icon">
<mdi-light-logout />
<mdi:light-logout />
</span>
</a>
</div>
@@ -49,15 +66,21 @@
<div class="is-flex-grow-1 is-ellipsis">
{{ item.name }}
</div>
<div class="is-flex-shrink-1 column-icon">
<span
class="icon is-small"
<div class="is-flex-shrink-1 is-flex icons">
<div
class="icon is-small pin"
@click.stop.prevent="store.appendActiveContainer(item)"
v-show="!activeContainersById[item.id]"
:title="$t('tooltip.pin-column')"
>
<cil-columns />
</span>
<cil:columns />
</div>
<div class="icon is-small health" :health="item.health" v-if="item.health" :title="item.health">
<cil:check-circle v-if="item.health == 'healthy'" />
<cil:x-circle v-else-if="item.health == 'unhealthy'" />
<cil:circle v-else />
</div>
</div>
</div>
</router-link>
@@ -71,6 +94,7 @@
<script lang="ts" setup>
import { Container } from "@/models/Container";
import { sessionHost } from "@/composables/storage";
const { base, secured, hostname } = config;
const store = useContainerStore();
@@ -97,6 +121,12 @@ aside {
}
}
.logo {
width: 122px;
height: 54px;
fill: var(--logo-color);
}
.loading {
opacity: 0.5;
}
@@ -105,26 +135,32 @@ li.exited a {
color: #777;
}
.logo {
width: 122px;
height: 54px;
fill: var(--logo-color);
}
.menu-list li {
.column-icon {
visibility: hidden;
& > span {
vertical-align: middle;
}
.health {
&[health="unhealthy"] {
color: var(--red-color);
}
&:hover .column-icon {
visibility: visible;
&[health="healthy"] {
color: var(--green-color);
}
}
.icons {
column-gap: 0.35em;
align-items: baseline;
}
a {
.pin {
display: none;
&:hover {
color: var(--secondary-color);
}
}
&:hover {
.pin {
display: block;
}
}
}
</style>
+3 -1
View File
@@ -58,7 +58,9 @@ export function useLogStream(container: ComputedRef<Container>) {
lastEventId = "";
}
es = new EventSource(`${config.base}/api/logs/stream?id=${container.value.id}&lastEventId=${lastEventId}`);
es = new EventSource(
`${config.base}/api/logs/stream?id=${container.value.id}&lastEventId=${lastEventId}&host=${sessionHost.value}`
);
es.addEventListener("container-stopped", () => {
es?.close();
es = null;
+1 -1
View File
@@ -87,5 +87,5 @@ export {
menuWidth,
size,
search,
settings
settings,
};
+3
View File
@@ -0,0 +1,3 @@
const sessionHost = useSessionStorage("host", "localhost");
export { sessionHost };
+4 -2
View File
@@ -32,10 +32,10 @@
v-if="!isMobile"
>
<span class="icon ml-2" v-if="collapseNav">
<mdi-light-chevron-right />
<mdi:light-chevron-right />
</span>
<span class="icon" v-else>
<mdi-light-chevron-left />
<mdi:light-chevron-left />
</span>
</button>
</main>
@@ -87,6 +87,7 @@ function onResized(e: any) {
:deep(.splitpanes--vertical > .splitpanes__splitter) {
min-width: 3px;
background: var(--border-color);
&:hover {
background: var(--border-hover-color);
}
@@ -110,6 +111,7 @@ function onResized(e: any) {
position: fixed;
left: 10px;
bottom: 10px;
&.collapsed {
left: -40px;
width: 60px;
+4 -3
View File
@@ -1,4 +1,4 @@
import type { ContainerStat, ContainerState } from "@/types/Container";
import type { ContainerHealth, ContainerStat, ContainerState } from "@/types/Container";
import type { UseThrottledRefHistoryReturn } from "@vueuse/core";
import { Ref } from "vue";
@@ -10,12 +10,13 @@ export class Container {
constructor(
public readonly id: string,
public readonly created: number,
public readonly created: Date,
public readonly image: string,
public readonly name: string,
public readonly command: string,
public status: string,
public state: ContainerState
public state: ContainerState,
public health?: ContainerHealth
) {
this.stat = ref({ cpu: 0, memory: 0, memoryUsage: 0 });
this.throttledStatHistory = useThrottledRefHistory(this.stat, { capacity: 300, deep: true, throttle: 1000 });
+4
View File
@@ -127,6 +127,10 @@ export class SkippedLogsEntry extends LogEntry<string> {
public get totalSkipped(): number {
return this._totalSkipped;
}
public get lastSkippedItem(): LogEntry<string | JSONObject> {
return this.lastSkipped;
}
}
export function asLogEntry(event: LogEvent): LogEntry<string | JSONObject> {
+1 -2
View File
@@ -1,8 +1,7 @@
import { type App } from "vue";
import { createPinia } from "pinia";
export const install = (app:App) => {
export const install = (app: App) => {
const pinia = createPinia();
app.use(pinia);
};
+1
View File
@@ -4,6 +4,7 @@
</template>
<script lang="ts" setup>
import search from "@/components/Search.vue";
const store = useContainerStore();
const { id } = defineProps<{ id: string }>();
+7 -4
View File
@@ -48,7 +48,7 @@
@keyup.enter="onEnter()"
/>
<span class="icon is-left">
<search-icon />
<mdi:light-magnify />
</span>
</p>
</div>
@@ -65,7 +65,7 @@
<span class="name">{{ item.name }}</span>
<div class="subtitle is-7 status">
<past-time :date="new Date(item.created * 1000)"></past-time>
<past-time :date="item.created"></past-time>
</div>
</router-link>
</div>
@@ -75,7 +75,6 @@
</template>
<script lang="ts" setup>
import SearchIcon from "~icons/mdi-light/magnify";
import { useFuse } from "@vueuse/integrations/useFuse";
const { version } = config;
@@ -86,7 +85,7 @@ const router = useRouter();
const sort = $ref("running");
const query = ref("");
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => +b.created - +a.created));
const runningContainers = $computed(() => mostRecentContainers.filter((c) => c.state === "running"));
const list = computed(() => {
@@ -147,17 +146,21 @@ function onEnter() {
<style lang="scss" scoped>
.panel {
border: 1px solid var(--border-color);
.panel-block,
.panel-tabs {
border-color: var(--border-color);
.is-active {
border-color: var(--border-hover-color);
}
.name {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.status {
margin-left: auto;
white-space: nowrap;
+1 -1
View File
@@ -57,7 +57,7 @@ setTitle(t("title.login"));
let error = $ref(false);
let username = $ref("");
let password = $ref("");
let form: HTMLFormElement = $ref();
let form: HTMLFormElement | undefined = $ref();
async function onLogin() {
const response = await fetch(`${config.base}/api/validateCredentials`, {
+4 -3
View File
@@ -39,7 +39,7 @@
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ hourStyle }}</span>
<span class="icon">
<carbon-caret-down />
<carbon:caret-down />
</span>
</o-button>
</template>
@@ -64,7 +64,7 @@
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ size }}</span>
<span class="icon">
<carbon-caret-down />
<carbon:caret-down />
</span>
</o-button>
</template>
@@ -92,7 +92,7 @@
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ lightTheme }}</span>
<span class="icon">
<carbon-caret-down />
<carbon:caret-down />
</span>
</o-button>
</template>
@@ -177,6 +177,7 @@ fetchNextRelease();
a.next-release {
text-decoration: underline;
&:hover {
text-decoration: none;
}
+3
View File
@@ -7,6 +7,7 @@ interface Config {
secured: boolean | "false" | "true";
maxLogs: number;
hostname: string;
hosts: string[] | string;
}
const pageConfig = JSON.parse(text);
@@ -22,10 +23,12 @@ if (config.version == "{{ .Version }}") {
config.authorizationNeeded = false;
config.secured = false;
config.hostname = "localhost";
config.hosts = ["localhost"];
} else {
config.version = config.version.replace(/^v/, "");
config.authorizationNeeded = config.authorizationNeeded === "true";
config.secured = config.secured === "true";
config.hosts = (config.hosts as string).split(",");
}
export default config as Config;
+46 -23
View File
@@ -1,11 +1,13 @@
import { acceptHMRUpdate, defineStore } from "pinia";
import { Ref, UnwrapNestedRefs } from "vue";
import type { ContainerJson, ContainerStat } from "@/types/Container";
import type { ContainerHealth, ContainerJson, ContainerStat } from "@/types/Container";
import { Container } from "@/models/Container";
export const useContainerStore = defineStore("container", () => {
const containers: Ref<Container[]> = ref([]);
const activeContainerIds: Ref<string[]> = ref([]);
let es: EventSource | null = null;
const ready = ref(false);
const allContainersById = computed(() =>
containers.value.reduce((acc, container) => {
@@ -21,25 +23,48 @@ export const useContainerStore = defineStore("container", () => {
const activeContainers = computed(() => activeContainerIds.value.map((id) => allContainersById.value[id]));
const es = new EventSource(`${config.base}/api/events/stream`);
es.addEventListener("containers-changed", (e: Event) =>
setContainers(JSON.parse((e as MessageEvent).data) as ContainerJson[])
watch(
sessionHost,
() => {
connect();
},
{ immediate: true }
);
es.addEventListener("container-stat", (e) => {
const stat = JSON.parse((e as MessageEvent).data) as ContainerStat;
const container = allContainersById.value[stat.id] as unknown as UnwrapNestedRefs<Container>;
if (container) {
const { id, ...rest } = stat;
container.stat = rest;
}
});
es.addEventListener("container-die", (e) => {
const event = JSON.parse((e as MessageEvent).data) as { actorId: string };
const container = allContainersById.value[event.actorId];
if (container) {
container.state = "dead";
}
});
function connect() {
es?.close();
ready.value = false;
es = new EventSource(`${config.base}/api/events/stream?host=${sessionHost.value}`);
es.addEventListener("containers-changed", (e: Event) =>
setContainers(JSON.parse((e as MessageEvent).data) as ContainerJson[])
);
es.addEventListener("container-stat", (e) => {
const stat = JSON.parse((e as MessageEvent).data) as ContainerStat;
const container = allContainersById.value[stat.id] as unknown as UnwrapNestedRefs<Container>;
if (container) {
const { id, ...rest } = stat;
container.stat = rest;
}
});
es.addEventListener("container-die", (e) => {
const event = JSON.parse((e as MessageEvent).data) as { actorId: string };
const container = allContainersById.value[event.actorId];
if (container) {
container.state = "dead";
}
});
es.addEventListener("container-health", (e) => {
const event = JSON.parse((e as MessageEvent).data) as { actorId: string; health: ContainerHealth };
const container = allContainersById.value[event.actorId];
if (container) {
container.health = event.health;
}
});
watchOnce(containers, () => (ready.value = true));
}
const setContainers = (newContainers: ContainerJson[]) => {
containers.value = newContainers.map((c) => {
@@ -47,9 +72,10 @@ export const useContainerStore = defineStore("container", () => {
if (existing) {
existing.status = c.status;
existing.state = c.state;
existing.health = c.health;
return existing;
}
return new Container(c.id, c.created, c.image, c.name, c.command, c.status, c.state);
return new Container(c.id, new Date(c.created * 1000), c.image, c.name, c.command, c.status, c.state, c.health);
});
};
@@ -58,9 +84,6 @@ export const useContainerStore = defineStore("container", () => {
const removeActiveContainer = ({ id }: Container) =>
activeContainerIds.value.splice(activeContainerIds.value.indexOf(id), 1);
const ready = ref(false);
watchOnce(containers, () => (ready.value = true));
return {
containers,
activeContainerIds,
+10
View File
@@ -97,6 +97,13 @@ $light-toolbar-color: rgba($grey-darker, 0.7);
--text-light-color: #{$grey};
}
:root {
--green-color: #00b5ad;
--red-color: #f44336;
--purple-color: #9c27b0;
--orange-color: #ff9800;
}
[data-theme="dark"] {
@include dark;
}
@@ -177,6 +184,9 @@ html.has-custom-scrollbars {
.modal {
z-index: 1000;
.modal-background {
backdrop-filter: blur(4px);
}
}
.button .button-wrapper > span {
+2
View File
@@ -13,6 +13,8 @@ export type ContainerJson = {
readonly command: string;
readonly status: string;
readonly state: ContainerState;
readonly health?: ContainerHealth;
};
export type ContainerState = "created" | "running" | "exited" | "dead" | "paused" | "restarting";
export type ContainerHealth = "healthy" | "unhealthy" | "starting";
+67
View File
@@ -5,6 +5,10 @@ import (
"encoding/json"
"fmt"
"io"
"net/url"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@@ -14,6 +18,7 @@ import (
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
log "github.com/sirupsen/logrus"
)
@@ -62,6 +67,58 @@ func NewClientWithFilters(f map[string][]string) Client {
return &dockerClient{cli, filterArgs}
}
func NewClientWithTlsAndFilter(f map[string][]string, connection string) Client {
filterArgs := filters.NewArgs()
for key, values := range f {
for _, value := range values {
filterArgs.Add(key, value)
}
}
log.Debugf("filterArgs = %v", filterArgs)
remoteUrl, err := url.Parse(connection)
if err != nil {
log.Fatal(err)
}
if remoteUrl.Scheme != "tcp" {
log.Fatal("Only tcp scheme is supported")
}
host := remoteUrl.Hostname()
basePath := "/certs"
if _, err := os.Stat(filepath.Join(basePath, host)); !os.IsNotExist(err) {
basePath = filepath.Join(basePath, host)
}
cacertPath := filepath.Join(basePath, "ca.pem")
certPath := filepath.Join(basePath, "cert.pem")
keyPath := filepath.Join(basePath, "key.pem")
opts := []client.Opt{
client.WithHost(connection),
}
if _, err := os.Stat(cacertPath); os.IsNotExist(err) {
log.Debugf("%s does not exist, using plain HTTP", cacertPath)
} else {
log.Debugf("Using TLS client config with certs at: %s", basePath)
opts = append(opts, client.WithTLSClientConfig(cacertPath, certPath, keyPath))
}
opts = append(opts, client.WithAPIVersionNegotiation())
cli, err := client.NewClientWithOpts(opts...)
if err != nil {
log.Fatal(err)
}
return &dockerClient{cli, filterArgs}
}
func (d *dockerClient) FindContainer(id string) (Container, error) {
var container Container
containers, err := d.ListContainers()
@@ -106,6 +163,7 @@ func (d *dockerClient) ListContainers() ([]Container, error) {
Created: c.Created,
State: c.State,
Status: c.Status,
Health: findBetweenParentheses(c.Status),
}
containers = append(containers, container)
}
@@ -260,3 +318,12 @@ func (d *dockerClient) ContainerLogsBetweenDates(ctx context.Context, id string,
func (d *dockerClient) Ping(ctx context.Context) (types.Ping, error) {
return d.cli.Ping(ctx)
}
var PARENTHESIS_RE = regexp.MustCompile(`\(([^)]+)\)`)
func findBetweenParentheses(s string) string {
if results := PARENTHESIS_RE.FindStringSubmatch(s); results != nil {
return results[1]
}
return ""
}
+5 -5
View File
@@ -6,7 +6,7 @@ import (
"encoding/binary"
"errors"
"io"
"io/ioutil"
"strings"
"testing"
@@ -125,7 +125,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
binary.BigEndian.PutUint32(b[4:], uint32(len(expected)))
b = append(b, []byte(expected)...)
reader := ioutil.NopCloser(bytes.NewReader(b))
reader := io.NopCloser(bytes.NewReader(b))
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true, Since: "since"}
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
@@ -135,7 +135,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
client := &dockerClient{proxy, filters.NewArgs()}
logReader, _ := client.ContainerLogs(context.Background(), id, "since")
actual, _ := ioutil.ReadAll(logReader)
actual, _ := io.ReadAll(logReader)
assert.Equal(t, expected, string(actual), "message doesn't match expected")
proxy.AssertExpectations(t)
}
@@ -146,7 +146,7 @@ func Test_dockerClient_ContainerLogs_happy_with_tty(t *testing.T) {
proxy := new(mockedProxy)
expected := "INFO Testing logs..."
reader := ioutil.NopCloser(strings.NewReader(expected))
reader := io.NopCloser(strings.NewReader(expected))
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true}
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
@@ -156,7 +156,7 @@ func Test_dockerClient_ContainerLogs_happy_with_tty(t *testing.T) {
client := &dockerClient{proxy, filters.NewArgs()}
logReader, _ := client.ContainerLogs(context.Background(), id, "")
actual, _ := ioutil.ReadAll(logReader)
actual, _ := io.ReadAll(logReader)
assert.Equal(t, expected, string(actual), "message doesn't match expected")
proxy.AssertExpectations(t)
+15 -9
View File
@@ -115,7 +115,7 @@ func (g *eventGenerator) consume() {
logEvent.Level = guessLogLevel(logEvent)
g.channel <- logEvent
}
if readerError != nil {
g.lastError = readerError
close(g.channel)
@@ -124,18 +124,24 @@ func (g *eventGenerator) consume() {
}
}
var NON_ASCII_REGEX = regexp.MustCompile("^[^a-z ]+[^ewidtf]?")
var KEY_VALUE_REGEX = regexp.MustCompile("level=([^ ]+)")
var NON_ASCII_REGEX = regexp.MustCompile("^[^a-zA-z ]+[^ewidtf]?")
var KEY_VALUE_REGEX = regexp.MustCompile(`level=(\w+)`)
func guessLogLevel(logEvent *LogEvent) string {
switch value := logEvent.Message.(type) {
case string:
value = NON_ASCII_REGEX.ReplaceAllString(strings.ToLower(value), "")
levels := []string{"error", "warn", "info", "debug", "trace", "fatal"}
stripped := NON_ASCII_REGEX.ReplaceAllString(value, "")
for _, level := range levels {
prefix := regexp.MustCompile("^" + level + "[^a-z]")
if prefix.MatchString(value) {
if match, _ := regexp.MatchString("(?i)^"+level+"[^a-z]", stripped); match {
return level
}
if strings.Contains(value, "["+strings.ToUpper(level)+"]") {
return level
}
if strings.Contains(value, " "+strings.ToUpper(level)+" ") {
return level
}
}
@@ -145,8 +151,8 @@ func guessLogLevel(logEvent *LogEvent) string {
}
case map[string]interface{}:
if value["level"] != nil {
return strings.ToLower(value["level"].(string))
if level, ok := value["level"].(string); ok {
return level
}
}
+56
View File
@@ -0,0 +1,56 @@
package docker
import (
"bufio"
"io"
"strings"
"testing"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/require"
)
func TestNewEventIterator(t *testing.T) {
input := "example input"
reader := bufio.NewReader(strings.NewReader(input))
generator := NewEventIterator(reader)
require.NotNil(t, generator, "Expected generator to not be nil, but got nil")
}
func TestEventGenerator_Next(t *testing.T) {
input := "example input"
reader := bufio.NewReader(strings.NewReader(input))
generator := NewEventIterator(reader)
event, err := generator.Next()
require.NoError(t, err, "Expected no error, but got: %v", err)
require.NotNil(t, event, "Expected event to not be nil, but got nil")
}
func TestEventGenerator_LastError(t *testing.T) {
input := "example input"
reader := bufio.NewReader(strings.NewReader(input))
generator := NewEventIterator(reader)
require.Nil(t, generator.LastError(), "Expected LastError to return nil, but got: %v", generator.LastError())
generator.Next()
// expert error to be EOF
assert.Equal(t, generator.LastError(), io.EOF, "Expected LastError to return EOF, but got: %v", generator.LastError().Error())
}
func TestEventGenerator_Peek(t *testing.T) {
input := "example input"
reader := bufio.NewReader(strings.NewReader(input))
generator := NewEventIterator(reader)
event := generator.Peek()
require.NotNil(t, event, "Expected event to not be nil, but got nil")
assert.Equal(t, event.Message, input, "Expected event message to be %s, but got: %s", input, event.Message.(string))
}
+1
View File
@@ -13,6 +13,7 @@ type Container struct {
Created int64 `json:"created"`
State string `json:"state"`
Status string `json:"status"`
Health string `json:"health,omitempty"`
}
// ContainerStat represent stats instant for a container
+10
View File
@@ -0,0 +1,10 @@
.DS_Store
.vite-ssg-dist
.vite-ssg-temp
*.local
dist
dist-ssr
node_modules
.idea/
*.log
!.vscode
+2
View File
@@ -0,0 +1,2 @@
dist
cache
+118
View File
@@ -0,0 +1,118 @@
import { createRequire } from "module";
import { defineConfig } from "vitepress";
import { createWriteStream } from "node:fs";
import { resolve } from "node:path";
import { SitemapStream, streamToPromise } from "sitemap";
const require = createRequire(import.meta.url);
const pkg = require("dozzle/package.json");
const links = [] as { url: string; lastmod?: number }[];
export default defineConfig({
lang: "en-US",
title: "Dozzle",
description: "A lightweight, open-source, and secure log viewer for Docker.",
lastUpdated: true,
cleanUrls: true,
head: [
[
"script",
{
async: "",
src: "https://www.googletagmanager.com/gtag/js?id=G-X3Z4496XFK",
},
],
[
"script",
{},
`function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-X3Z4496XFK")`,
],
],
themeConfig: {
editLink: {
pattern: "https://github.com/amir20/dozzle/edit/master/docs/:path",
},
nav: [
{ text: "Home", link: "/" },
{ text: "Guide", link: "/guide/what-is-dozzle", activeMatch: "/guide/" },
{
text: `v${pkg.version}`,
items: [
{
text: "Releases",
link: "https://github.com/amir20/dozzle/releases",
},
{
text: "New Issue",
link: "https://github.com/amir20/dozzle/issues/new/choose",
},
],
},
],
sidebar: [
{
text: "Introduction",
items: [
{ text: "What is Dozzle?", link: "/guide/what-is-dozzle" },
{ text: "Getting Started", link: "/guide/getting-started" },
],
},
{
text: "Advanced Configuration",
items: [
{ text: "Authentication", link: "/guide/authentication" },
{ text: "Healthcheck", link: "/guide/healthcheck" },
{ text: "Remote Hosts", link: "/guide/remote-hosts" },
{ text: "Supported Env Vars", link: "/guide/supported-env-vars" },
{ text: "Analytics", link: "/guide/analytics" },
],
},
{
text: "Troubleshooting",
items: [
{ text: "FAQ", link: "/guide/faq" },
{ text: "Debugging", link: "/guide/debugging" },
],
},
],
footer: {
message: "Released under the MIT License. Open sourced and sponsored by Docker OSS.",
copyright: "Copyright © 2019-present <a href='https://amirraminfar.me'>Amir Raminfar</a>",
},
socialLinks: [
{ icon: "github", link: "https://github.com/amir20/dozzle" },
{
icon: {
svg: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M21.81 10.25c-.06-.04-.56-.43-1.64-.43c-.28 0-.56.03-.84.08c-.21-1.4-1.38-2.11-1.43-2.14l-.29-.17l-.18.27c-.24.36-.43.77-.51 1.19c-.2.8-.08 1.56.33 2.21c-.49.28-1.29.35-1.46.35H2.62c-.34 0-.62.28-.62.63c0 1.15.18 2.3.58 3.38c.45 1.19 1.13 2.07 2 2.61c.98.6 2.59.94 4.42.94c.79 0 1.61-.07 2.42-.22c1.12-.2 2.2-.59 3.19-1.16A8.3 8.3 0 0 0 16.78 16c1.05-1.17 1.67-2.5 2.12-3.65h.19c1.14 0 1.85-.46 2.24-.85c.26-.24.45-.53.59-.87l.08-.24l-.19-.14m-17.96.99h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H3.85c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.43 0h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H6.28c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.47 0h1.75c.1 0 .17-.07.17-.16V9.5c0-.08-.06-.16-.17-.16H8.75c-.08 0-.15.07-.15.16v1.58c0 .09.06.16.15.16m2.44 0h1.77c.08 0 .15-.07.15-.16V9.5c0-.08-.06-.16-.15-.16h-1.77c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16M6.28 9h1.76c.08 0 .16-.09.16-.18V7.25c0-.09-.07-.16-.16-.16H6.28c-.09 0-.16.06-.16.16v1.57c.01.09.07.18.16.18m2.47 0h1.75c.1 0 .17-.09.17-.18V7.25c0-.09-.06-.16-.17-.16H8.75c-.08 0-.15.06-.15.16v1.57c0 .09.06.18.15.18m2.44 0h1.77c.08 0 .15-.09.15-.18V7.25c0-.09-.07-.16-.15-.16h-1.77c-.08 0-.15.06-.15.16v1.57c0 .09.07.18.15.18m0-2.28h1.77c.08 0 .15-.07.15-.16V5c0-.1-.07-.17-.15-.17h-1.77c-.08 0-.15.06-.15.17v1.56c0 .08.07.16.15.16m2.46 4.52h1.76c.09 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16h-1.76c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16"/></svg>`,
},
link: "https://hub.docker.com/r/amir20/dozzle",
},
],
},
transformHtml: (_, id, { pageData }) => {
if (!/[\\/]404\.html$/.test(id))
links.push({
// you might need to change this if not using clean urls mode
url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, "$2"),
lastmod: pageData.lastUpdated,
});
},
buildEnd: async ({ outDir }) => {
const sitemap = new SitemapStream({
hostname: "https://dozzle.dev/",
});
const writeStream = createWriteStream(resolve(outDir, "sitemap.xml"));
sitemap.pipe(writeStream);
links.forEach((link) => sitemap.write(link));
const promise = streamToPromise(sitemap);
sitemap.end();
await promise;
},
});
@@ -0,0 +1,14 @@
<script setup></script>
<template>
<div border="~ solid rounded light-100 dark:dark-50" bg="[#eee] dark:[#222]">
<div flex gap="2" p-2>
<div circle-red />
<div circle-yellow />
<div circle-green />
</div>
<div class="content">
<slot />
</div>
</div>
</template>
@@ -0,0 +1,37 @@
<script lang="ts" setup>
import { useMutationObserver } from "@vueuse/core";
import { onMounted, ref } from "vue";
const isDark = ref(false);
onMounted(() => {
isDark.value = document.documentElement.classList.contains("dark");
});
useMutationObserver(
document.documentElement,
(mutations) => {
isDark.value = document.documentElement.classList.contains("dark");
},
{
attributes: true,
attributeFilter: ["class"],
}
);
</script>
<template>
<browser-window drop-shadow-md>
<video muted loop autoplay playsinline poster="../media/poster.png" v-if="isDark">
<source src="../media/dozzle-dark.webm" type="video/webm" />
<source src="../media/dozzle-dark.mp4" type="video/mp4" />
<img src="../media/poster.png" alt="" />
</video>
<video muted loop autoplay playsinline poster="../media/poster.png" v-else>
<source src="../media/dozzle-light.webm" type="video/webm" />
<source src="../media/dozzle-light.mp4" type="video/mp4" />
<img src="../media/poster.png" alt="" />
</video>
</browser-window>
</template>
<style scoped></style>
+16
View File
@@ -0,0 +1,16 @@
// https://vitepress.dev/guide/custom-theme
import { h } from "vue";
import Theme from "vitepress/theme";
import "uno.css";
import "./style.css";
import HeroVideo from "./components/HeroVideo.vue";
export default {
...Theme,
Layout: () => {
return h(Theme.Layout, null, {
"home-hero-image": () => h(HeroVideo),
});
},
enhanceApp({ app, router, siteData }) {},
};
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

+157
View File
@@ -0,0 +1,157 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
* -------------------------------------------------------------------------- */
:root {
--vp-c-brand: #646cff;
--vp-c-brand-light: #747bff;
--vp-c-brand-lighter: #9499ff;
--vp-c-brand-lightest: #bcc0ff;
--vp-c-brand-dark: #535bf2;
--vp-c-brand-darker: #454ce1;
--vp-c-brand-dimm: rgba(100, 108, 255, 0.08);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: var(--vp-c-brand-light);
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand);
--vp-button-brand-hover-border: var(--vp-c-brand-light);
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-light);
--vp-button-brand-active-border: var(--vp-c-brand-light);
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-button-brand-bg);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(124deg, #de4daa 30%, #f6d327);
--vp-home-hero-image-background-image: linear-gradient(-45deg, hsl(44, 100%, 77%) 50%, #47caff 50%);
--vp-home-hero-image-filter: blur(40px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(72px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: var(--vp-c-brand);
--vp-custom-block-tip-text: var(--vp-c-brand-darker);
--vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
--tag-line-color: #333;
--bar-title-color: #ef5734;
}
.dark {
--vp-custom-block-tip-border: var(--vp-c-brand);
--vp-custom-block-tip-text: var(--vp-c-brand-lightest);
--vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
--tag-line-color: white;
--bar-title-color: hsl(44, 100%, 77%);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand) !important;
}
/**
* VitePress: Custom fix
* -------------------------------------------------------------------------- */
/*
Use lighter colors for links in dark mode for a11y.
Also specify some classes twice to have higher specificity
over scoped class data attribute.
*/
.dark .vp-doc a,
.dark .vp-doc a > code,
.dark .VPNavBarMenuLink.VPNavBarMenuLink:hover,
.dark .VPNavBarMenuLink.VPNavBarMenuLink.active,
.dark .link.link:hover,
.dark .link.link.active,
.dark .edit-link-button.edit-link-button,
.dark .pager-link .title {
color: var(--vp-c-brand-lighter);
}
.dark .vp-doc a:hover,
.dark .vp-doc a > code:hover {
color: var(--vp-c-brand-lightest);
opacity: 1;
}
/* Transition by color instead of opacity */
.dark .vp-doc .custom-block a {
transition: color 0.25s;
}
/**
* Dozzle css
* --------------------------------------------------------------------------
**/
@media (max-width: 960px) {
.is-home .VPHero {
margin-top: -1em;
}
}
.VPHero .container {
max-width: 100%;
justify-content: center;
}
@media (min-width: 960px) {
.is-home .container .main {
padding-right: 4em;
}
}
.VPHomeHero .tagline {
@apply font-sans text-4xl font-light;
color: var(--tag-line-color);
}
.VPHomeHero .name {
@apply font-playfair font-light text-7xl;
}
.VPNavBarTitle .title {
@apply font-playfair font-light text-3xl;
color: var(--bar-title-color);
}
.VPHero .container .image {
max-width: 1200px;
}
+17
View File
@@ -0,0 +1,17 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
BrowserWindow: typeof import('./.vitepress/theme/components/BrowserWindow.vue')['default']
HeroVideo: typeof import('./.vitepress/theme/components/HeroVideo.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
+15
View File
@@ -0,0 +1,15 @@
---
title: Anonymous Analytics
---
# Data Collection of Analytics
Dozzle collects anonymous user configurations using Google Analytics. _Why?_ Dozzle is an open source project with no funding. As a result, there is no time to do user studies of Dozzle. Analytics is collected to prioritize features and fixes based on how people use Dozzle. This data is completely public and can be viewed live using [dashboard](/guide/usage-dashboard).
## Where is data stored
Dozzle's sends anonymous data to Google Analytics. Retention policy is 14 months which is lowest [possible value for Google Analytics](https://support.google.com/analytics/answer/7667196).
## Opting out
Dozzle analytics helps to prioritize features and spend time on most important features. If you do not want to be tracked at all, use `--no-analytics` flag or `DOZZLE_NO_ANALYTICS` env variable.
+57
View File
@@ -0,0 +1,57 @@
---
title: Authentication
---
# Setting Up Authentication
Dozzle supports a very simple authentication out of the box with just username and password. You should deploy using SSL to keep the credentials safe. See configuration to use `--username` and `--password`. You can also use docker secrets `--usernamefile` and `--passwordfile`.
::: code-group
```sh [cli]
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --username amirraminfar --password supersecretpassword
```
```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_USERNAME: amirraminfar
DOZZLE_PASSWORD: supersecretpassword
```
:::
## Setting up authentication with Docker secrets
Dozzle also support path to file for username and password which can be used to with Docker Secrets.
```yaml
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
environment:
DOZZLE_LEVEL: debug
DOZZLE_USERNAME_FILE: /run/secrets/dozzle_user
DOZZLE_PASSWORD_FILE: /run/secrets/dozzle_password
volumes:
- /var/run/docker.sock:/var/run/docker.sock
secrets:
- dozzle_user
- dozzle_password
ports:
- 8080:8080
secrets:
dozzle_user:
file: dozzle_user.txt
dozzle_password:
file: dozzle_password.txt
```
+20
View File
@@ -0,0 +1,20 @@
---
title: Debugging
---
# Debugging with logs
By default, Dozzle does not output a lot of logs. However this can be changed with `--level` flag. The default value is `info` which only prints limited logs. You can use `debug` or `trace` which will show details about memory, configuration and other stats. `DOZZLE_LEVEL` can be used in compose configurations. Below is an example of using `docker-compose.yml` file to enable `debug` level.
```yaml
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8080:8080
environment:
DOZZLE_LEVEL: debug
```
+51
View File
@@ -0,0 +1,51 @@
---
title: FAQ
---
# Frequently Asked Questions
## I installed Dozzle, but logs are slow or they never load. What do I do?
Dozzle uses Server Sent Events (SSE) which connects to a server using a HTTP stream without closing the connection. If any proxy tries to buffer this connection, then Dozzle never receives the data and hangs forever waiting for the reverse proxy to flush the buffer. Since version `1.23.0`, Dozzle sends the `X-Accel-Buffering: no` header which should stop reverse proxies buffering. However, some proxies may ignore this header. In those cases, you need to explicitly disable any buffering.
Below is an example with nginx and using `proxy_pass` to disable buffering:
```
server {
...
location / {
proxy_pass http://<dozzle.container.ip.address>:8080;
}
location /api {
proxy_pass http://<dozzle.container.ip.address>:8080;
proxy_buffering off;
proxy_cache off;
}
}
```
## We have tools that uses Dozzle when a new container is created. How can I get a direct link to a container by name?
Dozzle has a special [route](https://github.com/amir20/dozzle/blob/master/assets/pages/show.vue) that can be used to search containers by name and then forward to that container. For example, if you have a container with name `"foo.bar"` and id `abc123`, you can send your users to `/show?name=foo.bar` which will be forwarded to `/container/abc123`.
## I installed Dozzle but memory consumption doesn't show up!
_This is an issue specific to ARM devices._
Dozzle uses the Docker API to gather information about the containers' memory usage. If the memory usage is not showing up, then it is likely that the Docker API is not returning the memory usage.
You can verify this by running docker info, and you should see the following:
```
WARNING: No memory limit support
WARNING: No swap limit support
```
In this case, you'll need to add the following line to your `/boot/cmdline.txt` file and reboot your device.
```
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1
```
+31
View File
@@ -0,0 +1,31 @@
---
title: Getting Started
---
# Getting Started
This section will help you to setup Dozzle locally. Dozzle can also be used to connect to remote hosts via `tcp://` and tls. See remote host if you want to connect to other hosts.
## Using Docker CLI
The easiest way to setup Dozzle is to use the CLI and mount `docker.sock` file. This file is usually located at `/var/run/docker.sock` and can be mounted with the `--volume` flag. You also need to expose the port to view Dozzle. By default, Dozzle listens on port 8080, but you can change the external port using `-p`.
```sh
docker run --detach --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle
```
## Using Docker Compose
Docker compose makes it easier to configure Dozzle as part of an existing configuration.
```yaml
version: "3"
services:
dozzle:
container_name: dozzle
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 9999:8080
```
+24
View File
@@ -0,0 +1,24 @@
---
title: Healthcheck
---
# Adding healthcheck
Dozzle doesn't enable healthcheck by default as it adds extra CPU usage. `healthcheck` can be enabled manually.
```yaml
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8080:8080
healthcheck:
test: ["CMD", "/dozzle", "healthcheck"]
interval: 3s
timeout: 30s
retries: 5
start_period: 30s
```
+55
View File
@@ -0,0 +1,55 @@
---
title: Remote Host Setup
---
# Remote Host Setup
Dozzle supports connecting to multiple remote hosts via `tcp://` using TLS and non-secured connections. Dozzle will need to have appropriate certs mounted to use secured connection. `ssh://` is not supported because Dozzle docker image does not ship with any ssh clients.
## Connecting to remote hosts
Remote hosts can be configured with `--remote-host` or `DOZZLE_REMOTE_HOST`. All certs must be mounted to `/certs` directory. The `/cert` directory expects to have `/certs/{ca,cert,key}.pem` or `/certs/{host}/{ca,cert,key}.pem` in case of multiple hosts.
Multiple `--remote-host` flags can be used to specify multiple hosts. However, using `DOZZLE_REMOTE_HOST` the value should be comma separated.
::: code-group
```sh [cli]
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/certs:/certs -p 8080:8080 amir20/dozzle --remote-host tcp://167.99.1.1:2376 --remote-host tcp://167.99.1.2:2376
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/certs:/certs
ports:
- 8080:8080
environment:
DOZZLE_REMOTE_HOST: tcp://167.99.1.1:2376,tcp://167.99.1.2:2376
```
:::
## Connecting with a socket proxy
If you are in a private network then you can use [Docker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy) which expose `docker.sock` file without the need of TLS. Dozzle will never try to write to Docker but it will need access to list APIs. The following command will start a proxy with minimal access.
```sh
docker container run --privileged -e CONTAINERS=1 -v /var/run/docker.sock:/var/run/docker.sock -p 2375:2375 tecnativa/docker-socket-proxy
```
Note that `CONTAINERS=1` is required to list running containers. `EVENTS` is also needed but it is enabled by default.
Running Dozzle without any certs should work. Here is an example:
```sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --remote-host tcp://123.1.1.1:2375
```
::: warning
Exposing `docker.sock` publicly is not safe. Only use a proxy for an internal network where all clients are trusted.
:::
+21
View File
@@ -0,0 +1,21 @@
---
title: Environment variables and configuration
---
# Environment variables and configuration
Configurations can be done with flags or environment variables. The table below outlines all supported options and their respective env vars.
| Flag | Env Variable | Default |
| ---------------- | ---------------------- | ------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
+9
View File
@@ -0,0 +1,9 @@
---
title: Usage Dashboard
---
# Usage Dashboard
This data is based on collected data which is completely anonymous.
<iframe width="1024" height="800" src="https://lookerstudio.google.com/embed/reporting/50f34d3a-3c7e-47b2-a85e-09da19e2a666/page/GbvFC" frameborder="0" style="border:0" allowfullscreen></iframe>
+11
View File
@@ -0,0 +1,11 @@
---
title: What is Dozzle?
---
# What is Dozzle?
Dozzle is open-sourced project sponsored by Docker OSS. It is a log viewer designed to simplify the process of monitoring and debugging containers. It is a lightweight, web-based application that provides real-time log streaming, filtering, and searching capabilities through an intuitive user interface.
Users can quickly and easily access logs generated by their Docker containers, making it an essential tool for debugging and troubleshooting applications running in a Docker environment. Out of the box, Dozzle supports JSON logs with intelligent color coding.
Dozzle is easy to install and can be configured with minimal effort, making it an ideal solution for developers and system administrators looking for an efficient and user-friendly log viewer for their Docker environment. The tool is available under the MIT license and is actively maintained by its developer, Amir Raminfar.
+22
View File
@@ -0,0 +1,22 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
title: Home
hero:
name: "Dozzle"
tagline: A lightweight, web-based Docker log viewer that provides real-time monitoring and easy troubleshooting.
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
features:
- title: Real-time logging
details: Allows you to view logs of other Docker containers in real-time. As new log entries are generated, they are streamed to the web interface without needing to refresh the page.
- title: Lightweight
details: An application written in Go consuming very little memory and CPU. It can be run alongside other containers without causing performance issues.
- title: Multi-host Support
details: Dozzle UI support connecting to multiple remote hosts with a simple drop down to choose between different hosts.
---
+12
View File
@@ -0,0 +1,12 @@
{
"private": true,
"type": "module",
"devDependencies": {
"@unocss/preset-typography": "^0.50.6",
"@unocss/reset": "^0.50.6",
"@unocss/transformer-directives": "^0.50.6",
"dozzle": "workspace:*",
"sitemap": "^7.1.1",
"unocss": "^0.50.6"
}
}
+1131
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
/assets/*
cache-control: max-age=31536000
cache-control: immutable
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

+32
View File
@@ -0,0 +1,32 @@
import { defineConfig, presetAttributify, presetIcons, presetUno, presetWebFonts } from "unocss";
import { presetTypography } from "@unocss/preset-typography";
import transformerDirectives from "@unocss/transformer-directives";
export default defineConfig({
shortcuts: [[/^circle-(\w+)$/, ([, c]) => `rounded-full bg-${c}500 w-2 h-2 lg:w-3 lg:h-3`]],
transformers: [transformerDirectives()],
presets: [
presetUno(),
presetAttributify(),
presetTypography(),
presetIcons({
scale: 1.2,
warn: true,
}),
presetWebFonts({
fonts: {
sans: "Roboto:200",
playfair: [
{
name: "Playfair Display",
weights: [100, 200, 400, 700],
},
],
},
}),
],
theme: {
colors: {},
},
});
+18
View File
@@ -0,0 +1,18 @@
import path from "path";
import { defineConfig } from "vite";
import Components from "unplugin-vue-components/vite";
import Unocss from "unocss/vite";
export default defineConfig({
plugins: [
Components({
dirs: [path.resolve(__dirname, ".vitepress/theme/components")],
extensions: ["vue", "md"],
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
dts: true,
}),
Unocss(),
],
});
+3 -3
View File
@@ -1,6 +1,6 @@
FROM cypress/included:12.4.1
FROM cypress/included:12.9.0
RUN apt update && apt install curl -y && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
RUN npm i -g pnpm
WORKDIR /e2e
@@ -8,4 +8,4 @@ COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY package.json tsconfig.json ./
RUN pnpm install -r --offline
RUN pnpm install --offline
+1 -1
View File
@@ -1,5 +1,5 @@
import { defineConfig } from "cypress";
import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/dist/plugins';
import { initPlugin } from "@frsource/cypress-plugin-visual-regression-diff/dist/plugins";
export default defineConfig({
fixturesFolder: false,
+2 -2
View File
@@ -14,8 +14,8 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
import '@frsource/cypress-plugin-visual-regression-diff/dist/support';
import "./commands";
import "@frsource/cypress-plugin-visual-regression-diff/dist/support";
// Alternatively you can use CommonJS syntax:
// require('./commands')
+3 -3
View File
@@ -6,8 +6,8 @@
},
"license": "ISC",
"dependencies": {
"@frsource/cypress-plugin-visual-regression-diff": "^3.2.8",
"cypress": "^12.4.1",
"typescript": "^4.9.4"
"@frsource/cypress-plugin-visual-regression-diff": "^3.2.14",
"cypress": "^12.9.0",
"typescript": "^5.0.3"
}
}
+255 -254
View File
File diff suppressed because it is too large Load Diff
+12 -10
View File
@@ -1,13 +1,13 @@
module github.com/amir20/dozzle
require (
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/alexflint/go-arg v1.4.3
github.com/beme/abide v0.0.0-20190723115211-635a09831760
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/docker v20.10.23+incompatible
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v23.0.3+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gorilla/mux v1.8.0
@@ -19,24 +19,26 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.9.0
github.com/spf13/afero v1.9.3
github.com/spf13/afero v1.9.5
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.1
golang.org/x/net v0.0.0-20211104170005-ce137452f963 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
github.com/stretchr/testify v1.8.2
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/alexflint/go-scalar v1.1.0 // indirect
github.com/alexflint/go-scalar v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
golang.org/x/tools v0.6.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
)
+35 -20
View File
@@ -40,12 +40,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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
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 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM=
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/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
github.com/beme/abide v0.0.0-20190723115211-635a09831760/go.mod h1:6+8gCKsZnxzhGTmKRh4BSkLos9CbWRJNcrp55We4SqQ=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -60,18 +61,18 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.22+incompatible h1:6jX4yB+NtcbldT90k7vBSaWJDB3i+zkVJT9BEK8kQkk=
github.com/docker/docker v20.10.22+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.23+incompatible h1:1ZQUUYAdh+oylOT85aA2ZcfRp22jmLhoaEcVEfK8dyA=
github.com/docker/docker v20.10.23+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v23.0.2+incompatible h1:q81C2qQ/EhPm8COZMUGOQYh4qLv4Xu6CXELJ3WK/mlU=
github.com/docker/docker v23.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -182,11 +183,14 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs=
github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
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=
@@ -200,6 +204,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -217,6 +223,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -250,6 +257,8 @@ 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -281,8 +290,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211104170005-ce137452f963 h1:8gJUadZl+kWvZBqG/LautX0X6qe5qTC2VI/3V3NBRAY=
golang.org/x/net v0.0.0-20211104170005-ce137452f963/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -302,6 +312,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -312,7 +323,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -335,12 +345,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -348,8 +359,10 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
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 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -405,6 +418,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
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=
+2 -1
View File
@@ -10,7 +10,8 @@
"version": "{{ .Version }}",
"authorizationNeeded": "{{ .AuthorizationNeeded }}",
"secured": "{{ .Secured }}",
"hostname": "{{ .Hostname }}"
"hostname": "{{ .Hostname }}",
"hosts": "{{ .Hosts }}"
}
</script>
<link
+26 -14
View File
@@ -48,6 +48,7 @@ type args struct {
FilterStrings []string `arg:"env:DOZZLE_FILTER,--filter,separate" help:"filters docker containers using Docker syntax."`
Filter map[string][]string `arg:"-"`
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running."`
RemoteHost []string `arg:"env:DOZZLE_REMOTE_HOST,--remote-host,separate" help:"list of hosts to connect remotely"`
}
type HealthcheckCmd struct {
@@ -91,6 +92,7 @@ func main() {
}
log.Infof("Dozzle version %s", version)
dockerClient := docker.NewClientWithFilters(args.Filter)
for i := 1; ; i++ {
_, err := dockerClient.ListContainers()
@@ -105,6 +107,15 @@ func main() {
}
}
clients := make(map[string]docker.Client)
clients["localhost"] = dockerClient
for _, host := range args.RemoteHost {
log.Infof("Creating client for %s", host)
client := docker.NewClientWithTlsAndFilter(args.Filter, host)
clients[host] = client
}
if args.Username == "" && args.UsernameFile != nil {
args.Username = args.UsernameFile.Value
}
@@ -120,12 +131,12 @@ func main() {
}
config := web.Config{
Addr: args.Addr,
Base: args.Base,
Version: version,
Username: args.Username,
Password: args.Password,
Hostname: args.Hostname,
Addr: args.Addr,
Base: args.Base,
Version: version,
Username: args.Username,
Password: args.Password,
Hostname: args.Hostname,
NoAnalytics: args.NoAnalytics,
}
@@ -139,7 +150,7 @@ func main() {
assets = os.DirFS("./dist")
}
srv := web.CreateServer(dockerClient, assets, config)
srv := web.CreateServer(clients, assets, config)
go doStartEvent(args)
go func() {
log.Infof("Accepting connections on %s", srv.Addr)
@@ -171,13 +182,14 @@ func doStartEvent(arg args) {
}
event := analytics.StartEvent{
ClientId: host,
Version: version,
FilterLength: len(arg.Filter),
CustomAddress: arg.Addr != ":8080",
CustomBase: arg.Base != "/",
Protected: arg.Username != "",
HasHostname: arg.Hostname != "",
ClientId: host,
Version: version,
FilterLength: len(arg.Filter),
CustomAddress: arg.Addr != ":8080",
CustomBase: arg.Base != "/",
RemoteHostLength: len(arg.RemoteHost),
Protected: arg.Username != "",
HasHostname: arg.Hostname != "",
}
if err := analytics.SendStartEvent(event); err != nil {
Executable
+3
View File
@@ -0,0 +1,3 @@
[build]
publish = "docs/.vitepress/dist"
command = "pnpm docs:build"
+107 -99
View File
@@ -1,103 +1,111 @@
{
"name": "dozzle",
"version": "4.7.4",
"description": "Realtime log viewer for docker containers. ",
"homepage": "https://github.com/amir20/dozzle#readme",
"bugs": {
"url": "https://github.com/amir20/dozzle/issues"
"name": "dozzle",
"version": "4.9.2",
"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.1.1",
"repository": {
"type": "git",
"url": "git+https://github.com/amir20/dozzle.git"
},
"license": "ISC",
"author": "Amir Raminfar <findamir@gmail.com>",
"scripts": {
"watch:assets": "vite --open",
"watch:server": "LIVE_FS=true DOZZLE_ADDR=localhost:3100 reflex -c .reflex",
"dev": "make fake_assets && npm-run-all -p watch:assets watch:server",
"build": "vite build",
"release": "release-it",
"test": "TZ=UTC vitest",
"typecheck": "vue-tsc --noEmit",
"docs:dev": "vitepress dev docs --open",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.16",
"@iconify-json/cil": "^1.1.4",
"@iconify-json/mdi": "^1.1.50",
"@iconify-json/mdi-light": "^1.1.6",
"@iconify-json/octicon": "^1.1.37",
"@intlify/unplugin-vue-i18n": "^0.10.0",
"@oruga-ui/oruga-next": "^0.5.10",
"@oruga-ui/theme-bulma": "^0.2.9",
"@vueuse/core": "^9.13.0",
"@vueuse/integrations": "^9.13.0",
"@vueuse/router": "^9.13.0",
"ansi-to-html": "^0.7.2",
"bulma": "^0.9.4",
"d3-array": "^3.2.3",
"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",
"date-fns": "^2.29.3",
"fuse.js": "^6.6.2",
"lodash.debounce": "^4.0.8",
"pinia": "^2.0.33",
"semver": "^7.3.8",
"splitpanes": "^3.1.5",
"vue": "^3.2.47",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@pinia/testing": "^0.0.15",
"@types/d3-array": "^3.0.4",
"@types/d3-ease": "^3.0.0",
"@types/d3-scale": "^4.0.3",
"@types/d3-selection": "^3.0.5",
"@types/d3-shape": "^3.1.1",
"@types/d3-transition": "^3.0.3",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.15.11",
"@types/semver": "^7.3.13",
"@vitejs/plugin-vue": "4.1.0",
"@vue/compiler-sfc": "^3.2.47",
"@vue/test-utils": "^2.3.2",
"c8": "^7.13.0",
"eventsourcemock": "^2.0.0",
"jest-serializer-vue": "^3.1.0",
"jsdom": "^21.1.1",
"lint-staged": "^13.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.7",
"release-it": "^15.10.1",
"sass": "^1.60.0",
"simple-git-hooks": "^2.8.1",
"ts-node": "^10.9.1",
"typescript": "^5.0.3",
"unplugin-auto-import": "^0.15.2",
"unplugin-icons": "^0.16.1",
"unplugin-vue-components": "^0.24.1",
"vite": "4.2.1",
"vite-plugin-pages": "^0.29.0",
"vite-plugin-vue-layouts": "^0.8.0",
"vitepress": "1.0.0-alpha.65",
"vitest": "^0.29.8",
"vue-tsc": "^1.2.0"
},
"lint-staged": {
"*.{js,vue,css,ts,html,md}": [
"prettier --write"
]
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"release-it": {
"github": {
"release": false,
"releaseNotes": "git log --pretty=format:\"* %s (%h)\" $(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))...HEAD~1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/amir20/dozzle.git"
},
"license": "ISC",
"author": "Amir Raminfar <findamir@gmail.com>",
"scripts": {
"watch:assets": "vite --open",
"watch:server": "LIVE_FS=true DOZZLE_ADDR=localhost:3100 reflex -c .reflex",
"dev": "make fake_assets && npm-run-all -p watch:assets watch:server",
"build": "vite build",
"release": "release-it",
"test": "TZ=UTC vitest",
"postinstall": "husky install"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.14",
"@iconify-json/cil": "^1.1.3",
"@iconify-json/mdi": "^1.1.44",
"@iconify-json/mdi-light": "^1.1.5",
"@iconify-json/octicon": "^1.1.30",
"@intlify/unplugin-vue-i18n": "^0.8.1",
"@oruga-ui/oruga-next": "^0.5.10",
"@oruga-ui/theme-bulma": "^0.2.8",
"@vueuse/core": "^9.12.0",
"@vueuse/integrations": "^9.12.0",
"@vueuse/router": "^9.12.0",
"ansi-to-html": "^0.7.2",
"bulma": "^0.9.4",
"d3-array": "^3.2.2",
"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",
"date-fns": "^2.29.3",
"fuse.js": "^6.6.2",
"lodash.debounce": "^4.0.8",
"pinia": "^2.0.29",
"semver": "^7.3.8",
"splitpanes": "^3.1.5",
"vue": "^3.2.45",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@pinia/testing": "^0.0.14",
"@types/d3-array": "^3.0.4",
"@types/d3-ease": "^3.0.0",
"@types/d3-scale": "^4.0.3",
"@types/d3-selection": "^3.0.4",
"@types/d3-shape": "^3.1.1",
"@types/d3-transition": "^3.0.3",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.11.18",
"@types/semver": "^7.3.13",
"@vitejs/plugin-vue": "4.0.0",
"@vue/compiler-sfc": "^3.2.45",
"@vue/test-utils": "^2.2.8",
"c8": "^7.12.0",
"eventsourcemock": "^2.0.0",
"husky": "^8.0.3",
"jest-serializer-vue": "^3.0.0",
"jsdom": "^21.1.0",
"lint-staged": "^13.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.3",
"release-it": "^15.6.0",
"sass": "^1.57.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.4",
"unplugin-auto-import": "^0.13.0",
"unplugin-icons": "^0.15.2",
"unplugin-vue-components": "^0.23.0",
"vite": "4.0.4",
"vite-plugin-pages": "^0.28.0",
"vite-plugin-vue-layouts": "^0.7.0",
"vitest": "^0.28.3",
"vue-tsc": "^1.0.24"
},
"lint-staged": {
"*.{js,vue,css}": [
"prettier --write"
]
},
"release-it": {
"github": {
"release": false,
"releaseNotes": "git log --pretty=format:\"* %s (%h)\" $(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))...HEAD~1"
},
"npm": {
"publish": false
}
"npm": {
"publish": false
}
}
}
+1997 -2126
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
packages:
- docs
- e2e
+6 -1
View File
@@ -61,13 +61,18 @@ export default defineConfig(() => ({
VueI18nPlugin({
runtimeOnly: true,
compositionOnly: true,
strictMessage: false,
include: [path.resolve(__dirname, "locales/**")],
}),
],
server: {
proxy: {
"/api": {
target: "http://localhost:3100/",
target: {
host: "127.0.0.1",
port: 3100,
},
changeOrigin: false,
},
},
},
+37 -15
View File
@@ -1,7 +1,9 @@
package web
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -25,25 +27,27 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
events, err := h.client.Events(ctx)
client := h.clientFromRequest(r)
events, err := client.Events(ctx)
stats := make(chan docker.ContainerStat)
if containers, err := h.client.ListContainers(); err == nil {
for _, c := range containers {
if c.State == "running" {
if err := h.client.ContainerStats(ctx, c.ID, stats); err != nil {
log.Errorf("error while streaming container stats: %v", err)
}
}
}
}
if err := sendContainersJSON(h.client, w); err != nil {
if err := sendContainersJSON(client, w); err != nil {
log.Errorf("error while encoding containers to stream: %v", err)
}
f.Flush()
if containers, err := client.ListContainers(); err == nil {
go func() {
for _, c := range containers {
if c.State == "running" {
if err := client.ContainerStats(ctx, c.ID, stats); err != nil && !errors.Is(err, context.Canceled) {
log.Errorf("error while streaming container stats: %v", err)
}
}
}
}()
}
for {
select {
case stat := <-stats:
@@ -62,10 +66,10 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
log.Debugf("triggering docker event: %v", event.Name)
if event.Name == "start" {
log.Debugf("found new container with id: %v", event.ActorID)
if err := h.client.ContainerStats(ctx, event.ActorID, stats); err != nil {
if err := client.ContainerStats(ctx, event.ActorID, stats); err != nil && !errors.Is(err, context.Canceled) {
log.Errorf("error when streaming new container stats: %v", err)
}
if err := sendContainersJSON(h.client, w); err != nil {
if err := sendContainersJSON(client, w); err != nil {
log.Errorf("error encoding containers to stream: %v", err)
return
}
@@ -78,7 +82,25 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
}
f.Flush()
case "health_status: healthy", "health_status: unhealthy":
log.Debugf("triggering docker health event: %v", event.Name)
healthy := "unhealthy"
if event.Name == "health_status: healthy" {
healthy = "healthy"
}
payload := map[string]string{
"actorId": event.ActorID,
"health": healthy,
}
bytes, _ := json.Marshal(payload)
if _, err := fmt.Fprintf(w, "event: container-health\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing event to event stream: %v", err)
return
}
f.Flush()
default:
log.Tracef("ignoring docker event: %v", event.Name)
// do nothing
}
case <-ctx.Done():

Some files were not shown because too many files have changed in this diff Show More