Compare commits

...

99 Commits

Author SHA1 Message Date
IrwenXYZ
ae3283adda Update readme for memory consumption on ARM devices (#1967) 2022-11-25 20:19:39 -08:00
renovate[bot]
145158a925 Update dependency unplugin-auto-import to ^0.12.0 (#1966) 2022-11-25 09:56:19 -08:00
renovate[bot]
e46e1a0814 Update cypress/included Docker tag to v11.2.0 (#1961) 2022-11-23 06:34:10 -08:00
renovate[bot]
7a624cd065 Update dependency jest-serializer-vue to v3 (#1959) 2022-11-23 06:33:56 -08:00
Amir Raminfar
fe7c8743b9 Removes depedenbot 2022-11-15 15:40:58 -08:00
renovate[bot]
ccc1aae78e Update cypress/included Docker tag to v11.1.0 (#1958)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-15 13:05:01 -08:00
renovate[bot]
935e17c200 Update dependency vite to v3.2.4 (#1957) 2022-11-15 07:11:03 -08:00
renovate[bot]
63bb1f24a3 Update module github.com/spf13/afero to v1.9.3 (#1954) 2022-11-14 20:11:08 -08:00
renovate[bot]
cdc8189ad1 Update module go to 1.19 (#1955) 2022-11-14 20:09:59 -08:00
renovate[bot]
f6daab2111 Add renovate.json (#1953) 2022-11-14 19:43:38 -08:00
Amir Raminfar
7ed461e6ef Removes npmrc and hositing is not needed anymore (#1952) 2022-11-14 09:42:01 -08:00
Amir Raminfar
15636348b5 Updates modules 2022-11-10 09:35:11 -08:00
kodiakhq[bot]
a370ad48d5 Merge pull request #1950 from amir20/dependabot/docker/e2e/cypress/included-11.0.1
Bump cypress/included from 11.0.0 to 11.0.1 in /e2e
2022-11-10 09:11:52 +00:00
dependabot[bot]
8900631b20 Bump cypress/included from 11.0.0 to 11.0.1 in /e2e
Bumps cypress/included from 11.0.0 to 11.0.1.

---
updated-dependencies:
- dependency-name: cypress/included
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-10 09:06:19 +00:00
Amir Raminfar
fa3ef24999 Updates cypress to 11.x (#1949) 2022-11-09 09:11:02 -08:00
kodiakhq[bot]
deaf58c47b Merge pull request #1948 from amir20/dependabot/docker/e2e/cypress/included-11.0.0
Bump cypress/included from 10.11.0 to 11.0.0 in /e2e
2022-11-09 09:11:24 +00:00
dependabot[bot]
1d4573a657 Bump cypress/included from 10.11.0 to 11.0.0 in /e2e
Bumps cypress/included from 10.11.0 to 11.0.0.

---
updated-dependencies:
- dependency-name: cypress/included
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 09:07:24 +00:00
Amir Raminfar
84300fc734 Release 4.4.1 2022-11-08 15:10:14 -08:00
Dmitry Mazurov
2b9873e76b Update mobile menu (#1946) 2022-11-08 15:09:57 -08:00
Amir Raminfar
c589c599fc Release 4.4.0 2022-11-08 12:48:48 -08:00
Amir Raminfar
2702e80314 Adds hover for buttons 2022-11-08 12:47:10 -08:00
Dmitry Mazurov
bb1dda41ff Add save collapse to localstorage (#1945) 2022-11-08 12:40:13 -08:00
Amir Raminfar
ddafd95b69 Fixes int tests 2022-11-08 11:54:37 -08:00
Amir Raminfar
d6e87a7137 Fixes collapseNav 2022-11-08 11:49:40 -08:00
Dmitry Mazurov
54eca89c4e Moved logout button to sidebar and fix default width sidebar. (#1942)
* Moved logout button to sidebar and fix default width sidebar.

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

* Fix logout url. Save collapse to localstorage.

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

* Clear code.

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

* Update logout button if collapse

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

* Fix dark mode for collapse button. Update toolbar. Remove localstorage.

* Fix collapse auto-import

* Remove collapse from settings

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-11-08 11:45:11 -08:00
Amir Raminfar
fbe34d7377 Updates modules 2022-11-07 09:08:29 -08:00
Dmitry Mazurov
e2b497f158 Fix search modal for mobile view (#1941)
Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-11-04 10:36:19 -07:00
Amir Raminfar
ca9fcf8e1d Updates snapshots 2022-11-03 13:43:24 -07:00
Dmitry Mazurov
4fb012c0e4 Merge branch 'amir20:master' into responsible 2022-11-03 22:48:44 +03:00
Dmitry Mazurov
b77448f2a9 Fix responsible list containers
Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-11-03 22:09:56 +03:00
Amir Raminfar
3bc921c8d7 Updates auto import 2022-11-03 09:51:17 -07:00
Amir Raminfar
54c08cf79b Removes duplicate case sensistive files (#1939) 2022-11-03 09:41:59 -07:00
Amir Raminfar
199c51722c Also skip docker login 2022-11-02 10:43:08 -07:00
Amir Raminfar
379af164dd Skips only the push int tests (#1937) 2022-11-02 09:16:45 -07:00
Dmitry Mazurov
dc4ba652b4 Skip Integration Tests and Push branches and PRs from fork (#1936)
Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-11-02 07:24:39 -07:00
kodiakhq[bot]
0e8561a1c5 Merge pull request #1935 from amir20/dependabot/docker/golang-1.19.3-alpine
Bump golang from 1.19.2-alpine to 1.19.3-alpine
2022-11-02 09:29:57 +00:00
dependabot[bot]
60ec928a8e Bump golang from 1.19.2-alpine to 1.19.3-alpine
Bumps golang from 1.19.2-alpine to 1.19.3-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-02 09:26:23 +00:00
Dmitry Mazurov
26f3ab65a7 Update ru locale (#1934) 2022-11-01 11:36:02 -07:00
Amir Raminfar
0cc209484d Use trim instead 2022-11-01 10:57:49 -07:00
Amir Raminfar
f09a7815af Cleans up DockerSecret 2022-11-01 09:08:05 -07:00
Amir Raminfar
70b573cec0 Fixes spacing 2022-10-31 15:11:33 -07:00
Amir Raminfar
0d79196eda Updates cypress 2022-10-31 10:21:41 -07:00
Amir Raminfar
8eb4e05055 Release 4.3.0 2022-10-31 09:33:26 -07:00
Amir Raminfar
cd793a9b53 Updates modules 2022-10-31 09:32:21 -07:00
Dmitry Mazurov
fad75e2f5b Fix read username and password from file (#1930)
* Fix read username and password from file

* Update Readme

* Add ru locale

* Update read the file from UnmarshalText

* Fix if not set username and password

* Fix if content in file empty

* Update ru locale

* Fix errorMsg for auth

* Removed local ru

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

* Remove struct for username and password

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-10-31 09:31:27 -07:00
Dmitry Mazurov
d6710453e5 Add ru locale (#1932)
Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
2022-10-31 09:03:47 -07:00
Amir Raminfar
3a8c9cbcfa Release 4.2.3 2022-10-28 14:42:46 -07:00
Dmitry Mazurov
8586fee5ab Add docker secrets env (#1929) 2022-10-28 14:31:48 -07:00
Amir Raminfar
8ccae473ed Updates modules 2022-10-28 12:24:42 -07:00
kodiakhq[bot]
02434afef3 Merge pull request #1923 from amir20/dependabot/docker/e2e/cypress/included-10.11.0
Bump cypress/included from 10.10.0 to 10.11.0 in /e2e
2022-10-26 09:45:31 +00:00
dependabot[bot]
192ee98dd1 Bump cypress/included from 10.10.0 to 10.11.0 in /e2e
Bumps cypress/included from 10.10.0 to 10.11.0.

---
updated-dependencies:
- dependency-name: cypress/included
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-26 09:41:20 +00:00
kodiakhq[bot]
79e9dc7be6 Merge pull request #1922 from amir20/dependabot/go_modules/github.com/docker/docker-20.10.21incompatible
Bump github.com/docker/docker from 20.10.20+incompatible to 20.10.21+incompatible
2022-10-26 09:15:01 +00:00
dependabot[bot]
19fe7e8d18 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.20+incompatible to 20.10.21+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.20...v20.10.21)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-26 09:11:08 +00:00
Amir Raminfar
e4ecfca5be Updats modules 2022-10-24 16:08:59 -07:00
kodiakhq[bot]
f37925b637 Merge pull request #1920 from amir20/dependabot/go_modules/github.com/stretchr/testify-1.8.1
Bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-24 09:15:06 +00:00
dependabot[bot]
822b783063 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 09:11:05 +00:00
Amir Raminfar
7dcf1d4f15 Release 4.2.2 2022-10-21 13:14:07 -07:00
Amir Raminfar
d0af303d6f Updates colors (#1919)
* Updates colors

* Updates colors int tests
2022-10-21 13:13:48 -07:00
Amir Raminfar
fd31d394a5 Fixes tooltips 2022-10-19 15:16:42 -07:00
Amir Raminfar
1c0af19c88 Release 4.2.1 2022-10-19 08:31:26 -07:00
Amir Raminfar
9d77613ee9 Updates node 2022-10-19 08:31:13 -07:00
kodiakhq[bot]
62f747797c Merge pull request #1917 from amir20/dependabot/go_modules/github.com/docker/docker-20.10.20incompatible
Bump github.com/docker/docker from 20.10.19+incompatible to 20.10.20+incompatible
2022-10-19 09:18:28 +00:00
kodiakhq[bot]
fccb7fc2d4 Merge pull request #1916 from amir20/dependabot/github_actions/docker/setup-buildx-action-2.2.1
Bump docker/setup-buildx-action from 2.2.0 to 2.2.1
2022-10-19 09:17:37 +00:00
kodiakhq[bot]
7fecbdd000 Merge pull request #1915 from amir20/dependabot/docker/node-19-alpine
Bump node from 18-alpine to 19-alpine
2022-10-19 09:17:24 +00:00
dependabot[bot]
46a2f2b810 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.19+incompatible to 20.10.20+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.19...v20.10.20)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-19 09:12:59 +00:00
dependabot[bot]
e9dcdda64d Bump docker/setup-buildx-action from 2.2.0 to 2.2.1
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-19 09:12:50 +00:00
dependabot[bot]
67a0644c37 Bump node from 18-alpine to 19-alpine
Bumps node from 18-alpine to 19-alpine.

---
updated-dependencies:
- dependency-name: node
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-19 09:12:48 +00:00
Amir Raminfar
21fc2ce2fd Adds on mouse over effect for stats (#1914) 2022-10-18 15:29:19 -07:00
kodiakhq[bot]
20425bf6b1 Merge pull request #1913 from amir20/dependabot/github_actions/docker/setup-buildx-action-2.2.0
Bump docker/setup-buildx-action from 2.1.0 to 2.2.0
2022-10-18 09:17:58 +00:00
dependabot[bot]
828c288570 Bump docker/setup-buildx-action from 2.1.0 to 2.2.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-18 09:15:02 +00:00
Amir Raminfar
cde2589755 Fixes iOS font size changing 2022-10-17 10:48:11 -07:00
kodiakhq[bot]
5fb2f452e2 Merge pull request #1911 from amir20/dependabot/github_actions/pnpm/action-setup-2.2.4
Bump pnpm/action-setup from 2.2.3 to 2.2.4
2022-10-17 09:16:40 +00:00
dependabot[bot]
6aea252d3e Bump pnpm/action-setup from 2.2.3 to 2.2.4
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 2.2.3 to 2.2.4.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v2.2.3...v2.2.4)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 09:13:39 +00:00
Amir Raminfar
7337dcb5d4 Updates modules 2022-10-14 14:57:46 -07:00
Amir Raminfar
deeb5fc100 Release 4.2.0 2022-10-14 13:23:59 -07:00
Amir Raminfar
5b15a0b29d stat cleanup (#1910)
* Updates modules

* Cleans up stats
2022-10-14 13:00:57 -07:00
kodiakhq[bot]
e2ad2e0193 Merge pull request #1909 from amir20/dependabot/go_modules/github.com/docker/docker-20.10.19incompatible
Bump github.com/docker/docker from 20.10.18+incompatible to 20.10.19+incompatible
2022-10-14 09:11:40 +00:00
dependabot[bot]
5b48426fc1 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.18+incompatible to 20.10.19+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.18...v20.10.19)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-14 09:08:05 +00:00
kodiakhq[bot]
a24947ab3b Merge pull request #1908 from amir20/dependabot/github_actions/docker/setup-buildx-action-2.1.0
Bump docker/setup-buildx-action from 2.0.0 to 2.1.0
2022-10-13 09:19:44 +00:00
dependabot[bot]
8926b451d0 Bump docker/setup-buildx-action from 2.0.0 to 2.1.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 09:16:51 +00:00
Amir Raminfar
afd37d3455 Add trending CPU and Memory usage (#1896)
* Refactors to stat history

* Uses markRaw

* Cleans up proxies

* Removes id from snapshots

* Adds d3

* Adds more d3 modules

* Fixes package

* Cleans up packages

* Updates modules

* Adds initital d3 chart

* Cleans up svg

* Fixes @types/d3-array

* Adds memory

* Moves charts around
2022-10-12 14:21:41 -07:00
kodiakhq[bot]
4f84beb835 Merge pull request #1906 from amir20/dependabot/docker/e2e/cypress/included-10.10.0
Bump cypress/included from 10.9.0 to 10.10.0 in /e2e
2022-10-12 10:43:39 +00:00
dependabot[bot]
d54d894a66 Bump cypress/included from 10.9.0 to 10.10.0 in /e2e
Bumps cypress/included from 10.9.0 to 10.10.0.

---
updated-dependencies:
- dependency-name: cypress/included
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 10:39:36 +00:00
kodiakhq[bot]
7c854d31a7 Merge pull request #1905 from amir20/dependabot/github_actions/docker/login-action-2.1.0
Bump docker/login-action from 2.0.0 to 2.1.0
2022-10-12 09:33:27 +00:00
kodiakhq[bot]
4c2caad4a0 Merge pull request #1904 from amir20/dependabot/github_actions/docker/build-push-action-3.2.0
Bump docker/build-push-action from 3.1.1 to 3.2.0
2022-10-12 09:33:14 +00:00
dependabot[bot]
9684fd978b Bump docker/login-action from 2.0.0 to 2.1.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 09:29:20 +00:00
dependabot[bot]
a79a3f680f Bump docker/build-push-action from 3.1.1 to 3.2.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 09:29:17 +00:00
kodiakhq[bot]
1e18423b04 Merge pull request #1903 from amir20/dependabot/github_actions/pnpm/action-setup-2.2.3
Bump pnpm/action-setup from 2.2.2 to 2.2.3
2022-10-11 09:14:13 +00:00
dependabot[bot]
82b208c0bf Bump pnpm/action-setup from 2.2.2 to 2.2.3
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v2.2.2...v2.2.3)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-11 09:08:59 +00:00
Amir Raminfar
8a3e9504a4 Updates modules 2022-10-10 15:38:05 -07:00
Amir Raminfar
d8748c6e27 Updats modules 2022-10-08 13:26:47 -07:00
kodiakhq[bot]
af8192fbc0 Merge pull request #1897 from amir20/dependabot/docker/golang-1.19.2-alpine
Bump golang from 1.19.1-alpine to 1.19.2-alpine
2022-10-05 09:20:23 +00:00
dependabot[bot]
3492587d63 Bump golang from 1.19.1-alpine to 1.19.2-alpine
Bumps golang from 1.19.1-alpine to 1.19.2-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-05 09:16:19 +00:00
Amir Raminfar
b8c522021d Updates modules 2022-10-04 13:26:11 -07:00
James Cote
5850c319f2 Fix json log line still says "expand json" when expanded (#1894) 2022-10-01 15:06:59 -07:00
Amir Raminfar
afbed43185 Release 4.1.9 2022-09-28 12:29:26 -07:00
Altynbek Kaliakbarov
d083430c73 Change calculation of memory usage. (#1892) 2022-09-28 12:23:24 -07:00
kodiakhq[bot]
79f553ff0c Merge pull request #1890 from amir20/dependabot/docker/e2e/cypress/included-10.9.0
Bump cypress/included from 10.8.0 to 10.9.0 in /e2e
2022-09-28 09:38:56 +00:00
dependabot[bot]
a02551f5ec Bump cypress/included from 10.8.0 to 10.9.0 in /e2e
Bumps cypress/included from 10.8.0 to 10.9.0.

---
updated-dependencies:
- dependency-name: cypress/included
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-28 09:33:10 +00:00
53 changed files with 1803 additions and 1564 deletions

View File

@@ -1,2 +0,0 @@
version = 1
merge.notify_on_conflict = false

View File

@@ -1,47 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
labels:
- "dependencies"
- "automerge"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/"
labels:
- "dependencies"
- "automerge"
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: "/"
labels:
- "gomod"
- "dependencies"
- "automerge"
schedule:
interval: daily
- package-ecosystem: npm
directory: "/"
labels:
- "npm"
- "dependencies"
- "automerge"
schedule:
interval: daily
- package-ecosystem: npm
directory: "/e2e"
labels:
- "npm"
- "dependencies"
- "automerge"
schedule:
interval: daily
- package-ecosystem: "docker"
directory: "/e2e"
labels:
- "dependencies"
- "automerge"
schedule:
interval: "daily"

View File

@@ -13,7 +13,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.2
uses: pnpm/action-setup@v2.2.4
with:
version: 6.20.1
- name: Install dependencies
@@ -27,7 +27,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.19.x
- name: Checkout code
uses: actions/checkout@v3
- name: Run Go Tests with Coverage
@@ -53,14 +53,14 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.0.0
uses: docker/setup-buildx-action@v2.2.1
- name: Login to DockerHub
uses: docker/login-action@v2.0.0
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v3.1.1
uses: docker/build-push-action@v3.2.0
with:
push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
@@ -81,7 +81,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.2
uses: pnpm/action-setup@v2.2.4
with:
version: 6.20.1
- name: Install dependencies

View File

@@ -10,7 +10,7 @@ jobs:
buildx:
name: Push branches and PRs
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == 'amir20/dozzle' }}
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == 'amir20/dozzle') }}
steps:
- name: Docker meta
id: meta
@@ -18,14 +18,14 @@ jobs:
with:
images: amir20/dozzle
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.0.0
uses: docker/setup-buildx-action@v2.2.1
- name: Login to DockerHub
uses: docker/login-action@v2.0.0
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v3.1.1
uses: docker/build-push-action@v3.2.0
with:
push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8

View File

@@ -16,7 +16,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.2
uses: pnpm/action-setup@v2.2.4
with:
version: 6.20.1
- name: Install dependencies
@@ -30,7 +30,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.19.x
- name: Checkout code
uses: actions/checkout@v3
- name: Run Go Tests with Coverage
@@ -44,15 +44,17 @@ jobs:
with:
fetch-depth: 2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.0.0
uses: docker/setup-buildx-action@v2.2.1
- name: Login to DockerHub
uses: docker/login-action@v2.0.0
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork }}
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build images
run: COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f e2e/docker-compose.yml build --build-arg BUILDKIT_INLINE_CACHE=1
- name: Push images
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork }}
run: COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f e2e/docker-compose.yml push
- name: Set commit message for push
if: github.event_name == 'push'

1
.npmrc
View File

@@ -1 +0,0 @@
shamefully-hoist=true

View File

@@ -1,5 +1,5 @@
# Build assets
FROM --platform=$BUILDPLATFORM node:18-alpine as node
FROM --platform=$BUILDPLATFORM node:19-alpine as node
RUN npm install -g pnpm
@@ -8,11 +8,11 @@ WORKDIR /build
# Install dependencies from lock file
COPY pnpm-lock.yaml ./
RUN pnpm fetch --prod
RUN pnpm fetch
# Copy package.json and install dependencies
COPY package.json ./
RUN pnpm install -r --offline --prod --ignore-scripts
RUN pnpm install -r --offline --ignore-scripts
# Copy assets and translations to build
COPY .* vite.config.ts index.html ./
@@ -22,7 +22,7 @@ COPY locales ./locales
# Build assets
RUN pnpm build
FROM --platform=$BUILDPLATFORM golang:1.19.1-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.19.3-alpine AS builder
RUN apk add --no-cache ca-certificates && mkdir /dozzle

View File

@@ -98,7 +98,7 @@ this would then only allow you to view containers with a name starting with "foo
#### 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`.
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](https://docs.docker.com/engine/swarm/secrets/) `--usernamefile` and `--passwordfile`.
#### Changing base URL
@@ -129,6 +129,8 @@ Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE`| `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE`| `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
## Troubleshooting and FAQs
@@ -181,6 +183,26 @@ Dozzle has a [special route](https://github.com/amir20/dozzle/blob/master/assets
</details>
<details>
<summary>I installed Dozzle but memory consumption doesn't show up!</summary>
*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
```
</details>
## License
[MIT](LICENSE)

View File

@@ -14,6 +14,7 @@ declare global {
const arrayEquals: typeof import('./utils/index')['arrayEquals']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const collapseNav: typeof import('./composables/settings')['collapseNav']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
@@ -68,6 +69,8 @@ declare global {
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
@@ -100,6 +103,7 @@ 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']
@@ -207,6 +211,7 @@ declare global {
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useLogStream: typeof import('./composables/eventsource')['useLogStream']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
@@ -250,6 +255,7 @@ declare global {
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
@@ -307,7 +313,7 @@ declare global {
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module '@vue/runtime-core' {
declare module 'vue' {
interface ComponentCustomProperties {
readonly $$: UnwrapRef<typeof import('vue/macros')['$$']>
readonly $: UnwrapRef<typeof import('vue/macros')['$']>
@@ -322,6 +328,7 @@ declare module '@vue/runtime-core' {
readonly arrayEquals: UnwrapRef<typeof import('./utils/index')['arrayEquals']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly collapseNav: UnwrapRef<typeof import('./composables/settings')['collapseNav']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
@@ -376,6 +383,8 @@ declare module '@vue/runtime-core' {
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onClickOutside: UnwrapRef<typeof import('@vueuse/core')['onClickOutside']>
@@ -408,6 +417,7 @@ declare module '@vue/runtime-core' {
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']>
@@ -515,6 +525,7 @@ declare module '@vue/runtime-core' {
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useLogStream: UnwrapRef<typeof import('./composables/eventsource')['useLogStream']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
@@ -558,6 +569,7 @@ declare module '@vue/runtime-core' {
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']>
readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']>

View File

@@ -29,6 +29,7 @@ declare module '@vue/runtime-core' {
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']
MobileMenu: typeof import('./components/MobileMenu.vue')['default']
OcticonContainer24: typeof import('~icons/octicon/container24')['default']
@@ -44,6 +45,8 @@ declare module '@vue/runtime-core' {
SideMenu: typeof import('./components/SideMenu.vue')['default']
SimpleLogItem: typeof import('./components/LogViewer/SimpleLogItem.vue')['default']
SkippedEntriesLogItem: typeof import('./components/LogViewer/SkippedEntriesLogItem.vue')['default']
StatMonitor: typeof import('./components/LogViewer/StatMonitor.vue')['default']
StatSparkline: typeof import('./components/LogViewer/StatSparkline.vue')['default']
ZigZag: typeof import('./components/LogViewer/ZigZag.vue')['default']
}
}

View File

@@ -36,7 +36,7 @@
</template>
<script lang="ts" setup>
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { useFuse } from "@vueuse/integrations/useFuse";
const { maxResults: resultLimit = 20 } = defineProps<{
@@ -105,6 +105,15 @@ function addColumn(container: Container) {
width: 580px;
}
@media screen and (max-width: 768px) {
.panel {
min-height: 200px;
width: auto;
margin-left: 0.25rem!important;
margin-right: 0.25rem!important;
}
}
.running {
color: var(--primary-color);
}

View File

@@ -4,7 +4,7 @@
<log-date :date="logEntry.date"></log-date>
</div>
<div class="column">
<ul class="fields" @click="expanded = !expanded">
<ul class="fields" :class="{ expanded }" @click="expanded = !expanded">
<li v-for="(value, name) in validValues(logEntry.message)">
<span class="has-text-grey">{{ name }}=</span>
<span class="has-text-weight-bold" v-html="markSearch(value)"></span>
@@ -47,6 +47,12 @@ function validValues(obj: Record<string, any>) {
}
}
&.expanded:hover {
&::after {
content: "collapse json";
}
}
li {
display: inline-block;
& + li {

View File

@@ -1,27 +1,51 @@
<template>
<div class="is-size-7 is-uppercase columns is-marginless is-mobile" v-if="container.stat">
<div class="column is-narrow has-text-weight-bold">
{{ container.state }}
</div>
<div class="column is-narrow" v-if="container.stat.memoryUsage !== null">
<span class="has-text-weight-light has-spacer">mem</span>
<span class="has-text-weight-bold">
{{ formatBytes(container.stat.memoryUsage) }}
</span>
</div>
<div class="column is-narrow" v-if="container.stat.cpu !== null">
<span class="has-text-weight-light has-spacer">load</span>
<span class="has-text-weight-bold"> {{ container.stat.cpu }}% </span>
</div>
<div class="is-size-7 is-uppercase columns is-marginless is-mobile is-vcentered" v-if="container.stat">
<stat-monitor
class="column is-narrow"
:data="memoryData"
label="mem"
:stat-value="formatBytes(container.stat.memoryUsage)"
></stat-monitor>
<stat-monitor
class="column is-narrow"
:data="cpuData"
label="load"
:stat-value="container.stat.cpu + '%'"
></stat-monitor>
</div>
</template>
<script lang="ts" setup>
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { type ComputedRef } from "vue";
const container = inject("container") as ComputedRef<Container>;
const cpuData = computedWithControl(
() => container.value.getLastStat(),
() => {
const history = container.value.getStatHistory();
const points: Point<unknown>[] = history.map((stat, i) => ({
x: i,
y: stat.snapshot.cpu,
value: stat.snapshot.cpu + "%",
}));
return points;
}
);
const memoryData = computedWithControl(
() => container.value.getLastStat(),
() => {
const history = container.value.getStatHistory();
const points: Point<string>[] = history.map((stat, i) => ({
x: i,
y: stat.snapshot.memory,
value: formatBytes(stat.snapshot.memoryUsage),
}));
return points;
}
);
</script>
<style lang="scss" scoped>
@@ -30,4 +54,23 @@ const container = inject("container") as ComputedRef<Container>;
content: " ";
}
}
.has-border {
border: 1px solid var(--primary-color);
border-radius: 3px;
padding: 1px 1px 0 1px;
display: flex;
overflow: hidden;
padding-top: 0.25em;
}
.has-background-body-color {
background-color: var(--body-background-color);
}
.is-top-left {
position: absolute;
top: 0;
left: 0.75em;
}
</style>

View File

@@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { type ComputedRef } from "vue";
const container = inject("container") as ComputedRef<Container>;

View File

@@ -42,7 +42,7 @@
<script lang="ts" setup>
import { type ComputedRef } from "vue";
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
const { showSearch } = useSearchFilter();
const { base } = config;

View File

@@ -1,12 +1,12 @@
<template>
<scrollable-view :scrollable="scrollable" v-if="container">
<template #header v-if="showTitle">
<div class="mr-0 columns is-vcentered is-marginless is-hidden-mobile">
<div class="mr-0 columns is-vcentered is-marginless is-hidden-mobile has-boxshadow">
<div class="column is-clipped is-paddingless">
<container-title @close="$emit('close')" />
</div>
<div class="column is-narrow is-paddingless">
<container-stat v-if="container.stat" />
<container-stat />
</div>
<div class="mr-2 column is-narrow is-paddingless">

View File

@@ -13,33 +13,7 @@ defineProps<{
padding-right: 5px;
border-radius: 3px;
white-space: nowrap;
}
@media (prefers-color-scheme: dark) {
.date {
background-color: #262626;
color: #258ccd;
}
}
[data-theme="dark"] {
.date {
background-color: #262626;
color: #258ccd;
}
}
@media (prefers-color-scheme: light) {
.date {
background-color: #f0f0f0;
color: #009900;
}
}
[data-theme="light"] {
.date {
background-color: #f0f0f0;
color: #009900;
}
background-color: var(--scheme-main-ter);
color: #258ccd;
}
</style>

View File

@@ -4,7 +4,7 @@
</template>
<script lang="ts" setup>
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { type ComputedRef } from "vue";
const emit = defineEmits<{

View File

@@ -30,7 +30,7 @@
<script lang="ts" setup>
import { type ComputedRef, toRaw } from "vue";
import { useRouteHash } from "@vueuse/router";
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { type JSONObject, LogEntry } from "@/models/LogEntry";
const props = defineProps<{

View File

@@ -0,0 +1,56 @@
<template>
<div class="has-text-centered is-relative host" @mouseenter="mouseOver = true" @mouseleave="mouseOver = false">
<div class="has-border has-boxshadow">
<stat-sparkline :data="data" @selected-point="onSelectedPoint"></stat-sparkline>
</div>
<div class="has-background-body-color is-top-left">
<span class="has-text-weight-light has-spacer">{{ label }}</span>
<span class="has-text-weight-bold">
{{ mouseOver ? selectedPoint?.value ?? selectedPoint?.y ?? statValue : statValue }}
</span>
</div>
</div>
</template>
<script lang="ts" setup>
const { data, label, statValue } = defineProps<{ data: Point<unknown>[]; label: string; statValue: string | number }>();
let selectedPoint: Point<unknown> = $ref();
function onSelectedPoint(point: Point<unknown>) {
selectedPoint = point;
}
let mouseOver = $ref(false);
</script>
<style lang="scss" scoped>
.has-spacer {
&::after {
content: " ";
}
}
.has-border {
border: 1px solid var(--primary-color);
border-radius: 3px;
padding: 1px 1px 0 1px;
display: flex;
overflow: hidden;
padding-top: 0.25em;
}
.has-background-body-color {
background-color: var(--body-background-color);
}
.host:hover span {
color: var(--secondary-color);
}
.is-top-left {
position: absolute;
top: 0;
left: 0.75em;
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<svg :width="width" :height="height" @mousemove="onMove">
<path :d="path" class="area" />
<line :x1="lineX" y1="0" :x2="lineX" :y2="height" class="line" />
</svg>
</template>
<script lang="ts" setup>
import { extent } from "d3-array";
import { scaleLinear } from "d3-scale";
import { area, curveStep } from "d3-shape";
const d3 = { extent, scaleLinear, area, curveStep };
const { data, width = 150, height = 30 } = defineProps<{ data: Point<unknown>[]; width?: number; height?: number }>();
const x = d3.scaleLinear().range([width, 0]);
const y = d3.scaleLinear().range([height, 0]);
const emit = defineEmits<{
(event: "selected-point", value: Point<unknown>): void;
}>();
const shape = d3
.area<Point<unknown>>()
.curve(d3.curveStep)
.x((d) => x(d.x))
.y0(height)
.y1((d) => y(d.y));
const path = computed(() => {
x.domain(d3.extent(data, (d) => d.x) as [number, number]);
y.domain(d3.extent(data, (d) => d.y) as [number, number]);
return shape(data) ?? "";
});
let lineX = $ref(0);
function onMove(e: MouseEvent) {
const { offsetX } = e;
const xValue = x.invert(offsetX);
const index = Math.round(xValue);
lineX = x(index);
const point = data[index];
emit("selected-point", point);
}
</script>
<style scoped>
:deep(.area) {
fill: var(--primary-color);
}
:deep(.line) {
stroke: var(--secondary-color);
stroke-width: 2;
display: none;
}
svg:hover :deep(.line) {
display: unset;
}
</style>

View File

@@ -24,6 +24,36 @@
</div>
</div>
<div class="menu-label level is-mobile is-hidden-mobile" :class="{ 'is-active': showNav }">
<div class="level-item has-text-centered">
<div>
<button class="button is-small is-rounded" @click="$emit('search')" :title="$t('tooltip.search')">
<span class="icon">
<mdi-light-magnify />
</span>
</button>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<router-link :to="{ name: 'settings' }" active-class="is-active" class="button is-small is-rounded">
<span class="icon">
<mdi-light-cog />
</span>
</router-link>
</div>
</div>
<div class="level-item has-text-centered" v-if="secured">
<div>
<a class="button is-small is-rounded" :href="`${base}/logout`" :title="$t('button.logout')">
<span class="icon">
<mdi-light-logout />
</span>
</a>
</div>
</div>
</div>
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">{{ $t("label.containers") }}</p>
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
<li v-for="item in visibleContainers" :key="item.id">
@@ -42,6 +72,7 @@
</template>
<script lang="ts" setup>
const { base, secured } = config;
const store = useContainerStore();
const route = useRoute();
const { visibleContainers, allContainersById } = storeToRefs(store);
@@ -63,6 +94,10 @@ aside {
max-height: 100vh;
overflow: auto;
.level.is-hidden-mobile.is-active {
display: flex !important;
}
.menu-label {
margin-top: 1em;
}

View File

@@ -16,7 +16,12 @@
<div class="is-scrollbar-notification">
<transition name="fade">
<button class="button" :class="hasMore ? 'has-more' : ''" @click="scrollToBottom()" v-show="paused">
<button
class="button has-boxshadow"
:class="hasMore ? 'has-more' : ''"
@click="scrollToBottom()"
v-show="paused"
>
<mdi-light-chevron-double-down />
</button>
</transition>
@@ -109,18 +114,18 @@ section {
button {
position: fixed;
bottom: 30px;
background-color: var(--secondary-color);
transition: background-color 1s ease-out;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
background-color: var(--primary-color);
transition: background-color 0.24s ease-out;
border: none !important;
color: #222;
color: #eee;
&.has-more {
background-color: var(--primary-color);
background-color: var(--secondary-color);
animation-name: bounce;
animation-duration: 1000ms;
animation-fill-mode: both;
color: #fff;
color: #222;
}
}
}

View File

@@ -8,20 +8,29 @@
</svg>
</router-link>
</div>
<div class="column is-narrow has-text-right px-1">
<button class="button is-rounded" @click="$emit('search')" title="$t('tooltip.search')">
</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 />
</span>
</button>
</div>
<div class="column is-narrow has-text-right px-0">
<router-link :to="{ name: 'settings' }" active-class="is-active" class="button is-rounded">
<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 />
</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 />
</span>
</a>
</div>
</div>
<p class="menu-label is-hidden-mobile">{{ $t("label.containers") }}</p>
<ul class="menu-list is-hidden-mobile" v-if="ready">
@@ -40,7 +49,7 @@
class="icon is-small"
@click.stop.prevent="store.appendActiveContainer(item)"
v-show="!activeContainersById[item.id]"
title="$t('tooltip.pin-column')"
:title="$t('tooltip.pin-column')"
>
<cil-columns />
</span>
@@ -58,6 +67,7 @@
<script lang="ts" setup>
import type { Container } from "@/types/Container";
const { base, secured } = config;
const store = useContainerStore();
const { activeContainers, visibleContainers, ready } = storeToRefs(store);

View File

@@ -8,7 +8,7 @@ import {
DockerEventLogEntry,
SkippedLogsEntry,
} from "@/models/LogEntry";
import { type Container } from "@/types/Container";
import { Container } from "@/models/Container";
function parseMessage(data: string): LogEntry<string | JSONObject> {
const e = JSON.parse(data) as LogEvent;
@@ -37,7 +37,7 @@ export function useLogStream(container: ComputedRef<Container>) {
} else {
messages.push(...buffer);
buffer = [];
messages.splice(0, messages.length - config.maxLogs);
messages = messages.slice(-config.maxLogs);
}
} else {
messages.push(...buffer);

View File

@@ -10,6 +10,7 @@ export const DEFAULT_SETTINGS: {
lightTheme: "auto" | "dark" | "light";
hourStyle: "auto" | "24" | "12";
softWrap: boolean;
collapseNav: boolean;
} = {
search: true,
size: "medium",
@@ -20,6 +21,7 @@ export const DEFAULT_SETTINGS: {
lightTheme: "auto",
hourStyle: "auto",
softWrap: true,
collapseNav: false,
};
const settings = useStorage(DOZZLE_SETTINGS_KEY, DEFAULT_SETTINGS);
@@ -69,7 +71,13 @@ const softWrap = computed({
set: (value) => (settings.value.softWrap = value),
});
const collapseNav = computed({
get: () => settings.value.collapseNav,
set: (value) => (settings.value.collapseNav = value),
});
export {
collapseNav,
softWrap,
hourStyle,
lightTheme,

View File

@@ -1,6 +1,6 @@
<template>
<main v-if="!authorizationNeeded">
<mobile-menu v-if="isMobile"></mobile-menu>
<mobile-menu v-if="isMobile" @search="showFuzzySearch"></mobile-menu>
<splitpanes @resized="onResized($event)">
<pane min-size="10" :size="menuWidth" v-if="!isMobile && !collapseNav">
<side-menu @search="showFuzzySearch"></side-menu>
@@ -25,8 +25,8 @@
</pane>
</splitpanes>
<button
@click="collapseNav = !collapseNav"
class="button is-rounded"
@click="collapse"
class="button is-small is-rounded"
:class="{ collapsed: collapseNav }"
id="hide-nav"
v-if="!isMobile"
@@ -47,7 +47,6 @@ import { Splitpanes, Pane } from "splitpanes";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import FuzzySearchModal from "@/components/FuzzySearchModal.vue";
const collapseNav = ref(false);
const { oruga } = useProgrammatic();
const { authorizationNeeded } = config;
@@ -74,6 +73,9 @@ function showFuzzySearch() {
active: true,
});
}
function collapse() {
collapseNav.value = !collapseNav.value;
}
function onResized(e: any) {
if (e.length == 2) {
menuWidth.value = e[0].size;
@@ -112,7 +114,8 @@ function onResized(e: any) {
left: -40px;
width: 60px;
padding-left: 40px;
background: rgba(0, 0, 0, 0.95);
color: var(--text-strong-color);
background: var(--scheme-main);
&:hover {
left: -25px;

View File

@@ -0,0 +1,31 @@
import type { ContainerStat, ContainerState } from "@/types/Container";
import type { UseThrottledRefHistoryReturn } from "@vueuse/core";
import { Ref } from "vue";
type Stat = Omit<ContainerStat, "id">;
export class Container {
public stat: Ref<Stat>;
private readonly throttledStatHistory: UseThrottledRefHistoryReturn<Stat, Stat>;
constructor(
public readonly id: string,
public readonly created: number,
public readonly image: string,
public readonly name: string,
public readonly command: string,
public status: string,
public state: ContainerState
) {
this.stat = ref({ cpu: 0, memory: 0, memoryUsage: 0 });
this.throttledStatHistory = useThrottledRefHistory(this.stat, { capacity: 300, deep: true, throttle: 1000 });
}
public getStatHistory() {
return unref(this.throttledStatHistory.history);
}
public getLastStat() {
return unref(this.throttledStatHistory.last);
}
}

View File

@@ -1,170 +0,0 @@
<template>
<div>
<section class="hero is-small mt-4">
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column is-narrow" v-if="secured">
<a class="button is-primary is-small" :href="`${base}/logout`">{{ $t("button.logout") }}</a>
</div>
</div>
</div>
</div>
</section>
<section class="level section">
<div class="level-item has-text-centered">
<div>
<p class="title">{{ containers.length }}</p>
<p class="heading">{{ $t("label.total-containers") }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title">{{ runningContainers.length }}</p>
<p class="heading">{{ $t("label.running") }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title" data-ci-skip>{{ totalCpu }}%</p>
<p class="heading">{{ $t("label.total-cpu-usage") }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title" data-ci-skip>{{ formatBytes(totalMem) }}</p>
<p class="heading">{{ $t("label.total-mem-usage") }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title">{{ version }}</p>
<p class="heading">{{ $t("label.dozzle-version") }}</p>
</div>
</div>
</section>
<section class="columns is-centered section is-marginless">
<div class="column is-4">
<div class="panel">
<p class="panel-heading">{{ $t("label.containers") }}</p>
<div class="panel-block">
<p class="control has-icons-left">
<input
class="input"
type="text"
:placeholder="$t('placeholder.search-containers')"
v-model="query"
@keyup.esc="query = ''"
@keyup.enter="onEnter()"
/>
<span class="icon is-left">
<search-icon />
</span>
</p>
</div>
<p class="panel-tabs" v-if="query === ''">
<a :class="{ 'is-active': sort === 'running' }" @click="sort = 'running'">{{ $t("label.running") }}</a>
<a :class="{ 'is-active': sort === 'all' }" @click="sort = 'all'">{{ $t("label.all") }}</a>
</p>
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
v-for="item in data.slice(0, 10)"
:key="item.id"
class="panel-block"
>
<span class="name">{{ item.name }}</span>
<div class="subtitle is-7 status">
<past-time :date="new Date(item.created * 1000)"></past-time>
</div>
</router-link>
</div>
</div>
</section>
</div>
</template>
<script lang="ts" setup>
import SearchIcon from "~icons/mdi-light/magnify";
import { useFuse } from "@vueuse/integrations/useFuse";
const { base, version, secured } = config;
const containerStore = useContainerStore();
const { containers } = storeToRefs(containerStore);
const router = useRouter();
const sort = $ref("running");
const query = ref("");
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const runningContainers = $computed(() => mostRecentContainers.filter((c) => c.state === "running"));
const { results } = useFuse(query, containers, {
fuseOptions: { keys: ["name"] },
matchAllWhenSearchEmpty: false,
});
const data = computed(() => {
if (results.value.length) {
return results.value.map(({ item }) => item);
}
switch (sort) {
case "all":
return mostRecentContainers;
case "running":
return runningContainers;
default:
throw `Invalid sort order: ${sort}`;
}
});
let totalCpu = $ref(0);
useIntervalFn(
() => {
totalCpu = runningContainers.reduce((acc, c) => acc + (c.stat?.cpu ?? 0), 0);
},
1000,
{ immediate: true }
);
let totalMem = $ref(0);
useIntervalFn(
() => {
totalMem = runningContainers.reduce((acc, c) => acc + (c.stat?.memoryUsage ?? 0), 0);
},
1000,
{ immediate: true }
);
function onEnter() {
if (data.value.length > 0) {
const item = data.value[0];
router.push({ name: "container-id", params: { id: item.id } });
}
}
</script>
<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;
}
}
}
.icon {
padding: 10px 3px;
}
</style>

View File

@@ -1,79 +0,0 @@
<template>
<div class="hero is-halfheight">
<div class="hero-body">
<div class="container">
<section class="columns is-centered section">
<div class="column is-4">
<div class="card">
<div class="card-content">
<form action="" method="post" @submit.prevent="onLogin" ref="form">
<div class="field">
<label class="label">{{ $t("label.username") }}</label>
<div class="control">
<input
class="input"
type="text"
name="username"
autocomplete="username"
v-model="username"
autofocus
/>
</div>
</div>
<div class="field">
<label class="label">{{ $t("label.password") }}</label>
<div class="control">
<input
class="input"
type="password"
name="password"
autocomplete="current-password"
v-model="password"
/>
</div>
<p class="help is-danger" v-if="error">{{ $t("error.invalid-auth") }}</p>
</div>
<div class="field is-grouped is-grouped-centered mt-5">
<p class="control">
<button class="button is-primary" type="submit">{{ $t("button.login") }}</button>
</p>
</div>
</form>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
const { t } = useI18n();
setTitle(t("title.login"));
let error = $ref(false);
let username = $ref("");
let password = $ref("");
let form: HTMLFormElement = $ref();
async function onLogin() {
const response = await fetch(`${config.base}/api/validateCredentials`, {
body: new FormData(form),
method: "post",
});
if (response.status == 200) {
error = false;
window.location.href = `${config.base}/`;
} else {
error = true;
}
}
</script>
<route lang="yaml">
meta:
layout: splash
</route>

View File

@@ -1,203 +0,0 @@
<template>
<div>
<section class="section">
<div class="has-underline">
<h2 class="title is-4">{{ $t("settings.about") }}</h2>
</div>
<div>
<span v-html="$t('settings.using-version', { version: currentVersion })"></span>
<div
v-if="hasUpdate"
v-html="$t('settings.update-available', { nextVersion: nextRelease.name, href: nextRelease.html_url })"
></div>
</div>
</section>
<section class="section">
<div class="has-underline">
<h2 class="title is-4">{{ $t("settings.display") }}</h2>
</div>
<div class="item">
<o-switch v-model="smallerScrollbars"> {{ $t("settings.small-scrollbars") }} </o-switch>
</div>
<div class="item">
<o-switch v-model="showTimestamp"> {{ $t("settings.show-timesamps") }} </o-switch>
</div>
<div class="item">
<o-switch v-model="softWrap"> {{ $t("settings.soft-wrap") }}</o-switch>
</div>
<div class="item">
<div class="columns is-vcentered">
<div class="column is-narrow">
<o-field>
<o-dropdown v-model="hourStyle" aria-role="list">
<template #trigger>
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ hourStyle }}</span>
<span class="icon">
<carbon-caret-down />
</span>
</o-button>
</template>
<o-dropdown-item :value="value" aria-role="listitem" v-for="value in ['auto', '12', '24']" :key="value">
<span class="is-capitalized">{{ value }}</span>
</o-dropdown-item>
</o-dropdown>
</o-field>
</div>
<div class="column">
{{ $t("settings.12-24-format") }}
</div>
</div>
</div>
<div class="item">
<div class="columns is-vcentered">
<div class="column is-narrow">
<o-field>
<o-dropdown v-model="size" aria-role="list">
<template #trigger>
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ size }}</span>
<span class="icon">
<carbon-caret-down />
</span>
</o-button>
</template>
<o-dropdown-item
:value="value"
aria-role="listitem"
v-for="value in ['small', 'medium', 'large']"
:key="value"
>
<span class="is-capitalized">{{ value }}</span>
</o-dropdown-item>
</o-dropdown>
</o-field>
</div>
<div class="column">{{ $t("settings.font-size") }}</div>
</div>
</div>
<div class="item">
<div class="columns is-vcentered">
<div class="column is-narrow">
<o-field>
<o-dropdown v-model="lightTheme" aria-role="list">
<template #trigger>
<o-button variant="primary" type="button">
<span class="is-capitalized">{{ lightTheme }}</span>
<span class="icon">
<carbon-caret-down />
</span>
</o-button>
</template>
<o-dropdown-item
:value="value"
aria-role="listitem"
v-for="value in ['auto', 'dark', 'light']"
:key="value"
>
<span class="is-capitalized">{{ value }}</span>
</o-dropdown-item>
</o-dropdown>
</o-field>
</div>
<div class="column">{{ $t("settings.color-scheme") }}</div>
</div>
</div>
</section>
<section class="section">
<div class="has-underline">
<h2 class="title is-4">{{ $t("settings.options") }}</h2>
</div>
<div class="item">
<o-switch v-model="search">
<span v-html="$t('settings.search')"></span>
</o-switch>
</div>
<div class="item">
<o-switch v-model="showAllContainers"> {{ $t("settings.show-stopped-containers") }} </o-switch>
</div>
</section>
</div>
</template>
<script lang="ts" setup>
import gt from "semver/functions/gt";
import {
search,
lightTheme,
smallerScrollbars,
showTimestamp,
hourStyle,
showAllContainers,
size,
softWrap,
} from "@/composables/settings";
const { t } = useI18n();
setTitle(t("title.settings"));
const currentVersion = $ref(config.version);
let nextRelease = $ref({ html_url: "", name: "" });
let hasUpdate = $ref(false);
async function fetchNextRelease() {
if (!["dev", "master"].includes(currentVersion)) {
const response = await fetch("https://api.github.com/repos/amir20/dozzle/releases/latest");
if (response.ok) {
const release = await response.json();
hasUpdate = gt(release.tag_name, currentVersion);
nextRelease = release;
}
} else {
hasUpdate = true;
nextRelease = {
html_url: "",
name: "master",
};
}
}
fetchNextRelease();
</script>
<style lang="scss" scoped>
.title {
color: var(--title-color);
}
a.next-release {
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
.section {
padding: 1rem 1.5rem;
}
.has-underline {
border-bottom: 1px solid var(--border-color);
padding: 1em 0px;
margin-bottom: 1em;
}
.item {
padding: 1em 0;
}
code {
border-radius: 4px;
background-color: #444;
}
</style>

View File

@@ -1,25 +0,0 @@
<script lang="ts" setup>
const router = useRouter();
const route = useRoute();
const store = useContainerStore();
const { visibleContainers } = storeToRefs(store);
watch(visibleContainers, (newValue) => {
if (newValue) {
if (route.query.name) {
const [container, _] = visibleContainers.value.filter((c) => c.name == route.query.name);
if (container) {
router.push({ name: "container-id", params: { id: container.id } });
} else {
console.error(`No containers found matching name=${route.query.name}. Redirecting to /`);
router.push({ name: "index" });
}
} else {
console.error(`Expection query parameter name to be set. Redirecting to /`);
router.push({ name: "index" });
}
}
});
</script>
<template></template>

View File

@@ -1,17 +1,6 @@
<template>
<div>
<section class="hero is-small mt-4">
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column is-narrow" v-if="secured">
<a class="button is-primary is-small" :href="`${base}/logout`">{{ $t("button.logout") }}</a>
</div>
</div>
</div>
</div>
</section>
<section class="level section">
<section class="level section pb-0-is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="title">{{ containers.length }}</p>
@@ -44,8 +33,8 @@
</div>
</section>
<section class="columns is-centered section is-marginless">
<div class="column is-4">
<section class="columns is-centered section is-marginless pt-0-is-mobile">
<div class="column is-12-mobile is-6-tablet is-5-desktop is-4-fullhd">
<div class="panel">
<p class="panel-heading">{{ $t("label.containers") }}</p>
<div class="panel-block">
@@ -164,6 +153,16 @@ function onEnter() {
}
}
@media screen and (max-width: 768px) {
.pb-0-is-mobile {
padding-bottom: 0 !important;
}
.pt-0-is-mobile {
padding-top: 0 !important;
}
}
.icon {
padding: 10px 3px;
}

View File

@@ -1,14 +1,11 @@
import { acceptHMRUpdate, defineStore } from "pinia";
import { ref, Ref, computed } from "vue";
import { showAllContainers } from "@/composables/settings";
import config from "@/stores/config";
import type { Container, ContainerStat } from "@/types/Container";
import { watchOnce } from "@vueuse/core";
import { Ref, UnwrapNestedRefs } from "vue";
import type { ContainerJson, ContainerStat } from "@/types/Container";
import { Container } from "@/models/Container";
export const useContainerStore = defineStore("container", () => {
const containers = ref<Container[]>([]);
const activeContainerIds = ref<string[]>([]);
const containers: Ref<Container[]> = ref([]);
const activeContainerIds: Ref<string[]> = ref([]);
const allContainersById = computed(() =>
containers.value.reduce((acc, container) => {
@@ -25,33 +22,36 @@ 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) => (containers.value = JSON.parse((e as MessageEvent).data)),
false
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];
if (container) {
container.stat = stat;
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";
}
});
const setContainers = (newContainers: ContainerJson[]) => {
containers.value = newContainers.map((c) => {
const existing = allContainersById.value[c.id];
if (existing) {
existing.status = c.status;
existing.state = c.state;
return existing;
}
},
false
);
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";
}
},
false
);
return new Container(c.id, c.created, c.image, c.name, c.command, c.status, c.state);
});
};
const currentContainer = (id: Ref<string>) => computed(() => allContainersById.value[id.value]);
const appendActiveContainer = ({ id }: Container) => activeContainerIds.value.push(id);

View File

@@ -75,10 +75,10 @@ $light-toolbar-color: rgba($grey-darker, 0.7);
--border-color: #{$grey-lighter};
--border-hover-color: var(--secondary-color);
--logo-color: #{$grey-darker};
--logo-color: var(--secondary-color);
--primary-color: #{$turquoise};
--secondary-color: #d8f0ca;
--secondary-color: rgb(249 115 22);
--body-background-color: #{$white-bis};
--action-toolbar-background-color: #{$light-toolbar-color};
@@ -159,6 +159,12 @@ html.has-custom-scrollbars {
}
}
@media screen and (max-device-width: 480px) {
body {
-webkit-text-size-adjust: 100%;
}
}
.splitpanes__splitter {
z-index: 99;
}
@@ -177,6 +183,14 @@ html.has-custom-scrollbars {
display: contents;
}
.has-dropshadow {
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
}
.has-boxshadow {
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}
mark {
border-radius: 2px;
background-color: var(--secondary-color);
@@ -192,3 +206,8 @@ mark {
transform: scale(1.05);
}
}
.button.is-rounded:hover {
color: var(--text-strong-color);
background: var(--scheme-main-ter);
}

View File

@@ -1,17 +1,18 @@
export interface Container {
readonly id: string;
readonly created: number;
readonly image: string;
readonly name: string;
readonly status: string;
readonly command: string;
state: "created" | "running" | "exited" | "dead" | "paused" | "restarting";
stat?: ContainerStat;
}
export interface ContainerStat {
readonly id: string;
readonly cpu: number;
readonly memory: number;
readonly memoryUsage: number;
}
export type ContainerJson = {
readonly id: string;
readonly created: number;
readonly image: string;
readonly name: string;
readonly command: string;
readonly status: string;
readonly state: ContainerState;
};
export type ContainerState = "created" | "running" | "exited" | "dead" | "paused" | "restarting";

1
assets/types/Point.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
type Point<T> = { x: number; y: number; value?: T };

View File

@@ -1,4 +1,4 @@
import { Container } from "@/types/Container";
import { Container } from "@/models/Container";
import { useStorage } from "@vueuse/core";
import { computed, ComputedRef } from "vue";

17
docker/calculation.go Normal file
View File

@@ -0,0 +1,17 @@
package docker
import "github.com/docker/docker/api/types"
func calculateMemUsageUnixNoCache(mem types.MemoryStats) float64 {
// re implementation of the docker calculation
// https://github.com/docker/cli/blob/53f8ed4bec07084db4208f55987a2ea94b7f01d6/cli/command/container/stats_helpers.go#L227-L249
// cgroup v1
if v, isCGroup := mem.Stats["total_inactive_file"]; isCGroup && v < mem.Usage {
return float64(mem.Usage - v)
}
// cgroup v2
if v := mem.Stats["inactive_file"]; v < mem.Usage {
return float64(mem.Usage - v)
}
return float64(mem.Usage)
}

View File

@@ -0,0 +1,57 @@
package docker
import (
"github.com/docker/docker/api/types"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_calculateMemUsageUnixNoCache(t *testing.T) {
type args struct {
mem types.MemoryStats
}
tests := []struct {
name string
args args
want float64
}{
{
name: "with cgroup v1",
args: args{
mem: types.MemoryStats{
Usage: 100,
Stats: map[string]uint64{
"total_inactive_file": 1,
},
},
},
want: 99,
},
{
name: "with cgroup v2",
args: args{
mem: types.MemoryStats{
Usage: 100,
Stats: map[string]uint64{
"inactive_file": 2,
},
},
},
want: 98,
},
{
name: "without cgroup",
args: args{
mem: types.MemoryStats{
Usage: 100,
},
},
want: 100,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, calculateMemUsageUnixNoCache(tt.args.mem), "calculateMemUsageUnixNoCache(%v)", tt.args.mem)
})
}
}

View File

@@ -147,7 +147,7 @@ func (d *dockerClient) ContainerStats(ctx context.Context, id string, stats chan
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(v.PreCPUStats.CPUUsage.TotalUsage)
systemDelta = float64(v.CPUStats.SystemUsage) - float64(v.PreCPUStats.SystemUsage)
cpuPercent = int64((cpuDelta / systemDelta) * float64(ncpus) * 100)
memUsage = int64(v.MemoryStats.Usage - v.MemoryStats.Stats["cache"])
memUsage = int64(calculateMemUsageUnixNoCache(v.MemoryStats))
memPercent = int64(float64(memUsage) / float64(v.MemoryStats.Limit) * 100)
)

View File

@@ -1,4 +1,4 @@
FROM cypress/included:10.8.0
FROM cypress/included:11.2.0
RUN apt install curl && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -6,8 +6,8 @@
},
"license": "ISC",
"dependencies": {
"@frsource/cypress-plugin-visual-regression-diff": "^2.0.0",
"cypress": "^10.8.0",
"typescript": "^4.8.3"
"@frsource/cypress-plugin-visual-regression-diff": "^3.1.2",
"cypress": "^11.0.0",
"typescript": "^4.8.4"
}
}

127
e2e/pnpm-lock.yaml generated
View File

@@ -1,14 +1,14 @@
lockfileVersion: 5.4
specifiers:
'@frsource/cypress-plugin-visual-regression-diff': ^2.0.0
cypress: ^10.8.0
typescript: ^4.8.3
'@frsource/cypress-plugin-visual-regression-diff': ^3.1.2
cypress: ^11.0.0
typescript: ^4.8.4
dependencies:
'@frsource/cypress-plugin-visual-regression-diff': 2.0.0_cypress@10.8.0
cypress: 10.8.0
typescript: 4.8.3
'@frsource/cypress-plugin-visual-regression-diff': 3.1.2_cypress@11.0.0
cypress: 11.0.0
typescript: 4.8.4
packages:
@@ -52,21 +52,28 @@ packages:
- supports-color
dev: false
/@frsource/cypress-plugin-visual-regression-diff/2.0.0_cypress@10.8.0:
resolution: {integrity: sha512-hwMEZzD0tFbNNNbEjdF8q2ELpxhby25VQ665QUJy1soGPiGwY+rBDdktFg3EXVGs/Euq0Nj8hq/K18BWYoX10g==}
/@frsource/base64/1.0.4:
resolution: {integrity: sha512-IphM1ro1cvV5CqJWzX/LvPJcUE26cwgt/zMtBdssvTrfSNhh9KjfS2VXTA8SivkvPHK1DsdPnoMoXitmx8c3Cg==}
dev: false
/@frsource/cypress-plugin-visual-regression-diff/3.1.2_cypress@11.0.0:
resolution: {integrity: sha512-Tl04uSoyRyt/mUBEWnU49ZOYtQNRJIel6DwrBADGfohgQ81AGBTbO7yXOhVqIrUZ/PIJaAJG2J2vw6MDw1ObmA==}
engines: {node: '>=10'}
peerDependencies:
cypress: '>=4.5.0'
dependencies:
cypress: 10.8.0
'@frsource/base64': 1.0.4
cypress: 11.0.0
glob: 8.0.3
meta-png: 1.0.3
move-file: 2.1.0
pixelmatch: 5.3.0
pngjs: 6.0.0
sharp: 0.30.7
sharp: 0.31.2
dev: false
/@types/node/14.18.29:
resolution: {integrity: sha512-LhF+9fbIX4iPzhsRLpK5H7iPdvW8L4IwGciXQIOEcuF62+9nw/VQVsOViAOOGxY3OlOKGLFv0sWwJXdwQeTn6A==}
/@types/node/14.18.33:
resolution: {integrity: sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==}
dev: false
/@types/sinonjs__fake-timers/8.1.1:
@@ -81,7 +88,7 @@ packages:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
dependencies:
'@types/node': 14.18.29
'@types/node': 14.18.33
dev: false
optional: true
@@ -195,6 +202,12 @@ packages:
concat-map: 0.0.1
dev: false
/brace-expansion/2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: false
/buffer-crc32/0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: false
@@ -232,8 +245,8 @@ packages:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: false
/ci-info/3.4.0:
resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==}
/ci-info/3.5.0:
resolution: {integrity: sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==}
dev: false
/clean-stack/2.2.0:
@@ -329,15 +342,15 @@ packages:
which: 2.0.2
dev: false
/cypress/10.8.0:
resolution: {integrity: sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==}
/cypress/11.0.0:
resolution: {integrity: sha512-mYXGi2Wjmy9shRjAUDugSMOr4uuzE2nl7hXQi3oQkIQsnwwBx2HNB8Vbfsix3A0zyPXlL5jTcbb6rCVWKRaXbg==}
engines: {node: '>=12.0.0'}
hasBin: true
requiresBuild: true
dependencies:
'@cypress/request': 2.88.10
'@cypress/xvfb': 1.2.4_supports-color@8.1.1
'@types/node': 14.18.29
'@types/node': 14.18.33
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.3
arch: 2.2.0
@@ -351,7 +364,7 @@ packages:
cli-table3: 0.6.3
commander: 5.1.0
common-tags: 1.8.2
dayjs: 1.11.5
dayjs: 1.11.6
debug: 4.3.4_supports-color@8.1.1
enquirer: 2.3.6
eventemitter2: 6.4.7
@@ -367,12 +380,12 @@ packages:
listr2: 3.14.0_enquirer@2.3.6
lodash: 4.17.21
log-symbols: 4.1.0
minimist: 1.2.6
minimist: 1.2.7
ospath: 1.2.2
pretty-bytes: 5.6.0
proxy-from-env: 1.0.0
request-progress: 3.0.0
semver: 7.3.7
semver: 7.3.8
supports-color: 8.1.1
tmp: 0.2.1
untildify: 4.0.0
@@ -386,8 +399,8 @@ packages:
assert-plus: 1.0.0
dev: false
/dayjs/1.11.5:
resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==}
/dayjs/1.11.6:
resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==}
dev: false
/debug/3.2.7_supports-color@8.1.1:
@@ -598,6 +611,17 @@ packages:
path-is-absolute: 1.0.1
dev: false
/glob/8.0.3:
resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==}
engines: {node: '>=12'}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.0
once: 1.4.0
dev: false
/global-dirs/3.0.0:
resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==}
engines: {node: '>=10'}
@@ -665,7 +689,7 @@ packages:
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
hasBin: true
dependencies:
ci-info: 3.4.0
ci-info: 3.5.0
dev: false
/is-fullwidth-code-point/3.0.0:
@@ -758,7 +782,7 @@ packages:
log-update: 4.0.0
p-map: 4.0.0
rfdc: 1.3.0
rxjs: 7.5.6
rxjs: 7.5.7
through: 2.3.8
wrap-ansi: 7.0.0
dev: false
@@ -800,6 +824,10 @@ packages:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: false
/meta-png/1.0.3:
resolution: {integrity: sha512-ts8SCT3qTvHBA723m1NYUKwzGDxYNCkMrHYrX0t7YaIch8giqpX+KyJEpCUFW7XfCWXiX5KSlQr4p3Ci9lrSug==}
dev: false
/mime-db/1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -828,8 +856,15 @@ packages:
brace-expansion: 1.1.11
dev: false
/minimist/1.2.6:
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
/minimatch/5.1.0:
resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: false
/minimist/1.2.7:
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
dev: false
/mkdirp-classic/0.5.3:
@@ -855,11 +890,11 @@ packages:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: false
/node-abi/3.24.0:
resolution: {integrity: sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==}
/node-abi/3.28.0:
resolution: {integrity: sha512-fRlDb4I0eLcQeUvGq7IY3xHrSb0c9ummdvDSYWfT9+LKP+3jCKw/tKoqaM7r1BAoiAC6GtwyjaGnOz6B3OtF+A==}
engines: {node: '>=10'}
dependencies:
semver: 7.3.7
semver: 7.3.8
dev: false
/node-addon-api/5.0.0:
@@ -945,10 +980,10 @@ packages:
detect-libc: 2.0.1
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.6
minimist: 1.2.7
mkdirp-classic: 0.5.3
napi-build-utils: 1.0.2
node-abi: 3.24.0
node-abi: 3.28.0
pump: 3.0.0
rc: 1.2.8
simple-get: 4.0.1
@@ -992,7 +1027,7 @@ packages:
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
minimist: 1.2.6
minimist: 1.2.7
strip-json-comments: 2.0.1
dev: false
@@ -1030,10 +1065,10 @@ packages:
glob: 7.2.3
dev: false
/rxjs/7.5.6:
resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==}
/rxjs/7.5.7:
resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==}
dependencies:
tslib: 2.4.0
tslib: 2.4.1
dev: false
/safe-buffer/5.2.1:
@@ -1044,24 +1079,24 @@ packages:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/semver/7.3.7:
resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==}
/semver/7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: false
/sharp/0.30.7:
resolution: {integrity: sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==}
engines: {node: '>=12.13.0'}
/sharp/0.31.2:
resolution: {integrity: sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==}
engines: {node: '>=14.15.0'}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.1
node-addon-api: 5.0.0
prebuild-install: 7.1.1
semver: 7.3.7
semver: 7.3.8
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
@@ -1224,8 +1259,8 @@ packages:
punycode: 2.1.1
dev: false
/tslib/2.4.0:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
/tslib/2.4.1:
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
dev: false
/tunnel-agent/0.6.0:
@@ -1243,8 +1278,8 @@ packages:
engines: {node: '>=10'}
dev: false
/typescript/4.8.3:
resolution: {integrity: sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==}
/typescript/4.8.4:
resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: false

10
go.mod
View File

@@ -5,7 +5,7 @@ require (
github.com/alexflint/go-arg v1.4.3
github.com/beme/abide v0.0.0-20190723115211-635a09831760
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v20.10.18+incompatible
github.com/docker/docker v20.10.21+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dustin/go-humanize v1.0.0
@@ -19,9 +19,9 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.9.0
github.com/spf13/afero v1.9.2
github.com/stretchr/objx v0.4.0 // indirect
github.com/stretchr/testify v1.8.0
github.com/spf13/afero v1.9.3
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
gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -40,4 +40,4 @@ require (
gotest.tools/v3 v3.0.3 // indirect
)
go 1.18
go 1.19

12
go.sum
View File

@@ -62,8 +62,8 @@ 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.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog=
github.com/docker/docker v20.10.21+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=
@@ -183,17 +183,21 @@ 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.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
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/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 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
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/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=

49
locales/ru.yml Normal file
View File

@@ -0,0 +1,49 @@
toolbar:
clear: Очистить
download: Скачать
search: Поиск
label:
containers: Контейнеры
total-containers: Всего Контейнеров
running: Запущенные
total-cpu-usage: Использование процессора
total-mem-usage: Использование памяти
dozzle-version: Версия Dozzle
all: Все
password: Пароль
username: Имя пользователя
tooltip:
search: Поиск контейнеров (⌘ + k, ⌃k)
pin-column: Закрепить столбец
error:
page-not-found: Эта страница не доступна.
invalid-auth: Имя пользователя или пароль неверны.
logs-skipped: Пропущено {total} записей
title:
page-not-found: Страница не найдена
login: Требуется авторизация
settings: Настройки
button:
logout: Выйти
login: Войти
placeholder:
search-containers: Поиск контейнеров (⌘ + k, ⌃k)
settings:
display: Вид
small-scrollbars: Уменьшенная полоса прокрутки
show-timesamps: Показывать временные метки
soft-wrap: Плавный перенос текста
12-24-format: >-
Формат времени. По умолчанию, Dozzle будет использовать локализацию вашего браузера.
font-size: Размер шрифта
color-scheme: Цветовая схема
options: Опции
show-stopped-containers: Показывать остановленные контейнеры
about: Информация
search: >-
Включить поиск с помощью Dozzle, используя <code>command+f</code> или
<code>ctrl+f</code>
using-version: Вы используете версию Dozzle {version}.
update-available: >-
Доступна новая версия! Обновить до <a :href="{href}" class="next-release"
target="_blank" rel="noreferrer noopener">{nextVersion}</a>.

20
main.go
View File

@@ -24,6 +24,16 @@ var (
version = "head"
)
type DockerSecret struct {
Value string
}
func (s *DockerSecret) UnmarshalText(b []byte) error {
v, err := os.ReadFile(string(b))
s.Value = strings.Trim(string(v), "\r\n")
return err
}
type args struct {
Addr string `arg:"env:DOZZLE_ADDR" default:":8080" help:"sets host:port to bind for server. This is rarely needed inside a docker container."`
Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`
@@ -31,6 +41,8 @@ type args struct {
TailSize int `arg:"env:DOZZLE_TAILSIZE" default:"300" help:"update the initial tail size when fetching logs."`
Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username for auth."`
Password string `arg:"env:DOZZLE_PASSWORD" help:"sets password for auth"`
UsernameFile *DockerSecret `arg:"env:DOZZLE_USERNAME_FILE" help:"sets the secret path read username for auth."`
PasswordFile *DockerSecret `arg:"env:DOZZLE_PASSWORD_FILE" help:"sets the secret path read password for auth"`
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS" help:"disables anonymous analytics"`
WaitForDockerSeconds int `arg:"--wait-for-docker-seconds,env:DOZZLE_WAIT_FOR_DOCKER_SECONDS" help:"wait for docker to be available for at most this many seconds before starting the server."`
FilterStrings []string `arg:"env:DOZZLE_FILTER,--filter,separate" help:"filters docker containers using Docker syntax."`
@@ -93,6 +105,14 @@ func main() {
}
}
if args.Username == "" && args.UsernameFile != nil {
args.Username = args.UsernameFile.Value
}
if args.Password == "" && args.PasswordFile != nil {
args.Password = args.PasswordFile.Value
}
if args.Username != "" || args.Password != "" {
if args.Username == "" || args.Password == "" {
log.Fatalf("Username AND password are required for authentication")

View File

@@ -1,6 +1,6 @@
{
"name": "dozzle",
"version": "4.1.8",
"version": "4.4.1",
"description": "Realtime log viewer for docker containers. ",
"homepage": "https://github.com/amir20/dozzle#readme",
"bugs": {
@@ -22,57 +22,69 @@
"postinstall": "husky install"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.8",
"@iconify-json/carbon": "^1.1.10",
"@iconify-json/cil": "^1.1.2",
"@iconify-json/mdi": "^1.1.33",
"@iconify-json/mdi": "^1.1.34",
"@iconify-json/mdi-light": "^1.1.2",
"@iconify-json/octicon": "^1.1.19",
"@intlify/vite-plugin-vue-i18n": "^6.0.2",
"@oruga-ui/oruga-next": "^0.5.6",
"@iconify-json/octicon": "^1.1.23",
"@oruga-ui/oruga-next": "^0.5.8",
"@oruga-ui/theme-bulma": "^0.2.7",
"@vitejs/plugin-vue": "3.1.0",
"@vue/compiler-sfc": "^3.2.39",
"@vueuse/core": "^9.3.0",
"@vueuse/integrations": "^9.3.0",
"@vueuse/router": "^9.3.0",
"@vueuse/core": "^9.5.0",
"@vueuse/integrations": "^9.5.0",
"@vueuse/router": "^9.5.0",
"ansi-to-html": "^0.7.2",
"bulma": "^0.9.4",
"d3-array": "^3.2.0",
"d3-ease": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-shape": "^3.1.0",
"d3-transition": "^3.0.1",
"date-fns": "^2.29.3",
"fuse.js": "^6.6.2",
"lodash.debounce": "^4.0.8",
"pinia": "^2.0.22",
"sass": "^1.55.0",
"semver": "^7.3.7",
"splitpanes": "^3.1.1",
"typescript": "^4.8.3",
"unplugin-auto-import": "^0.11.2",
"unplugin-icons": "^0.14.10",
"unplugin-vue-components": "^0.22.7",
"vite": "3.1.3",
"vite-plugin-pages": "^0.26.0",
"vite-plugin-vue-layouts": "^0.7.0",
"vue": "^3.2.39",
"pinia": "^2.0.23",
"semver": "^7.3.8",
"splitpanes": "^3.1.5",
"vue": "^3.2.45",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.5"
"vue-router": "^4.1.6"
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "^6.0.3",
"@pinia/testing": "^0.0.14",
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-scale": "^4.0.2",
"@types/d3-selection": "^3.0.3",
"@types/d3-shape": "^3.1.0",
"@types/d3-transition": "^3.0.2",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.7.22",
"@types/semver": "^7.3.12",
"@vue/test-utils": "^2.0.2",
"@types/node": "^18.11.9",
"@types/semver": "^7.3.13",
"@vitejs/plugin-vue": "3.2.0",
"@vue/compiler-sfc": "^3.2.45",
"@vue/test-utils": "^2.2.2",
"c8": "^7.12.0",
"eventsourcemock": "^2.0.0",
"husky": "^8.0.1",
"jest-serializer-vue": "^2.0.2",
"jsdom": "^20.0.0",
"husky": "^8.0.2",
"jest-serializer-vue": "^3.0.0",
"jsdom": "^20.0.2",
"lint-staged": "^13.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"release-it": "^15.4.2",
"release-it": "^15.5.0",
"sass": "^1.56.1",
"ts-node": "^10.9.1",
"vitest": "^0.23.4",
"vue-tsc": "^0.40.13"
"typescript": "^4.8.4",
"unplugin-auto-import": "^0.12.0",
"unplugin-icons": "^0.14.13",
"unplugin-vue-components": "^0.22.9",
"vite": "3.2.4",
"vite-plugin-pages": "^0.27.1",
"vite-plugin-vue-layouts": "^0.7.0",
"vitest": "^0.25.2",
"vue-tsc": "^1.0.9"
},
"lint-staged": {
"*.{js,vue,css}": [

1849
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

6
renovate.json Normal file
View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}