Compare commits

...

256 Commits

Author SHA1 Message Date
Amir Raminfar 28c2cdc571 chore: release v6.2.3 2024-02-09 11:13:20 -08:00
Amir Raminfar d67f2da1ba fix: uses github.com/goccy/go-json for less CPU (#2766) 2024-02-09 11:11:20 -08:00
Ahmet a99672a5da chore: adjust supported env variables in readme (#2768) 2024-02-08 23:18:09 +00:00
renovate[bot] eefa472516 fix(deps): update all non-major dependencies (#2764)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 18:58:12 +00:00
Amir Raminfar 11cde3d5fc chore: fixes improvements with go1.22 2024-02-07 12:33:28 -08:00
Amir Raminfar 935d76e539 chore: release v6.2.2 2024-02-07 11:20:47 -08:00
Amir Raminfar 2497d62fe2 chore: fixes tests 2024-02-07 11:13:33 -08:00
Amir Raminfar 7103cc3c5c fix: fixes initital load not being ready yet (#2763) 2024-02-07 18:53:19 +00:00
Amir Raminfar cd877c910c fix: fixes a possible race condition (#2762) 2024-02-07 17:48:48 +00:00
renovate[bot] 946f59e603 fix(deps): update all non-major dependencies (#2758)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 16:29:05 +00:00
Amir Raminfar a03e5d10aa chore: disables static check (#2760) 2024-02-07 16:24:02 +00:00
renovate[bot] 183326db52 fix(deps): update all non-major dependencies (#2754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 01:57:41 +00:00
Amir Raminfar 0ca3058f7b chore: release v6.2.1 2024-02-04 09:03:07 -08:00
Amir Raminfar 9c7ccc0a59 fix: stops streaming of stats for new containers when stopped (#2753) 2024-02-04 16:28:39 +00:00
Amir Raminfar 85d989450d chore: uses xsync package for maps (#2752) 2024-02-04 16:25:05 +00:00
Amir Raminfar 3f81f355bb chore: release v6.2.0 2024-02-03 14:08:26 -08:00
Amir Raminfar 25cb3c4c53 chore: adds tests for store (#2751) 2024-02-03 09:06:05 -08:00
Amir Raminfar 1d4c162fba Revert "chore: updates tests"
This reverts commit 19a83b7b62.
2024-02-02 16:48:58 -08:00
Amir Raminfar 19a83b7b62 chore: updates tests 2024-02-02 16:20:08 -08:00
Amir Raminfar 454156ae2c fix: fixes a regression bug in map. fixes #2749 (#2750) 2024-02-02 15:54:00 -08:00
EDIflyer 2d8b263df4 fix: reword message on similar container switch countdown banner (#2748) 2024-02-02 14:42:39 -08:00
Amir Raminfar fc8b67a902 fix: fixes race issues (#2747) 2024-02-02 13:56:48 -08:00
Amir Raminfar b8869784d4 fix: fixes a potential deadlock when reading stats (#2745) 2024-02-02 09:57:06 -08:00
Amir Raminfar 099c053341 feat: sorts most recent containers on top for fuzzy search (#2746) 2024-02-02 09:41:26 -08:00
Amir Raminfar bd86b2289b feat: updates labels between running and all containers (#2742) 2024-02-01 22:07:16 +00:00
Amir Raminfar 8677a34087 feat: collects all stats like cpu and mem in background for up to 5 minutes (#2740) 2024-02-01 12:43:44 -08:00
renovate[bot] 2c398cc227 chore(deps): update all non-major dependencies (#2741)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-01 20:29:39 +00:00
Amir Raminfar bb0ca30b81 feat: search textbox is now draggable (#2737) 2024-01-30 17:22:58 +00:00
renovate[bot] f9eb8186d1 chore(deps): update dependency @types/node to ^20.11.10 (#2735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 00:16:05 +00:00
Amir Raminfar d767be3757 chore: updates go modules (#2734) 2024-01-28 19:06:49 +00:00
renovate[bot] 86f9839211 chore(deps): update all non-major dependencies (#2733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-27 18:52:22 +00:00
Amir Raminfar 67f25b7bb0 chore: renames cts to cjs (#2732) 2024-01-25 10:03:46 -08:00
Amir Raminfar 025ce28134 chore: release v6.1.1 2024-01-24 10:27:28 -08:00
renovate[bot] 25fced7bb9 chore(deps): update all non-major dependencies (#2731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 17:52:53 +00:00
Amir Raminfar ef636c86fc fix: removes deprecated code (#2730) 2024-01-24 17:48:29 +00:00
Amir Raminfar 808abd57bb fix: improves logfmt (#2729) 2024-01-23 13:08:03 -08:00
Amir Raminfar d07dc2e983 fix: fixes cpu on popup being too long (#2728) 2024-01-23 09:01:15 -08:00
renovate[bot] 099f778ca2 fix(deps): update all non-major dependencies (#2724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 16:32:02 +00:00
renovate[bot] 9a760a2738 chore(deps): update dependency jsdom to v24 (#2723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 08:23:33 -08:00
renovate[bot] b53c6c8275 fix(deps): update dependency @iconify-json/carbon to ^1.1.28 (#2722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 00:46:05 +00:00
renovate[bot] 8dbe9a5774 chore(deps): update all non-major dependencies (#2720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-20 22:33:32 +00:00
Amir Raminfar 5d28917d11 fix(deps): update module github.com/docker/docker to v25 (#2719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-19 14:43:18 -08:00
renovate[bot] 586d717aff fix(deps): update all non-major dependencies (#2717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-19 16:32:24 +00:00
Amir Raminfar 8aba7debe8 chore: release v6.1.0 2024-01-18 09:11:38 -08:00
renovate[bot] c6647133dd chore(deps): update all non-major dependencies to ^3.4.15 (#2713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-18 17:08:47 +00:00
Amir Raminfar 82765f4b68 feat: adds support for compact mode. fixes #2710 (#2712) 2024-01-18 17:04:58 +00:00
Amir Raminfar 0c33d0f921 feat: defaults locale to auto (#2711) 2024-01-18 08:56:38 -08:00
Amir Raminfar 89fe9e6b91 feat: adds a dropdown to switch language locale (#2708) 2024-01-18 01:22:06 +00:00
Amir Raminfar ef359d339c feat: adds support for LogFmt. fixes #2695 (#2705) 2024-01-17 14:01:40 -08:00
Amir Raminfar 9d2a8af435 feat: makes button for page size smaller (#2707) 2024-01-17 14:01:18 -08:00
renovate[bot] 6f96303f1d chore(deps): update all non-major dependencies (#2709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-17 21:54:37 +00:00
Amir Raminfar 48d7109079 Revert "feat: adds support for LogFmt. fixes #2695"
This reverts commit ccd7f0dd86.
2024-01-16 13:39:04 -08:00
Amir Raminfar ccd7f0dd86 feat: adds support for LogFmt. fixes #2695 2024-01-16 13:30:57 -08:00
Amir Raminfar 390d1a3062 chore: release v6.0.8 2024-01-15 10:39:24 -08:00
Amir Raminfar c6c2c13404 feat: adds option to home page to show more than default containers per page (#2703) 2024-01-15 18:38:21 +00:00
renovate[bot] f80dd56bdf chore(deps): update all non-major dependencies (#2702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-15 02:22:22 +00:00
Amir Raminfar 89babeab9a chore: moves warn to debug for releases 2024-01-12 11:37:38 -08:00
Feriman 9abab4ce77 read only docker.sock (#2698) 2024-01-12 11:35:49 -08:00
Feriman 19c3daf2f7 read only docker.sock (#2699) 2024-01-12 11:35:31 -08:00
AmadeusGraves 04fb4f3fa4 Update locale es.yml (#2690) 2024-01-11 05:29:01 -08:00
Amir Raminfar c5680a9b3f chore: makes faked data better. (#2689) 2024-01-10 13:42:23 -08:00
Amir Raminfar 5cd6fc1243 chore: release v6.0.7 2024-01-10 11:26:24 -08:00
Amir Raminfar 1e2ce61991 fix: fixes title bar alignment to base of text (#2688) 2024-01-10 11:23:53 -08:00
renovate[bot] b5e78ae979 fix(deps): update all non-major dependencies (#2687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 19:15:56 +00:00
Amir Raminfar f7a36483f9 feat: moves search box higher. fixes #2679 (#2684) 2024-01-10 11:10:24 -08:00
Amir Raminfar cc6ae1d63c fix: fixes 24 hour style being broken on chrome. fixes #2680 (#2685) 2024-01-10 11:10:10 -08:00
Amir Raminfar 9cb973f30e fix: moves cache for releases to API. fixes #2681 (#2686) 2024-01-10 11:07:09 -08:00
renovate[bot] c98bcd3fcf chore(deps): update all non-major dependencies (#2677)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 00:18:05 +00:00
Amir Raminfar b0cc0a0377 chore: release v6.0.6 2024-01-09 14:09:34 -08:00
Amir Raminfar 3a705f2de0 fix: fixes title container stats in small screen (#2678) 2024-01-09 14:09:16 -08:00
Amir Raminfar e1d38a3582 fix: fixes stats on windows (#2675) 2024-01-09 13:03:30 -08:00
Amir Raminfar da24ca249a chore: enables container queries (#2676) 2024-01-09 11:49:56 -08:00
Amir Raminfar 6ca33b5a62 feat: alt-click on side menu opens a new panel (#2674) 2024-01-09 10:25:54 -08:00
Amir Raminfar 9a431dbcb0 fix: adds long message for preview (#2672) 2024-01-08 15:53:02 -08:00
Amir Raminfar 3b5370fbae chore: release v6.0.5 2024-01-08 14:40:09 -08:00
Amir Raminfar 8b183e98ff feat: allows for date format to be overriden with locale. fixes #2665 (#2671) 2024-01-08 14:36:08 -08:00
renovate[bot] 7038bb35b2 chore(deps): update all non-major dependencies (#2670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-08 20:25:25 +00:00
Amir Raminfar 72a580574a feat: adds preview for settings (#2669) 2024-01-08 12:21:37 -08:00
renovate[bot] 27fe0663cf chore(deps): update all non-major dependencies (#2668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-08 01:20:43 +00:00
Amir Raminfar 00cb7a6018 chore: release v6.0.4 2024-01-07 14:21:56 -08:00
Amir Raminfar 89b763af59 feat: if password hash is != 64 then error is displayed. (#2667) 2024-01-07 22:17:05 +00:00
Amir Raminfar 71d76e9228 chore: release v6.0.3 2024-01-06 09:00:43 -08:00
renovate[bot] d8d7c9fd4f fix(deps): update all non-major dependencies (#2663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-06 16:32:46 +00:00
Amir Raminfar f87d3bbe3e chore: improves icon size (#2662) 2024-01-06 16:29:13 +00:00
renovate[bot] de88a25bcc chore(deps): update all non-major dependencies (#2661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-06 03:31:24 +00:00
Amir Raminfar fc93e2d33c chore: adds more tests (#2660) 2024-01-05 19:28:02 -08:00
Amir Raminfar c1eb699dc6 feat: moves settings to top of the home page (#2659) 2024-01-06 01:56:29 +00:00
Amir Raminfar 77517a76da chore: release v6.0.2 2024-01-05 13:07:04 -08:00
Amir Raminfar aac38d954d fix: fixes broken enter on fuzy dialog (#2658) 2024-01-05 21:01:21 +00:00
renovate[bot] d5724d2652 fix(deps): update all non-major dependencies (#2657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-05 20:50:50 +00:00
Amir Raminfar eafd2f144f fix: improves performance for fuzzy search dialog (#2656) 2024-01-05 12:47:07 -08:00
Amir Raminfar 589cad54d9 fix: makes minor improvements to fuzzy search dialog (#2653) 2024-01-04 15:12:47 -08:00
renovate[bot] 3a8054f68b chore(deps): update all non-major dependencies to ^3.4.5 (#2652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-04 15:52:20 +00:00
Amir Raminfar a4026e16e8 feat: adds labels to fuzzy search dialog (#2649) 2024-01-04 07:47:51 -08:00
Amir Raminfar ee4f94ce2e fix: fixes z-index on scroll progress component (#2651) 2024-01-03 23:16:05 +00:00
renovate[bot] 942f05e8d8 chore(deps): update pnpm to v8.14.0 (#2650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-03 21:33:11 +00:00
Amir Raminfar cc67ee94f5 chore: updates modules 2024-01-03 11:57:58 -08:00
Amir Raminfar f4dd232259 chore: release v6.0.1 2024-01-02 14:18:41 -08:00
Amir Raminfar a730eb9660 chore: fixes docs 2024-01-02 14:14:14 -08:00
Amir Raminfar f43f4d4ee4 chore: cleans up docs for env vars 2024-01-02 14:08:31 -08:00
Amir Raminfar bcb3c326e8 docs: swamp sections for authentication docs 2024-01-02 13:58:55 -08:00
Amir Raminfar 3e72a7c2ff fix: fixes authentication documentation 2024-01-02 06:57:02 -08:00
Amir Raminfar 78eceb2237 chore: fixes typo 2024-01-01 15:20:51 -08:00
Amir Raminfar ef5954d7cf feat: caches releases for up to an hour (#2646) 2024-01-01 15:11:59 -08:00
Amir Raminfar 66b9f8ab45 chore: release v6.0.0 2024-01-01 13:38:38 -08:00
Amir Raminfar 45cf78c256 fix: removes extra slash for healthcheck. Fixes #2643 (#2645) 2024-01-01 13:37:00 -08:00
Ankit R Gadiya ed43889e3b feat: add support for custom headers for forward-proxy auth (#2642) 2023-12-31 20:59:14 -08:00
renovate[bot] 77789aca73 chore(deps): update all non-major dependencies (#2641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-01 00:14:57 +00:00
Amir Raminfar 6eb356aaa9 feat!: removes legacy authentication model (#2633) 2023-12-31 22:05:41 +00:00
Amir Raminfar cb6adf275d fix: adds timestamp to logs 2023-12-31 08:09:47 -08:00
Amir Raminfar 4bd66601cb docs: removes custom content link in docs 2023-12-30 18:53:00 -08:00
Amir Raminfar 98bf45c5bc feat!: removes custom content help support (#2628) 2023-12-30 14:18:14 -08:00
renovate[bot] 943cfab0e5 chore(deps): update all non-major dependencies (#2638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-30 17:07:06 +00:00
Amir Raminfar 1835404376 chore: cleans up snapshot 2023-12-29 19:16:13 -08:00
renovate[bot] 38c84710f1 chore(deps): update all non-major dependencies (#2637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-30 03:14:27 +00:00
renovate[bot] e73ae6cbb7 chore(deps): update dependency @vitejs/plugin-vue to v5.0.1 (#2635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-29 21:05:26 +00:00
Amir Raminfar 45fa933f12 test: add e2e tests for simple auth mode (#2634) 2023-12-29 17:29:15 +00:00
Amir Raminfar feaf9c35d4 chore: removes ref-macros 2023-12-29 08:22:32 -08:00
renovate[bot] b7dba3851d chore(deps): update all non-major dependencies (#2632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-29 16:10:55 +00:00
Amir Raminfar 86bdf747da chore: release v5.8.1 2023-12-28 13:54:23 -08:00
Amir Raminfar 06bf8a312f feat: displays warning for legacy auth (#2631) 2023-12-28 13:51:05 -08:00
Amir Raminfar 48b0a64c7a chore: release v5.8.0 2023-12-28 07:30:44 -08:00
Amir Raminfar 0ab9c730c7 feat!: strips all ansi colors and disables showing any colors (#2626) 2023-12-28 07:27:52 -08:00
renovate[bot] 15fc01d265 chore(deps): update all non-major dependencies (#2629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-28 04:51:26 +00:00
Amir Raminfar 6a4c384eb6 Revert "feat!: removes custom content help support"
This reverts commit 904b2a4f89.
2023-12-27 20:41:58 -08:00
Amir Raminfar 904b2a4f89 feat!: removes custom content help support 2023-12-27 19:08:04 -08:00
renovate[bot] a65dcbadb7 chore(deps): update dependency @vitejs/plugin-vue to v5 (#2625) 2023-12-26 21:15:40 +00:00
renovate[bot] 5ef251d021 chore(deps): update all non-major dependencies (#2624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-26 19:23:16 +00:00
Javadz 681d286f5c docs: Update authentication.md (#2623) 2023-12-25 06:32:08 -08:00
renovate[bot] bfa33f0c35 fix(deps): update dependency daisyui to ^4.4.24 (#2622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-25 01:15:56 +00:00
Akash Ramaswamy 7a0fc84842 fix: copy log ansi character parsing and html decode (#2621) 2023-12-23 06:48:22 -08:00
renovate[bot] 9e5b6a88e2 fix(deps): update all non-major dependencies (#2620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-22 23:08:38 +00:00
renovate[bot] 3402b050d5 fix(deps): update all non-major dependencies (#2618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 16:29:06 +00:00
Amir Raminfar 1d2e97b513 chore: updates go modules 2023-12-20 18:59:05 -08:00
Amir Raminfar a8c9237c72 fix: improves padding for mobile (#2616) 2023-12-20 22:42:30 +00:00
Amir Raminfar 3a214590be chore: uses size-x instead of w-x h-x (#2615) 2023-12-20 11:56:48 -08:00
renovate[bot] f46900ec28 fix(deps): update dependency @intlify/unplugin-vue-i18n to v2 (#2613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 09:47:42 -08:00
renovate[bot] b1068fdaf8 fix(deps): update all non-major dependencies (#2612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 05:06:26 +00:00
Amir Raminfar c88c1ed643 fix: fixes login page height by using dynamic dvh (#2611) 2023-12-19 20:45:40 -08:00
renovate[bot] 56cb2ab346 chore(deps): update actions/deploy-pages action to v4 (#2609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-19 17:20:49 -08:00
renovate[bot] b9f6828703 chore(deps): update actions/upload-pages-artifact action to v3 (#2610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-19 17:20:40 -08:00
renovate[bot] 0ed8192cae fix(deps): update all non-major dependencies (#2608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 01:02:30 +00:00
Amir Raminfar aea1b67a7f chore: updates date-fns (#2607) 2023-12-19 16:37:36 +00:00
renovate[bot] 0def791638 fix(deps): update all non-major dependencies (#2605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-19 16:33:26 +00:00
Amir Raminfar f84013ffb8 chore: release v5.7.3 2023-12-18 08:24:07 -08:00
Amir Raminfar 0db179c312 fix: uses username instead of email to validate remote user proxy. see #2598 (#2604) 2023-12-18 08:21:56 -08:00
renovate[bot] 5a4432244c chore(deps): update dependency @types/node to ^20.10.5 (#2603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 01:29:58 +00:00
Amir Raminfar 6b09fc4ab7 fix: escapes avatar url correctly. see #2601 (#2602) 2023-12-17 14:26:12 -08:00
Amir Raminfar 0cf728473b chore: release v5.7.2 2023-12-16 14:10:20 -08:00
renovate[bot] 729b1d9ce2 chore(deps): update all non-major dependencies (#2600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-16 22:07:09 +00:00
Amir Raminfar 4b658fbf1c fix: proxies avatars which should fix #2591 and remove all CSP errors (#2599) 2023-12-16 14:01:53 -08:00
Amir Raminfar 83746fdce0 chore: release v5.7.1 2023-12-15 10:18:56 -08:00
renovate[bot] 8778d67174 fix(deps): update all non-major dependencies (#2595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-15 18:16:26 +00:00
renovate[bot] becfc9e8d5 chore(deps): update actions/upload-artifact action to v4 (#2596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-15 10:12:58 -08:00
Amir Raminfar f332b5c9b5 chore: updates icons 2023-12-15 10:12:24 -08:00
Amir Raminfar 0f3093ae61 fix: generates an avatar even if email is missing (#2594) 2023-12-14 13:11:13 -08:00
Amir Raminfar 7306a4eeb1 feat: updates users if users.yml is changed without needing to restart dozzle (#2593) 2023-12-14 13:05:53 -08:00
Amir Raminfar 937638dce5 chore: fixes component file (#2588) 2023-12-11 14:29:06 -08:00
renovate[bot] 21078e79e8 chore(deps): update all non-major dependencies (#2586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-11 01:23:06 +00:00
EDIflyer 1c8b9f50d7 docs: Update supported-env-vars.md (#2585) 2023-12-10 13:39:44 -08:00
Amir Raminfar 9cd388642c chore: release v5.7.0 2023-12-10 13:19:36 -08:00
Amir Raminfar 3bd97f98b2 fix: disables hover on mobile (#2583) 2023-12-10 13:13:55 -08:00
Akash Ramaswamy d776777b7e feat: Moves Jump-To-Context right (#2584) 2023-12-10 13:13:29 -08:00
Amir Raminfar 8baa5021a6 feat: adds some translations (#2582) 2023-12-09 22:40:11 +00:00
renovate[bot] 65d464541e fix(deps): update all non-major dependencies (#2581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-09 22:06:29 +00:00
Akash Ramaswamy 5d883c0825 fix: actions on log UI issues (#2580) 2023-12-09 14:02:24 -08:00
Akash Ramaswamy e6fa8ced58 feat: actions on log messages (#2572) 2023-12-09 05:56:51 -08:00
Gregory D. Estes cdf9005b6f docs: Update supported-env-vars.md (#2578) 2023-12-08 18:16:12 -08:00
renovate[bot] 89aa10d4b7 fix(deps): update all non-major dependencies (#2577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-08 22:12:09 +00:00
renovate[bot] caa68df169 chore(deps): update actions/setup-go action to v5 (#2574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 11:30:21 -08:00
renovate[bot] e2230d5adc fix(deps): update all non-major dependencies (#2573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 18:32:10 +00:00
Amir Raminfar 2fbb65c921 fix: fixes profile disk configuration when pinned is an empty set (#2570) 2023-12-07 08:05:19 -08:00
Amir Raminfar 185538b794 test: fixes test snapshot and updates vitest (#2569) 2023-12-05 23:00:29 +00:00
renovate[bot] 3bb2a509f2 chore(deps): update actions/configure-pages action to v4 (#2566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 14:58:47 -08:00
renovate[bot] cf43d36500 chore(deps): update actions/deploy-pages action to v3 (#2567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 14:58:36 -08:00
renovate[bot] 7819b3fec3 chore(deps): update all non-major dependencies (#2565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-05 21:33:35 +00:00
Amir Raminfar 0d6c9305c7 test: fixes snapshots (#2564) 2023-12-05 08:09:02 -08:00
ab623 24f298b94d fix: Updates to sidebar on mobile to align closer to desktop (#2562) 2023-12-05 07:53:10 -08:00
Akash Ramaswamy f240b7011b test: added test cases for container actions (#2559) 2023-12-05 07:48:37 -08:00
renovate[bot] f027c753c1 fix(deps): update all non-major dependencies (#2560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 02:26:54 +00:00
Amir Raminfar 5c5b4bb622 chore: release v5.6.1 2023-12-01 11:27:57 -08:00
Amir Raminfar 7da60edb1a chore(analytics): removes GA and only pings with beacon. also adds support actions (#2557) 2023-12-01 18:26:41 +00:00
ab623 23fbf361ce fix: Icon placement when hovering over containers with health checks. (#2556) 2023-12-01 10:13:18 -08:00
Amir Raminfar 6dab94d3b4 fix: changes --enableactions to --enable-actions (#2554) 2023-12-01 06:32:49 -08:00
Amir Raminfar fd5ed091d5 fix: moves favorite star to the left and healthcheck to the right of container name (#2555) 2023-12-01 06:31:26 -08:00
Akash Ramaswamy 3d198bf6c1 docs: actions feature (#2552) 2023-11-30 16:20:55 -08:00
Amir Raminfar 4806b63ea8 chore: release v5.6.0 2023-11-30 15:19:49 -08:00
renovate[bot] c2ee3d50c9 chore(deps): update all non-major dependencies (#2550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-30 23:07:27 +00:00
Amir Raminfar 10e4a7cfaa fix: disables action routes if not configured (#2549) 2023-11-30 22:28:42 +00:00
Akash Ramaswamy f78534f529 feat: can start and stop containers now using the drop down. This feature is not enabled by default. (#2548) 2023-11-30 14:04:22 -08:00
ab623 050e499f8e fix: Added .gitkeep to dist folder, to enable fresh build with no errors (#2547) 2023-11-29 09:33:36 -08:00
Amir Raminfar 29e1bbc981 chore: release v5.5.0 2023-11-28 15:55:34 -08:00
Amir Raminfar 1fa44cc920 chore: updates modules (#2544) 2023-11-28 15:53:31 -08:00
Amir Raminfar 23aaf99947 chore: minor perf improvement 2023-11-28 14:31:37 -08:00
Amir Raminfar 60650ddc2c feat: syncs all profile localStorage to disk (#2537) 2023-11-27 15:57:54 -08:00
Amir Raminfar b54b419a08 fix: removes pages content from index.html (#2541) 2023-11-26 19:41:24 -08:00
Amir Raminfar 9bc5496b0e fix: imroves margin left on title bar (#2542) 2023-11-26 19:41:11 -08:00
renovate[bot] 2016f8d8b0 fix(deps): update all non-major dependencies (#2538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-27 00:59:58 +00:00
renovate[bot] 72c6382d73 chore(deps): update dependency jsdom to v23 (#2539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-26 16:56:25 -08:00
Amir Raminfar ae72e3f4a6 chore: updates icons (#2540) 2023-11-26 16:55:26 -08:00
Amir Raminfar 1ec25e6a64 chore: release v5.4.5 2023-11-25 13:29:18 -08:00
Amir Raminfar ee4f344346 chore: updates imports 2023-11-25 13:29:07 -08:00
renovate[bot] 06e05bca45 chore(deps): update all non-major dependencies (#2534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-25 21:26:11 +00:00
Amir Raminfar 5635ba4619 fix: improves secondary color (#2533) 2023-11-25 13:22:23 -08:00
Amir Raminfar 32dec4e817 fix: improves primary colors (#2531) 2023-11-25 11:47:37 -08:00
Amir Raminfar 2ed41282a5 fix: uses a single key in localStorage to save all visible JSON keys. (#2530) 2023-11-23 18:37:00 -08:00
Ahmet 26cd16a403 fix: update de.yml to use consistent wording and fix small issues (#2528) 2023-11-22 09:26:31 -08:00
Alban Browaeys b243783f5d fix: make README container host port consistent (#2529) 2023-11-22 05:50:48 -08:00
Amir Raminfar 8ab71e2caa feat: improves search style and adding a slight animation (#2527) 2023-11-22 05:48:26 -08:00
Amir Raminfar 318b07d1a9 feat: when doing modifier+f again in search, hides search and show browser search (#2526) 2023-11-22 05:48:03 -08:00
renovate[bot] 7ff26b822f chore(deps): update all non-major dependencies (#2525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-21 22:28:56 +00:00
Amir Raminfar cacf2cd261 chore: release v5.4.4 2023-11-21 14:19:11 -08:00
Amir Raminfar 47bc4b7fc5 fix: fixes bugs with reporting on addr and base (#2521) 2023-11-21 14:14:54 -08:00
renovate[bot] 38c4d9e4b7 chore(deps): update all non-major dependencies (#2517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 00:54:55 +00:00
Amir Raminfar 1d87d09e89 chore: release v5.4.3 2023-11-19 14:18:50 -08:00
Amir Raminfar 70645be134 fix: fixes security warnings with net package (#2516) 2023-11-19 05:14:37 +00:00
Amir Raminfar c659f103f8 feat: improves analytics by removing google analytics in favor of a simple ping (#2515) 2023-11-19 03:35:24 +00:00
renovate[bot] 58fef4e16a chore(deps): update all non-major dependencies (#2514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 15:57:55 +00:00
Amir Raminfar 70e0c359da fix: hides errors when trying to parse simple numbers as json. fixes #2510 (#2512) 2023-11-18 07:52:27 -08:00
Amir Raminfar 0ad8f32588 chore: moves warn log to debug 2023-11-17 15:43:41 -08:00
Amir Raminfar 07147d1e5c fix: returns afters healthcheck 2023-11-17 15:38:17 -08:00
EDIflyer 02a40fdd8e docs: Add docker-compose.yml option (#2511) 2023-11-17 12:48:43 -08:00
renovate[bot] eb94da7b92 chore(deps): update all non-major dependencies (#2507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 03:27:35 +00:00
Amir Raminfar 660b4c6f51 chore: updates to vite v5 (#2505) 2023-11-16 19:24:05 -08:00
renovate[bot] f08518d442 chore(deps): update all non-major dependencies (#2502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 16:47:45 +00:00
Amir Raminfar b066433933 chore: release v5.4.2 2023-11-14 13:06:04 -08:00
Amir Raminfar 52dc1ff2d1 fix: fixes collapse button to not cover the menu on left. see #2500 (#2501) 2023-11-14 13:05:01 -08:00
Suresh Ramakrishnaiah 10ea7cfb51 docs: update authentication.md (#2492) 2023-11-13 18:41:25 -08:00
renovate[bot] eea9813f84 fix(deps): update all non-major dependencies (#2498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-14 00:52:30 +00:00
Amir Raminfar 8ac79c707c chore: updates go version for CI (#2499) 2023-11-14 00:48:28 +00:00
Amir Raminfar 4637b49075 chore: fixes golang version 2023-11-13 14:29:21 -08:00
renovate[bot] 147902f191 fix(deps): update all non-major dependencies (#2493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 14:28:59 -08:00
Amir Raminfar f83b5c948e fix: converts HSL colors OKLCH with DaisyUI v4 (#2497) 2023-11-13 14:00:35 -08:00
Amir Raminfar 542c895812 chore: fixes padding 2023-11-11 14:08:11 -08:00
Amir Raminfar d14d140cb6 chore(test): adds test for simple mode (#2491) 2023-11-11 22:04:45 +00:00
Amir Raminfar be74d48de7 fix: improves the alignment of drop downs 2023-11-11 13:48:14 -08:00
Amir Raminfar f8a9396f6e chore(test): adds test for proxy mode (#2490) 2023-11-11 13:22:06 -08:00
Amir Raminfar 009529f20e chore: release v5.4.1 2023-11-11 06:41:45 -08:00
Amir Raminfar 63f16ef575 fix: fixes proxy pass not finding user (#2488) 2023-11-11 06:37:06 -08:00
Amir Raminfar 9de964dac4 chore: release v5.4.0 2023-11-10 15:20:41 -08:00
Amir Raminfar 1ff0b5541d chore: fixes incorrect version in doc 2023-11-10 13:47:30 -08:00
Amir Raminfar 55326cdd41 docs: adds sponsor section (#2487) 2023-11-10 21:08:32 +00:00
Amir Raminfar 9fa4911aec feat: list of releases can now be seen at the top (#2480) 2023-11-10 19:45:47 +00:00
Amir Raminfar 7dae54df26 chore: fixes golang version 2023-11-10 09:04:30 -08:00
renovate[bot] 1b101e62da chore(deps): update all non-major dependencies (#2485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 09:04:07 -08:00
Amir Raminfar 5c5cd17fb6 chore: removes netlify files 2023-11-10 08:46:27 -08:00
Amir Raminfar c697be659b docs: adds docs for custom content (#2483) 2023-11-08 14:58:48 -08:00
Amir Raminfar d469066c07 feat: moves user's name and email to dropdown (#2479) 2023-11-07 11:07:34 -08:00
Amir Raminfar 4380681812 feat: supports default avatar when gravatar is missing (#2478) 2023-11-07 09:26:48 -08:00
Amir Raminfar 75cf2f045a chore: release v5.3.1 2023-11-06 15:29:56 -08:00
Amir Raminfar ab5807b3ae chore: fixes go.mod 2023-11-06 15:24:50 -08:00
renovate[bot] fe0f163090 chore(deps): update all non-major dependencies (#2477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:23:04 -08:00
Amir Raminfar 38d1f99387 fix: reduces padding on mobile (#2476) 2023-11-06 21:59:53 +00:00
Amir Raminfar 11298e83b0 chore: release v5.3.0 2023-11-06 12:39:29 -08:00
Amir Raminfar 32de313c27 feat: supports having custom content with markdown (#2466) 2023-11-06 12:39:11 -08:00
renovate[bot] 1e1c64d6e7 fix(deps): update all non-major dependencies (#2474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 01:13:20 +00:00
161 changed files with 6074 additions and 3636 deletions
+5 -4
View File
@@ -29,9 +29,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.21.x
check-latest: true
- name: Checkout code
uses: actions/checkout@v4
- name: Run Go Tests with Coverage
@@ -66,7 +67,7 @@ jobs:
*.cache-to=type=gha,mode=max
- name: Run Playwright tests
run: docker compose up --exit-code-from playwright
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
@@ -90,7 +91,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.0.0
uses: docker/build-push-action@v5.1.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5.0.0
uses: docker/build-push-action@v5.1.0
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
+3 -3
View File
@@ -36,7 +36,7 @@ jobs:
node-version: latest
cache: pnpm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v4
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
@@ -44,7 +44,7 @@ jobs:
pnpm docs:build
touch docs/.vitepress/dist/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
@@ -59,4 +59,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4
+8 -3
View File
@@ -52,13 +52,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.21.x
check-latest: true
- name: Checkout code
uses: actions/checkout@v4
- name: Run Go Tests with Coverage
run: make test SKIP_ASSET=1
# - name: Stactic checker
# uses: dominikh/staticcheck-action@v1.3.0
# with:
# install-go: false
int-test:
name: Integration Tests
timeout-minutes: 60
@@ -89,7 +94,7 @@ jobs:
*.cache-to=type=gha,mode=max
- name: Run Playwright tests
run: docker compose up --exit-code-from playwright
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
+1 -1
View File
@@ -1,5 +1,5 @@
certs
data
/data
dist
node_modules
.cache
View File
+1 -1
View File
@@ -1 +1 @@
-r '\.(go)$' -R 'node_modules' -G '\*\_test.go' -s -- go run main.go --level debug
-r '\.(go)$' -R 'node_modules' -G '\*\_test.go' -s -- go run -race main.go --level debug
+2 -2
View File
@@ -14,7 +14,7 @@ COPY package.json ./
RUN pnpm install --offline --ignore-scripts --no-optional
# Copy assets and translations to build
COPY .* *.config.ts *.config.js ./
COPY .* *.config.ts *.config.js *.config.cjs ./
COPY assets ./assets
COPY locales ./locales
COPY public ./public
@@ -22,7 +22,7 @@ COPY public ./public
# Build assets
RUN pnpm build
FROM --platform=$BUILDPLATFORM golang:1.21.3-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.22.0-alpine AS builder
RUN apk add --no-cache ca-certificates && mkdir /dozzle
+1 -1
View File
@@ -15,7 +15,7 @@ fake_assets:
.PHONY: test
test: fake_assets
go test -cover ./...
go test -cover -race ./...
.PHONY: build
build: dist
+30 -18
View File
@@ -31,7 +31,7 @@ Dozzle is a small container (4 MB compressed). Pull the latest release with:
The simplest way to use dozzle is to run the docker container. Also, mount the Docker Unix socket with `--volume` to `/var/run/docker.sock`:
$ docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:8080 amir20/dozzle:latest
$ docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock:ro -p 8888:8080 amir20/dozzle:latest
Dozzle will be available at [http://localhost:8888/](http://localhost:8888/).
@@ -43,9 +43,9 @@ Here is the Docker Compose file:
container_name: dozzle
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 8080:8080
- 8888:8080
For advanced options like [authentication](https://dozzle.dev/guide/authentication), [remote hosts](https://dozzle.dev/guide/remote-hosts) or common [questions](https://dozzle.dev/guide/faq) see documentation at [dozzle.dev](https://dozzle.dev/guide/getting-started).
@@ -76,7 +76,7 @@ If it's not enabled please follow [this tutorial](https://github.com/containers/
Once you have the podman remote socket you can run Dozzle on podman.
```
podman run --volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock -d -p 8080:8080 amir20/dozzle:latest
podman run --volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock:ro -d -p 8888:8080 amir20/dozzle:latest
```
## Security
@@ -89,23 +89,35 @@ Dozzle collects anonymous user configurations using Google Analytics. Why? Dozzl
If you do not want to be tracked at all, see the `--no-analytics` flag below.
#### Environment variables and configuration
## Environment variables and configuration
Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can use the CLI flags or environment variables. The table below outlines all supported options and their respective env vars.
| Flag | Env Variable | Default |
| ---------------- | ---------------------- | ------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
| Flag | Env Variable | Default |
| --------------------------- | -------------------------------- | -------------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--auth-provider` | `DOZZLE_AUTH_PROVIDER` | `none` |
| `--auth-header-user` | `DOZZLE_AUTH_HEADER_USER` | `Remote-User` |
| `--auth-header-email` | `DOZZLE_AUTH_HEADER_EMAIL` | `Remote-Email` |
| `--auth-header-name` | `DOZZLE_AUTH_HEADER_NAME` | `Remote-Name` |
| `--enable-actions` | `DOZZLE_ENABLE_ACTIONS` | false |
| `--wait-for-docker-seconds` | `DOZZLE_WAIT_FOR_DOCKER_SECONDS` | 0 |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
## Support
There are many ways you can support Dozzle:
- Use it! Write about it! Star it! If you love Dozzle, drop me a line and tell me what you love.
- Blog about Dozzle to spread the word. If you are good at writing send PRs to improve the documentation at [dozzle.dev](https://dozzle.dev/)
- Sponsor my work at https://www.buymeacoffee.com/amirraminfar
<a href="https://www.buymeacoffee.com/amirraminfar" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
## License
+70 -57
View File
@@ -20,28 +20,28 @@ declare global {
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const automaticRedirect: typeof import('./stores/settings')['automaticRedirect']
const collapseNav: typeof import('./stores/settings')['collapseNav']
const compact: typeof import('./stores/settings')['compact']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const config: typeof import('./stores/config')['default']
const containerContext: typeof import('./composables/containerContext')['containerContext']
const containerContext: typeof import('./composable/containerContext')['containerContext']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGenericProjection: typeof import('@vueuse/math')['createGenericProjection']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createPinia: typeof import('pinia')['createPinia']
const createProjection: typeof import('@vueuse/math')['createProjection']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: typeof import('vue')['customRef']
const dateLocale: typeof import('./stores/settings')['dateLocale']
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
@@ -56,23 +56,22 @@ declare global {
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const getDeep: typeof import('./utils/index')['getDeep']
const globalShowPopup: typeof import('./composables/popup')['globalShowPopup']
const globalShowPopup: typeof import('./composable/popup')['globalShowPopup']
const h: typeof import('vue')['h']
const hourStyle: typeof import('./stores/settings')['hourStyle']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: typeof import('@vueuse/core')['isDefined']
const isMobile: typeof import('./composables/media')['isMobile']
const isMobile: typeof import('./composable/media')['isMobile']
const isObject: typeof import('./utils/index')['isObject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const lightTheme: typeof import('./stores/settings')['lightTheme']
const logicAnd: typeof import('@vueuse/math')['logicAnd']
const logicNot: typeof import('@vueuse/math')['logicNot']
const logicOr: typeof import('@vueuse/math')['logicOr']
const locale: typeof import('./stores/settings')['locale']
const logSearchContext: typeof import('./composable/logSearchContext')['logSearchContext']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
@@ -102,10 +101,10 @@ declare global {
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const persistentVisibleKeys: typeof import('./composables/storage')['persistentVisibleKeys']
const pinnedContainers: typeof import('./composables/storage')['pinnedContainers']
const persistentVisibleKeys: typeof import('./composable/storage')['persistentVisibleKeys']
const pinnedContainers: typeof import('./composable/storage')['pinnedContainers']
const provide: typeof import('vue')['provide']
const provideContainerContext: typeof import('./composables/containerContext')['provideContainerContext']
const provideContainerContext: typeof import('./composable/containerContext')['provideContainerContext']
const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
@@ -124,10 +123,10 @@ declare global {
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const search: typeof import('./stores/settings')['search']
const sessionHost: typeof import('./composables/storage')['sessionHost']
const sessionHost: typeof import('./composable/storage')['sessionHost']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const setTitle: typeof import('./composables/title')['setTitle']
const setTitle: typeof import('./composable/title')['setTitle']
const settings: typeof import('./stores/settings')['settings']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
@@ -159,7 +158,6 @@ declare global {
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useAbs: typeof import('@vueuse/math')['useAbs']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
@@ -177,7 +175,6 @@ declare global {
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useAverage: typeof import('@vueuse/math')['useAverage']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
@@ -185,13 +182,13 @@ declare global {
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useCeil: typeof import('@vueuse/math')['useCeil']
const useClamp: typeof import('@vueuse/math')['useClamp']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useContainerContext: typeof import('./composables/containerContext')['useContainerContext']
const useContainerActions: typeof import('./composable/containerActions')['useContainerActions']
const useContainerContext: typeof import('./composable/containerContext')['useContainerContext']
const useContainerStore: typeof import('./stores/container')['useContainerStore']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
@@ -226,7 +223,6 @@ declare global {
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFloor: typeof import('@vueuse/math')['useFloor']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
@@ -245,16 +241,14 @@ declare global {
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useLogStream: typeof import('./composables/eventsource')['useLogStream']
const useLogSearchContext: typeof import('./composable/logSearchContext')['useLogSearchContext']
const useLogStream: typeof import('./composable/eventsource')['useLogStream']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMath: typeof import('@vueuse/math')['useMath']
const useMax: typeof import('@vueuse/math')['useMax']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useMin: typeof import('@vueuse/math')['useMin']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
@@ -274,18 +268,17 @@ declare global {
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePrecision: typeof import('@vueuse/math')['usePrecision']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useProjection: typeof import('@vueuse/math')['useProjection']
const useProfileStorage: typeof import('./composable/profileStorage')['useProfileStorage']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useReleases: typeof import('./stores/releases')['useReleases']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRound: typeof import('@vueuse/math')['useRound']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
@@ -293,10 +286,11 @@ declare global {
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSearchFilter: typeof import('./composables/search')['useSearchFilter']
const useSearchFilter: typeof import('./composable/search')['useSearchFilter']
const useSeoMeta: typeof import('@vueuse/head')['useSeoMeta']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSimpleRefHistory: typeof import('./utils/index')['useSimpleRefHistory']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
@@ -305,7 +299,6 @@ declare global {
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSum: typeof import('@vueuse/math')['useSum']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
@@ -323,17 +316,16 @@ declare global {
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: typeof import('@vueuse/core')['useToString']
const useToast: typeof import('./composables/toast')['useToast']
const useToast: typeof import('./composable/toast')['useToast']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useTrunc: typeof import('@vueuse/math')['useTrunc']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useVisibleFilter: typeof import('./composables/visible')['useVisibleFilter']
const useVisibleFilter: typeof import('./composable/visible')['useVisibleFilter']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
@@ -364,10 +356,12 @@ declare global {
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly $$: UnwrapRef<typeof import('vue/macros')['$$']>
readonly $: UnwrapRef<typeof import('vue/macros')['$']>
@@ -384,13 +378,14 @@ declare module 'vue' {
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly automaticRedirect: UnwrapRef<typeof import('./stores/settings')['automaticRedirect']>
readonly collapseNav: UnwrapRef<typeof import('./stores/settings')['collapseNav']>
readonly compact: UnwrapRef<typeof import('./stores/settings')['compact']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
readonly containerContext: UnwrapRef<typeof import('./composable/containerContext')['containerContext']>
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
@@ -404,6 +399,7 @@ declare module 'vue' {
readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']>
readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly dateLocale: UnwrapRef<typeof import('./stores/settings')['dateLocale']>
readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
@@ -418,20 +414,21 @@ declare module 'vue' {
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getDeep: UnwrapRef<typeof import('./utils/index')['getDeep']>
readonly globalShowPopup: UnwrapRef<typeof import('./composables/popup')['globalShowPopup']>
readonly globalShowPopup: UnwrapRef<typeof import('./composable/popup')['globalShowPopup']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly hourStyle: UnwrapRef<typeof import('./stores/settings')['hourStyle']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
readonly isMobile: UnwrapRef<typeof import('./composables/media')['isMobile']>
readonly isMobile: UnwrapRef<typeof import('./composable/media')['isMobile']>
readonly isObject: UnwrapRef<typeof import('./utils/index')['isObject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly lightTheme: UnwrapRef<typeof import('./stores/settings')['lightTheme']>
readonly locale: UnwrapRef<typeof import('./stores/settings')['locale']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
@@ -461,10 +458,10 @@ declare module 'vue' {
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composable/storage')['persistentVisibleKeys']>
readonly pinnedContainers: UnwrapRef<typeof import('./composable/storage')['pinnedContainers']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
readonly provideContainerContext: UnwrapRef<typeof import('./composable/containerContext')['provideContainerContext']>
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
@@ -483,10 +480,10 @@ declare module 'vue' {
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
readonly search: UnwrapRef<typeof import('./stores/settings')['search']>
readonly sessionHost: UnwrapRef<typeof import('./composables/storage')['sessionHost']>
readonly sessionHost: UnwrapRef<typeof import('./composable/storage')['sessionHost']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly setTitle: UnwrapRef<typeof import('./composables/title')['setTitle']>
readonly setTitle: UnwrapRef<typeof import('./composable/title')['setTitle']>
readonly settings: UnwrapRef<typeof import('./stores/settings')['settings']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
@@ -543,10 +540,12 @@ declare module 'vue' {
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
readonly useContainerActions: UnwrapRef<typeof import('./composable/containerActions')['useContainerActions']>
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
@@ -599,7 +598,8 @@ declare module 'vue' {
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useLogStream: UnwrapRef<typeof import('./composables/eventsource')['useLogStream']>
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
@@ -631,8 +631,10 @@ declare module 'vue' {
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly useProfileStorage: UnwrapRef<typeof import('./composable/profileStorage')['useProfileStorage']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
@@ -641,10 +643,11 @@ declare module 'vue' {
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
readonly useSearchFilter: UnwrapRef<typeof import('./composables/search')['useSearchFilter']>
readonly useSearchFilter: UnwrapRef<typeof import('./composable/search')['useSearchFilter']>
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
readonly useSimpleRefHistory: UnwrapRef<typeof import('./utils/index')['useSimpleRefHistory']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
@@ -670,7 +673,7 @@ declare module 'vue' {
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
readonly useToast: UnwrapRef<typeof import('./composable/toast')['useToast']>
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
@@ -679,7 +682,7 @@ declare module 'vue' {
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
readonly useVisibleFilter: UnwrapRef<typeof import('./composables/visible')['useVisibleFilter']>
readonly useVisibleFilter: UnwrapRef<typeof import('./composable/visible')['useVisibleFilter']>
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
@@ -708,6 +711,7 @@ declare module 'vue' {
}
}
declare module '@vue/runtime-core' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly $$: UnwrapRef<typeof import('vue/macros')['$$']>
readonly $: UnwrapRef<typeof import('vue/macros')['$']>
@@ -724,13 +728,14 @@ declare module '@vue/runtime-core' {
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly automaticRedirect: UnwrapRef<typeof import('./stores/settings')['automaticRedirect']>
readonly collapseNav: UnwrapRef<typeof import('./stores/settings')['collapseNav']>
readonly compact: UnwrapRef<typeof import('./stores/settings')['compact']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
readonly containerContext: UnwrapRef<typeof import('./composable/containerContext')['containerContext']>
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
@@ -744,6 +749,7 @@ declare module '@vue/runtime-core' {
readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']>
readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly dateLocale: UnwrapRef<typeof import('./stores/settings')['dateLocale']>
readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
@@ -758,20 +764,21 @@ declare module '@vue/runtime-core' {
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getDeep: UnwrapRef<typeof import('./utils/index')['getDeep']>
readonly globalShowPopup: UnwrapRef<typeof import('./composables/popup')['globalShowPopup']>
readonly globalShowPopup: UnwrapRef<typeof import('./composable/popup')['globalShowPopup']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly hourStyle: UnwrapRef<typeof import('./stores/settings')['hourStyle']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
readonly isMobile: UnwrapRef<typeof import('./composables/media')['isMobile']>
readonly isMobile: UnwrapRef<typeof import('./composable/media')['isMobile']>
readonly isObject: UnwrapRef<typeof import('./utils/index')['isObject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly lightTheme: UnwrapRef<typeof import('./stores/settings')['lightTheme']>
readonly locale: UnwrapRef<typeof import('./stores/settings')['locale']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
@@ -801,10 +808,10 @@ declare module '@vue/runtime-core' {
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composable/storage')['persistentVisibleKeys']>
readonly pinnedContainers: UnwrapRef<typeof import('./composable/storage')['pinnedContainers']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
readonly provideContainerContext: UnwrapRef<typeof import('./composable/containerContext')['provideContainerContext']>
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
@@ -823,10 +830,10 @@ declare module '@vue/runtime-core' {
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
readonly search: UnwrapRef<typeof import('./stores/settings')['search']>
readonly sessionHost: UnwrapRef<typeof import('./composables/storage')['sessionHost']>
readonly sessionHost: UnwrapRef<typeof import('./composable/storage')['sessionHost']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly setTitle: UnwrapRef<typeof import('./composables/title')['setTitle']>
readonly setTitle: UnwrapRef<typeof import('./composable/title')['setTitle']>
readonly settings: UnwrapRef<typeof import('./stores/settings')['settings']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
@@ -883,10 +890,12 @@ declare module '@vue/runtime-core' {
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
readonly useContainerActions: UnwrapRef<typeof import('./composable/containerActions')['useContainerActions']>
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
@@ -939,7 +948,8 @@ declare module '@vue/runtime-core' {
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useLogStream: UnwrapRef<typeof import('./composables/eventsource')['useLogStream']>
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
@@ -971,8 +981,10 @@ declare module '@vue/runtime-core' {
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly useProfileStorage: UnwrapRef<typeof import('./composable/profileStorage')['useProfileStorage']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
@@ -981,10 +993,11 @@ declare module '@vue/runtime-core' {
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
readonly useSearchFilter: UnwrapRef<typeof import('./composables/search')['useSearchFilter']>
readonly useSearchFilter: UnwrapRef<typeof import('./composable/search')['useSearchFilter']>
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
readonly useSimpleRefHistory: UnwrapRef<typeof import('./utils/index')['useSimpleRefHistory']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
@@ -1010,7 +1023,7 @@ declare module '@vue/runtime-core' {
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
readonly useToast: UnwrapRef<typeof import('./composable/toast')['useToast']>
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
@@ -1019,7 +1032,7 @@ declare module '@vue/runtime-core' {
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
readonly useVisibleFilter: UnwrapRef<typeof import('./composables/visible')['useVisibleFilter']>
readonly useVisibleFilter: UnwrapRef<typeof import('./composable/visible')['useVisibleFilter']>
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
+13 -2
View File
@@ -10,10 +10,15 @@ declare module 'vue' {
BarChart: typeof import('./components/BarChart.vue')['default']
'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default']
'Carbon:circleSolid': typeof import('~icons/carbon/circle-solid')['default']
'Carbon:copyFile': typeof import('~icons/carbon/copy-file')['default']
'Carbon:information': typeof import('~icons/carbon/information')['default']
'Carbon:macShift': typeof import('~icons/carbon/mac-shift')['default']
'Carbon:play': typeof import('~icons/carbon/play')['default']
'Carbon:restart': typeof import('~icons/carbon/restart')['default']
'Carbon:searchLocate': typeof import('~icons/carbon/search-locate')['default']
'Carbon:star': typeof import('~icons/carbon/star')['default']
'Carbon:starFilled': typeof import('~icons/carbon/star-filled')['default']
'Carbon:stopFilledAlt': typeof import('~icons/carbon/stop-filled-alt')['default']
'Carbon:warning': typeof import('~icons/carbon/warning')['default']
'Cil:checkCircle': typeof import('~icons/cil/check-circle')['default']
'Cil:circle': typeof import('~icons/cil/circle')['default']
@@ -21,6 +26,7 @@ declare module 'vue' {
'Cil:xCircle': typeof import('~icons/cil/x-circle')['default']
ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default']
ContainerHealth: typeof import('./components/LogViewer/ContainerHealth.vue')['default']
ContainerLogViewer: typeof import('./components/LogViewer/ContainerLogViewer.vue')['default']
ContainerPopup: typeof import('./components/LogViewer/ContainerPopup.vue')['default']
ContainerStat: typeof import('./components/LogViewer/ContainerStat.vue')['default']
ContainerTable: typeof import('./components/ContainerTable.vue')['default']
@@ -29,20 +35,24 @@ declare module 'vue' {
DistanceTime: typeof import('./components/common/DistanceTime.vue')['default']
DockerEventLogItem: typeof import('./components/LogViewer/DockerEventLogItem.vue')['default']
Dropdown: typeof import('./components/common/Dropdown.vue')['default']
DropdownMenu: typeof import('./components/common/DropdownMenu.vue')['default']
FieldList: typeof import('./components/LogViewer/FieldList.vue')['default']
FuzzySearchModal: typeof import('./components/FuzzySearchModal.vue')['default']
'Ic:sharpFindInPage': typeof import('~icons/ic/sharp-find-in-page')['default']
'Ic:sharpKeyboardReturn': typeof import('~icons/ic/sharp-keyboard-return')['default']
InfiniteLoader: typeof import('./components/InfiniteLoader.vue')['default']
KeyShortcut: typeof import('./components/common/KeyShortcut.vue')['default']
LabeledInput: typeof import('./components/common/LabeledInput.vue')['default']
Links: typeof import('./components/Links.vue')['default']
LogActionsToolbar: typeof import('./components/LogViewer/LogActionsToolbar.vue')['default']
LogContainer: typeof import('./components/LogViewer/LogContainer.vue')['default']
LogDate: typeof import('./components/LogViewer/LogDate.vue')['default']
LogEventSource: typeof import('./components/LogViewer/LogEventSource.vue')['default']
LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default']
LogMessageActions: typeof import('./components/LogViewer/LogMessageActions.vue')['default']
LogStd: typeof import('./components/LogViewer/LogStd.vue')['default']
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
'Mdi:announcement': typeof import('~icons/mdi/announcement')['default']
'Mdi:arrowUp': typeof import('~icons/mdi/arrow-up')['default']
'Mdi:check': typeof import('~icons/mdi/check')['default']
'Mdi:chevronDoubleDown': typeof import('~icons/mdi/chevron-double-down')['default']
@@ -52,16 +62,17 @@ declare module 'vue' {
'Mdi:cog': typeof import('~icons/mdi/cog')['default']
'Mdi:hamburgerMenu': typeof import('~icons/mdi/hamburger-menu')['default']
'Mdi:keyboardEsc': typeof import('~icons/mdi/keyboard-esc')['default']
'Mdi:logout': typeof import('~icons/mdi/logout')['default']
'Mdi:magnify': typeof import('~icons/mdi/magnify')['default']
MobileMenu: typeof import('./components/common/MobileMenu.vue')['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']
PageWithLinks: typeof import('./components/PageWithLinks.vue')['default']
'Ph:command': typeof import('~icons/ph/command')['default']
'Ph:computerTower': typeof import('~icons/ph/computer-tower')['default']
'Ph:controlBold': typeof import('~icons/ph/control-bold')['default']
Popup: typeof import('./components/Popup.vue')['default']
Releases: typeof import('./components/Releases.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ScrollableView: typeof import('./components/ScrollableView.vue')['default']
+16 -5
View File
@@ -1,5 +1,14 @@
!
<template>
<div class="text-right">
Show per page
<dropdown-menu
class="dropdown-left btn-xs md:btn-sm"
v-model="perPage"
:options="pageSizes.map((i) => ({ label: i.toLocaleString(), value: i }))"
v-if="containers.length > 0"
/>
</div>
<table class="table table-lg bg-base">
<thead>
<tr :data-direction="direction > 0 ? 'asc' : 'desc'">
@@ -95,12 +104,14 @@ const fields = {
},
};
const { containers, perPage = 15 } = defineProps<{
const { containers } = defineProps<{
containers: Container[];
perPage?: number;
}>();
type keys = keyof typeof fields;
const perPage = useStorage("DOZZLE_TABLE_PAGE_SIZE", 15);
const pageSizes = [15, 30, 50, 100];
const storage = useStorage<{ column: keys; direction: 1 | -1 }>("DOZZLE_TABLE_CONTAINERS_SORT", {
column: "created",
direction: -1,
@@ -115,12 +126,12 @@ const sortedContainers = computedWithControl(
},
);
const totalPages = computed(() => Math.ceil(sortedContainers.value.length / perPage));
const totalPages = computed(() => Math.ceil(sortedContainers.value.length / perPage.value));
const isPaginated = computed(() => totalPages.value > 1);
const currentPage = ref(1);
const paginated = computed(() => {
const start = (currentPage.value - 1) * perPage;
const end = start + perPage;
const start = (currentPage.value - 1) * perPage.value;
const end = start + perPage.value;
return sortedContainers.value.slice(start, end);
});
@@ -0,0 +1,79 @@
import { createTestingPinia } from "@pinia/testing";
import { mount } from "@vue/test-utils";
import FuzzySearchModal from "./FuzzySearchModal.vue";
import { Container } from "@/models/Container";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { createI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { router } from "@/modules/router";
// @ts-ignore
import EventSource, { sources } from "eventsourcemock";
vi.mock("vue-router");
vi.mock("@/stores/config", () => ({
__esModule: true,
default: { base: "", hosts: [{ name: "localhost", id: "localhost" }] },
withBase: (path: string) => path,
}));
function createFuzzySearchModal() {
global.EventSource = EventSource;
const wrapper = mount(FuzzySearchModal, {
global: {
plugins: [
createI18n({}),
createTestingPinia({
createSpy: vi.fn,
initialState: {
container: {
containers: [
new Container("123", new Date(), "image", "test", "command", "host", {}, "status", "running", []),
new Container("345", new Date(), "image", "foo bar", "command", "host", {}, "status", "running", []),
new Container("567", new Date(), "image", "baz", "command", "host", {}, "status", "exited", []),
],
},
},
}),
],
},
});
return wrapper;
}
/**
* @vitest-environment jsdom
*/
describe("<FuzzySearchModal />", () => {
vi.mocked(useRouter).mockReturnValue({
...router,
push: vi.fn(),
});
beforeEach(() => {
vi.mocked(useRouter().push).mockReset();
});
test("shows all", async () => {
const wrapper = createFuzzySearchModal();
expect(wrapper.findAll("li").length).toBe(3);
});
test("search for foo", async () => {
const wrapper = createFuzzySearchModal();
await wrapper.find("input").setValue("foo");
expect(wrapper.findAll("li").length).toBe(1);
expect(wrapper.find("ul [data-name]").html()).toMatchInlineSnapshot(
`"<span data-v-dc2e8c61="" data-name=""><mark>foo</mark> bar</span>"`,
);
});
test("choose baz", async () => {
const wrapper = createFuzzySearchModal();
await wrapper.find("input").setValue("baz");
await wrapper.find("input").trigger("keyup.enter");
expect(useRouter().push).toHaveBeenCalledWith({ name: "container-id", params: { id: "567" } });
});
});
+53 -21
View File
@@ -1,36 +1,43 @@
<template>
<div class="dropdown dropdown-open w-full">
<div class="input input-primary flex h-auto items-center">
<mdi:magnify class="flex h-8 w-8" />
<mdi:magnify class="flex size-8" />
<input
tabindex="0"
class="input input-ghost input-lg flex-1 px-1"
ref="input"
@keyup.down="selectedIndex = Math.min(selectedIndex + 1, data.length - 1)"
@keyup.up="selectedIndex = Math.max(selectedIndex - 1, 0)"
@keyup.enter.exact="selected(data[selectedIndex])"
@keyup.enter.exact="selected(data[selectedIndex].item)"
@keyup.alt.enter="addColumn(data[selectedIndex])"
v-model="query"
:placeholder="$t('placeholder.search-containers')"
/>
<mdi:keyboard-esc class="flex" />
</div>
<ul tabindex="0" class="menu dropdown-content rounded-box !relative mt-2 w-full bg-base-lighter p-2">
<li v-for="(item, index) in data">
<ul tabindex="0" class="menu dropdown-content !relative mt-2 w-full rounded-box bg-base-lighter p-2">
<li v-for="(result, index) in data">
<a
class="grid auto-cols-max grid-cols-[min-content,auto] gap-2 py-4"
@click.prevent="selected(item)"
@click.prevent="selected(result.item)"
@mouseenter="selectedIndex = index"
:class="index === selectedIndex ? 'focus' : ''"
>
<div :class="{ 'text-primary': item.state === 'running' }">
<div :class="{ 'text-primary': result.item.state === 'running' }">
<octicon:container-24 />
</div>
<div class="truncate">
<span class="font-light">{{ item.host }}</span> / {{ item.name }}
<template v-if="config.hosts.length > 1">
<span class="font-light">{{ result.item.host }}</span> /
</template>
<span data-name v-html="matchedName(result)"></span>
</div>
<distance-time :date="item.created" class="ml-auto text-xs font-light" />
<a @click.stop.prevent="addColumn(item)" :title="$t('tooltip.pin-column')" class="hover:text-secondary">
<distance-time :date="result.item.created" class="text-xs font-light" />
<a
@click.stop.prevent="addColumn(result.item)"
:title="$t('tooltip.pin-column')"
class="hover:text-secondary"
>
<ic:sharp-keyboard-return v-if="index === selectedIndex" />
<cil:columns v-else />
</a>
@@ -42,8 +49,9 @@
<script lang="ts" setup>
import { useFuse } from "@vueuse/integrations/useFuse";
import { type FuseResultMatch } from "fuse.js";
const { maxResults: resultLimit = 5 } = defineProps<{
const { maxResults = 5 } = defineProps<{
maxResults?: number;
}>();
@@ -58,44 +66,46 @@ const store = useContainerStore();
const { containers } = storeToRefs(store);
const list = computed(() => {
return containers.value.map(({ id, created, name, state, hostLabel: host }) => {
return containers.value.map(({ id, created, name, state, labels, hostLabel: host }) => {
return {
id,
created,
name,
state,
host,
labels: Object.entries(labels).map(([_, value]) => value),
};
});
});
const { results } = useFuse(query, list, {
fuseOptions: {
keys: ["name", "host"],
keys: ["name", "host", "labels"],
includeScore: true,
useExtendedSearch: true,
threshold: 0.3,
includeMatches: true,
},
resultLimit,
resultLimit: 10,
matchAllWhenSearchEmpty: true,
});
const data = computed(() => {
return results.value
.sort((a, b) => {
.toSorted((a, b) => {
if (a.score === b.score) {
if (a.item.state === "running" && b.item.state !== "running") {
if (a.item.state === b.item.state) {
return b.item.created - a.item.created;
} else if (a.item.state === "running" && b.item.state !== "running") {
return -1;
} else {
return 1;
}
} else if (a.score && b.score) {
return a.score - b.score;
} else {
return 0;
return a.score - b.score;
}
})
.map(({ item }) => item)
.slice(0, resultLimit);
.slice(0, maxResults);
});
watch(query, (data) => {
@@ -114,6 +124,28 @@ function addColumn(container: { id: string }) {
store.appendActiveContainer(container);
close();
}
function matchedName({ item, matches = [] }: { item: { name: string }; matches?: FuseResultMatch[] }) {
const matched = matches.find((match) => match.key === "name");
if (matched) {
const { indices } = matched;
const result = [];
let lastIndex = 0;
for (const [start, end] of indices) {
result.push(item.name.slice(lastIndex, start));
result.push(`<mark>${item.name.slice(start, end + 1)}</mark>`);
lastIndex = end + 1;
}
result.push(item.name.slice(lastIndex));
return result.join("");
} else {
return item.name;
}
}
</script>
<style scoped lang="postcss"></style>
<style scoped lang="postcss">
:deep(mark) {
@apply bg-transparent text-inherit underline underline-offset-2;
}
</style>
+63
View File
@@ -0,0 +1,63 @@
<template>
<div class="flex items-center justify-end gap-4">
<dropdown class="dropdown-end" @closed="latestTag = latest?.tag ?? config.version">
<template #trigger>
<mdi:announcement class="size-6 -rotate-12" />
<span
class="absolute right-px top-0 size-2 rounded-full bg-red"
v-if="hasUpdate && latestTag != latest?.tag"
></span>
</template>
<template #content>
<div class="w-72">
<releases />
</div>
</template>
</dropdown>
<router-link
:to="{ name: 'settings' }"
:aria-label="$t('title.settings')"
data-testid="settings"
class="btn btn-circle btn-sm"
>
<mdi:cog class="size-6" />
</router-link>
<dropdown class="dropdown-end" v-if="config.user">
<template #trigger>
<img
class="size-6 max-w-none rounded-full p-px ring-1 ring-base-content/60"
:src="withBase('/api/profile/avatar')"
/>
</template>
<template #content>
<div class="p-2">
<div class="font-bold">
{{ config.user.name }}
</div>
<div class="text-sm font-light">
{{ config.user.email }}
</div>
</div>
<ul class="menu mt-4 p-0">
<li v-if="config.authProvider === 'simple'">
<button @click.prevent="logout()" class="p-2 text-primary">{{ $t("button.logout") }}</button>
</li>
</ul>
</template>
</dropdown>
</div>
</template>
<script lang="ts" setup>
async function logout() {
await fetch(withBase("/api/token"), {
method: "DELETE",
});
location.reload();
}
const { hasUpdate, latest } = useReleases();
const latestTag = useProfileStorage("releaseSeen", config.version);
</script>
@@ -1,5 +1,5 @@
<template>
<div class="flex gap-x-2">
<div class="relative flex w-full gap-x-2">
<div v-if="showStd">
<log-std :std="logEntry.std"></log-std>
</div>
@@ -22,6 +22,11 @@
</ul>
<field-list :fields="logEntry.unfilteredMessage" :expanded="expanded" :visible-keys="visibleKeys"></field-list>
</div>
<log-message-actions
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
:message="JSON.stringify(logEntry.message)"
:log-entry="logEntry"
/>
</div>
</template>
<script lang="ts" setup>
@@ -45,6 +50,7 @@ const validValues = computed(() => {
.text-light {
@apply text-base-content/70;
}
.fields {
&:hover {
&::after {
@@ -1,5 +1,5 @@
<template>
<div class="inline-flex h-4 w-4" :health="health" v-if="health" :title="health">
<div class="inline-flex size-4" :health="health" v-if="health" :title="health">
<cil:check-circle v-if="health == 'healthy'" />
<cil:x-circle v-else-if="health == 'unhealthy'" />
<cil:circle v-else />
@@ -0,0 +1,39 @@
<template>
<log-viewer :messages="filtered" :last-selected-item="lastSelectedItem" :visible-keys="visibleKeys" />
</template>
<script lang="ts" setup>
import { useRouteHash } from "@vueuse/router";
import { type JSONObject, LogEntry } from "@/models/LogEntry";
const props = defineProps<{
messages: LogEntry<string | JSONObject>[];
}>();
const { container } = useContainerContext();
const visibleKeys = persistentVisibleKeys(container);
const { filteredPayload } = useVisibleFilter(visibleKeys);
const { filteredMessages } = useSearchFilter();
const { messages } = toRefs(props);
const visible = filteredPayload(messages);
const filtered = filteredMessages(visible);
const { lastSelectedItem } = useLogSearchContext() as {
lastSelectedItem: Ref<LogEntry<string | JSONObject> | undefined>;
};
const routeHash = useRouteHash();
watch(
routeHash,
(hash) => {
if (hash) {
document.querySelector(`[data-key="${hash.substring(1)}"]`)?.scrollIntoView({ block: "center" });
}
},
{ immediate: true, flush: "post" },
);
</script>
<style scoped lang="postcss"></style>
@@ -7,7 +7,7 @@
</div>
<div>
<span class="font-light capitalize"> Load </span>
<span class="font-semibold"> {{ container.stat.cpu }}% </span>
<span class="font-semibold"> {{ container.stat.cpu.toFixed(2) }}% </span>
</div>
<div>
<span class="font-light capitalize"> MEM </span>
@@ -1,7 +1,7 @@
<template>
<div class="flex gap-4" v-if="container.stat">
<stat-monitor :data="memoryData" label="mem" :stat-value="formatBytes(unref(container.stat).memoryUsage)" />
<stat-monitor :data="cpuData" label="load" :stat-value="Math.max(0, unref(container.stat).cpu) + '%'" />
<stat-monitor :data="cpuData" label="load" :stat-value="Math.max(0, unref(container.stat).cpu).toFixed(2) + '%'" />
</div>
</template>
@@ -11,11 +11,11 @@ const { container } = useContainerContext();
const cpuData = computedWithControl(
() => container.value.stat,
() => {
const history = container.value.statHistory;
const history = container.value.statsHistory;
const points: Point<unknown>[] = history.map((stat, i) => ({
x: i,
y: Math.max(0, stat.snapshot.cpu),
value: Math.max(0, stat.snapshot.cpu) + "%",
y: Math.max(0, stat.cpu),
value: Math.max(0, stat.cpu).toFixed(2) + "%",
}));
return points;
},
@@ -24,11 +24,11 @@ const cpuData = computedWithControl(
const memoryData = computedWithControl(
() => container.value.stat,
() => {
const history = container.value.statHistory;
const history = container.value.statsHistory;
const points: Point<string>[] = history.map((stat, i) => ({
x: i,
y: stat.snapshot.memory,
value: formatBytes(stat.snapshot.memoryUsage),
y: stat.memory,
value: formatBytes(stat.memoryUsage),
}));
return points;
},
+13 -11
View File
@@ -1,6 +1,10 @@
<template>
<div class="flex flex-1 items-center gap-2 truncate">
<container-health :health="container.health" v-if="container.health"></container-health>
<div class="flex flex-1 gap-1.5 truncate @container md:gap-2">
<label class="swap swap-rotate size-4">
<input type="checkbox" v-model="pinned" />
<carbon:star-filled class="swap-on text-secondary" />
<carbon:star class="swap-off" />
</label>
<div class="inline-flex font-mono text-sm">
<div v-if="config.hosts.length > 1" class="mobile-hidden font-thin">
{{ container.hostLabel }}<span class="mx-2">/</span>
@@ -13,24 +17,22 @@
{{ container.swarmId }}
</div>
</div>
<tag class="mobile-hidden font-mono" size="small">{{ container.image.replace(/@sha.*/, "") }}</tag>
<label class="swap swap-rotate">
<input type="checkbox" v-model="pinned" />
<carbon:star-filled class="swap-on text-secondary" />
<carbon:star class="swap-off" />
</label>
<container-health :health="container.health" v-if="container.health"></container-health>
<tag class="mobile-hidden hidden font-mono @3xl:block" size="small">
{{ container.image.replace(/@sha.*/, "") }}
</tag>
</div>
</template>
<script lang="ts" setup>
const { container } = useContainerContext();
const pinned = computed({
get: () => pinnedContainers.value.has(container.value.storageKey),
get: () => pinnedContainers.value.has(container.value.name),
set: (value) => {
if (value) {
pinnedContainers.value.add(container.value.storageKey);
pinnedContainers.value.add(container.value.name);
} else {
pinnedContainers.value.delete(container.value.storageKey);
pinnedContainers.value.delete(container.value.name);
}
},
});
@@ -5,7 +5,7 @@
class="alert alert-info mt-8 w-auto text-[1rem] md:mx-auto md:w-1/2"
v-if="nextContainer && logEntry.event === 'container-stopped'"
>
<carbon:information class="h-6 w-6 shrink-0 stroke-current" />
<carbon:information class="size-6 shrink-0 stroke-current" />
<div>
<h3 class="text-lg font-bold">{{ $t("alert.similar-container-found.title") }}</h3>
{{ $t("alert.similar-container-found.message", { containerId: nextContainer.id }) }}
@@ -4,7 +4,7 @@
<carbon:circle-solid class="w-2.5 text-red" v-if="streamConfig.stderr" />
<carbon:circle-solid class="w-2.5 text-blue" v-if="streamConfig.stdout" />
</label>
<ul tabindex="0" class="menu dropdown-content rounded-box z-50 w-52 bg-base p-1 shadow">
<ul tabindex="0" class="menu dropdown-content z-50 w-52 rounded-box bg-base p-1 shadow">
<li>
<a @click.prevent="clear()">
<octicon:trash-24 /> {{ $t("toolbar.clear") }}
@@ -30,7 +30,7 @@
streamConfig.stderr = true;
"
>
<div class="flex h-4 w-4 gap-0.5">
<div class="flex size-4 gap-0.5">
<template v-if="streamConfig.stderr && streamConfig.stdout">
<carbon:circle-solid class="w-2 text-red" />
<carbon:circle-solid class="w-2 text-blue" />
@@ -46,7 +46,7 @@
streamConfig.stderr = false;
"
>
<div class="flex h-4 w-4 flex-col gap-1">
<div class="flex size-4 flex-col gap-1">
<carbon:circle-solid class="w-2 text-blue" v-if="!streamConfig.stderr && streamConfig.stdout" />
</div>
{{ $t("toolbar.show", { std: "STDOUT" }) }}
@@ -59,23 +59,63 @@
streamConfig.stderr = true;
"
>
<div class="flex h-4 w-4 flex-col gap-1">
<div class="flex size-4 flex-col gap-1">
<carbon:circle-solid class="w-2 text-red" v-if="streamConfig.stderr && !streamConfig.stdout" />
</div>
{{ $t("toolbar.show", { std: "STDERR" }) }}
</a>
</li>
<!-- Container Actions (Enabled via config) -->
<template v-if="enableActions">
<li class="line"></li>
<li>
<button
@click="stop()"
:disabled="actionStates.stop || actionStates.restart"
v-if="container.state == 'running'"
>
<carbon:stop-filled-alt /> {{ $t("toolbar.stop") }}
</button>
<button
@click="start()"
:disabled="actionStates.start || actionStates.restart"
v-if="container.state != 'running'"
>
<carbon:play /> {{ $t("toolbar.start") }}
</button>
</li>
<li>
<button @click="restart()" :disabled="disableRestart">
<carbon:restart
:class="{
'animate-spin': actionStates.restart,
'text-secondary': actionStates.restart,
}"
/>
{{ $t("toolbar.restart") }}
</button>
</li>
</template>
</ul>
</div>
</template>
<script lang="ts" setup>
const { showSearch } = useSearchFilter();
const { base } = config;
const { base, enableActions } = config;
const clear = defineEmit();
const { container, streamConfig } = useContainerContext();
// container context is provided in the parent component: <LogContainer>
const { actionStates, start, stop, restart } = useContainerActions();
const disableRestart = computed(() => {
return actionStates.stop || actionStates.start || actionStates.restart;
});
</script>
<style scoped lang="postcss">
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<scrollable-view :scrollable="scrollable" v-if="container">
<template #header v-if="showTitle">
<div class="mx-2 flex items-center gap-2">
<div class="mx-2 flex items-center gap-2 md:ml-4">
<container-title @close="$emit('close')" />
<container-stat class="ml-auto" />
@@ -1,15 +1,16 @@
import { mount } from "@vue/test-utils";
import { createTestingPinia } from "@pinia/testing";
import { mount } from "@vue/test-utils";
import { containerContext } from "@/composable/containerContext";
import { useSearchFilter } from "@/composable/search";
import { settings } from "@/stores/settings";
// @ts-ignore
import EventSource, { sources } from "eventsourcemock";
import LogEventSource from "./LogEventSource.vue";
import LogViewer from "./LogViewer.vue";
import { settings } from "@/stores/settings";
import { useSearchFilter } from "@/composables/search";
import { vi, describe, expect, beforeEach, test, afterEach } from "vitest";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { computed, nextTick } from "vue";
import { createI18n } from "vue-i18n";
import { createRouter, createWebHistory } from "vue-router";
import { containerContext } from "@/composables/containerContext";
import LogEventSource from "./LogEventSource.vue";
import ContainerLogViewer from "./ContainerLogViewer.vue";
vi.mock("@/stores/config", () => ({
__esModule: true,
@@ -68,9 +69,9 @@ describe("<LogEventSource />", () => {
return mount(LogEventSource, {
global: {
plugins: [router, createTestingPinia({ createSpy: vi.fn })],
plugins: [router, createTestingPinia({ createSpy: vi.fn }), createI18n({})],
components: {
LogViewer,
ContainerLogViewer,
},
provide: {
[containerContext as symbol]: {
@@ -82,7 +83,7 @@ describe("<LogEventSource />", () => {
},
slots: {
default: `
<template #scoped="params"><log-viewer :messages="params.messages"></log-viewer></template>
<template #scoped="params"><container-log-viewer :messages="params.messages" /></template>
`,
},
props: {},
@@ -139,19 +140,6 @@ describe("<LogEventSource />", () => {
expect(wrapper.find("ul.events").html()).toMatchSnapshot();
});
test("should render messages with color", async () => {
const wrapper = createLogEventSource();
sources[sourceUrl].emitOpen();
sources[sourceUrl].emitMessage({
data: '{"ts":1560336942459,"m":"\\u001b[30mblack\\u001b[37mwhite", "id":1}',
});
vi.runAllTimers();
await nextTick();
expect(wrapper.find("ul.events").html()).toMatchSnapshot();
});
test("should render messages with html entities", async () => {
const wrapper = createLogEventSource();
sources[sourceUrl].emitOpen();
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div :data-level="level" :data-position="position" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg"></div>
<div :data-level="level" :data-position="position" class="mt-1.5 size-2.5 flex-none rounded-lg"></div>
</template>
<script lang="ts" setup>
import { Position } from "@/models/LogEntry";
@@ -0,0 +1,60 @@
<template>
<div class="flex gap-2">
<div
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
v-if="isSupported && message.trim() != ''"
:title="t('log_actions.copy_log')"
>
<span
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
@click="copyLogMessageToClipBoard()"
>
<carbon:copy-file />
</span>
</div>
<div
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
:title="t('log_actions.jump_to_context')"
v-if="isSearching()"
>
<a
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
@click="handleJumpLineSelected($event, logEntry)"
:href="`#${logEntry.id}`"
>
<carbon:search-locate />
</a>
</div>
</div>
</template>
<script lang="ts" setup>
import { LogEntry, JSONObject } from "@/models/LogEntry";
const { message, logEntry } = defineProps<{
message: string;
logEntry: LogEntry<string | JSONObject>;
}>();
const { showToast } = useToast();
const { copy, isSupported, copied } = useClipboard();
const { t } = useI18n();
const { isSearching } = useSearchFilter();
const { handleJumpLineSelected } = useLogSearchContext();
async function copyLogMessageToClipBoard() {
await copy(message);
if (copied.value) {
showToast(
{
title: t("toasts.copied.title"),
message: t("toasts.copied.message"),
type: "info",
},
{ expire: 2000 },
);
}
}
</script>
+15 -45
View File
@@ -1,63 +1,27 @@
<template>
<ul class="events group py-4" :class="{ 'disable-wrap': !softWrap, [size]: true }">
<ul class="events group py-4" :class="{ 'disable-wrap': !softWrap, [size]: true, compact }">
<li
v-for="item in filtered"
v-for="item in messages"
:key="item.id"
:data-key="item.id"
:class="{ 'border border-secondary': toRaw(item) === toRaw(lastSelectedItem) }"
class="group/entry"
>
<a
class="jump-context tooltip-primary tooltip tooltip-right"
v-if="isSearching()"
data-tip="Jump to Context"
@click="handleJumpLineSelected($event, item)"
:href="`#${item.id}`"
>
<ic:sharp-find-in-page />
</a>
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys.value" />
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys" />
</li>
</ul>
</template>
<script lang="ts" setup>
import { toRaw } from "vue";
import { useRouteHash } from "@vueuse/router";
import { type JSONObject, LogEntry } from "@/models/LogEntry";
const props = defineProps<{
defineProps<{
messages: LogEntry<string | JSONObject>[];
visibleKeys: string[][];
lastSelectedItem: LogEntry<string | JSONObject> | undefined;
}>();
const { container } = useContainerContext();
let visibleKeys = persistentVisibleKeys(container);
const { filteredPayload } = useVisibleFilter(visibleKeys);
const { filteredMessages, resetSearch, isSearching } = useSearchFilter();
const { messages } = toRefs(props);
const visible = filteredPayload(messages);
const filtered = filteredMessages(visible);
let lastSelectedItem: LogEntry<string | JSONObject> | undefined = $ref(undefined);
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
lastSelectedItem = item;
resetSearch();
}
const routeHash = useRouteHash();
watch(
routeHash,
(hash) => {
if (hash) {
document.querySelector(`[data-key="${hash.substring(1)}"]`)?.scrollIntoView({ block: "center" });
}
},
{ immediate: true, flush: "post" },
);
</script>
<style scoped lang="postcss">
.events {
@@ -72,13 +36,13 @@ watch(
monospace;
> li {
@apply flex break-words px-4 py-1 last:snap-end odd:bg-gray-400/[0.07];
@apply flex break-words px-2 py-1 last:snap-end odd:bg-gray-400/[0.07] md:px-4;
&:last-child {
scroll-margin-block-end: 5rem;
}
.jump-context {
@apply mr-2 flex items-center font-sans text-secondary hover:text-secondary-focus;
@apply mr-2 flex items-center font-sans text-secondary;
}
}
@@ -93,5 +57,11 @@ watch(
&.large {
@apply text-lg;
}
&.compact {
> li {
@apply py-0;
}
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<log-event-source ref="source" #default="{ messages }" @loading-more="loadingMore($event)">
<log-viewer :messages="messages"></log-viewer>
<container-log-viewer :messages="messages" />
</log-event-source>
</template>
+11 -5
View File
@@ -1,20 +1,26 @@
<template>
<div class="flex items-start gap-x-2">
<div class="relative flex w-full items-start gap-x-2">
<log-std :std="logEntry.std" v-if="showStd" />
<log-date :date="logEntry.date" v-if="showTimestamp" />
<log-level class="flex" :level="logEntry.level" :position="logEntry.position" />
<div class="whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap" v-html="colorize(logEntry.message)"></div>
<div
class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"
v-html="markSearch(logEntry.message)"
></div>
<log-message-actions
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
:message="decodeXML(logEntry.message)"
:log-entry="logEntry"
/>
</div>
</template>
<script lang="ts" setup>
import { SimpleLogEntry } from "@/models/LogEntry";
import AnsiConvertor from "ansi-to-html";
import { decodeXML } from "entities";
const ansiConvertor = new AnsiConvertor({ escapeXML: false, fg: "var(--text-color)" });
defineProps<{
logEntry: SimpleLogEntry;
}>();
const { markSearch } = useSearchFilter();
const colorize = (value: string) => markSearch(ansiConvertor.toHtml(value));
</script>
+2 -2
View File
@@ -1,9 +1,9 @@
<template>
<div class="relative hover:text-secondary" @mouseenter="mouseOver = true" @mouseleave="mouseOver = false">
<div class="mobile-hidden flex overflow-hidden rounded-sm border border-primary px-px pb-px pt-1">
<div class="hidden overflow-hidden rounded-sm border border-primary px-px pb-px pt-1 md:flex">
<stat-sparkline :data="data" @selected-point="onSelectedPoint"></stat-sparkline>
</div>
<div class="inline-flex gap-1 rounded bg-base p-px text-xs lg:absolute lg:-left-0.5 lg:-top-2">
<div class="inline-flex gap-1 rounded bg-base p-px text-xs md:absolute md:-left-0.5 md:-top-2">
<div class="font-light uppercase">{{ label }}</div>
<div class="select-none font-bold">
{{ mouseOver ? selectedPoint?.value ?? selectedPoint?.y ?? statValue : statValue }}
@@ -12,7 +12,7 @@ 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 x = d3.scaleLinear().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);
const selectedPoint = defineEmit<[value: Point<unknown>]>();
@@ -1,105 +1,105 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<LogEventSource /> > render html correctly > should render dates with 12 hour style 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 size-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render dates with 24 hour style 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 size-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">This is a message.</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with color 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-e625cddd="" class="mt-1.5 size-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">This is a message.</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with filter 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"2\\" class=\\"\\"><a data-v-2e92daca=\\"\\" class=\\"jump-context tooltip-primary tooltip tooltip-right\\" data-tip=\\"Jump to Context\\" href=\\"#2\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\">
<path fill=\\"currentColor\\" d=\\"M20 19.59V8l-6-6H4v20l15.57-.02l-4.81-4.81c-.8.52-1.74.83-2.76.83c-2.76 0-5-2.24-5-5s2.24-5 5-5s5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3s-3 1.34-3 3z\\"></path>
</svg></a>
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="2" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 size-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><mark>test</mark> bar</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<div class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer" title="log_actions.jump_to_context"><a class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700" href="#2"><svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
<path fill="currentColor" d="m30 28.586l-4.688-4.688a8.028 8.028 0 1 0-1.415 1.414L28.586 30zM19 25a6 6 0 1 1 6-6a6.007 6.007 0 0 1-6 6M2 12h8v2H2zM2 2h16v2H2zm0 5h16v2H2z"></path>
</svg></a></div>
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><mark>test</mark> bar</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > render html correctly > should render messages with html entities 1`] = `
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
<!--v-if-->
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
"<ul data-v-2e92daca="" class="events group py-4 medium">
<li data-v-2e92daca="" data-key="1" class="group/entry">
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
<!--v-if-->
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
<div class="inline-flex gap-2 whitespace-nowrap text-blue"><time datetime="2019-06-12T10:55:42.459Z" class="mobile-hidden">06/12/2019</time><time datetime="2019-06-12T10:55:42.459Z">10:55:42 AM</time></div>
</div>
<div data-v-e625cddd="" class="mt-1.5 size-2.5 flex-none rounded-lg flex"></div>
<div class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">&lt;test&gt;foo bar&lt;/test&gt;</div>
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
<!--v-if-->
<!--v-if-->
</div>
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">&lt;test&gt;foo bar&lt;/test&gt;</div>
</div>
</li>
</ul>"
`;
exports[`<LogEventSource /> > renders correctly 1`] = `
"<div class=\\"flex min-h-[1px] justify-center\\"><span class=\\"loading loading-bars loading-md mt-4 text-primary\\" style=\\"display: none;\\"></span></div>
<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\"></ul>"
"<div class="flex min-h-[1px] justify-center"><span class="loading loading-bars loading-md mt-4 text-primary" style="display: none;"></span></div>
<ul data-v-2e92daca="" class="events group py-4 medium"></ul>"
`;
exports[`<LogEventSource /> > should parse messages 1`] = `
+12
View File
@@ -0,0 +1,12 @@
<template>
<div class="flex flex-col gap-8 px-4 py-4 md:px-8">
<section>
<links />
</section>
<slot></slot>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
+2 -2
View File
@@ -4,7 +4,7 @@
<transition name="fade">
<div
v-show="show && (delayedShow || glopbalShow)"
class="fixed z-50 rounded border border-secondary/50 bg-base-lighter p-4 shadow"
class="fixed z-50 rounded border border-base-content/20 bg-base-lighter p-4 shadow"
ref="content"
>
<slot name="content"></slot>
@@ -14,7 +14,7 @@
</template>
<script lang="ts" setup>
import { globalShowPopup } from "@/composables/popup";
import { globalShowPopup } from "@/composable/popup";
let glopbalShow = globalShowPopup();
let show = ref(glopbalShow.value);
+74
View File
@@ -0,0 +1,74 @@
<template>
<ul class="space-y-4 p-2">
<li v-for="release in releases" v-if="releases?.length">
<div class="flex items-baseline gap-1">
<carbon:warning class="self-center stroke-orange" v-if="release.breaking > 0" />
<a :href="release.htmlUrl" class="link-primary text-lg font-bold" target="_blank" rel="noreferrer noopener">
{{ release.name }}
</a>
<span class="ml-1 text-xs"><distance-time :date="new Date(release.createdAt)" /></span>
<tag class="ml-auto bg-red px-1 py-1 text-xs" v-if="release.tag === latest?.tag">
{{ $t("releases.latest") }}
</tag>
</div>
<div class="text-sm text-base-content/80">
{{ summary(release) }}
</div>
</li>
<li v-else>
<div class="text-sm text-base-content/80">
{{ $t("releases.no_releases") }}
</div>
</li>
</ul>
</template>
<script setup lang="ts">
const { releases, latest } = useReleases();
const { t } = useI18n();
function summary(release: { features: number; bugFixes: number; breaking: number }) {
if (release.features > 0 && release.bugFixes > 0 && release.breaking > 0) {
return t("releases.three_parts", {
first: t("releases.breaking", { count: release.breaking }),
second: t("releases.features", { count: release.features }),
third: t("releases.bugFixes", { count: release.bugFixes }),
});
}
if (release.features > 0 && release.bugFixes > 0) {
return t("releases.two_parts", {
first: t("releases.features", { count: release.features }),
second: t("releases.bugFixes", { count: release.bugFixes }),
});
}
if (release.features > 0 && release.breaking > 0) {
return t("releases.two_parts", {
first: t("releases.features", { count: release.features }),
second: t("releases.breaking", { count: release.breaking }),
});
}
if (release.bugFixes > 0 && release.breaking > 0) {
return t("releases.two_parts", {
first: t("releases.bugFixes", { count: release.bugFixes }),
second: t("releases.breaking", { count: release.breaking }),
});
}
if (release.features > 0) {
return t("releases.features", { count: release.features });
}
if (release.bugFixes > 0) {
return t("releases.bugFixes", { count: release.bugFixes });
}
if (release.breaking > 0) {
return t("releases.breaking", { count: release.breaking });
}
}
</script>
<style scoped></style>
+3 -3
View File
@@ -8,7 +8,7 @@
</header>
<main :data-scrolling="scrollable ? true : undefined" class="snap-y overflow-auto">
<div class="invisible mr-28 text-right md:visible" v-show="paused">
<scroll-progress :indeterminate="loading" :auto-hide="!loading" class="z-2 !fixed top-16" />
<scroll-progress :indeterminate="loading" :auto-hide="!loading" class="!fixed top-16 z-10" />
</div>
<div ref="scrollableContent">
<slot :setLoading="setLoading"></slot>
@@ -20,8 +20,8 @@
<div class="mr-16 text-right">
<transition name="fade">
<button
class="fixed bottom-8 rounded bg-primary p-3 text-primary-content shadow transition-colors hover:bg-primary-focus"
:class="hasMore ? 'animate-bounce-fast bg-secondary text-secondary-content hover:bg-secondary-focus' : ''"
class="btn btn-primary fixed bottom-8 rounded p-3 text-primary-content shadow transition-colors"
:class="hasMore ? 'btn-secondary animate-bounce-fast text-secondary-content' : ''"
@click="scrollToBottom()"
v-show="paused"
>
+50 -18
View File
@@ -1,29 +1,35 @@
<template>
<div
class="fixed -right-px -top-px z-10 flex w-96 items-center gap-4 rounded-bl border border-secondary/20 bg-base-darker p-4 shadow"
v-show="showSearch"
v-if="search"
>
<div class="input input-primary flex h-auto items-center">
<mdi:magnify />
<input
class="input flex-1"
type="text"
placeholder="Find / RegEx"
ref="input"
v-model="searchFilter"
@keyup.esc="resetSearch()"
/>
<transition name="slide">
<div
class="fixed z-10 flex w-full justify-end p-2"
v-show="showSearch"
v-if="search"
ref="container"
:style="style"
>
<div class="input input-primary flex h-auto items-center !shadow-lg">
<mdi:magnify />
<input
class="input input-ghost w-72 flex-1"
type="text"
placeholder="Find / RegEx"
ref="input"
v-model="searchFilter"
@keyup.esc="resetSearch()"
/>
<a class="btn btn-circle btn-xs" @click="resetSearch()"> <mdi:close /></a>
</div>
</div>
<a class="btn btn-circle btn-xs" @click="resetSearch()"> <mdi:close /></a>
</div>
</transition>
</template>
<script lang="ts" setup>
const input = ref<HTMLInputElement>();
const container = ref<HTMLDivElement>();
const { searchFilter, showSearch, resetSearch } = useSearchFilter();
const { style } = useDraggable(container);
onKeyStroke("f", (e) => {
if (e.ctrlKey || e.metaKey) {
showSearch.value = true;
@@ -32,5 +38,31 @@ onKeyStroke("f", (e) => {
}
});
onMounted(() => {
onKeyStroke(
"f",
(e) => {
if (e.ctrlKey || e.metaKey) {
e.stopPropagation();
resetSearch();
}
},
{ target: input.value },
);
});
onUnmounted(() => resetSearch());
</script>
<style lang="postcss" scoped>
.slide-enter-active,
.slide-leave-active {
transition: all 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.slide-enter-from,
.slide-leave-to {
transform: translateY(-150px);
opacity: 0;
}
</style>
+16 -20
View File
@@ -20,22 +20,21 @@
<transition-group tag="ul" name="list" class="containers menu p-0 [&_li.menu-title]:px-0" v-else>
<li
v-for="item in menuItems"
:key="item.id"
:class="isLabel(item) ? 'menu-title' : item.state"
:data-testid="item.id"
:key="isContainer(item) ? item.id : item.keyLabel"
:class="isContainer(item) ? item.state : 'menu-title'"
:data-testid="isContainer(item) ? null : item.keyLabel"
>
<template v-if="isLabel(item)">
{{ item.label }}
</template>
<popup v-else>
<popup v-if="isContainer(item)">
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
active-class="active-primary"
@click.alt.stop.prevent="store.appendActiveContainer(item)"
:title="item.name"
>
<div class="truncate">
{{ item.name }}<span class="font-light opacity-70" v-if="item.isSwarm">{{ item.swarmId }}</span>
</div>
<container-health :health="item.health"></container-health>
<span
class="pin"
@click.stop.prevent="store.appendActiveContainer(item)"
@@ -44,13 +43,14 @@
>
<cil:columns />
</span>
<container-health :health="item.health"></container-health>
</router-link>
<template #content>
<container-popup :container="item"></container-popup>
</template>
</popup>
<template v-else>
{{ $t(item.keyLabel) }}
</template>
</li>
</transition-group>
</transition>
@@ -63,9 +63,7 @@
<script lang="ts" setup>
import { Container } from "@/models/Container";
import { sessionHost } from "@/composables/storage";
const { t } = useI18n();
import { sessionHost } from "@/composable/storage";
const store = useContainerStore();
@@ -93,7 +91,7 @@ const sortedContainers = computed(() =>
const groupedContainers = computed(() =>
sortedContainers.value.reduce(
(acc, item) => {
if (debouncedIds.value.has(item.storageKey)) {
if (debouncedIds.value.has(item.name)) {
acc.pinned.push(item);
} else {
acc.unpinned.push(item);
@@ -104,15 +102,13 @@ const groupedContainers = computed(() =>
),
);
type MenuLabel = { label: string; id: string; state: string };
const pinnedLabel = { label: t("label.pinned"), id: "pinned", state: "label" } as MenuLabel;
const allLabel = { label: t("label.containers"), id: "containers", state: "label" } as MenuLabel;
function isLabel(item: Container | MenuLabel): item is MenuLabel {
return (item as MenuLabel).label !== undefined;
function isContainer(item: any): item is Container {
return item.hasOwnProperty("image");
}
const menuItems = computed(() => {
const pinnedLabel = { keyLabel: "label.pinned" };
const allLabel = { keyLabel: showAllContainers.value ? "label.all-containers" : "label.running-containers" };
if (groupedContainers.value.pinned.length > 0) {
return [pinnedLabel, ...groupedContainers.value.pinned, allLabel, ...groupedContainers.value.unpinned];
} else {
@@ -145,7 +141,7 @@ const activeContainersById = computed(() =>
@apply text-[0.95rem];
}
.containers a {
@apply auto-cols-[auto_max-content];
@apply auto-cols-[auto_max-content_max-content];
.pin {
display: none;
+1 -14
View File
@@ -12,19 +12,6 @@
</small>
</h1>
<div class="flex gap-4">
<router-link
:to="{ name: 'settings' }"
:aria-label="$t('title.settings')"
data-testid="settings"
class="btn btn-circle btn-sm"
>
<mdi:cog />
</router-link>
<a :href="`${base}/logout`" :title="$t('button.logout')" v-if="secured" class="btn btn-circle btn-sm">
<mdi:logout />
</a>
</div>
<a
class="input input-sm mt-4 inline-flex cursor-pointer items-center gap-2 font-light hover:border-primary"
@click="$emit('search')"
@@ -40,5 +27,5 @@
</template>
<script lang="ts" setup>
const { base, secured, hostname } = config;
const { hostname } = config;
</script>
+23 -6
View File
@@ -10,13 +10,30 @@ const props = defineProps<{
date: Date;
}>();
const dateFormatter = new Intl.DateTimeFormat(undefined, { day: "2-digit", month: "2-digit", year: "numeric" });
const use12Hour = $computed(() => ({ auto: undefined, "12": true, "24": false })[hourStyle.value]);
const timeFormatter = $computed(
const dateOverride = computed(() => (dateLocale.value === "auto" ? undefined : dateLocale.value));
const dateFormatter = computed(
() => new Intl.DateTimeFormat(dateOverride.value, { day: "2-digit", month: "2-digit", year: "numeric" }),
);
const hourCycle = computed(() => {
switch (hourStyle.value) {
case "auto":
return undefined;
case "12":
return "h12";
case "24":
return "h23";
}
});
const timeFormatter = computed(
() =>
new Intl.DateTimeFormat(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: use12Hour }),
new Intl.DateTimeFormat(undefined, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: hourCycle.value,
}),
);
const dateStr = $computed(() => dateFormatter.format(props.date));
const timeStr = $computed(() => timeFormatter.format(props.date));
const dateStr = computed(() => dateFormatter.value.format(props.date));
const timeStr = computed(() => timeFormatter.value.format(props.date));
</script>
+2 -2
View File
@@ -3,8 +3,8 @@
</template>
<script lang="ts" setup>
import formatDistanceToNow from "date-fns/formatDistanceToNow";
import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict";
import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
import { formatDistanceToNowStrict } from "date-fns/formatDistanceToNowStrict";
const {
date,
+21 -40
View File
@@ -1,44 +1,25 @@
<template>
<details class="dropdown" ref="details" v-on-click-outside="close">
<summary class="btn btn-primary flex-nowrap" v-bind="$attrs">
<slot name="trigger"> {{ values[modelValue] ?? defaultLabel }} <carbon:caret-down /></slot>
</summary>
<ul class="menu dropdown-content rounded-box z-50 w-52 bg-base p-2 shadow">
<slot>
<li v-for="item in options">
<a @click="modelValue = item.value">
<mdi:check class="w-4" v-if="modelValue == item.value" />
<div v-else class="w-4"></div>
{{ item.label }}
</a>
</li>
</slot>
</ul>
</details>
<div class="dropdown">
<label tabindex="0" class="btn btn-circle btn-sm" @mousedown="checkAndCloseDropDown($event)" @blur="closed()">
<slot name="trigger"></slot>
</label>
<div
tabindex="0"
class="dropdown-content z-50 mt-1 min-w-52 rounded-box border border-base-content/20 bg-base p-2 shadow"
>
<slot name="content"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { vOnClickOutside } from "@vueuse/components";
type DropdownItem = {
label: string;
value: string;
};
const { options = [], defaultLabel = "" } = defineProps<{ options?: DropdownItem[]; defaultLabel?: string }>();
const { modelValue } = defineModels<{
modelValue: string;
}>();
const values = computed(() =>
options.reduce(
(acc, curr) => {
acc[curr.value] = curr.label;
return acc;
},
{} as Record<string, string>,
),
);
const details = ref<HTMLElement | null>(null);
const close = () => details.value?.removeAttribute("open");
watch(modelValue, () => close());
<script setup lang="ts">
const closed = defineEmit();
function checkAndCloseDropDown(e: MouseEvent) {
const target = e.currentTarget as HTMLElement;
if (target?.matches(":focus")) {
setTimeout(() => target.blur(), 0);
}
}
</script>
<style scoped></style>
+40
View File
@@ -0,0 +1,40 @@
<template>
<details class="dropdown" ref="details" v-on-click-outside="close">
<summary class="btn btn-primary flex-nowrap" v-bind="$attrs">
<slot name="trigger"> {{ label }} <carbon:caret-down /></slot>
</summary>
<ul class="menu dropdown-content z-50 mt-1 w-52 rounded-box border border-base-content/20 bg-base p-2 shadow">
<slot>
<li v-for="item in options">
<a @click="modelValue = item.value">
<mdi:check class="w-4" v-if="modelValue == item.value" />
<div v-else class="w-4"></div>
{{ item.label }}
</a>
</li>
</slot>
</ul>
</details>
</template>
<script lang="ts" setup generic="T">
import { vOnClickOutside } from "@vueuse/components";
type DropdownItem = {
label: string;
value: T;
};
const { modelValue } = defineModels<{
modelValue: T;
}>();
const { options = [], defaultLabel = "" } = defineProps<{
options?: DropdownItem[];
defaultLabel?: string;
}>();
const label = computed(() => options.find((item) => item.value === modelValue.value)?.label ?? defaultLabel);
const details = ref<HTMLElement | null>(null);
const close = () => details.value?.removeAttribute("open");
watch(modelValue, () => close());
</script>
+2 -2
View File
@@ -4,8 +4,8 @@
<carbon:mac-shift />
</template>
<template v-if="modifiers.includes('meta')">
<ph:command v-if="isMac" class="h-4 w-4" />
<ph:control-bold v-else class="h-4 w-4" />
<ph:command v-if="isMac" class="size-4" />
<ph:control-bold v-else class="size-4" />
</template>
<kbd class="uppercase">{{ char }}</kbd>
</div>
+10
View File
@@ -0,0 +1,10 @@
<template>
<label class="label cursor-pointer gap-4">
<div class="label-text"><slot name="label" /></div>
<slot name="input" />
</label>
</template>
<script setup lang="ts"></script>
<style scoped></style>
+9 -12
View File
@@ -9,7 +9,7 @@
<div class="ml-auto flex items-center gap-2">
<a class="btn btn-circle flex" @click="$emit('search')" :title="$t('tooltip.search')">
<mdi:magnify class="h-5 w-5" />
<mdi:magnify class="size-5" />
</a>
<label class="btn btn-circle swap swap-rotate" data-testid="hamburger">
<input type="checkbox" v-model="show" />
@@ -20,26 +20,20 @@
</div>
<transition name="fade">
<div v-show="show">
<div v-show="show" class="h-[calc(100vh-60px)] overflow-auto">
<div class="mt-4 flex items-center justify-center gap-2">
<dropdown
<dropdown-menu
v-model="sessionHost"
:options="hosts"
defaultLabel="Hosts"
class="btn-sm"
v-if="config.hosts.length > 1"
/>
<router-link :to="{ name: 'settings' }" class="btn btn-outline btn-sm">
<mdi:cog /> {{ $t("button.settings") }}
</router-link>
<a class="btn btn-outline btn-sm" :href="`${base}/logout`" :title="$t('button.logout')" v-if="secured">
<mdi:logout /> {{ $t("button.logout") }}
</a>
</div>
<ul class="menu">
<li class="menu-title">{{ $t("label.containers") }}</li>
<li v-for="item in sortedContainers" :key="item.id">
<li v-for="item in sortedContainers" :key="item.id" :class="item.state">
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
active-class="active-primary"
@@ -56,8 +50,7 @@
</template>
<script lang="ts" setup>
const { base, secured } = config;
import { sessionHost } from "@/composables/storage";
import { sessionHost } from "@/composable/storage";
const store = useContainerStore();
const route = useRoute();
const { visibleContainers } = storeToRefs(store);
@@ -85,6 +78,10 @@ const sortedContainers = computed(() =>
const hosts = computed(() => config.hosts.map(({ id, name }) => ({ value: id, label: name })));
</script>
<style scoped lang="postcss">
li.exited {
@apply opacity-50;
}
.fade-enter-active,
.fade-leave-active {
@apply transition-opacity;
+8 -4
View File
@@ -1,8 +1,12 @@
<template>
<label class="label inline-flex cursor-pointer gap-4 font-normal">
<input type="checkbox" class="toggle toggle-primary hover:bg-primary-focus" v-model="modelValue" />
<slot />
</label>
<labeled-input>
<template #label>
<slot />
</template>
<template #input>
<input type="checkbox" class="toggle toggle-primary" v-model="modelValue" />
</template>
</labeled-input>
</template>
<script lang="ts" setup>
+45
View File
@@ -0,0 +1,45 @@
type ContainerActions = "start" | "stop" | "restart";
export const useContainerActions = () => {
const { container } = useContainerContext();
const { showToast } = useToast();
const actionStates = reactive({
stop: false,
restart: false,
start: false,
});
async function actionHandler(action: ContainerActions) {
const actionUrl = `/api/actions/${action}/${container.value.host}/${container.value.id}`;
const errors = {
404: "container not found",
500: "unable to complete action",
400: "invalid action",
} as Record<number, string>;
const defaultError = "something went wrong";
const toastTitle = "Action Failed";
actionStates[action] = true;
try {
const response = await fetch(withBase(actionUrl), { method: "POST" });
if (!response.ok) {
const message = errors[response.status] ?? defaultError;
showToast({ type: "error", message, title: toastTitle });
}
} catch (error) {
showToast({ type: "error", message: defaultError, title: toastTitle });
}
actionStates[action] = false;
}
return {
actionStates,
start: () => actionHandler("start"),
stop: () => actionHandler("stop"),
restart: () => actionHandler("restart"),
};
};
+16
View File
@@ -0,0 +1,16 @@
import { JSONObject, LogEntry } from "@/models/LogEntry";
const lastSelectedItem = ref<LogEntry<string | JSONObject> | undefined>(undefined);
export const useLogSearchContext = () => {
const { resetSearch } = useSearchFilter();
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
lastSelectedItem.value = item;
resetSearch();
}
return {
lastSelectedItem,
handleJumpLineSelected,
};
};
+36
View File
@@ -0,0 +1,36 @@
import { Profile } from "@/stores/config";
export function useProfileStorage<K extends keyof Profile>(key: K, defaultValue: NonNullable<Profile[K]>) {
const storageKey = "DOZZLE_" + key.toUpperCase();
const storage = useStorage<NonNullable<Profile[K]>>(storageKey, defaultValue, undefined, {
writeDefaults: false,
mergeDefaults: true,
});
if (config.profile?.[key]) {
if (storage.value instanceof Set && config.profile[key] instanceof Array) {
storage.value = new Set([...(config.profile[key] as Iterable<any>)]) as unknown as NonNullable<Profile[K]>;
} else if (config.profile[key] instanceof Array) {
storage.value = config.profile[key] as NonNullable<Profile[K]>;
} else if (config.profile[key] instanceof Object) {
Object.assign(storage.value, config.profile[key]);
} else {
storage.value = config.profile[key] as NonNullable<Profile[K]>;
}
}
if (config.user) {
watch(
storage,
(value) => {
fetch(withBase("/api/profile"), {
method: "PATCH",
body: JSON.stringify({ [key]: value }, (_, value) => (value instanceof Set ? [...value] : value)),
});
},
{ deep: true },
);
}
return storage;
}
+24
View File
@@ -0,0 +1,24 @@
import { Container } from "@/models/Container";
const DOZZLE_HOST = "DOZZLE_HOST";
export const sessionHost = useSessionStorage<string | null>(DOZZLE_HOST, null);
if (config.hosts.length === 1 && !sessionHost.value) {
sessionHost.value = config.hosts[0].id;
}
export function persistentVisibleKeys(container: Ref<Container>): Ref<string[][]> {
const storage = useProfileStorage("visibleKeys", {});
return computed(() => {
if (!(container.value.storageKey in storage.value)) {
// Returning a temporary ref here to avoid writing an empty array to storage
const visibleKeys = ref<string[][]>([]);
watchOnce(visibleKeys, () => (storage.value[container.value.storageKey] = visibleKeys.value), { deep: true });
return visibleKeys.value;
}
return storage.value[container.value.storageKey];
});
}
export const pinnedContainers = useProfileStorage("pinned", new Set<string>());
@@ -1,12 +1,12 @@
import { ComplexLogEntry, type JSONObject, type LogEntry } from "@/models/LogEntry";
import type { ComputedRef, Ref } from "vue";
import type { Ref } from "vue";
export function useVisibleFilter(visibleKeys: ComputedRef<Ref<string[][]>>) {
export function useVisibleFilter(visibleKeys: Ref<string[][]>) {
function filteredPayload(messages: Ref<LogEntry<string | JSONObject>[]>) {
return computed(() => {
return messages.value.map((d) => {
if (d instanceof ComplexLogEntry) {
return ComplexLogEntry.fromLogEvent(d, visibleKeys.value);
return ComplexLogEntry.fromLogEvent(d, visibleKeys);
} else {
return d;
}
-15
View File
@@ -1,15 +0,0 @@
import { Container } from "@/models/Container";
const DOZZLE_HOST = "DOZZLE_HOST";
export const sessionHost = useSessionStorage<string | null>(DOZZLE_HOST, null);
if (config.hosts.length === 1 && !sessionHost.value) {
sessionHost.value = config.hosts[0].id;
}
export function persistentVisibleKeys(container: Ref<Container>) {
return computed(() => useStorage(container.value.storageKey, []));
}
const DOZZLE_PINNED_CONTAINERS = "DOZZLE_PINNED_CONTAINERS";
export const pinnedContainers = useStorage(DOZZLE_PINNED_CONTAINERS, new Set<string>());
+13 -9
View File
@@ -1,5 +1,5 @@
<template>
<div v-if="!authorizationNeeded">
<div>
<mobile-menu v-if="isMobile" @search="showFuzzySearch"></mobile-menu>
<splitpanes @resized="onResized($event)">
<pane min-size="10" :size="menuWidth" v-if="!isMobile && !collapseNav">
@@ -25,12 +25,12 @@
</pane>
</splitpanes>
<label
class="btn btn-circle swap swap-rotate fixed bottom-8 left-4"
:class="{ '!-left-3': collapseNav }"
class="btn btn-circle swap btn-neutral swap-rotate fixed -left-12 bottom-4 w-16 transition-all hover:-left-4"
:class="{ '!-left-6': collapseNav }"
v-if="!isMobile"
>
<input type="checkbox" v-model="collapseNav" />
<mdi:chevron-right class="swap-on text-secondary" />
<mdi:chevron-right class="swap-on" />
<mdi:chevron-left class="swap-off" />
</label>
</div>
@@ -47,13 +47,18 @@
class="alert max-w-xl"
v-for="toast in toasts"
:key="toast.id"
:class="{ 'alert-error': toast.type === 'error', 'alert-info': toast.type === 'info' }"
:class="{
'alert-error': toast.type === 'error',
'alert-info': toast.type === 'info',
'alert-warning': toast.type === 'warning',
}"
>
<carbon:information class="h-6 w-6 shrink-0 stroke-current" v-if="toast.type === 'info'" />
<carbon:warning class="h-6 w-6 shrink-0 stroke-current" v-else-if="toast.type === 'error'" />
<carbon:information class="size-6 shrink-0 stroke-current" v-if="toast.type === 'info'" />
<carbon:warning class="size-6 shrink-0 stroke-current" v-else-if="toast.type === 'error'" />
<carbon:warning class="size-6 shrink-0 stroke-current" v-else-if="toast.type === 'warning'" />
<div>
<h3 class="text-lg font-bold" v-if="toast.title">{{ toast.title }}</h3>
{{ toast.message }}
<div v-html="toast.message" class="[&>a]:underline"></div>
</div>
<div>
<button class="btn btn-circle btn-xs" @click="removeToast(toast.id)"><mdi:close /></button>
@@ -66,7 +71,6 @@
// @ts-ignore - splitpanes types are not available
import { Splitpanes, Pane } from "splitpanes";
import { collapseNav } from "@/stores/settings";
const { authorizationNeeded } = config;
const containerStore = useContainerStore();
const { activeContainers } = storeToRefs(containerStore);
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div class="hero min-h-screen">
<div class="hero min-h-dvh">
<div class="hero-content">
<router-view></router-view>
</div>
+30 -26
View File
@@ -1,25 +1,21 @@
@import "splitpanes/dist/splitpanes.css";
@define-mixin light {
--base-lighter-color: 0 0% 100%;
--base-color: 0 0% 97%;
--base-darker-color: 0 0% 90%;
--base-content-color: 0 0% 21%;
--primary-color: 171 100% 41%;
--primary-focus-color: 171 100% 31%;
--secondary-color: 34 95% 53%;
--secondary-focus-color: 34 95% 43%;
--base-lighter-color: 100% 0 0;
--base-color: 97% 0 0;
--base-darker-color: 90% 0 0;
--base-content-color: 33% 0 0;
--primary-color: 79.96% 0.143 176.65;
--secondary-color: 82.67% 0.15380739746702807 77.82;
}
@define-mixin dark {
--base-lighter-color: 0 0% 14%;
--base-color: 0 0% 7%;
--base-darker-color: 0 0% 4%;
--base-content-color: 0 0% 86%;
--primary-color: 171 100% 41%;
--primary-focus-color: 171 100% 31%;
--secondary-color: 44 100% 77%;
--secondary-focus-color: 44 100% 67%;
--base-lighter-color: 25% 0 0;
--base-color: 18% 0 0;
--base-darker-color: 11% 0 0;
--base-content-color: 89.23% 0 0;
--primary-color: 70.96% 0.143 176.65;
--secondary-color: 91.36% 0.11098 90.1243;
}
@tailwind base;
@@ -29,19 +25,20 @@
@layer base {
:root {
--p: var(--primary-color);
--pf: var(--primary-focus-color);
--pc: 0 0% 100%;
--pc: 89.23% 0;
--s: var(--secondary-color);
--sf: var(--secondary-focus-color);
--sc: 0 0% 4%;
--sc: 14.57% 0 0;
--b1: var(--base-lighter-color);
--b2: var(--base-color);
--b3: var(--base-darker-color);
--bc: var(--base-content-color);
--in: 207 90% 54%;
--inc: 207 90% 94%;
--er: 4 90% 58%;
--erc: 4 90% 98%;
--in: 65% 0.171 249.5;
--inc: 100% 0 0;
--er: 64% 0.218 28.85;
--erc: 100% 0 0;
--wa: 70% 0.186 48.13;
--wac: 100% 0 0;
--n: var(--base-lighter-color);
}
html[data-theme="dark"] {
@mixin dark;
@@ -77,7 +74,7 @@
@apply text-lg;
}
mark {
.events mark {
@apply inline-block rounded-sm bg-secondary;
animation: pops 200ms ease-out;
}
@@ -94,7 +91,10 @@
@layer components {
.input {
@apply focus:outline-none;
@apply !outline-none;
&.input-ghost {
@apply focus:border-transparent;
}
}
.btn {
@@ -118,4 +118,8 @@
--nc: var(--pc);
@apply active;
}
.link-primary {
@apply underline-offset-4 hover:underline;
}
}
-1
View File
@@ -1,4 +1,3 @@
// import "./styles.scss";
import "./main.css";
import { createApp, App as VueApp } from "vue";
import App from "./App.vue";
+1 -1
View File
@@ -18,7 +18,7 @@ describe("Container", () => {
];
test.each(names)("name %s should be %s and %s", (name, expectedName, expectedSwarmId) => {
const c = new Container("id", new Date(), "image", name!, "command", "host", "status", "created");
const c = new Container("id", new Date(), "image", name!, "command", "host", {}, "status", "created", []);
expect(c.name).toBe(expectedName);
expect(c.swarmId).toBe(expectedSwarmId);
});
+7 -6
View File
@@ -1,6 +1,5 @@
import type { ContainerHealth, ContainerStat, ContainerState } from "@/types/Container";
import type { UseThrottledRefHistoryReturn } from "@vueuse/core";
import { useExponentialMovingAverage } from "@/utils";
import { useExponentialMovingAverage, useSimpleRefHistory } from "@/utils";
import { Ref } from "vue";
type Stat = Omit<ContainerStat, "id">;
@@ -19,7 +18,7 @@ const hosts = computed(() =>
export class Container {
private _stat: Ref<Stat>;
private readonly throttledStatHistory: UseThrottledRefHistoryReturn<Stat, Stat>;
private readonly _statsHistory: Ref<Stat[]>;
public readonly swarmId: string | null = null;
public readonly isSwarm: boolean = false;
private readonly movingAverageStat: Ref<Stat>;
@@ -31,12 +30,14 @@ export class Container {
public readonly name: string,
public readonly command: string,
public readonly host: string,
public readonly labels = {} as Record<string, string>,
public status: string,
public state: ContainerState,
stats: Stat[],
public health?: ContainerHealth,
) {
this._stat = ref({ cpu: 0, memory: 0, memoryUsage: 0 });
this.throttledStatHistory = useThrottledRefHistory(this._stat, { capacity: 300, deep: true, throttle: 1000 });
this._statsHistory = useSimpleRefHistory(this._stat, { capacity: 300, deep: true, initial: stats });
this.movingAverageStat = useExponentialMovingAverage(this._stat, 0.2);
const match = name.match(SWARM_ID_REGEX);
@@ -47,8 +48,8 @@ export class Container {
}
}
get statHistory() {
return unref(this.throttledStatHistory.history);
get statsHistory() {
return unref(this._statsHistory);
}
get movingAverage() {
+5 -4
View File
@@ -9,11 +9,12 @@ export type JSONValue = string | number | boolean | JSONObject | Array<JSONValue
export type JSONObject = { [x: string]: JSONValue };
export type Position = "start" | "end" | "middle" | undefined;
export type Std = "stdout" | "stderr";
export type Level = "debug" | "info" | "warn" | "error" | "fatal" | "trace" | "unknown";
export interface LogEvent {
readonly m: string | JSONObject;
readonly ts: number;
readonly id: number;
readonly l: string;
readonly l: Level;
readonly p: Position;
readonly s: "stdout" | "stderr" | "unknown";
}
@@ -25,7 +26,7 @@ export abstract class LogEntry<T extends string | JSONObject> {
public readonly id: number,
public readonly date: Date,
public readonly std: Std,
public readonly level?: string,
public readonly level?: Level,
) {
this._message = message;
}
@@ -42,7 +43,7 @@ export class SimpleLogEntry extends LogEntry<string> {
message: string,
id: number,
date: Date,
public readonly level: string,
public readonly level: Level,
public readonly position: Position,
public readonly std: Std,
) {
@@ -60,7 +61,7 @@ export class ComplexLogEntry extends LogEntry<JSONObject> {
message: JSONObject,
id: number,
date: Date,
public readonly level: string,
public readonly level: Level,
public readonly std: Std,
visibleKeys?: Ref<string[][]>,
) {
+9 -5
View File
@@ -1,19 +1,23 @@
import { type App } from "vue";
import { createI18n } from "vue-i18n";
import { locale } from "@/stores/settings";
import messages from "@intlify/unplugin-vue-i18n/messages";
const locale = messages?.hasOwnProperty(navigator.language) ? navigator.language : navigator.language.slice(0, 2);
const defaultLocale = messages?.hasOwnProperty(navigator.language)
? navigator.language
: navigator.language.slice(0, 2);
const i18n = createI18n({
legacy: false,
locale: locale,
locale: locale.value || defaultLocale,
fallbackLocale: "en",
messages,
});
export const install = (app: App) => {
app.use(i18n);
};
watch(locale, (value) => {
i18n.global.locale.value = value || defaultLocale;
});
export const install = (app: App) => app.use(i18n);
export default i18n;
+6 -7
View File
@@ -3,13 +3,12 @@ import { createRouter, createWebHistory } from "vue-router";
import pages from "~pages";
import { setupLayouts } from "virtual:generated-layouts";
const routes = setupLayouts(pages);
export const router = createRouter({
history: createWebHistory(withBase("/")),
routes,
});
export const install = (app: App) => {
const routes = setupLayouts(pages);
const router = createRouter({
history: createWebHistory(withBase("/")),
routes,
});
app.use(router);
};
+7 -5
View File
@@ -1,11 +1,13 @@
<template>
<div class="hero min-h-screen bg-base-200">
<div class="hero-content text-center">
<div class="max-w-md">
<p class="py-6 text-2xl font-bold">{{ $t("error.page-not-found") }}</p>
<page-with-links>
<div class="hero min-h-screen bg-base-200">
<div class="hero-content text-center">
<div class="max-w-md">
<p class="py-6 text-2xl font-bold">{{ $t("error.page-not-found") }}</p>
</div>
</div>
</div>
</div>
</page-with-links>
</template>
<script lang="ts" setup>
+3 -22
View File
@@ -1,16 +1,5 @@
<template>
<div class="flex flex-col gap-16 px-4 pt-8 md:px-8">
<section v-if="config.user">
<div class="flex items-center justify-end gap-4">
<div v-if="config.authProvider === 'simple'">
<button @click.prevent="logout()" class="link-primary">{{ $t("button.logout") }}</button>
</div>
<div>
{{ config.user.name ? config.user.name : config.user.email }}
</div>
<img class="h-10 w-10 rounded-full p-1 ring-2 ring-base-content/50" :src="config.user.avatar" />
</div>
</section>
<page-with-links class="gap-16">
<section>
<div class="stats grid bg-base-lighter shadow">
<div class="stat">
@@ -18,7 +7,7 @@
<div class="stat-title">{{ $t("label.running") }} / {{ $t("label.total-containers") }}</div>
</div>
<div class="stat">
<div class="stat-value">{{ totalCpu }}%</div>
<div class="stat-value">{{ totalCpu.toFixed(0) }}%</div>
<div class="stat-title">{{ $t("label.total-cpu-usage") }}</div>
</div>
<div class="stat">
@@ -36,7 +25,7 @@
<section>
<container-table :containers="runningContainers"></container-table>
</section>
</div>
</page-with-links>
</template>
<script lang="ts" setup>
@@ -76,14 +65,6 @@ watchEffect(() => {
setTitle(t("title.dashboard", { count: runningContainers.length }));
}
});
async function logout() {
await fetch(withBase("/api/token"), {
method: "DELETE",
});
location.reload();
}
</script>
<style lang="postcss" scoped>
:deep(tr td) {
+149 -82
View File
@@ -1,79 +1,127 @@
<template>
<div class="mt-10 flex flex-col gap-8 px-4 md:px-8">
<page-with-links>
<section>
<div class="has-underline">
<h2>{{ $t("settings.about") }}</h2>
</div>
<div>
<span v-html="$t('settings.using-version', { version: currentVersion })"></span>
<span v-html="$t('settings.using-version', { version: config.version })"></span>
<div
v-if="hasUpdate"
v-html="$t('settings.update-available', { nextVersion: nextRelease.name, href: nextRelease.html_url })"
v-html="$t('settings.update-available', { nextVersion: latest?.name, href: latest?.htmlUrl })"
></div>
</div>
</section>
<section class="flex flex-col gap-4">
<section class="flex flex-col @container">
<div class="has-underline">
<h2>{{ $t("settings.display") }}</h2>
</div>
<div>
<toggle v-model="smallerScrollbars"> {{ $t("settings.small-scrollbars") }} </toggle>
</div>
<div>
<toggle v-model="showTimestamp">{{ $t("settings.show-timesamps") }}</toggle>
</div>
<div>
<toggle v-model="showStd">{{ $t("settings.show-std") }}</toggle>
</div>
<section class="grid-cols-2 gap-4 @3xl:grid">
<div class="flex flex-col gap-2 text-balance @3xl:pr-8">
<toggle v-model="compact"> {{ $t("settings.compact") }} </toggle>
<div>
<toggle v-model="softWrap">{{ $t("settings.soft-wrap") }}</toggle>
</div>
<toggle v-model="smallerScrollbars"> {{ $t("settings.small-scrollbars") }} </toggle>
<div class="flex items-center gap-6">
<dropdown
v-model="hourStyle"
:options="[
{ label: 'Auto', value: 'auto' },
{ label: '12', value: '12' },
{ label: '24', value: '24' },
]"
<toggle v-model="showTimestamp">{{ $t("settings.show-timesamps") }}</toggle>
<toggle v-model="showStd">{{ $t("settings.show-std") }}</toggle>
<toggle v-model="softWrap">{{ $t("settings.soft-wrap") }}</toggle>
<labeled-input>
<template #label>
{{ $t("settings.locale") }}
</template>
<template #input>
<dropdown-menu
v-model="locale"
:options="[
{ label: 'Auto', value: '' },
...availableLocales.map((l) => ({ label: l.toLocaleUpperCase(), value: l })),
]"
/>
</template>
</labeled-input>
<labeled-input>
<template #label>
{{ $t("settings.datetime-format") }}
</template>
<template #input>
<div class="flex gap-2">
<dropdown-menu
v-model="dateLocale"
:options="[
{ label: 'Auto', value: 'auto' },
{ label: 'MM/DD/YYYY', value: 'en-US' },
{ label: 'DD/MM/YYYY', value: 'en-GB' },
{ label: 'DD.MM.YYYY', value: 'de-DE' },
{ label: 'YYYY-MM-DD', value: 'en-CA' },
]"
/>
<dropdown-menu
v-model="hourStyle"
:options="[
{ label: 'Auto', value: 'auto' },
{ label: '12', value: '12' },
{ label: '24', value: '24' },
]"
/>
</div>
</template>
</labeled-input>
<labeled-input>
<template #label>
{{ $t("settings.font-size") }}
</template>
<template #input>
<dropdown-menu
v-model="size"
:options="[
{ label: 'Small', value: 'small' },
{ label: 'Medium', value: 'medium' },
{ label: 'Large', value: 'large' },
]"
/>
</template>
</labeled-input>
<labeled-input>
<template #label>
{{ $t("settings.color-scheme") }}
</template>
<template #input>
<dropdown-menu
v-model="lightTheme"
:options="[
{ label: 'Auto', value: 'auto' },
{ label: 'Dark', value: 'dark' },
{ label: 'Light', value: 'light' },
]"
/>
</template>
</labeled-input>
</div>
<log-viewer
:messages="fakeMessages"
:visible-keys="keys"
:last-selected-item="undefined"
class="hidden overflow-hidden rounded-lg border border-base-content/50 shadow @3xl:block"
/>
{{ $t("settings.12-24-format") }}
</div>
<div class="flex items-center gap-6">
<dropdown
v-model="size"
:options="[
{ label: 'Small', value: 'small' },
{ label: 'Medium', value: 'medium' },
{ label: 'Large', value: 'large' },
]"
/>
{{ $t("settings.font-size") }}
</div>
<div class="flex items-center gap-6">
<dropdown
v-model="lightTheme"
:options="[
{ label: 'Auto', value: 'auto' },
{ label: 'Dark', value: 'dark' },
{ label: 'Light', value: 'light' },
]"
/>
{{ $t("settings.color-scheme") }}
</div>
</section>
</section>
<section class="flex flex-col gap-2">
<div class="has-underline">
<h2>{{ $t("settings.options") }}</h2>
</div>
<div>
<toggle v-model="search">
<div>{{ $t("settings.search") }} <key-shortcut char="f" class="align-top"></key-shortcut></div>
{{ $t("settings.search") }} <key-shortcut char="f" class="align-top"></key-shortcut>
</toggle>
</div>
@@ -85,50 +133,69 @@
<toggle v-model="automaticRedirect">{{ $t("settings.automatic-redirect") }}</toggle>
</div>
</section>
</div>
</page-with-links>
</template>
<script lang="ts" setup>
import { ComplexLogEntry, SimpleLogEntry } from "@/models/LogEntry";
import {
search,
lightTheme,
smallerScrollbars,
showTimestamp,
showStd,
hourStyle,
showAllContainers,
size,
softWrap,
automaticRedirect,
compact,
hourStyle,
dateLocale,
lightTheme,
search,
showAllContainers,
showStd,
showTimestamp,
size,
smallerScrollbars,
softWrap,
locale,
} from "@/stores/settings";
const { t } = useI18n();
const { t, availableLocales } = useI18n();
setTitle(t("title.settings"));
const { latest, hasUpdate } = useReleases();
const currentVersion = config.version;
let nextRelease = $ref({ html_url: "", name: "" });
let hasUpdate = $ref(false);
const keys = ref<string[][]>([]);
const hoursAgo = (hours: number) => {
const date = new Date();
date.setHours(date.getHours() - hours);
return date;
};
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 =
release.tag_name.slice(1).localeCompare(currentVersion, undefined, { numeric: true, sensitivity: "base" }) > 0;
nextRelease = release;
}
} else {
hasUpdate = true;
nextRelease = {
html_url: "",
name: "master",
};
}
}
fetchNextRelease();
const fakeMessages = [
new SimpleLogEntry("This is a preview of the logs", 1, hoursAgo(16), "info", undefined, "stdout"),
new SimpleLogEntry("A warning log looks like this", 2, hoursAgo(12), "warn", undefined, "stdout"),
new SimpleLogEntry("This is a multi line error message", 3, hoursAgo(7), "error", "start", "stderr"),
new SimpleLogEntry("with a second line", 4, hoursAgo(2), "error", "middle", "stderr"),
new SimpleLogEntry("and finally third line.", 5, new Date(), "error", "end", "stderr"),
new ComplexLogEntry(
{
message: "This is a complex log entry as json",
context: {
key: "value",
key2: "value2",
},
},
6,
new Date(),
"info",
"stdout",
keys,
),
new SimpleLogEntry(
"This is a very very long message which would wrap by default. Disabling soft wraps would disable this. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
7,
new Date(),
"debug",
undefined,
"stderr",
),
];
</script>
<style lang="postcss" scoped>
.has-underline {
+10 -5
View File
@@ -2,22 +2,27 @@ import { type Settings } from "@/stores/settings";
const text = document.querySelector("script#config__json")?.textContent || "{}";
interface Config {
export interface Config {
version: string;
base: string;
authorizationNeeded: boolean;
secured: boolean;
maxLogs: number;
hostname: string;
hosts: { name: string; id: string }[];
authProvider: "simple" | "none" | "forward-proxy";
enableActions: boolean;
user?: {
username: string;
email: string;
name: string;
avatar: string;
};
serverSettings?: Settings;
profile?: Profile;
}
export interface Profile {
settings?: Settings;
pinned?: Set<string>;
visibleKeys?: { [key: string]: string[][] };
releaseSeen?: string;
}
const pageConfig = JSON.parse(text);
+3 -1
View File
@@ -64,7 +64,7 @@ export const useContainerStore = defineStore("container", () => {
const event = JSON.parse((e as MessageEvent).data) as { actorId: string };
const container = allContainersById.value[event.actorId];
if (container) {
container.state = "dead";
container.state = "exited";
}
});
@@ -124,8 +124,10 @@ export const useContainerStore = defineStore("container", () => {
c.name,
c.command,
c.host,
c.labels,
c.status,
c.state,
c.stats,
c.health,
);
}),
+29
View File
@@ -0,0 +1,29 @@
const { data: releases } = useFetch(withBase("/api/releases")).get().json<
{
name: string;
mentionsCount: number;
createdAt: string;
body: string;
tag: string;
htmlUrl: string;
latest: boolean;
features: number;
bugFixes: number;
breaking: number;
}[]
>();
const hasUpdate = computed(() => {
if (!releases.value?.length) return false;
return releases.value[0].tag !== config.version;
});
const latest = computed(() => releases.value?.find((release) => release.latest));
export function useReleases() {
return {
hasUpdate,
latest,
releases,
};
}
+10 -12
View File
@@ -1,9 +1,9 @@
import { toRefs } from "@vueuse/core";
const DOZZLE_SETTINGS_KEY = "DOZZLE_SETTINGS";
export type Settings = {
search: boolean;
size: "small" | "medium" | "large";
compact: boolean;
menuWidth: number;
smallerScrollbars: boolean;
showTimestamp: boolean;
@@ -11,12 +11,15 @@ export type Settings = {
showAllContainers: boolean;
lightTheme: "auto" | "dark" | "light";
hourStyle: "auto" | "24" | "12";
dateLocale: "auto" | "en-US" | "en-GB" | "de-DE" | "en-CA";
softWrap: boolean;
collapseNav: boolean;
automaticRedirect: boolean;
locale: string;
};
export const DEFAULT_SETTINGS: Settings = {
search: true,
compact: false,
size: "medium",
menuWidth: 15,
smallerScrollbars: false,
@@ -25,27 +28,21 @@ export const DEFAULT_SETTINGS: Settings = {
showAllContainers: false,
lightTheme: "auto",
hourStyle: "auto",
dateLocale: "auto",
softWrap: true,
collapseNav: false,
automaticRedirect: true,
locale: "",
};
export const settings = useStorage(DOZZLE_SETTINGS_KEY, DEFAULT_SETTINGS);
settings.value = { ...DEFAULT_SETTINGS, ...settings.value, ...config.serverSettings };
if (config.user) {
watch(settings, (value) => {
fetch(withBase("/api/profile/settings"), {
method: "PUT",
body: JSON.stringify(value),
});
});
}
export const settings = useProfileStorage("settings", DEFAULT_SETTINGS);
export const {
collapseNav,
compact,
softWrap,
hourStyle,
dateLocale,
lightTheme,
showAllContainers,
showTimestamp,
@@ -54,5 +51,6 @@ export const {
menuWidth,
size,
search,
locale,
automaticRedirect,
} = toRefs(settings);
+2
View File
@@ -14,6 +14,8 @@ export type ContainerJson = {
readonly status: string;
readonly state: ContainerState;
readonly host: string;
readonly labels: Record<string, string>;
readonly stats: ContainerStat[];
readonly health?: ContainerHealth;
};
+24
View File
@@ -51,3 +51,27 @@ export function useExponentialMovingAverage<T extends Record<string, number>>(so
return ema;
}
interface UseSimpleRefHistoryOptions<T> {
capacity: number;
deep?: boolean;
initial?: T[];
}
export function useSimpleRefHistory<T>(source: Ref<T>, options: UseSimpleRefHistoryOptions<T>) {
const { capacity, deep = true, initial = [] as T[] } = options;
const history = ref<T[]>(initial) as Ref<T[]>;
watch(
source,
(value) => {
history.value.push(value);
if (history.value.length > capacity) {
history.value.shift();
}
},
{ deep },
);
return history;
}
+9 -13
View File
@@ -3,7 +3,7 @@ services:
custom_base:
container_name: custom_base
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- DOZZLE_FILTER=name=custom_base
- DOZZLE_BASE=/foobarbase
@@ -12,23 +12,20 @@ services:
- 8080:8080
build:
context: .
auth:
container_name: auth
simple-auth:
container_name: simple-auth
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./e2e/data:/data
environment:
- DOZZLE_FILTER=name=auth
- DOZZLE_USERNAME=foo
- DOZZLE_PASSWORD=bar
- DOZZLE_AUTH_PROVIDER=simple
- DOZZLE_NO_ANALYTICS=1
ports:
- 9090:8080
build:
context: .
dozzle:
container_name: dozzle
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- DOZZLE_FILTER=name=dozzle
- DOZZLE_NO_ANALYTICS=1
@@ -53,7 +50,7 @@ services:
container_name: proxy
image: tecnativa/docker-socket-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
ports:
@@ -61,7 +58,7 @@ services:
playwright:
container_name: playwright
image: mcr.microsoft.com/playwright:v1.39.0-jammy
image: mcr.microsoft.com/playwright:v1.41.2-jammy
working_dir: /app
volumes:
- .:/app
@@ -72,5 +69,4 @@ services:
depends_on:
- dozzle
- custom_base
- auth
- remote
+1
View File
@@ -68,6 +68,7 @@ export default defineConfig({
{ text: "Healthcheck", link: "/guide/healthcheck" },
{ text: "Hostname", link: "/guide/hostname" },
{ text: "Remote Hosts", link: "/guide/remote-hosts" },
{ text: "Container Actions", link: "/guide/actions" },
{ text: "Supported Env Vars", link: "/guide/supported-env-vars" },
{ text: "Logging Files on Disk", link: "/guide/log-files-on-disk" },
{ text: "Analytics", link: "/guide/analytics" },
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

-30
View File
@@ -1,30 +0,0 @@
import { Handler, HandlerEvent, HandlerContext } from "@netlify/functions";
let cache;
let cacheTime = 0;
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
if (cache && Date.now() - cacheTime < 1000 * 60 * 10) {
const headers = {
"x-cache": "HIT",
};
return {
headers,
statusCode: 200,
body: JSON.stringify(cache),
};
}
const response = await fetch("https://hub.docker.com/v2/repositories/amir20/dozzle");
const data = await response.json();
const { full_description, ...rest } = data;
cache = rest;
cacheTime = Date.now();
return {
statusCode: 200,
body: JSON.stringify(rest),
};
};
export { handler };
+32
View File
@@ -0,0 +1,32 @@
---
title: Container Actions
---
# Using Container Actions
Dozzle now supports **Container Actions**, which allows you to `start`, `stop` and `restart` container from within the UI in the dropdown menu.
<img title="Container Actions" alt="Container Acions Menu UI" width="250" src="/.vitepress/theme/media/dozzle-ui-actions.png">
This feature is **disabled** by default, which can be enabled by setting environment variable`DOZZLE_ENABLE_ACTIONS` to `true`
::: code-group
```sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --enable-actions
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8080:8080
environment:
DOZZLE_ENABLE_ACTIONS: true
```
:::
+76 -90
View File
@@ -8,6 +8,62 @@ Dozzle support two configurations for authentication. In the first configuration
If you do not have an authentication solution then Dozzle has a simple file based user management solution. Authentication providers are setup using `--auth-provider` flag. In both of these configurations, Dozzle will try to save user settings to disk. This data is written to `/data`.
## File Based User Management
Dozzle supports multi-user authentication by setting `--auth-provider` to `simple`. In this mode, Dozzle will try to read `/data/users.yml`.
The content of the file looks like:
```yaml
users:
# "admin" here is username
admin:
name: "Admin"
# Just sha-256 which can be computed with "echo -n password | shasum -a 256"
password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
email: me@email.net
```
Dozzle uses `email` to generate avatars using [Gravatar](https://gravatar.com/). It is optional.
The password is hashed using `sha256` which can be generated with `echo -n 'secret-password' | shasum -a 256` or `echo -n 'secret-password' | sha256sum` on linux.
You will need to mount this file for Dozzle to find it. Here is an example:
::: code-group
```sh [cli]
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider simple
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/dozzle/data:/data
ports:
- 8080:8080
environment:
DOZZLE_AUTH_PROVIDER: simple
```
```yaml [users.yml]
users:
# "admin" here is username
admin:
name: "Admin"
# Just sha-256 which can be computed with "echo -n password | shasum -a 256"
password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
email: me@email.net
```
:::
Dozzle uses [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) to generate tokens for authentication. This token is saved in a cookie.
## Forward Proxy
Dozzle can be configured to read proxy headers by setting `--auth-provider` to `forward-proxy`.
@@ -178,103 +234,33 @@ notifier:
Valid SSL keys are required because Authelia only supports SSL.
## File Based User Management
### Setting up Dozzle with Cloudflare Zero Trust
Dozzle supports multi-user authentication by setting `--auth-provider` to `simple`. In this mode, Dozzle will try to read `/data/users.yml`. The content of the file looks like
```yml
users:
admin:
name: "Admin"
# Just sha-256 which can be computed with echo -n password | shasum -a 256
password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
email: me@email.net
```
Dozzle uses `email` to generate avatars using [Gravatar](https://gravatar.com/). It is optional.
The password is hashed using `sha256` which can be generated with `echo -n "secret-password" | shasum -a 256` or `echo -n "secret-password" | sha256sum` on linux.
You will need to mount this file for Dozzle to find it. Here is an example:
::: code-group
```sh [cli]
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider simple
```
Cloudflare Zero Trust is a service for authenticated access to selfhosted
software. This section defines how Dozzle can be setup to use Cloudflare Zero
Trust for authentication.
```yaml [docker-compose.yml]
version: "3"
version: "3.3"
services:
dozzle:
image: amir20/dozzle:latest
networks:
- net
environment:
DOZZLE_AUTH_PROVIDER: forward-proxy
DOZZLE_AUTH_HEADER_USER: Cf-Access-Authenticated-User-Email
DOZZLE_AUTH_HEADER_EMAIL: Cf-Access-Authenticated-User-Email
DOZZLE_AUTH_HEADER_NAME: Cf-Access-Authenticated-User-Email
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/dozzle/data:/data
ports:
- 8080:8080
environment:
DOZZLE_AUTH_PROVIDER: simple
expose:
- 8080
restart: unless-stopped
```
:::
Dozzle uses [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) to generate tokens for authentication. This token is saved in a cookie.
## Single Username/Password
::: danger
`--username` and `--password` flags will be removed in v6.x in favor of `--auth-provider`.
:::
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
```
After running the Dozzle container, configure the Application in Cloudflare Zero
Trust dashboard by following the
[guide](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/)
here.
+23 -1
View File
@@ -12,10 +12,32 @@ However, there are workarounds to be able to still access files using mounts.
## Mounting Local Log Files with Alpine
Dozzle reads any output stream. This can be used in combination with alpine to `tail` a mounted file. An example of this is:
Dozzle reads any output stream. This can be used in combination with alpine to `tail` a mounted file. An example of this is as follows:
::: code-group
```sh
docker run -v /var/log/system.log:/var/log/test.log alpine tail -f /var/log/test.log
```
```yaml [docker-compose.yml]
version: "3"
services:
dozzle-from-file:
container_name: dozzle-from-file
image: alpine
volumes:
- /var/log/system.log:/var/log/stream.log
command:
- tail
- -f
- /var/log/stream.log
network_mode: none
restart: unless-stopped
```
:::
In the above example `/var/log/system.log` is mounted from the host and used with `tail -f` to follow the file. `tail` is smart to follow log rotations. During my testing, using alpine only uses about `300KB` of memory.
The second tab shows a `docker-compose` file which is useful if you want the log stream to survive server reboot.
+15 -13
View File
@@ -6,16 +6,18 @@ title: Environment variables and configuration
Configurations can be done with flags or environment variables. The table below outlines all supported options and their respective env vars.
| Flag | Env Variable | Default |
| ---------------- | ---------------------- | ------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--username` | `DOZZLE_USERNAME` | `""` |
| `--password` | `DOZZLE_PASSWORD` | `""` |
| `--usernamefile` | `DOZZLE_USERNAME_FILE` | `""` |
| `--passwordfile` | `DOZZLE_PASSWORD_FILE` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
| Flag | Env Variable | Default |
| --------------------------- | -------------------------------- | -------------- |
| `--addr` | `DOZZLE_ADDR` | `:8080` |
| `--base` | `DOZZLE_BASE` | `/` |
| `--hostname` | `DOZZLE_HOSTNAME` | `""` |
| `--level` | `DOZZLE_LEVEL` | `info` |
| `--auth-provider` | `DOZZLE_AUTH_PROVIDER` | `none` |
| `--auth-header-user` | `DOZZLE_AUTH_HEADER_USER` | `Remote-User` |
| `--auth-header-email` | `DOZZLE_AUTH_HEADER_EMAIL` | `Remote-Email` |
| `--auth-header-name` | `DOZZLE_AUTH_HEADER_NAME` | `Remote-Name` |
| `--enable-actions` | `DOZZLE_ENABLE_ACTIONS` | false |
| `--wait-for-docker-seconds` | `DOZZLE_WAIT_FOR_DOCKER_SECONDS` | 0 |
| `--filter` | `DOZZLE_FILTER` | `""` |
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
| `--remote-host` | `DOZZLE_REMOTE_HOST` | |
+4 -5
View File
@@ -3,12 +3,11 @@
"type": "module",
"name": "docs",
"devDependencies": {
"@netlify/functions": "^2.3.0",
"@unocss/preset-typography": "^0.57.2",
"@unocss/reset": "^0.57.2",
"@unocss/transformer-directives": "^0.57.2",
"@unocss/preset-typography": "^0.58.5",
"@unocss/reset": "^0.58.5",
"@unocss/transformer-directives": "^0.58.5",
"dozzle": "workspace:*",
"sitemap": "^7.1.1",
"unocss": "^0.57.2"
"unocss": "^0.58.5"
}
}
+868 -300
View File
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
import { test, expect } from "@playwright/test";
test("authentication", async ({ page }) => {
await page.goto("http://auth:8080/");
await page.locator('input[name="username"]').fill("foo");
await page.locator('input[name="password"]').fill("bar");
await page.locator('button[type="submit"]').click();
await expect(page.getByTestId("containers")).toHaveText("Containers");
});
+4
View File
@@ -0,0 +1,4 @@
users:
admin:
name: Admin
password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
+1 -1
View File
@@ -34,6 +34,6 @@ test.describe("es locale", () => {
test.use({ locale: "es" });
test("translated text", async ({ page }) => {
await expect(page.getByTestId("containers")).toHaveText("Contenedores");
await expect(page.getByTestId("label.running-containers")).toHaveText("Contenedores en ejecución");
});
});
+9
View File
@@ -0,0 +1,9 @@
import { test, expect } from "@playwright/test";
test("simple authentication", async ({ page }) => {
await page.goto("http://simple-auth:8080/");
await page.locator('input[name="username"]').fill("admin");
await page.locator('input[name="password"]').fill("password");
await page.locator('button[type="submit"]').click();
await expect(page.getByTestId("label.running-containers")).toHaveText("Running Containers");
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

+30 -19
View File
@@ -4,13 +4,11 @@ require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/alexflint/go-arg v1.4.3
github.com/beme/abide v0.0.0-20190723115211-635a09831760
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/docker v25.0.3+incompatible
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gorilla/sessions v1.2.1
github.com/magiconair/properties v1.8.7
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/morikuni/aec v1.0.0 // indirect
@@ -18,42 +16,55 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.10.0
github.com/spf13/afero v1.11.0
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/jwtauth/v5 v5.1.1
github.com/PuerkitoBio/goquery v1.8.1
github.com/go-chi/chi/v5 v5.0.11
github.com/go-chi/jwtauth/v5 v5.3.0
github.com/go-logfmt/logfmt v0.6.0
github.com/puzpuzpuz/xsync/v3 v3.0.2
github.com/yuin/goldmark v1.7.0
)
require (
github.com/alexflint/go-scalar v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/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/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.11 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
golang.org/x/tools v0.12.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
)
go 1.21
go 1.22.0
+98 -432
View File
@@ -1,182 +1,87 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
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.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
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=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA=
github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY=
github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
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=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.1.1 h1:Pjixqu5YkjE9sCLpzE01L0Q4sQzJIPdo7uz9r8ftp/c=
github.com/go-chi/jwtauth/v5 v5.1.1/go.mod h1:CYP1WSbzD4MPuKCr537EM3kfFhSQgpUEtMJFuYJjqWU=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.3.0 h1:X7RKGks1lrVeIe2omGyz47pNaNjG2YmwlRN5UKhN8qg=
github.com/go-chi/jwtauth/v5 v5.3.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7FuKLQ=
github.com/lestrrat-go/jwx/v2 v2.0.11/go.mod h1:ZtPtMFlrfDrH2Y0iwfa3dRFn8VzwBrB+cyrm3IBWdDg=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY=
github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
@@ -192,19 +97,18 @@ github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=
github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -212,348 +116,120 @@ 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/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=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY=
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/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=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/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=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/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=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-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-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -562,13 +238,3 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

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