mirror of
https://github.com/warp-tech/warpgate.git
synced 2025-12-10 06:56:17 +08:00
UI update (#1175)
This commit is contained in:
parent
efcb2205ff
commit
409b382e8f
65 changed files with 1180 additions and 728 deletions
76
.github/readme/brand-dark.svg
vendored
Normal file
76
.github/readme/brand-dark.svg
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="60mm"
|
||||
height="8mm"
|
||||
viewBox="0 0 60 8"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
sodipodi:docname="brand-dark.svg"
|
||||
inkscape:version="1.1 (c4e8f9e, 2021-05-24)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview15"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0998592"
|
||||
inkscape:cx="98.578038"
|
||||
inkscape:cy="10.476893"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="204"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg5" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="180.24561"
|
||||
y="396.18341"
|
||||
width="458.64478"
|
||||
height="221.29164"
|
||||
id="rect3774" />
|
||||
</defs>
|
||||
<path
|
||||
d="m 11.538149,0.26599 -3.0395654,7.4295 h -2.19075 L 5.8482877,2.80599 3.8948336,7.69549 h -2.19075 L 0.92306998,0.26599 H 2.85982 L 3.1259729,5.67407 5.251653,0.26599 H 7.24132 L 7.7085559,5.67407 9.5908202,0.26599 Z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11089" />
|
||||
<path
|
||||
d="M 14.900136,6.38316 H 12.127303 L 11.591175,7.69549 H 9.696758 l 3.206895,-7.4295 h 2.0955 l 2.169438,7.4295 h -1.915583 z m -0.368131,-1.397 -0.730842,-2.71992 -1.100074,2.71992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11091" />
|
||||
<path
|
||||
d="M 21.621427,7.69549 20.076261,4.89091 h -0.433917 l 4e-6,2.80458 h -1.80975 v -7.4295 h 3.037417 q 0.878416,0 1.49225,0.30692 0.624416,0.30691 0.931333,0.84666 0.306917,0.52917 0.306917,1.18534 10e-7,0.74083 -0.423334,1.32291 -0.41275,0.58209 -1.227666,0.8255 l 1.714496,2.94217 z M 19.642344,3.61032 h 1.121833 q 0.497417,0 0.740834,-0.24341 0.254,-0.24342 0.254,-0.68792 1.1e-5,-0.42333 -0.254,-0.66675 -0.243417,-0.24342 -0.740834,-0.24342 h -1.121833 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11093" />
|
||||
<path
|
||||
d="m 29.90879,2.65782 q 0,0.64559 -0.296333,1.18534 -0.296333,0.52916 -0.910167,0.85725 -0.613833,0.32808 -1.524,0.32808 h -1.121833 v 2.667 h -1.80975 v -7.4295 h 2.931583 q 0.889,0 1.502834,0.30692 0.613833,0.30691 0.92075,0.84666 0.306916,0.53975 0.306916,1.23825 z m -2.868083,0.93134 q 0.518583,0 0.772583,-0.24342 0.254,-0.24342 0.254,-0.68792 0,-0.4445 -0.254,-0.68791 -0.254,-0.24342 -0.772583,-0.24342 h -0.98425 v 1.86267 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11095" />
|
||||
<path
|
||||
d="M 35.563571,2.61549 Q 35.362488,2.24507 34.981488,2.05457 34.611071,1.85349 34.103071,1.85349 q -0.878416,0 -1.407583,0.58208 -0.529166,0.5715 -0.529166,1.53459 0,1.02658 0.550333,1.60866 0.560916,0.5715 1.534583,0.5715 0.66675,0 1.121833,-0.33866 0.465667,-0.33867 0.677334,-0.97367 h -2.296584 v -1.3335 h 3.937 v 1.68275 q -0.201083,0.67733 -0.687916,1.25942 -0.47625,0.58208 -1.217084,0.94191 -0.740833,0.35984 -1.672166,0.35984 -1.100667,0 -1.9685,-0.47625 -0.85725,-0.48684 -1.344083,-1.34409 -0.47625,-0.85725 -0.47625,-1.95791 0,-1.10067 0.47625,-1.95792 0.486833,-0.86783 1.344083,-1.34408 0.85725,-0.48684 1.957916,-0.48684 1.3335,0 2.243667,0.64559 0.92075,0.64558 1.217083,1.78858 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11097" />
|
||||
<path
|
||||
d="m 42.606476,6.38316 h -2.772833 l -0.467407,1.31233 h -1.894417 l 2.817849,-7.4295 h 2.0955 l 2.558484,7.4295 H 43.028069 Z M 42.165193,4.98616 41.29192,2.26624 40.334277,4.98616 Z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11099" />
|
||||
<path
|
||||
d="m 50.129387,0.26599 -2e-6,1.44992 h -1.968499 l 0,5.97958 h -1.809752 l 0,-5.97958 h -1.9685 l 2e-6,-1.44992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11101" />
|
||||
<path
|
||||
d="m 52.634481,1.71591 2e-6,1.50283 h 2.42358 l 1.5e-5,1.397 h -2.42358 l -10e-7,1.62983 h 2.74108 l -2e-6,1.44992 h -4.55083 l 0,-7.4295 h 4.55083 l -2e-6,1.44992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583;fill:#d2f0fd;fill-opacity:1"
|
||||
id="path11103" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
10
README.md
10
README.md
|
|
@ -1,11 +1,17 @@
|
|||
<br/>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="warpgate-web/public/assets/logo.svg" width="100" />
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset=".github/readme/brand-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="warpgate-web/public/assets/brand.svg">
|
||||
<img alt="Shows a black logo in light color mode and a white one in dark color mode." src=".github/readme/brand-dark.svg">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="https://github.com/warp-tech/warpgate/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/warp-tech/warpgate/total.svg?label=DOWNLOADS&logo=github&style=for-the-badge"></a> <a href="https://nightly.link/warp-tech/warpgate/workflows/build/main"><img src="https://shields.io/badge/-Nightly%20Builds-orange?logo=hackthebox&logoColor=fff&style=for-the-badge"/></a> <a href="https://discord.gg/Vn7BjmzhtF"><img alt="Discord" src="https://img.shields.io/discord/1280890060195233934?style=for-the-badge&color=blue&logo=discord&logoColor=white&label=Discord"></a>
|
||||
<a href="https://github.com/warp-tech/warpgate/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/warp-tech/warpgate/total.svg?label=DOWNLOADS&logo=github&style=for-the-badge&color=8f8"></a> <a href="https://nightly.link/warp-tech/warpgate/workflows/build/main"><img src="https://shields.io/badge/-Nightly%20Builds-fa5?logo=hackthebox&logoColor=444&style=for-the-badge"/></a> <a href="https://discord.gg/Vn7BjmzhtF"><img alt="Discord" src="https://img.shields.io/discord/1280890060195233934?style=for-the-badge&color=acc&logo=discord&logoColor=white&label=Discord"></a>
|
||||
</p>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub fn error_page(e: poem::Error) -> impl IntoResponse {
|
|||
}}
|
||||
</style>
|
||||
<main>
|
||||
<img src="/@warpgate/assets/logo.svg" />
|
||||
<img src="/@warpgate/assets/brand.svg" />
|
||||
<h1>Request failed</h1>
|
||||
<p>{e}</p>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"openapi": "yarn run openapi:schema:admin && yarn run openapi:schema:gateway && yarn run openapi:client:admin && yarn run openapi:client:gateway"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fontsource/poppins": "^5.1.0",
|
||||
"@fontsource/work-sans": "^4.5.12",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.7.1",
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
"format-duration": "^3.0.2",
|
||||
"otpauth": "^9.3.5",
|
||||
"qrcode": "^1.5.4",
|
||||
"sass": "~1.82",
|
||||
"sass": "1.78",
|
||||
"svelte": "^5.11.0",
|
||||
"svelte-check": "^4.1.1",
|
||||
"svelte-fa": "^4.0.3",
|
||||
|
|
|
|||
76
warpgate-web/public/assets/brand.svg
Normal file
76
warpgate-web/public/assets/brand.svg
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="60mm"
|
||||
height="8mm"
|
||||
viewBox="0 0 60 8"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
sodipodi:docname="brand.svg"
|
||||
inkscape:version="1.1 (c4e8f9e, 2021-05-24)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview15"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0998592"
|
||||
inkscape:cx="98.578038"
|
||||
inkscape:cy="10.476893"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg5" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="180.24561"
|
||||
y="396.18341"
|
||||
width="458.64478"
|
||||
height="221.29164"
|
||||
id="rect3774" />
|
||||
</defs>
|
||||
<path
|
||||
d="m 11.538149,0.26599 -3.0395654,7.4295 h -2.19075 L 5.8482877,2.80599 3.8948336,7.69549 h -2.19075 L 0.92306998,0.26599 H 2.85982 L 3.1259729,5.67407 5.251653,0.26599 H 7.24132 L 7.7085559,5.67407 9.5908202,0.26599 Z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11089" />
|
||||
<path
|
||||
d="M 14.900136,6.38316 H 12.127303 L 11.591175,7.69549 H 9.696758 l 3.206895,-7.4295 h 2.0955 l 2.169438,7.4295 h -1.915583 z m -0.368131,-1.397 -0.730842,-2.71992 -1.100074,2.71992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11091" />
|
||||
<path
|
||||
d="M 21.621427,7.69549 20.076261,4.89091 h -0.433917 l 4e-6,2.80458 h -1.80975 v -7.4295 h 3.037417 q 0.878416,0 1.49225,0.30692 0.624416,0.30691 0.931333,0.84666 0.306917,0.52917 0.306917,1.18534 10e-7,0.74083 -0.423334,1.32291 -0.41275,0.58209 -1.227666,0.8255 l 1.714496,2.94217 z M 19.642344,3.61032 h 1.121833 q 0.497417,0 0.740834,-0.24341 0.254,-0.24342 0.254,-0.68792 1.1e-5,-0.42333 -0.254,-0.66675 -0.243417,-0.24342 -0.740834,-0.24342 h -1.121833 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11093" />
|
||||
<path
|
||||
d="m 29.90879,2.65782 q 0,0.64559 -0.296333,1.18534 -0.296333,0.52916 -0.910167,0.85725 -0.613833,0.32808 -1.524,0.32808 h -1.121833 v 2.667 h -1.80975 v -7.4295 h 2.931583 q 0.889,0 1.502834,0.30692 0.613833,0.30691 0.92075,0.84666 0.306916,0.53975 0.306916,1.23825 z m -2.868083,0.93134 q 0.518583,0 0.772583,-0.24342 0.254,-0.24342 0.254,-0.68792 0,-0.4445 -0.254,-0.68791 -0.254,-0.24342 -0.772583,-0.24342 h -0.98425 v 1.86267 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11095" />
|
||||
<path
|
||||
d="M 35.563571,2.61549 Q 35.362488,2.24507 34.981488,2.05457 34.611071,1.85349 34.103071,1.85349 q -0.878416,0 -1.407583,0.58208 -0.529166,0.5715 -0.529166,1.53459 0,1.02658 0.550333,1.60866 0.560916,0.5715 1.534583,0.5715 0.66675,0 1.121833,-0.33866 0.465667,-0.33867 0.677334,-0.97367 h -2.296584 v -1.3335 h 3.937 v 1.68275 q -0.201083,0.67733 -0.687916,1.25942 -0.47625,0.58208 -1.217084,0.94191 -0.740833,0.35984 -1.672166,0.35984 -1.100667,0 -1.9685,-0.47625 -0.85725,-0.48684 -1.344083,-1.34409 -0.47625,-0.85725 -0.47625,-1.95791 0,-1.10067 0.47625,-1.95792 0.486833,-0.86783 1.344083,-1.34408 0.85725,-0.48684 1.957916,-0.48684 1.3335,0 2.243667,0.64559 0.92075,0.64558 1.217083,1.78858 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11097" />
|
||||
<path
|
||||
d="m 42.606476,6.38316 h -2.772833 l -0.467407,1.31233 h -1.894417 l 2.817849,-7.4295 h 2.0955 l 2.558484,7.4295 H 43.028069 Z M 42.165193,4.98616 41.29192,2.26624 40.334277,4.98616 Z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11099" />
|
||||
<path
|
||||
d="m 50.129387,0.26599 -2e-6,1.44992 h -1.968499 l 0,5.97958 h -1.809752 l 0,-5.97958 h -1.9685 l 2e-6,-1.44992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11101" />
|
||||
<path
|
||||
d="m 52.634481,1.71591 2e-6,1.50283 h 2.42358 l 1.5e-5,1.397 h -2.42358 l -10e-7,1.62983 h 2.74108 l -2e-6,1.44992 h -4.55083 l 0,-7.4295 h 4.55083 l -2e-6,1.44992 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;stroke-width:0.264583"
|
||||
id="path11103" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
37
warpgate-web/public/assets/favicon.svg
Normal file
37
warpgate-web/public/assets/favicon.svg
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="24mm"
|
||||
height="24mm"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg914"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs911" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:#14141c;fill-opacity:1;fill-rule:evenodd;stroke:#311a1a;stroke-width:1.41432;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0;paint-order:markers fill stroke"
|
||||
id="rect1026"
|
||||
width="22.627413"
|
||||
height="22.529339"
|
||||
x="0.71679527"
|
||||
y="0.74948686"
|
||||
d="M 4.9501286,0.74948686 H 19.110875 A 4.2333333,4.2333333 45 0 1 23.344208,4.9828202 V 19.045492 a 4.2333333,4.2333333 135 0 1 -4.233333,4.233334 l -14.1607464,0 A 4.2333333,4.2333333 45 0 1 0.71679527,19.045492 V 4.9828202 A 4.2333333,4.2333333 135 0 1 4.9501286,0.74948686 Z" />
|
||||
<g
|
||||
id="g2449"
|
||||
transform="matrix(1.1046419,0,0,1.1046419,-1.0038918,1.1276169)">
|
||||
<path
|
||||
d="M 14.272807,6.1286052 11.233242,13.558105 H 9.0424929 L 8.5829469,8.6686052 6.6294929,13.558105 h -2.19075 L 3.6577289,6.1286052 h 1.93675 l 0.266153,5.4080798 2.12568,-5.4080798 h 1.9896666 l 0.4672355,5.4080798 1.882264,-5.4080798 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;fill:#bfcce7;fill-opacity:1;stroke-width:0.264583"
|
||||
id="path11089" />
|
||||
<path
|
||||
d="m 17.634794,12.245775 h -2.772833 l -0.536128,1.31233 h -1.894417 l 3.206895,-7.4294998 h 2.0955 l 2.169438,7.4294998 h -1.915583 z m -0.368131,-1.397 -0.730842,-2.7199198 -1.100074,2.7199198 z"
|
||||
style="font-weight:bold;font-size:40px;line-height:1.25;font-family:Poppins;-inkscape-font-specification:'Poppins Bold';white-space:pre;fill:#bfcce7;fill-opacity:1;stroke-width:0.264583"
|
||||
id="path11091" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
|
|
@ -1,62 +0,0 @@
|
|||
<svg viewBox="0 0 80 47"
|
||||
id="warpgate-logo"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
#warpgate-logo path {
|
||||
fill: rgb(64, 64, 236);
|
||||
}
|
||||
@media ( prefers-color-scheme: dark ) {
|
||||
#warpgate-logo path {
|
||||
fill: rgb(11, 207, 215);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path d="m9.2 4.7c-1 .3-2 .8-2.9 1.4l-1.1 1.2c-.6.8-1.1 1.7-1.3 2.7z"/>
|
||||
<path d="m4 13.3 8.3-8.7c-.4-.1-.8-.1-1.2-.1l-7.3 7.6c0 .4.1.8.2 1.2z"/>
|
||||
<path d="m13.4 5-8.9 9.8c.1.1.1.3.2.4 0 0 .1.2.3.5l9.2-10.2c-.2-.2-.5-.3-.8-.5z"/>
|
||||
<path d="m16 6.9c-.2-.3-.5-.5-.7-.7l-9.4 10.8.6.9z"/>
|
||||
<path d="m17.2 8.7c-.3-.5-.5-.9-.6-.9l-9.3 11.4c.2.3.4.6.6.9z"/>
|
||||
<path d="m18.4 10.6c-.2-.3-.4-.6-.6-.9l-9.1 11.7c.2.3.4.6.6.9z"/>
|
||||
<path d="m19.7 12.5c-.2-.3-.4-.6-.6-.9l-8.9 12.1c.2.3.4.6.6.9z"/>
|
||||
<path d="m20.9 14.5c-.2-.3-.4-.6-.6-.9l-8.7 12.4.6.9z"/>
|
||||
<path d="m22.2 16.5c-.2-.3-.4-.6-.6-.9l-8.5 12.7c.2.3.4.6.6.9z"/>
|
||||
<path d="m23.5 18.6c-.2-.3-.4-.6-.6-.9l-8.3 13.1.6.9z"/>
|
||||
<path d="m24.8 20.7c-.2-.3-.4-.6-.6-.9l-8.1 13.5c.2.3.4.6.6.9z"/>
|
||||
<path d="m35.5 6.5c-.4.4-.8.8-1.1 1.3l-8.1 12.8-8.6 15.2c.2.3.4.7.6 1z"/>
|
||||
<path d="m37.1 5.3-17.7 33c.1.2.2.4.2.4.1.2.3.4.4.6l18.5-34.5c-.5.1-1 .3-1.4.5z"/>
|
||||
<path d="m40.6 4.5c-.1 0-.2 0-.2 0-.3 0-.6 0-.9.1l-18.1 36c.3.2.6.4.8.5z"/>
|
||||
<path d="m41.4 4.6-17.6 37.1c.3.1.7.1 1 .2l17.6-37.1c-.4-.1-.7-.2-1-.2z"/>
|
||||
<path d="m43.9 5.5c-.3-.2-.6-.3-.9-.5l-16.3 36.9c.4-.1.8-.2 1.2-.3z"/>
|
||||
<path d="m33.2 36.1 12.1-29.5c-.2-.3-.5-.5-.8-.7l-14.1 34.2c.4-.4.8-.8 1.1-1.3z"/>
|
||||
<path d="m40.1 25.1 6.5-16.8-.3-.5c-.1-.2-.3-.4-.4-.6l-8.6 22.2z"/>
|
||||
<path d="m47.8 10.1-.7-1.1-6 16.7c.2.3.4.7.7 1.1z"/>
|
||||
<path d="m49 12.1-.7-1.1-5.7 17.2c.2.4.5.7.7 1.1z"/>
|
||||
<path d="m50.3 14.1-.7-1.1-5.4 17.7c.2.4.5.7.7 1.1z"/>
|
||||
<path d="m51.6 16.2-.7-1.1-5 18.2c.2.4.5.8.7 1.1z"/>
|
||||
<path d="m53 18.5-.7-1.2-4.7 18.8c.3.4.5.8.7 1.2z"/>
|
||||
<path d="m54.5 20.8-.8-1.2-4.3 19.4c.2.3.5.7.8 1z"/>
|
||||
<path d="m56.9 19.2-1.5 2.4-3.8 19.4c.3.2.6.3.9.5z"/>
|
||||
<path d="m59.6 14.9-1.4 2.2-4.3 24.8c.3.1.7.1 1 .1z"/>
|
||||
<path d="m62 11.1-1.3 2.1-4.3 28.7c.4-.1.7-.2 1.1-.3z"/>
|
||||
<path d="m64.1 7.8-1.2 1.8-3.8 31.2c.4-.3.8-.6 1.1-.9l4-32.2c-.1 0-.1 0-.1.1z"/>
|
||||
<path d="m66 5.8c-.4.3-.8.6-1.1 1l-3 30.7c.3-.5.7-1.1 1.2-1.9z"/>
|
||||
<path d="m67.7 4.9c-.4.1-.7.3-1 .5l-2.1 27.9c.4-.6.7-1.2 1.1-1.8z"/>
|
||||
<path d="m69.2 4.5c-.3 0-.7.1-1 .2l-1.2 24.6c.4-.6.7-1.1 1.1-1.7z"/>
|
||||
<path d="m70.8 4.6c-.3 0-.7-.1-1-.1l-.5 21.3c.4-.6.7-1.1 1-1.6z"/>
|
||||
<path d="m72.4 5c-.3-.1-.7-.2-1-.3v17.8c.3-.5.7-1.1 1-1.6z"/>
|
||||
<path d="m73.9 5.8c-.3-.2-.7-.4-1-.6l.3 14.2c.3-.6.7-1.1 1-1.5z"/>
|
||||
<path d="m75.6 7.3c-.3-.4-.7-.8-1.1-1.1l.5 10.4c.5-.9.9-1.4.9-1.4v-.1z"/>
|
||||
<path d="m76.7 13.6c.6-1.9.4-3.7-.4-5.1z"/>
|
||||
<path d="m3.5 11.8c.1 1.1.4 2.3 1.1 3.4l14.8 23.5c.3.4.6.8.9 1.2z"/>
|
||||
<path d="m3.7 9.8c-.1.5-.2 1-.2 1.6l19 30c.5.2 1 .4 1.5.5z"/>
|
||||
<path d="m4.2 8.5c-.2.4-.4.7-.5 1.1l21.6 32.4c.4 0 .8 0 1.1-.1z"/>
|
||||
<path d="m4.8 7.5c-.2.2-.4.5-.6.9l23.4 33.2c.3-.1.6-.3.9-.4z"/>
|
||||
<path d="m5.6 6.6c-.3.2-.5.5-.7.7l24.6 33.2c.3-.2.5-.4.7-.7z"/>
|
||||
<path d="m6.4 5.9c-.2.2-.5.4-.7.6l25.3 32.5c.2-.3.4-.6.5-.9z"/>
|
||||
<path d="m7.4 5.3c-.3.1-.6.3-.9.5l25.5 31.1c.1-.4.2-.8.3-1.3z"/>
|
||||
<path d="m7.5 5.2 24.8 28.7c-.1-.9-.5-1.8-1-2.7 0 0 0-.1-.1-.2l-22.7-26.2c-.4.1-.7.3-1 .4z"/>
|
||||
<path d="m9.7 4.5c-.3.1-.7.2-1.1.3l19.5 21.5c-.9-1.5-2-3.2-3.1-4.9z"/>
|
||||
<path d="m11.3 4.5c-.5 0-.9 0-1.4 0l13.2 13.8c-.9-1.5-1.9-3-2.7-4.3z"/>
|
||||
<path d="m11.6 4.6 7.6 7.6c-1.1-1.7-2-3.1-2.4-3.8l-3.1-3.1c-.7-.4-1.4-.6-2.1-.7z"/>
|
||||
<path d="m14.7 5.9 1 .9c-.3-.3-.6-.6-1-.9z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
|
|
@ -5,9 +5,9 @@ import Router, { link } from 'svelte-spa-router'
|
|||
import active from 'svelte-spa-router/active'
|
||||
import { wrap } from 'svelte-spa-router/wrap'
|
||||
import ThemeSwitcher from 'common/ThemeSwitcher.svelte'
|
||||
import Logo from 'common/Logo.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import AuthBar from 'common/AuthBar.svelte'
|
||||
import Brand from 'common/Brand.svelte'
|
||||
|
||||
async function init () {
|
||||
await reloadServerInfo()
|
||||
|
|
@ -25,15 +25,6 @@ const routes = {
|
|||
'/recordings/:id': wrap({
|
||||
asyncComponent: () => import('./Recording.svelte') as any,
|
||||
}),
|
||||
'/tickets': wrap({
|
||||
asyncComponent: () => import('./Tickets.svelte') as any,
|
||||
}),
|
||||
'/tickets/create': wrap({
|
||||
asyncComponent: () => import('./CreateTicket.svelte') as any,
|
||||
}),
|
||||
'/config': wrap({
|
||||
asyncComponent: () => import('./Config.svelte') as any,
|
||||
}),
|
||||
'/targets/create': wrap({
|
||||
asyncComponent: () => import('./CreateTarget.svelte') as any,
|
||||
}),
|
||||
|
|
@ -52,14 +43,32 @@ const routes = {
|
|||
'/users/:id': wrap({
|
||||
asyncComponent: () => import('./User.svelte') as any,
|
||||
}),
|
||||
'/ssh': wrap({
|
||||
asyncComponent: () => import('./SSH.svelte') as any,
|
||||
}),
|
||||
'/log': wrap({
|
||||
asyncComponent: () => import('./Log.svelte') as any,
|
||||
}),
|
||||
'/parameters': wrap({
|
||||
asyncComponent: () => import('./Parameters.svelte') as any,
|
||||
'/config': wrap({
|
||||
asyncComponent: () => import('./config/Config.svelte') as any,
|
||||
}),
|
||||
'/config/parameters': wrap({
|
||||
asyncComponent: () => import('./config/Parameters.svelte') as any,
|
||||
}),
|
||||
'/config/users': wrap({
|
||||
asyncComponent: () => import('./config/Users.svelte') as any,
|
||||
}),
|
||||
'/config/roles': wrap({
|
||||
asyncComponent: () => import('./config/Roles.svelte') as any,
|
||||
}),
|
||||
'/config/targets': wrap({
|
||||
asyncComponent: () => import('./config/Targets.svelte') as any,
|
||||
}),
|
||||
'/config/ssh': wrap({
|
||||
asyncComponent: () => import('./config/SSHKeys.svelte') as any,
|
||||
}),
|
||||
'/config/tickets': wrap({
|
||||
asyncComponent: () => import('./config/Tickets.svelte') as any,
|
||||
}),
|
||||
'/config/tickets/create': wrap({
|
||||
asyncComponent: () => import('./CreateTicket.svelte') as any,
|
||||
}),
|
||||
}
|
||||
</script>
|
||||
|
|
@ -69,18 +78,15 @@ const routes = {
|
|||
{:then}
|
||||
<div class="app container">
|
||||
<header>
|
||||
<a href="/@warpgate" class="d-flex">
|
||||
<div class="logo">
|
||||
<Logo />
|
||||
</div>
|
||||
<a href="/@warpgate" class="d-flex logo-link me-4">
|
||||
<Brand />
|
||||
</a>
|
||||
{#if $serverInfo?.username}
|
||||
<a use:link use:active href="/">Sessions</a>
|
||||
<a use:link use:active href="/config">Config</a>
|
||||
<a use:link use:active href="/tickets">Tickets</a>
|
||||
<a use:link use:active href="/ssh">SSH</a>
|
||||
<a use:link use:active href="/log">Log</a>
|
||||
{/if}
|
||||
<span class="ms-3"></span>
|
||||
<AuthBar />
|
||||
</header>
|
||||
<main>
|
||||
|
|
@ -88,7 +94,7 @@ const routes = {
|
|||
</main>
|
||||
|
||||
<footer class="mt-5">
|
||||
<span class="me-auto">
|
||||
<span class="me-auto ms-3">
|
||||
v{$serverInfo?.version}
|
||||
</span>
|
||||
<ThemeSwitcher />
|
||||
|
|
@ -97,18 +103,18 @@ const routes = {
|
|||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
@media (max-width: 767px) {
|
||||
.logo-link {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
padding-top: 2px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header, footer {
|
||||
flex: none;
|
||||
}
|
||||
|
|
@ -120,15 +126,12 @@ const routes = {
|
|||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
padding: 7px 0;
|
||||
margin: 10px 0 20px;
|
||||
|
||||
a, .logo {
|
||||
a {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
a:not(:first-child) {
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { Observable, from, map } from 'rxjs'
|
||||
import { type Role, type Target, type User, api } from 'admin/lib/api'
|
||||
import ItemList, { type LoadOptions, type PaginatedResponse } from 'common/ItemList.svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
|
||||
function getTargets (options: LoadOptions): Observable<PaginatedResponse<Target>> {
|
||||
return from(api.getTargets({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
|
||||
function getUsers (options: LoadOptions): Observable<PaginatedResponse<User>> {
|
||||
return from(api.getUsers({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
|
||||
function getRoles (options: LoadOptions): Observable<PaginatedResponse<Role>> {
|
||||
return from(api.getRoles({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-4 pe-4">
|
||||
<div class="page-summary-bar">
|
||||
<h1>Targets</h1>
|
||||
<a
|
||||
class="btn btn-outline-secondary ms-auto"
|
||||
href="/targets/create"
|
||||
use:link>
|
||||
Add a target
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getTargets} showSearch={true}>
|
||||
{#snippet item({ item: target })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/targets/{target.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{target.name}
|
||||
</strong>
|
||||
<small class="text-muted ms-auto">
|
||||
{#if target.options.kind === 'Http'}
|
||||
HTTP
|
||||
{/if}
|
||||
{#if target.options.kind === 'MySql'}
|
||||
MySQL
|
||||
{/if}
|
||||
{#if target.options.kind === 'Postgres'}
|
||||
PostgreSQL
|
||||
{/if}
|
||||
{#if target.options.kind === 'Ssh'}
|
||||
SSH
|
||||
{/if}
|
||||
{#if target.options.kind === 'WebAdmin'}
|
||||
This web admin interface
|
||||
{/if}
|
||||
</small>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 pe-4">
|
||||
<div class="page-summary-bar">
|
||||
<h1>Users</h1>
|
||||
<a
|
||||
class="btn btn-outline-secondary ms-auto"
|
||||
href="/users/create"
|
||||
use:link>
|
||||
Add a user
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getUsers} showSearch={true}>
|
||||
{#snippet item({ item: user })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/users/{user.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{user.username}
|
||||
</strong>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<div class="page-summary-bar mt-4">
|
||||
<h1>Roles</h1>
|
||||
<a
|
||||
class="btn btn-outline-secondary ms-auto"
|
||||
href="/roles/create"
|
||||
use:link>
|
||||
Add a role
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getRoles} showSearch={true}>
|
||||
{#snippet item({ item: role })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/roles/{role.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{role.name}
|
||||
</strong>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<h1>Misc.</h1>
|
||||
|
||||
<div class="list-group list-group-flush mb-3">
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/parameters"
|
||||
use:link
|
||||
>
|
||||
<strong>Global parameters</strong>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
ModalFooter,
|
||||
} from '@sveltestrap/sveltestrap'
|
||||
|
||||
import ModalHeader from 'common/ModalHeader.svelte'
|
||||
import ModalHeader from 'common/sveltestrap-s5-ports/ModalHeader.svelte'
|
||||
import QRCode from 'qrcode'
|
||||
import * as OTPAuth from 'otpauth'
|
||||
import base32Encode from 'base32-encode'
|
||||
|
|
@ -120,11 +120,11 @@
|
|||
<img class="qr mb-3" bind:this={qrImage} alt="OTP QR code" />
|
||||
|
||||
<div class="d-flex justify-content-center mb-4">
|
||||
<Button outline class="d-flex align-items-center" color="link" on:click={generateNewTotpKey}>
|
||||
<Button class="d-flex align-items-center me-3" on:click={generateNewTotpKey}>
|
||||
<Fa class="me-2" fw icon={faRefresh} />
|
||||
Regenerate
|
||||
</Button>
|
||||
<CopyButton outline class="d-flex align-items-center" color="link" text={totpUri!} label={'Copy URI'} />
|
||||
<CopyButton class="d-flex align-items-center" color="secondary" text={totpUri!} label={'Copy URI'} />
|
||||
</div>
|
||||
<FormGroup floating label="Paste the generated OTP code" class="mt-3">
|
||||
<Input
|
||||
|
|
@ -144,13 +144,11 @@
|
|||
<Button
|
||||
class="ms-auto"
|
||||
disabled={!totpValid}
|
||||
outline
|
||||
on:click={() => validated = true}
|
||||
>Create</Button>
|
||||
|
||||
<Button
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
on:click={_cancel}
|
||||
>Cancel</Button>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
ModalFooter,
|
||||
} from '@sveltestrap/sveltestrap'
|
||||
|
||||
import ModalHeader from 'common/ModalHeader.svelte'
|
||||
import ModalHeader from 'common/sveltestrap-s5-ports/ModalHeader.svelte'
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
Password
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<FormGroup floating class="mt-3" label="Enter a new password">
|
||||
<FormGroup floating label="Enter a new password">
|
||||
<Input
|
||||
bind:inner={field}
|
||||
type="password"
|
||||
|
|
@ -61,13 +61,11 @@
|
|||
<div class="d-flex">
|
||||
<Button
|
||||
class="ms-auto"
|
||||
outline
|
||||
on:click={() => validated = true}
|
||||
>Create</Button>
|
||||
|
||||
<Button
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
on:click={_cancel}
|
||||
>Cancel</Button>
|
||||
|
|
|
|||
|
|
@ -2,17 +2,14 @@
|
|||
import { api } from 'admin/lib/api'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { Form, FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let name = $state('')
|
||||
|
||||
async function create () {
|
||||
if (!name) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const role = await api.createRole({
|
||||
roleDataRequest: {
|
||||
|
|
@ -33,14 +30,18 @@ async function create () {
|
|||
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Add a role</h1>
|
||||
<h1>add a role</h1>
|
||||
</div>
|
||||
|
||||
<FormGroup floating label="Name">
|
||||
<input class="form-control" bind:value={name} required />
|
||||
</FormGroup>
|
||||
<div class="narrow-page">
|
||||
<Form>
|
||||
<FormGroup floating label="Name">
|
||||
<input class="form-control" bind:value={name} required />
|
||||
</FormGroup>
|
||||
|
||||
<AsyncButton
|
||||
outline
|
||||
click={create}
|
||||
>Create role</AsyncButton>
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
click={create}
|
||||
>Create role</AsyncButton>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,75 +1,74 @@
|
|||
<script lang="ts">
|
||||
import { api, type TargetOptions, TlsMode } from 'admin/lib/api'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import { api, type TargetOptions, TlsMode } from 'admin/lib/api'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { Button, ButtonGroup, Form, FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import { TargetKind } from 'gateway/lib/api'
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let name = $state('')
|
||||
let type: 'Http' | 'MySql' | 'Ssh' | 'Postgres' = $state('Ssh')
|
||||
let error: string|null = $state(null)
|
||||
let name = $state('')
|
||||
let type: TargetKind = $state(TargetKind.Ssh)
|
||||
|
||||
async function create () {
|
||||
if (!name || !type) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const options: TargetOptions|undefined = {
|
||||
Ssh: {
|
||||
kind: 'Ssh' as const,
|
||||
host: '192.168.0.1',
|
||||
port: 22,
|
||||
username: 'root',
|
||||
auth: {
|
||||
kind: 'PublicKey' as const,
|
||||
async function create () {
|
||||
try {
|
||||
const options: TargetOptions|undefined = {
|
||||
[TargetKind.Ssh]: {
|
||||
kind: TargetKind.Ssh,
|
||||
host: '192.168.0.1',
|
||||
port: 22,
|
||||
username: 'root',
|
||||
auth: {
|
||||
kind: 'PublicKey' as const,
|
||||
},
|
||||
},
|
||||
},
|
||||
Http: {
|
||||
kind: 'Http' as const,
|
||||
url: 'http://192.168.0.1',
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
[TargetKind.Http]: {
|
||||
kind: TargetKind.Http,
|
||||
url: 'http://192.168.0.1',
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
MySql: {
|
||||
kind: 'MySql' as const,
|
||||
host: '192.168.0.1',
|
||||
port: 3306,
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
[TargetKind.MySql]: {
|
||||
kind: TargetKind.MySql,
|
||||
host: '192.168.0.1',
|
||||
port: 3306,
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
},
|
||||
username: 'root',
|
||||
password: '',
|
||||
},
|
||||
username: 'root',
|
||||
password: '',
|
||||
},
|
||||
Postgres: {
|
||||
kind: 'Postgres' as const,
|
||||
host: '192.168.0.1',
|
||||
port: 5432,
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
[TargetKind.Postgres]: {
|
||||
kind: TargetKind.Postgres,
|
||||
host: '192.168.0.1',
|
||||
port: 5432,
|
||||
tls: {
|
||||
mode: TlsMode.Preferred,
|
||||
verify: true,
|
||||
},
|
||||
username: 'postgres',
|
||||
password: '',
|
||||
},
|
||||
username: 'postgres',
|
||||
password: '',
|
||||
},
|
||||
}[type]
|
||||
if (!options) {
|
||||
return
|
||||
[TargetKind.WebAdmin]: null as any,
|
||||
}[type]
|
||||
if (!options) {
|
||||
return
|
||||
}
|
||||
const target = await api.createTarget({
|
||||
targetDataRequest: {
|
||||
name,
|
||||
options,
|
||||
},
|
||||
})
|
||||
replace(`/targets/${target.id}`)
|
||||
} catch (err) {
|
||||
error = await stringifyError(err)
|
||||
}
|
||||
const target = await api.createTarget({
|
||||
targetDataRequest: {
|
||||
name,
|
||||
options,
|
||||
},
|
||||
})
|
||||
replace(`/targets/${target.id}`)
|
||||
} catch (err) {
|
||||
error = await stringifyError(err)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -79,23 +78,40 @@ async function create () {
|
|||
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Add a target</h1>
|
||||
<h1>add a target</h1>
|
||||
</div>
|
||||
|
||||
<FormGroup floating label="Name">
|
||||
<input class="form-control" bind:value={name} />
|
||||
</FormGroup>
|
||||
<div class="narrow-page">
|
||||
<Form>
|
||||
<!-- svelte-ignore a11y_label_has_associated_control -->
|
||||
<label class="mb-2">Type</label>
|
||||
<ButtonGroup class="w-100 mb-3">
|
||||
<Button
|
||||
active={type === TargetKind.Ssh}
|
||||
on:click={() => type = TargetKind.Ssh}
|
||||
>SSH</Button>
|
||||
<Button
|
||||
active={type === TargetKind.Http}
|
||||
on:click={() => type = TargetKind.Http}
|
||||
>HTTP</Button>
|
||||
<Button
|
||||
active={type === TargetKind.MySql}
|
||||
on:click={() => type = TargetKind.MySql}
|
||||
>MySQL</Button>
|
||||
<Button
|
||||
active={type === TargetKind.Postgres}
|
||||
on:click={() => type = TargetKind.Postgres}
|
||||
>PostgreSQL</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<FormGroup floating label="Type">
|
||||
<select bind:value={type} class="form-control">
|
||||
<option value={'Ssh'}>SSH</option>
|
||||
<option value={'Http'}>HTTP</option>
|
||||
<option value={'MySql'}>MySQL</option>
|
||||
<option value={'Postgres'}>PostgreSQL</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup floating label="Name">
|
||||
<input class="form-control" required bind:value={name} />
|
||||
</FormGroup>
|
||||
|
||||
<AsyncButton
|
||||
outline
|
||||
click={create}
|
||||
>Create target</AsyncButton>
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
|
||||
click={create}
|
||||
>Create target</AsyncButton>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { link } from 'svelte-spa-router'
|
|||
import { FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { firstBy } from 'thenby'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let targets: Target[]|undefined = $state()
|
||||
|
|
@ -58,7 +58,7 @@ async function create () {
|
|||
|
||||
{#if result}
|
||||
<div class="page-summary-bar">
|
||||
<h1>Ticket created</h1>
|
||||
<h1>ticket created</h1>
|
||||
</div>
|
||||
|
||||
<Alert color="warning" fade={false}>
|
||||
|
|
@ -77,13 +77,13 @@ async function create () {
|
|||
|
||||
<a
|
||||
class="btn btn-secondary"
|
||||
href="/tickets"
|
||||
href="/config/tickets"
|
||||
use:link
|
||||
>Done</a>
|
||||
{:else}
|
||||
<div class="narrow-page">
|
||||
<div class="page-summary-bar">
|
||||
<h1>Create an access ticket</h1>
|
||||
<h1>create an access ticket</h1>
|
||||
</div>
|
||||
|
||||
{#if users}
|
||||
|
|
@ -119,7 +119,7 @@ async function create () {
|
|||
</FormGroup>
|
||||
|
||||
<AsyncButton
|
||||
outline
|
||||
color="primary"
|
||||
click={create}
|
||||
>Create ticket</AsyncButton>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,17 +2,14 @@
|
|||
import { api } from 'admin/lib/api'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { Form, FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let username = $state('')
|
||||
|
||||
async function create () {
|
||||
if (!username) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const user = await api.createUser({
|
||||
createUserRequest: {
|
||||
|
|
@ -33,14 +30,17 @@ async function create () {
|
|||
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Add a user</h1>
|
||||
<h1>add a user</h1>
|
||||
</div>
|
||||
<div class="narrow-page">
|
||||
<Form>
|
||||
<FormGroup floating label="Username">
|
||||
<input class="form-control" required bind:value={username} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup floating label="Username">
|
||||
<input class="form-control" bind:value={username} />
|
||||
</FormGroup>
|
||||
|
||||
<AsyncButton
|
||||
outline
|
||||
click={create}
|
||||
>Create user</AsyncButton>
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
click={create}
|
||||
>Create user</AsyncButton>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
import Fa from 'svelte-fa'
|
||||
import { Button } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import CreatePasswordModal from './CreatePasswordModal.svelte'
|
||||
import SsoCredentialModal from './SsoCredentialModal.svelte'
|
||||
import PublicKeyCredentialModal from './PublicKeyCredentialModal.svelte'
|
||||
|
|
|
|||
|
|
@ -61,20 +61,21 @@
|
|||
_reloadSessions()
|
||||
const interval = setInterval(_reloadSessions, 1000000)
|
||||
onDestroy(() => clearInterval(interval))
|
||||
|
||||
</script>
|
||||
|
||||
{#if activeSessionCount !== undefined}
|
||||
<div class="page-summary-bar">
|
||||
{#if activeSessionCount }
|
||||
<h1>Sessions right now: {activeSessionCount}</h1>
|
||||
<h1>
|
||||
<span>active sessions:</span> <span class="counter">{activeSessionCount}</span>
|
||||
</h1>
|
||||
<div class="ms-auto">
|
||||
<AsyncButton outline click={closeAllSesssions}>
|
||||
Close all sessions
|
||||
<AsyncButton color="warning" click={closeAllSesssions}>
|
||||
Close all
|
||||
</AsyncButton>
|
||||
</div>
|
||||
{:else}
|
||||
<h1>No active sessions</h1>
|
||||
<h1>no active sessions</h1>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import LogViewer from './LogViewer.svelte'
|
|||
</script>
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Log</h1>
|
||||
<h1>log</h1>
|
||||
</div>
|
||||
|
||||
<LogViewer filters={{}} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import IntersectionObserver from 'svelte-intersection-observer'
|
|||
import { link } from 'svelte-spa-router'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
filters: {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
ModalFooter,
|
||||
} from '@sveltestrap/sveltestrap'
|
||||
|
||||
import ModalHeader from 'common/ModalHeader.svelte'
|
||||
import ModalHeader from 'common/sveltestrap-s5-ports/ModalHeader.svelte'
|
||||
import { type ExistingPublicKeyCredential } from './lib/api'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -74,13 +74,11 @@
|
|||
<Button
|
||||
type="submit"
|
||||
class="ms-auto"
|
||||
outline
|
||||
on:click={() => validated = true}
|
||||
>Save</Button>
|
||||
|
||||
<Button
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
on:click={_cancel}
|
||||
>Cancel</Button>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { api, type Recording } from 'admin/lib/api'
|
||||
import TerminalRecordingPlayer from 'admin/player/TerminalRecordingPlayer.svelte'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ load().catch(async e => {
|
|||
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Session recording</h1>
|
||||
<h1>session recording</h1>
|
||||
</div>
|
||||
|
||||
{#if !recording && !error}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
|||
import { replace } from 'svelte-spa-router'
|
||||
import { FormGroup } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
params: { id: string };
|
||||
|
|
@ -38,7 +38,7 @@ async function update () {
|
|||
async function remove () {
|
||||
if (confirm(`Delete role ${role!.name}?`)) {
|
||||
await api.deleteRole(role!)
|
||||
replace('/config')
|
||||
replace('/config/roles')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -49,7 +49,7 @@ async function remove () {
|
|||
<div class="page-summary-bar">
|
||||
<div>
|
||||
<h1>{role!.name}</h1>
|
||||
<div class="text-muted">Role</div>
|
||||
<div class="text-muted">role</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -64,14 +64,13 @@ async function remove () {
|
|||
|
||||
<div class="d-flex">
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
class="ms-auto"
|
||||
outline
|
||||
click={update}
|
||||
>Update</AsyncButton>
|
||||
|
||||
<AsyncButton
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
click={remove}
|
||||
>Remove</AsyncButton>
|
||||
|
|
|
|||
|
|
@ -1,66 +1,71 @@
|
|||
<script lang="ts">
|
||||
import { api, type SessionSnapshot, type Recording, type TargetSSHOptions, type TargetHTTPOptions, type TargetMySqlOptions, type TargetPostgresOptions } from 'admin/lib/api'
|
||||
import { timeAgo } from 'admin/lib/time'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import { formatDistance, formatDistanceToNow } from 'date-fns'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
import LogViewer from './LogViewer.svelte'
|
||||
import RelativeDate from './RelativeDate.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import { api, type SessionSnapshot, type Recording, type TargetSSHOptions, type TargetHTTPOptions, type TargetMySqlOptions, type TargetPostgresOptions } from 'admin/lib/api'
|
||||
import { timeAgo } from 'admin/lib/time'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import { formatDistance, formatDistanceToNow } from 'date-fns'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
import LogViewer from './LogViewer.svelte'
|
||||
import RelativeDate from './RelativeDate.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import Fa from 'svelte-fa'
|
||||
import { faUser } from '@fortawesome/free-regular-svg-icons'
|
||||
import Badge from 'common/sveltestrap-s5-ports/Badge.svelte'
|
||||
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import Tooltip from 'common/sveltestrap-s5-ports/Tooltip.svelte'
|
||||
|
||||
interface Props {
|
||||
params: { id: string }
|
||||
}
|
||||
|
||||
let { params = { id: '' } }: Props = $props()
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let session: SessionSnapshot|null = $state(null)
|
||||
let recordings: Recording[]|null = $state(null)
|
||||
|
||||
async function load () {
|
||||
session = await api.getSession(params)
|
||||
recordings = await api.getSessionRecordings(params)
|
||||
}
|
||||
|
||||
async function close () {
|
||||
api.closeSession(session!)
|
||||
}
|
||||
|
||||
function getTargetDescription () {
|
||||
if (session?.target) {
|
||||
let address = '<unknown>'
|
||||
if (session.target.options.kind === 'Ssh') {
|
||||
const options = session.target.options as TargetSSHOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'MySql') {
|
||||
const options = session.target.options as TargetMySqlOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'Postgres') {
|
||||
const options = session.target.options as TargetPostgresOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'Http') {
|
||||
const options = session.target.options as unknown as TargetHTTPOptions
|
||||
address = options.url
|
||||
}
|
||||
return `${session.target.name} (${address})`
|
||||
} else {
|
||||
return 'Not selected yet'
|
||||
interface Props {
|
||||
params: { id: string }
|
||||
}
|
||||
}
|
||||
|
||||
load().catch(async e => {
|
||||
error = await stringifyError(e)
|
||||
})
|
||||
let { params = { id: '' } }: Props = $props()
|
||||
|
||||
const interval = setInterval(load, 1000)
|
||||
onDestroy(() => clearInterval(interval))
|
||||
let error: string|null = $state(null)
|
||||
let session: SessionSnapshot|null = $state(null)
|
||||
let recordings: Recording[]|null = $state(null)
|
||||
|
||||
async function load () {
|
||||
session = await api.getSession(params)
|
||||
recordings = await api.getSessionRecordings(params)
|
||||
}
|
||||
|
||||
async function close () {
|
||||
api.closeSession(session!)
|
||||
}
|
||||
|
||||
function getTargetDescription () {
|
||||
if (session?.target) {
|
||||
let address = '<unknown>'
|
||||
if (session.target.options.kind === 'Ssh') {
|
||||
const options = session.target.options as TargetSSHOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'MySql') {
|
||||
const options = session.target.options as TargetMySqlOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'Postgres') {
|
||||
const options = session.target.options as TargetPostgresOptions
|
||||
address = `${options.host}:${options?.port}`
|
||||
}
|
||||
if (session.target.options.kind === 'Http') {
|
||||
const options = session.target.options as unknown as TargetHTTPOptions
|
||||
address = options.url
|
||||
}
|
||||
return `${session.target.name} (${address})`
|
||||
} else {
|
||||
return 'Not selected yet'
|
||||
}
|
||||
}
|
||||
|
||||
load().catch(async e => {
|
||||
error = await stringifyError(e)
|
||||
})
|
||||
|
||||
const interval = setInterval(load, 1000)
|
||||
onDestroy(() => clearInterval(interval))
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -75,17 +80,25 @@ onDestroy(() => clearInterval(interval))
|
|||
{#if session}
|
||||
<div class="page-summary-bar">
|
||||
<div>
|
||||
<h1>Session</h1>
|
||||
<div>
|
||||
<strong class="me-2">
|
||||
<h1>session</h1>
|
||||
<div class="d-flex align-items-center mt-1">
|
||||
<Tooltip delay="500" target="usernameBadge" animation>Authenticated user</Tooltip>
|
||||
<Tooltip delay="500" target="targetBadge" animation>Selected target</Tooltip>
|
||||
|
||||
<Badge id="usernameBadge" color="success" class="me-2 d-flex align-items-center">
|
||||
{#if session.username}
|
||||
<Fa icon={faUser} class="me-2" />
|
||||
{session.username}
|
||||
{:else}
|
||||
Logging in
|
||||
{/if}
|
||||
⇆
|
||||
{getTargetDescription()}
|
||||
</strong>
|
||||
</Badge>
|
||||
{#if session.target}
|
||||
<Badge id="targetBadge" color="info" class="me-2 d-flex align-items-center">
|
||||
<Fa icon={faArrowRight} class="me-2" />
|
||||
{getTargetDescription()}
|
||||
</Badge>
|
||||
{/if}
|
||||
<span class="text-muted">
|
||||
{#if session.ended}
|
||||
{formatDistance(new Date(session.started), new Date(session.ended))} long, <RelativeDate date={session.started} />
|
||||
|
|
@ -97,7 +110,7 @@ onDestroy(() => clearInterval(interval))
|
|||
</div>
|
||||
{#if !session.ended}
|
||||
<div class="ms-auto">
|
||||
<AsyncButton outline click={close}>
|
||||
<AsyncButton color="warning" click={close}>
|
||||
Close now
|
||||
</AsyncButton>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
ModalFooter,
|
||||
} from '@sveltestrap/sveltestrap'
|
||||
|
||||
import ModalHeader from 'common/ModalHeader.svelte'
|
||||
import ModalHeader from 'common/sveltestrap-s5-ports/ModalHeader.svelte'
|
||||
import { type ExistingSsoCredential } from './lib/api'
|
||||
import { api } from 'gateway/lib/api'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
|
|
@ -88,13 +88,11 @@
|
|||
<Button
|
||||
type="submit"
|
||||
class="ms-auto"
|
||||
outline
|
||||
on:click={() => validated = true}
|
||||
>Save</Button>
|
||||
|
||||
<Button
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
on:click={_cancel}
|
||||
>Cancel</Button>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { replace } from 'svelte-spa-router'
|
|||
import { FormGroup, Input } from '@sveltestrap/sveltestrap'
|
||||
import TlsConfiguration from './TlsConfiguration.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
params: { id: string };
|
||||
|
|
@ -56,7 +56,7 @@ async function update () {
|
|||
async function remove () {
|
||||
if (confirm(`Delete target ${target!.name}?`)) {
|
||||
await api.deleteTarget(target!)
|
||||
replace('/config')
|
||||
replace('/config/targets')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ async function toggleRole (role: Role) {
|
|||
<Input
|
||||
class="mb-0 me-2"
|
||||
type="switch"
|
||||
label="Allow insecure SSH algorithms (e.g. for older networks devices)"
|
||||
label="Allow insecure SSH algorithms (e.g. for older network devices)"
|
||||
bind:checked={target.options.allowInsecureAlgos} />
|
||||
</div>
|
||||
|
||||
|
|
@ -268,14 +268,13 @@ async function toggleRole (role: Role) {
|
|||
|
||||
<div class="d-flex">
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
class="ms-auto"
|
||||
outline
|
||||
click={update}
|
||||
>Update configuration</AsyncButton>
|
||||
|
||||
<AsyncButton
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
click={remove}
|
||||
>Remove</AsyncButton>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import { replace } from 'svelte-spa-router'
|
||||
import { FormGroup, Input } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import CredentialEditor from './CredentialEditor.svelte'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
async function remove () {
|
||||
if (confirm(`Delete user ${user!.username}?`)) {
|
||||
await api.deleteUser(user!)
|
||||
replace('/config')
|
||||
replace('/config/users')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,14 +114,13 @@
|
|||
|
||||
<div class="d-flex">
|
||||
<AsyncButton
|
||||
color="primary"
|
||||
class="ms-auto"
|
||||
outline
|
||||
click={update}
|
||||
>Update</AsyncButton>
|
||||
|
||||
<AsyncButton
|
||||
class="ms-2"
|
||||
outline
|
||||
color="danger"
|
||||
click={remove}
|
||||
>Remove</AsyncButton>
|
||||
|
|
|
|||
39
warpgate-web/src/admin/config/Config.svelte
Normal file
39
warpgate-web/src/admin/config/Config.svelte
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<script lang="ts">
|
||||
import NavListItem from 'common/NavListItem.svelte'
|
||||
</script>
|
||||
|
||||
<NavListItem
|
||||
title="Targets"
|
||||
description="Destinations for users to connect to"
|
||||
href="/config/targets"
|
||||
/>
|
||||
|
||||
<NavListItem
|
||||
title="Users"
|
||||
description="Manage accounts and credentials"
|
||||
href="/config/users"
|
||||
/>
|
||||
|
||||
<NavListItem
|
||||
title="Roles"
|
||||
description="Group users together"
|
||||
href="/config/roles"
|
||||
/>
|
||||
|
||||
<NavListItem
|
||||
title="Tickets"
|
||||
description="Temporary access credentials"
|
||||
href="/config/tickets"
|
||||
/>
|
||||
|
||||
<NavListItem
|
||||
title="SSH keys"
|
||||
description="Own keys and known hosts"
|
||||
href="/config/ssh"
|
||||
/>
|
||||
|
||||
<NavListItem
|
||||
title="Global parameters"
|
||||
description="Change instance-wide settings"
|
||||
href="/config/parameters"
|
||||
/>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { Input } from '@sveltestrap/sveltestrap'
|
||||
import { api, type ParameterValues } from 'admin/lib/api'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
</script>
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>Global parameters</h1>
|
||||
<h1>global parameters</h1>
|
||||
</div>
|
||||
|
||||
{#await load()}
|
||||
47
warpgate-web/src/admin/config/Roles.svelte
Normal file
47
warpgate-web/src/admin/config/Roles.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script lang="ts">
|
||||
import { Observable, from, map } from 'rxjs'
|
||||
import { type Role, api } from 'admin/lib/api'
|
||||
import ItemList, { type LoadOptions, type PaginatedResponse } from 'common/ItemList.svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
|
||||
function getRoles (options: LoadOptions): Observable<PaginatedResponse<Role>> {
|
||||
return from(api.getRoles({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="page-summary-bar mt-4">
|
||||
<h1>roles</h1>
|
||||
<a
|
||||
class="btn btn-primary ms-auto"
|
||||
href="/roles/create"
|
||||
use:link>
|
||||
Add a role
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getRoles} showSearch={true}>
|
||||
{#snippet item({ item: role })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/roles/{role.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{role.name}
|
||||
</strong>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<style lang="scss">
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { api, type SSHKey, type SSHKnownHost } from 'admin/lib/api'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import CopyButton from 'common/CopyButton.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
|
||||
65
warpgate-web/src/admin/config/Targets.svelte
Normal file
65
warpgate-web/src/admin/config/Targets.svelte
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts">
|
||||
import { Observable, from, map } from 'rxjs'
|
||||
import { type Target, api } from 'admin/lib/api'
|
||||
import ItemList, { type LoadOptions, type PaginatedResponse } from 'common/ItemList.svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
import { TargetKind } from 'gateway/lib/api'
|
||||
|
||||
function getTargets (options: LoadOptions): Observable<PaginatedResponse<Target>> {
|
||||
return from(api.getTargets({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>targets</h1>
|
||||
<a
|
||||
class="btn btn-primary ms-auto"
|
||||
href="/targets/create"
|
||||
use:link>
|
||||
Add a target
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getTargets} showSearch={true}>
|
||||
{#snippet item({ item: target })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
class:disabled={target.options.kind === TargetKind.WebAdmin}
|
||||
href="/targets/{target.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{target.name}
|
||||
</strong>
|
||||
<small class="text-muted ms-auto">
|
||||
{#if target.options.kind === TargetKind.Http}
|
||||
HTTP
|
||||
{/if}
|
||||
{#if target.options.kind === TargetKind.MySql}
|
||||
MySQL
|
||||
{/if}
|
||||
{#if target.options.kind === TargetKind.Postgres}
|
||||
PostgreSQL
|
||||
{/if}
|
||||
{#if target.options.kind === TargetKind.Ssh}
|
||||
SSH
|
||||
{/if}
|
||||
{#if target.options.kind === TargetKind.WebAdmin}
|
||||
This web admin interface
|
||||
{/if}
|
||||
</small>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<style lang="scss">
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { api, type Ticket } from 'admin/lib/api'
|
||||
import { link } from 'svelte-spa-router'
|
||||
import RelativeDate from './RelativeDate.svelte'
|
||||
import RelativeDate from '../RelativeDate.svelte'
|
||||
import Fa from 'svelte-fa'
|
||||
import { faCalendarXmark, faCalendarCheck, faSquareXmark, faSquareCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
let error: string|undefined = $state()
|
||||
let tickets: Ticket[]|undefined = $state()
|
||||
|
|
@ -32,13 +32,13 @@ async function deleteTicket (ticket: Ticket) {
|
|||
{#if tickets }
|
||||
<div class="page-summary-bar">
|
||||
{#if tickets.length }
|
||||
<h1>Access tickets: {tickets.length}</h1>
|
||||
<h1>access tickets: <span class="counter">{tickets.length}</span></h1>
|
||||
{:else}
|
||||
<h1>No tickets created yet</h1>
|
||||
{/if}
|
||||
<a
|
||||
class="btn btn-outline-secondary ms-auto"
|
||||
href="/tickets/create"
|
||||
class="btn btn-primary ms-auto"
|
||||
href="/config/tickets/create"
|
||||
use:link>
|
||||
Create a ticket
|
||||
</a>
|
||||
46
warpgate-web/src/admin/config/Users.svelte
Normal file
46
warpgate-web/src/admin/config/Users.svelte
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
import { Observable, from, map } from 'rxjs'
|
||||
import { type User, api } from 'admin/lib/api'
|
||||
import ItemList, { type LoadOptions, type PaginatedResponse } from 'common/ItemList.svelte'
|
||||
import { link } from 'svelte-spa-router'
|
||||
|
||||
function getUsers (options: LoadOptions): Observable<PaginatedResponse<User>> {
|
||||
return from(api.getUsers({
|
||||
search: options.search,
|
||||
})).pipe(map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<h1>users</h1>
|
||||
<a
|
||||
class="btn btn-primary ms-auto"
|
||||
href="/users/create"
|
||||
use:link>
|
||||
Add a user
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ItemList load={getUsers} showSearch={true}>
|
||||
{#snippet item({ item: user })}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/users/{user.id}"
|
||||
use:link>
|
||||
<strong class="me-auto">
|
||||
{user.username}
|
||||
</strong>
|
||||
</a>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<style lang="scss">
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/assets/logo.svg" />
|
||||
<link rel="icon" href="/@warpgate/assets/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Warpgate</title>
|
||||
<style>#app { display: none }</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fa from 'svelte-fa'
|
||||
import { Button, Spinner, type Color } from '@sveltestrap/sveltestrap'
|
||||
|
||||
|
|
@ -8,7 +8,8 @@ enum State {
|
|||
Normal = 'n',
|
||||
Progress = 'p',
|
||||
ProgressWithSpinner = 'ps',
|
||||
Done = 'd'
|
||||
Done = 'd',
|
||||
Failed = 'f'
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
@ -31,6 +32,15 @@ async function _click () {
|
|||
if (!button) {
|
||||
return
|
||||
}
|
||||
|
||||
const parentForm = button.closest<HTMLFormElement>('form')
|
||||
if (parentForm) {
|
||||
parentForm.classList.add('was-validated')
|
||||
if (!parentForm.checkValidity()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lastWidth = button.offsetWidth
|
||||
st = State.Progress
|
||||
setTimeout(() => {
|
||||
|
|
@ -40,10 +50,12 @@ async function _click () {
|
|||
}, 500)
|
||||
try {
|
||||
await click()
|
||||
} finally {
|
||||
st = State.Done
|
||||
} catch {
|
||||
st = State.Failed
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
if (st === State.Done) {
|
||||
if (st === State.Done || st === State.Failed) {
|
||||
st = State.Normal
|
||||
lastWidth = 0
|
||||
}
|
||||
|
|
@ -56,7 +68,7 @@ async function _click () {
|
|||
<Button
|
||||
on:click={_click}
|
||||
bind:inner={button}
|
||||
style="min-width: {lastWidth}px"
|
||||
style="min-width: {lastWidth}px; min-height: 40px;"
|
||||
class={cls}
|
||||
outline={outline}
|
||||
color={color}
|
||||
|
|
@ -73,5 +85,18 @@ async function _click () {
|
|||
{#if st === State.Done}
|
||||
<Fa icon={faCheck} fw />
|
||||
{/if}
|
||||
{#if st === State.Failed}
|
||||
<Fa icon={faTimes} fw />
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<style lang="scss">
|
||||
.overlay {
|
||||
margin: auto;
|
||||
|
||||
:global(svg) {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ async function singleLogout () {
|
|||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
{:else}
|
||||
<Button color="link" on:click={logout} title="Log out">
|
||||
<Button color="link" on:click={logout} title="Log out" class="p-0 ms-2">
|
||||
<Fa icon={faSignOut} fw />
|
||||
</Button>
|
||||
{/if}
|
||||
|
|
|
|||
50
warpgate-web/src/common/Brand.svelte
Normal file
50
warpgate-web/src/common/Brand.svelte
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import logo from '../../public/assets/brand.svg?raw'
|
||||
import { currentThemeFile } from 'theme'
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
let element: HTMLElement|undefined = $state()
|
||||
|
||||
let s = currentThemeFile.subscribe(colorizeByTheme)
|
||||
|
||||
function colorize (r: number, g: number, b: number, dr: number, dg: number, db: number) {
|
||||
element?.querySelectorAll('path').forEach((p, idx) => {
|
||||
let d = idx
|
||||
p.style.fill = `rgb(${r + d * dr}, ${g + d * dg}, ${b + d * db})`
|
||||
})
|
||||
}
|
||||
|
||||
function colorizeByTheme () {
|
||||
if (get(currentThemeFile) === 'light') {
|
||||
colorize(49, 57, 72, -1, 1, 3)
|
||||
} else {
|
||||
colorize(203, 212, 235, -3, -2, -1)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
colorizeByTheme()
|
||||
})
|
||||
|
||||
onDestroy(s)
|
||||
</script>
|
||||
|
||||
|
||||
<div bind:this={element} class="brand">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html logo}
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
:global(svg) {
|
||||
width: auto;
|
||||
display: block;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.brand {
|
||||
height: 22px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
import { serverInfo } from 'gateway/lib/store'
|
||||
import { makeExampleSSHCommand, makeSSHUsername, makeExampleMySQLCommand, makeExampleMySQLURI, makeMySQLUsername, makeTargetURL, makeExamplePostgreSQLCommand, makePostgreSQLUsername, makeExamplePostgreSQLURI } from 'common/protocols'
|
||||
import CopyButton from 'common/CopyButton.svelte'
|
||||
import Alert from './Alert.svelte'
|
||||
import Alert from './sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
targetName?: string;
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { get } from 'svelte/store'
|
||||
import { currentThemeFile } from 'theme'
|
||||
import logo from '../../public/assets/logo.svg?raw'
|
||||
|
||||
let element: HTMLElement|undefined = $state()
|
||||
|
||||
function colorize (r: number, g: number, b: number, dr: number, dg: number, db: number) {
|
||||
element?.querySelectorAll('path').forEach((p, idx) => {
|
||||
let d = idx
|
||||
p.style.fill = `rgb(${r + d * dr}, ${g + d * dg}, ${b + d * db})`
|
||||
})
|
||||
}
|
||||
|
||||
function colorizeByTheme () {
|
||||
if (get(currentThemeFile) === 'light') {
|
||||
colorize(81, 47, 185, -1, 1, 3)
|
||||
} else {
|
||||
colorize(131, 167, 255, -3, 1, -1)
|
||||
}
|
||||
}
|
||||
|
||||
let s = currentThemeFile.subscribe(colorizeByTheme)
|
||||
|
||||
onMount(() => {
|
||||
colorizeByTheme()
|
||||
})
|
||||
|
||||
onDestroy(s)
|
||||
|
||||
</script>
|
||||
|
||||
<div bind:this={element} class="d-flex">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html logo}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(svg) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
80
warpgate-web/src/common/NavListItem.svelte
Normal file
80
warpgate-web/src/common/NavListItem.svelte
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<script lang="ts">
|
||||
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fa from 'svelte-fa'
|
||||
import { link } from 'svelte-spa-router'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
description?: string
|
||||
href: string
|
||||
}
|
||||
|
||||
let {
|
||||
title,
|
||||
description,
|
||||
href,
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<a
|
||||
class="link"
|
||||
href={href}
|
||||
use:link
|
||||
>
|
||||
<div class="text">
|
||||
<h5 class="title">{title}</h5>
|
||||
{#if description}
|
||||
<div class="description text-muted">{description}</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="icon">
|
||||
<Fa class="icon" icon={faArrowRight} />
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
a {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: var(--bs-border-radius);
|
||||
align-items: center;
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-list-group-action-hover-bg);
|
||||
h5 {
|
||||
color: var(--bs-list-group-action-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--bs-list-group-action-active-bg);
|
||||
h5 {
|
||||
color: var(--bs-list-group-action-active-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-bottom: 0.25rem;
|
||||
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--wg-link-underline-color);
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.link:hover h5 {
|
||||
text-decoration-color: var(--wg-link-hover-underline-color);
|
||||
}
|
||||
|
||||
.description {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
import { get } from 'svelte/store'
|
||||
|
||||
import { currentTheme, setCurrentTheme } from 'theme'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import Tooltip from './sveltestrap-s5-ports/Tooltip.svelte'
|
||||
|
||||
function toggle () {
|
||||
if (get(currentTheme) === 'auto') {
|
||||
|
|
|
|||
72
warpgate-web/src/common/sveltestrap-s5-ports/Badge.svelte
Normal file
72
warpgate-web/src/common/sveltestrap-s5-ports/Badge.svelte
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<script>
|
||||
import { classnames } from './_sveltestrapUtils'
|
||||
|
||||
|
||||
/**
|
||||
* Additional CSS classes for container element.
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} [ariaLabel] - Text to be read by screen readers.
|
||||
* @property {boolean | string} [border] - Determines if the badge should have a border
|
||||
* @property {string} [class]
|
||||
* @property {string} [children] - The content to be displayed within the badge.
|
||||
* @property {string} [color] - The color theme for the badge.
|
||||
* @property {string} [href] - The href attribute for the badge, which turns it into a link if provided.
|
||||
* @property {boolean} [indicator] - Create a circular indicator for absolute positioned badge.
|
||||
* @property {boolean} [pill] - Flag to indicate if the badge should have a pill shape.
|
||||
* @property {boolean} [positioned] - Flag to indicate if the badge should be absolutely positioned.
|
||||
* @property {string} [placement] - Classes determining where the badge should be absolutely positioned.
|
||||
* @property {boolean | string} [shadow] - Determines if the badge should have a shadow
|
||||
* @property {string | undefined} [theme] - The theme name override to apply to this component instance.
|
||||
* @property {CallableFunction} [children]
|
||||
*/
|
||||
|
||||
let {
|
||||
ariaLabel = '',
|
||||
border = false,
|
||||
'class': className = '',
|
||||
color = 'secondary',
|
||||
href = '',
|
||||
indicator = false,
|
||||
pill = false,
|
||||
positioned = false,
|
||||
placement = 'top-0 start-100',
|
||||
shadow = false,
|
||||
theme = undefined,
|
||||
children,
|
||||
...rest
|
||||
} = $props()
|
||||
|
||||
let classes = $derived(classnames(
|
||||
'badge',
|
||||
`text-bg-${color}`,
|
||||
pill ? 'rounded-pill' : false,
|
||||
positioned ? 'position-absolute translate-middle' : false,
|
||||
positioned ? placement : false,
|
||||
indicator ? 'p-2' : false,
|
||||
border ? (typeof border === 'string' ? border : 'border') : false,
|
||||
shadow ? (typeof shadow === 'string' ? shadow : 'shadow') : false,
|
||||
className
|
||||
))
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a {...rest} {href} class={classes} data-bs-theme={theme}>
|
||||
{@render children?.()}
|
||||
{#if positioned || indicator}
|
||||
<span class="visually-hidden">{ariaLabel}</span>
|
||||
{/if}
|
||||
</a>
|
||||
{:else}
|
||||
<span {...rest} class={classes} data-bs-theme={theme}>
|
||||
{@render children?.()}
|
||||
{#if positioned || indicator}
|
||||
<span class="visually-hidden">{ariaLabel}</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { api } from 'gateway/lib/api'
|
||||
import { onMount } from 'svelte'
|
||||
import logo from '../../public/assets/logo.svg'
|
||||
import logo from '../../public/assets/favicon.svg'
|
||||
|
||||
let ready = false
|
||||
let menuVisible = false
|
||||
|
|
@ -67,8 +67,10 @@ async function logout () {
|
|||
class:wg-hidden={!ready}
|
||||
style="left: {position.x * 100}%; top: {position.y * 100}%"
|
||||
>
|
||||
<button
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<img
|
||||
class="menu-toggle"
|
||||
src={logo} alt="Warpgate"
|
||||
on:mouseup|stopPropagation|preventDefault={() => {
|
||||
if (!dragging) {
|
||||
menuVisible = !menuVisible
|
||||
|
|
@ -76,15 +78,13 @@ async function logout () {
|
|||
stopDragging()
|
||||
}
|
||||
}}
|
||||
on:mousemove={e => {
|
||||
on:mousedown|preventDefault
|
||||
on:mousemove|preventDefault={e => {
|
||||
if (e.buttons && !dragging) {
|
||||
startDragging(e)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<img class="logo" src={logo} alt="Warpgate" on:mousedown|preventDefault />
|
||||
</button>
|
||||
|
||||
{#if menuVisible}
|
||||
<div class="menu">
|
||||
|
|
@ -103,21 +103,20 @@ async function logout () {
|
|||
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
|
||||
&.wg-hidden button {
|
||||
&.wg-hidden > img.menu-toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
> button.menu-toggle {
|
||||
> img.menu-toggle {
|
||||
transition: 0.5s ease-out opacity;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
border-radius: 7px;
|
||||
border: 1px solid rgba(128, 128, 128, .25);
|
||||
background: rgba(255, 255, 255, .5);
|
||||
backdrop-filter: blur(4px);
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import { wrap } from 'svelte-spa-router/wrap'
|
|||
import { get } from 'svelte/store'
|
||||
import { reloadServerInfo, serverInfo } from 'gateway/lib/store'
|
||||
import ThemeSwitcher from 'common/ThemeSwitcher.svelte'
|
||||
import Logo from 'common/Logo.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import AuthBar from 'common/AuthBar.svelte'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import Brand from 'common/Brand.svelte'
|
||||
|
||||
let redirecting = false
|
||||
let serverInfoPromise = reloadServerInfo()
|
||||
|
|
@ -69,7 +69,7 @@ init()
|
|||
{:else}
|
||||
<div class="d-flex align-items-center mt-5 mb-5">
|
||||
<a class="logo" href="/@warpgate">
|
||||
<Logo />
|
||||
<Brand />
|
||||
</a>
|
||||
|
||||
<AuthBar />
|
||||
|
|
@ -80,7 +80,7 @@ init()
|
|||
</main>
|
||||
|
||||
<footer class="mt-5">
|
||||
<span class="me-auto">
|
||||
<span class="me-auto ms-3">
|
||||
v{$serverInfo?.version}
|
||||
</span>
|
||||
<ThemeSwitcher />
|
||||
|
|
@ -96,9 +96,4 @@ init()
|
|||
width: 500px;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 5rem;
|
||||
margin: 0 -0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { api, CredentialKind, PasswordState, type CredentialsState, type ExistingOtpCredential, type ExistingPublicKeyCredential } from 'gateway/lib/api'
|
||||
import { serverInfo } from 'gateway/lib/store'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import { faIdBadge, faKey, faKeyboard, faMobilePhone } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fa from 'svelte-fa'
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
<span class="ms-auto"></span>
|
||||
<Button size="sm" color="link" on:click={() => {
|
||||
creatingPublicKeyCredential = true
|
||||
}}>Add new</Button>
|
||||
}}>Add key</Button>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush mb-3">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { reloadServerInfo } from 'gateway/lib/store'
|
|||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
let error: string|null = $state(null)
|
||||
let username = $state('')
|
||||
|
|
@ -184,8 +184,8 @@ async function startSSO (provider: SsoProviderDescription) {
|
|||
</FormGroup>
|
||||
|
||||
<AsyncButton
|
||||
outline
|
||||
class="d-flex align-items-center"
|
||||
color="primary"
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
click={login}
|
||||
|
|
@ -208,25 +208,23 @@ async function startSSO (provider: SsoProviderDescription) {
|
|||
|
||||
{#await ssoProvidersPromise then ssoProviders}
|
||||
{#if authState === ApiAuthState.SsoNeeded || authState === ApiAuthState.NotStarted || authState === ApiAuthState.Failed}
|
||||
<div class="mt-5">
|
||||
<div class="mt-5 sso-buttons">
|
||||
{#each ssoProviders as ssoProvider}
|
||||
<button
|
||||
class="btn d-flex align-items-center w-100 mb-2 btn-outline-primary"
|
||||
class="btn btn-secondary"
|
||||
disabled={busy}
|
||||
onclick={() => startSSO(ssoProvider)}
|
||||
>
|
||||
<span class="m-auto">
|
||||
{#if ssoProvider.kind === SsoProviderKind.Google}
|
||||
<Fa fw class="me-2" icon={faGoogle} />
|
||||
{/if}
|
||||
{#if ssoProvider.kind === SsoProviderKind.Azure}
|
||||
<Fa fw class="me-2" icon={faMicrosoft} />
|
||||
{/if}
|
||||
{#if ssoProvider.kind === SsoProviderKind.Apple}
|
||||
<Fa fw class="me-2" icon={faApple} />
|
||||
{/if}
|
||||
{ssoProvider.label}
|
||||
</span>
|
||||
{#if ssoProvider.kind === SsoProviderKind.Google}
|
||||
<Fa fw class="me-2" icon={faGoogle} />
|
||||
{/if}
|
||||
{#if ssoProvider.kind === SsoProviderKind.Azure}
|
||||
<Fa fw class="me-2" icon={faMicrosoft} />
|
||||
{/if}
|
||||
{#if ssoProvider.kind === SsoProviderKind.Apple}
|
||||
<Fa fw class="me-2" icon={faApple} />
|
||||
{/if}
|
||||
{ssoProvider.label}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -235,10 +233,30 @@ async function startSSO (provider: SsoProviderDescription) {
|
|||
|
||||
{#if authState !== ApiAuthState.NotStarted && authState !== ApiAuthState.Failed}
|
||||
<button
|
||||
class="btn w-100 mt-3 btn-outline-secondary"
|
||||
class="btn w-100 mt-3 btn-secondary"
|
||||
onclick={cancel}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.sso-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.85rem 1rem;
|
||||
|
||||
button {
|
||||
flex: 1 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { api, ApiAuthState, type AuthStateResponseInternal } from 'gateway/lib/a
|
|||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import DelayedSpinner from 'common/DelayedSpinner.svelte'
|
||||
import RelativeDate from 'admin/RelativeDate.svelte'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
params: { stateId: string };
|
||||
|
|
@ -52,7 +52,7 @@ async function reject () {
|
|||
{:then}
|
||||
{#if authState}
|
||||
<div class="page-summary-bar">
|
||||
<h1>Authorization request</h1>
|
||||
<h1>authorization request</h1>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
|
|
@ -93,7 +93,6 @@ async function reject () {
|
|||
Authorize
|
||||
</AsyncButton>
|
||||
<AsyncButton
|
||||
outline
|
||||
color="secondary"
|
||||
class="d-flex align-items-center ms-2"
|
||||
click={reject}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { serverInfo } from 'gateway/lib/store'
|
||||
import Alert from 'common/Alert.svelte'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import CredentialManager from './CredentialManager.svelte'
|
||||
</script>
|
||||
|
||||
<div class="page-summary-bar">
|
||||
<div>
|
||||
<h1>{$serverInfo!.username}</h1>
|
||||
<div class="text-muted">User</div>
|
||||
</div>
|
||||
<h1>{$serverInfo!.username}</h1>
|
||||
</div>
|
||||
|
||||
{#if $serverInfo}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Fa from 'svelte-fa'
|
|||
import { Modal, ModalBody } from '@sveltestrap/sveltestrap'
|
||||
import { serverInfo } from './lib/store'
|
||||
import { firstBy } from 'thenby'
|
||||
import ModalHeader from 'common/ModalHeader.svelte'
|
||||
import ModalHeader from 'common/sveltestrap-s5-ports/ModalHeader.svelte'
|
||||
|
||||
let selectedTarget: TargetSnapshot|undefined = $state()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<base href="/@warpgate" />
|
||||
<link rel="icon" href="/assets/logo.svg" />
|
||||
<link rel="icon" href="/@warpgate/assets/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Warpgate</title>
|
||||
<style>#app { display: none }</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,37 @@
|
|||
@use "sass:map";
|
||||
|
||||
@mixin button-variant(
|
||||
$background,
|
||||
$border,
|
||||
$color: color-contrast($background),
|
||||
$hover-background: if($color == $color-contrast-dark, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)),
|
||||
$hover-border: if($color == $color-contrast-dark, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)),
|
||||
$hover-color: color-contrast($hover-background),
|
||||
$active-background: if($color == $color-contrast-dark, shade-color($background, $btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)),
|
||||
$active-border: if($color == $color-contrast-dark, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)),
|
||||
$active-color: color-contrast($active-background),
|
||||
$disabled-background: $background,
|
||||
$disabled-border: $border,
|
||||
$disabled-color: $btn-disabled-color,
|
||||
$text-color: if($color == $color-contrast-light, shade-color($background, $btn-color-shade-amount), tint-color($background, $btn-color-tint-amount))
|
||||
) {
|
||||
$real-background: if($color == $color-contrast-dark, shade-color($background, $btn-bg-shade-amount), tint-color($background, $btn-bg-tint-amount));
|
||||
$real-color: if($color == $color-contrast-light, shade-color($background, $btn-color-shade-amount), tint-color($background, $btn-color-tint-amount));
|
||||
--#{$prefix}btn-color: #{$real-color};
|
||||
--#{$prefix}btn-bg: #{$real-background};
|
||||
--#{$prefix}btn-border-color: #{if($color == $color-contrast-dark, shade-color($background, $btn-border-shade-amount), tint-color($background, $btn-border-tint-amount))};
|
||||
--#{$prefix}btn-hover-color: #{$hover-color};
|
||||
--#{$prefix}btn-hover-bg: #{$hover-background};
|
||||
--#{$prefix}btn-hover-border-color: #{$hover-border};
|
||||
--#{$prefix}btn-focus-shadow-rgb: #{to-rgb(mix($color, $border, 15%))};
|
||||
--#{$prefix}btn-active-color: #{$active-color};
|
||||
--#{$prefix}btn-active-bg: #{$active-background};
|
||||
--#{$prefix}btn-active-border-color: #{$active-border};
|
||||
--#{$prefix}btn-active-shadow: #{$btn-active-box-shadow};
|
||||
--#{$prefix}btn-disabled-color: #{$disabled-color};
|
||||
--#{$prefix}btn-disabled-bg: #{$real-background};
|
||||
}
|
||||
|
||||
// Layout & components
|
||||
@import "bootstrap/scss/root";
|
||||
@import "bootstrap/scss/reboot";
|
||||
|
|
@ -12,7 +44,7 @@
|
|||
@import "bootstrap/scss/buttons";
|
||||
@import "bootstrap/scss/transitions";
|
||||
@import "bootstrap/scss/dropdown";
|
||||
// @import "bootstrap/scss/button-group";
|
||||
@import "bootstrap/scss/button-group";
|
||||
// @import "bootstrap/scss/nav";
|
||||
// @import "bootstrap/scss/navbar";
|
||||
// @import "bootstrap/scss/card";
|
||||
|
|
@ -52,8 +84,19 @@ $font-family-os: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
|
|||
margin: 0.25rem 0 1.5rem;
|
||||
|
||||
h1 {
|
||||
font-family: 'Poppins';
|
||||
font-weight: 700;
|
||||
font-size: 2.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.counter {
|
||||
display: inline-block;
|
||||
padding: 0px 10px;
|
||||
background: var(--bs-body-color);
|
||||
color: var(--bs-body-bg);
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.alert {
|
||||
|
|
@ -72,7 +115,7 @@ footer {
|
|||
align-items: center;
|
||||
padding: .5rem 0;
|
||||
margin: 2rem 0 1rem;
|
||||
border-top: 1px solid rgba($body-color, .5);
|
||||
border-top: 1px solid rgba($body-color, .2);
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
|
|
@ -91,6 +134,11 @@ input:-webkit-autofill:focus {
|
|||
font-family: $font-family-os;
|
||||
}
|
||||
|
||||
.btn {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--#{$prefix}btn-active-bg);
|
||||
}
|
||||
|
||||
.page-item.active .page-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -109,3 +157,48 @@ input:-webkit-autofill:focus {
|
|||
width: map.get($grid-breakpoints, "md");
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration-color: var(--wg-link-underline-color);
|
||||
text-underline-offset: 2px;
|
||||
|
||||
&:hover, &.active {
|
||||
text-decoration-color: var(--wg-link-hover-underline-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make these vars reusable from everywhere
|
||||
body {
|
||||
--bs-list-group-color: #{$list-group-color};
|
||||
--bs-list-group-bg: #{$list-group-bg};
|
||||
--bs-list-group-border-color: #{$list-group-border-color};
|
||||
--bs-list-group-border-width: #{$list-group-border-width};
|
||||
--bs-list-group-border-radius: #{$list-group-border-radius};
|
||||
--bs-list-group-item-padding-x: #{$list-group-item-padding-x};
|
||||
--bs-list-group-item-padding-y: #{$list-group-item-padding-y};
|
||||
--bs-list-group-action-color: #{$list-group-action-color};
|
||||
--bs-list-group-action-hover-color: #{$list-group-action-hover-color};
|
||||
--bs-list-group-action-hover-bg: #{$list-group-hover-bg};
|
||||
--bs-list-group-action-active-color: #{$list-group-action-active-color};
|
||||
--bs-list-group-action-active-bg: #{$list-group-action-active-bg};
|
||||
--bs-list-group-disabled-color: #{$list-group-disabled-color};
|
||||
--bs-list-group-disabled-bg: #{$list-group-disabled-bg};
|
||||
--bs-list-group-active-color: #{$list-group-active-color};
|
||||
--bs-list-group-active-bg: #{$list-group-active-bg};
|
||||
--bs-list-group-active-border-color: #{$list-group-active-border-color};
|
||||
|
||||
--wg-link-underline-color: #{$link-underline-color};
|
||||
--wg-link-hover-underline-color: #{$link-hover-underline-color};
|
||||
}
|
||||
|
||||
@each $theme-color, $value in $theme-colors {
|
||||
.text-bg-#{$theme-color} {
|
||||
$color: color-contrast($value);
|
||||
$real-background: if($color == $color-contrast-dark, shade-color($value, $btn-bg-shade-amount), tint-color($value, $btn-bg-tint-amount));
|
||||
$real-color: if($color == $color-contrast-light, shade-color($value, $btn-color-shade-amount), tint-color($value, $btn-color-tint-amount));
|
||||
|
||||
color: $real-color if($enable-important-utilities, !important, null);
|
||||
background-color: $real-background if($enable-important-utilities, !important, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
warpgate-web/src/theme/fonts.css
Normal file
7
warpgate-web/src/theme/fonts.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-display: block; /* changed */
|
||||
font-weight: 700;
|
||||
src: url(./../../node_modules/@fontsource/poppins/files/poppins-latin-700-normal.woff2) format('woff2'), url(./../../node_modules/@fontsource/poppins/files/poppins-latin-700-normal.woff) format('woff');
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import '@fontsource/work-sans'
|
||||
import './fonts.css'
|
||||
|
||||
import { get, writable } from 'svelte/store'
|
||||
|
||||
|
|
|
|||
|
|
@ -29,18 +29,8 @@
|
|||
|
||||
@import "theme";
|
||||
|
||||
a {
|
||||
text-decoration-color: rgba($link-color, .5);
|
||||
text-underline-offset: 2px;
|
||||
|
||||
&:hover, &.active {
|
||||
color: $link-hover-color;
|
||||
text-decoration-color: rgba($link-hover-color, .75);
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid rgba($body-color, .5);
|
||||
border-bottom: 1px solid rgba($body-color, .2);
|
||||
}
|
||||
|
||||
.list-group-item-action {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,6 @@
|
|||
@import "bootstrap/scss/utilities";
|
||||
@import "theme";
|
||||
|
||||
a {
|
||||
text-decoration-color: rgba($body-color, 0.25);
|
||||
text-underline-offset: 2px;
|
||||
|
||||
&:hover, &.active {
|
||||
text-decoration-color: $body-color;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid rgba($body-color, .75);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,3 +8,29 @@ $pagination-hover-bg: transparent;
|
|||
$pagination-focus-bg: transparent;
|
||||
$modal-header-border-color: transparent;
|
||||
$dropdown-link-hover-bg: transparent;
|
||||
|
||||
$btn-padding-x: 1.5rem;
|
||||
|
||||
$btn-bg-shade-amount: 75%;
|
||||
$btn-bg-tint-amount: 60%;
|
||||
$btn-border-shade-amount: 75%;
|
||||
$btn-border-tint-amount: 65%;
|
||||
$btn-color-shade-amount: 10%;
|
||||
$btn-color-tint-amount: 50%;
|
||||
|
||||
$btn-hover-bg-shade-amount: 60%;
|
||||
$btn-hover-bg-tint-amount: 50%;
|
||||
$btn-hover-border-shade-amount: 60%;
|
||||
$btn-hover-border-tint-amount: 10%;
|
||||
|
||||
$btn-active-bg-shade-amount: 50%;
|
||||
// $btn-active-bg-tint-amount
|
||||
$btn-active-border-shade-amount: 40%;
|
||||
// $btn-active-border-tint-amount
|
||||
|
||||
$tooltip-color: #c1c9e4;
|
||||
|
||||
$badge-font-size: .8em;
|
||||
$badge-font-weight: 400;
|
||||
$badge-padding-y: .55em;
|
||||
$badge-padding-x: .85em;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import "bootstrap/scss/functions";
|
||||
|
||||
$body-bg: #1d1d20;
|
||||
$body-bg: #14141a;
|
||||
$body-color: #c1c9e4;
|
||||
|
||||
$font-family-sans-serif: "Work Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
|
|
@ -21,22 +21,19 @@ $teal: #20c997;
|
|||
$cyan: #0dcaf0;
|
||||
|
||||
$primary: $blue;
|
||||
$secondary: #627c84;
|
||||
$secondary: #748d95;
|
||||
$light: transparent;
|
||||
$info: $blue;
|
||||
|
||||
$component-active-bg: $primary;
|
||||
|
||||
$input-bg: #ffffff08;
|
||||
$input-bg: #1c1c24;
|
||||
$input-border-color: #ced4da40;
|
||||
$input-color: #ccc;
|
||||
$input-focus-bg: #ffffff08;
|
||||
$input-focus-bg: #1b1b22;
|
||||
$input-focus-border-color: tint-color($component-active-bg, 25%) ;
|
||||
$input-disabled-bg: $input-bg;
|
||||
|
||||
$form-check-input-border: 2px solid $input-border-color;
|
||||
$form-switch-color: $input-color;
|
||||
|
||||
$input-btn-focus-color-opacity: .25;
|
||||
$input-btn-border-width: 2px;
|
||||
|
||||
|
|
@ -60,6 +57,7 @@ $modal-content-bg: $body-bg;
|
|||
|
||||
$btn-close-color: $secondary;
|
||||
$btn-link-disabled-color: rgba(255, 255, 255, .5);
|
||||
$btn-disabled-color: #969696;
|
||||
|
||||
$alert-bg-scale: 100%;
|
||||
$alert-border-scale: 50%;
|
||||
|
|
@ -67,5 +65,21 @@ $alert-color-scale: 0%;
|
|||
|
||||
$code-color: #84f1fe;
|
||||
|
||||
$form-check-input-border: 2px solid $input-border-color;
|
||||
$form-switch-color: $secondary;
|
||||
$form-check-color: $primary;
|
||||
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "./vars.common.scss";
|
||||
|
||||
$link-underline-color: rgba($link-color, .5);
|
||||
$link-hover-underline-color: rgba($link-hover-color, .75);
|
||||
|
||||
|
||||
|
||||
$form-check-input-width: 1.3em;
|
||||
$form-switch-width: 2.5em;
|
||||
$form-switch-padding-start: 3em;
|
||||
|
||||
$form-check-input-checked-border-color: shade-color($form-check-color, $btn-active-border-shade-amount);
|
||||
$form-check-input-checked-bg-color: shade-color($form-check-color, $btn-active-bg-shade-amount);
|
||||
|
|
|
|||
|
|
@ -9,20 +9,40 @@ $alert-border-radius: 0;
|
|||
$alert-border-scale: -30%;
|
||||
|
||||
|
||||
$blue: #306F84 !default;
|
||||
$blue: #3573ac !default;
|
||||
$purple: #5C398F !default;
|
||||
$pink: #B53D6D !default;
|
||||
$red: #D35D47 !default;
|
||||
$orange: #fd7e14 !default;
|
||||
$yellow: #D38F47 !default;
|
||||
$green: #87C041 !default;
|
||||
$red: #842716 !default;
|
||||
$orange: #a84c01 !default;
|
||||
$yellow: #916101 !default;
|
||||
$green: #417106 !default;
|
||||
$teal: #20c997 !default;
|
||||
$cyan: #0dcaf0 !default;
|
||||
$cyan: #0e6b7e !default;
|
||||
|
||||
// $primary: $blue;
|
||||
$secondary: #506579;
|
||||
|
||||
$btn-disabled-color: #969696;
|
||||
|
||||
|
||||
$form-switch-color: $secondary;
|
||||
// $form-check-color: $primary;
|
||||
$form-check-input-border: 2px solid rgba(#000, .25);
|
||||
|
||||
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "./vars.common.scss";
|
||||
|
||||
$form-check-color: $primary;
|
||||
|
||||
$text-muted: $gray-500;
|
||||
|
||||
$link-underline-color: rgba($body-color, 0.25);
|
||||
$link-hover-underline-color: $body-color;
|
||||
|
||||
|
||||
$form-check-input-width: 1.3em;
|
||||
$form-switch-width: 2.5em;
|
||||
$form-switch-padding-start: 3em;
|
||||
|
||||
$form-check-input-checked-border-color: tint-color($form-check-color, $btn-active-border-tint-amount);
|
||||
$form-check-input-checked-bg-color: tint-color($form-check-color, $btn-active-bg-tint-amount);
|
||||
|
|
|
|||
|
|
@ -213,6 +213,11 @@
|
|||
dependencies:
|
||||
levn "^0.4.1"
|
||||
|
||||
"@fontsource/poppins@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-5.1.0.tgz#919205f53d99ec4751a945057ef525f06681df8a"
|
||||
integrity sha512-tpLXlnNi2fwQjiipvuj4uNFHCdoLA8izRsKdoexZuEzjx0r/g1aKLf4ta6lFgF7L+/+AFdmaXFlUwwvmDzYH+g==
|
||||
|
||||
"@fontsource/work-sans@^4.5.12":
|
||||
version "4.5.14"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/work-sans/-/work-sans-4.5.14.tgz#965bf103baa9f812d042b5f678a34f632fa76f2e"
|
||||
|
|
@ -426,95 +431,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@otplib/preset-browser/-/preset-browser-12.0.1.tgz#3f7f4dec25f8fcb5eed533c90df76784f219f088"
|
||||
integrity sha512-64Eb6JLRRcER2NuIIVQIVNb3yn4mJLUwN1i3icmmNpTS+r4izwdM3eQs9wbeRjjX46kgfMZqPFYsC9JuGM0TKw==
|
||||
|
||||
"@parcel/watcher-android-arm64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a"
|
||||
integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==
|
||||
|
||||
"@parcel/watcher-darwin-arm64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f"
|
||||
integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==
|
||||
|
||||
"@parcel/watcher-darwin-x64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb"
|
||||
integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==
|
||||
|
||||
"@parcel/watcher-freebsd-x64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82"
|
||||
integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==
|
||||
|
||||
"@parcel/watcher-linux-arm-glibc@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42"
|
||||
integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==
|
||||
|
||||
"@parcel/watcher-linux-arm-musl@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4"
|
||||
integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==
|
||||
|
||||
"@parcel/watcher-linux-arm64-glibc@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03"
|
||||
integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==
|
||||
|
||||
"@parcel/watcher-linux-arm64-musl@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732"
|
||||
integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==
|
||||
|
||||
"@parcel/watcher-linux-x64-glibc@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d"
|
||||
integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==
|
||||
|
||||
"@parcel/watcher-linux-x64-musl@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef"
|
||||
integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==
|
||||
|
||||
"@parcel/watcher-win32-arm64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154"
|
||||
integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==
|
||||
|
||||
"@parcel/watcher-win32-ia32@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220"
|
||||
integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==
|
||||
|
||||
"@parcel/watcher-win32-x64@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7"
|
||||
integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==
|
||||
|
||||
"@parcel/watcher@^2.4.1":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10"
|
||||
integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
is-glob "^4.0.3"
|
||||
micromatch "^4.0.5"
|
||||
node-addon-api "^7.0.0"
|
||||
optionalDependencies:
|
||||
"@parcel/watcher-android-arm64" "2.5.0"
|
||||
"@parcel/watcher-darwin-arm64" "2.5.0"
|
||||
"@parcel/watcher-darwin-x64" "2.5.0"
|
||||
"@parcel/watcher-freebsd-x64" "2.5.0"
|
||||
"@parcel/watcher-linux-arm-glibc" "2.5.0"
|
||||
"@parcel/watcher-linux-arm-musl" "2.5.0"
|
||||
"@parcel/watcher-linux-arm64-glibc" "2.5.0"
|
||||
"@parcel/watcher-linux-arm64-musl" "2.5.0"
|
||||
"@parcel/watcher-linux-x64-glibc" "2.5.0"
|
||||
"@parcel/watcher-linux-x64-musl" "2.5.0"
|
||||
"@parcel/watcher-win32-arm64" "2.5.0"
|
||||
"@parcel/watcher-win32-ia32" "2.5.0"
|
||||
"@parcel/watcher-win32-x64" "2.5.0"
|
||||
|
||||
"@popperjs/core@^2.11.8":
|
||||
version "2.11.8"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||
|
|
@ -1076,7 +992,7 @@ chardet@^0.7.0:
|
|||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
chokidar@^3.5.1:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
|
|
@ -1091,7 +1007,7 @@ chokidar@^3.5.1:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
chokidar@^4.0.0, chokidar@^4.0.1:
|
||||
chokidar@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
|
||||
integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
|
||||
|
|
@ -1351,11 +1267,6 @@ detect-europe-js@^0.1.2:
|
|||
resolved "https://registry.yarnpkg.com/detect-europe-js/-/detect-europe-js-0.1.2.tgz#aa76642e05dae786efc2e01a23d4792cd24c7b88"
|
||||
integrity sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==
|
||||
|
||||
detect-libc@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||
|
||||
dijkstrajs@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23"
|
||||
|
|
@ -2144,10 +2055,10 @@ ignore@^5.1.1, ignore@^5.2.0, ignore@^5.3.1:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||
|
||||
immutable@^5.0.2:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.0.3.tgz#aa037e2313ea7b5d400cd9298fa14e404c933db1"
|
||||
integrity sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==
|
||||
immutable@^4.0.0:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381"
|
||||
integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
|
||||
|
||||
import-fresh@^3.2.1:
|
||||
version "3.3.0"
|
||||
|
|
@ -2519,7 +2430,7 @@ merge2@^1.3.0:
|
|||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
micromatch@^4.0.4, micromatch@^4.0.5:
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
|
|
@ -2610,11 +2521,6 @@ netmask@^2.0.2:
|
|||
resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
|
||||
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
|
||||
|
||||
node-addon-api@^7.0.0:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
|
|
@ -3101,16 +3007,14 @@ safe-regex-test@^1.0.3:
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sass@~1.82:
|
||||
version "1.82.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.82.0.tgz#30da277af3d0fa6042e9ceabd0d984ed6d07df70"
|
||||
integrity sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==
|
||||
sass@1.78:
|
||||
version "1.78.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.78.0.tgz#cef369b2f9dc21ea1d2cf22c979f52365da60841"
|
||||
integrity sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==
|
||||
dependencies:
|
||||
chokidar "^4.0.0"
|
||||
immutable "^5.0.2"
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
optionalDependencies:
|
||||
"@parcel/watcher" "^2.4.1"
|
||||
|
||||
semver@^6.1.0, semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,14 @@ pub(crate) async fn command(cli: &crate::Cli, enable_admin_token: bool) -> Resul
|
|||
})
|
||||
});
|
||||
|
||||
let config = load_config(&cli.config, true)?;
|
||||
let config = match load_config(&cli.config, true) {
|
||||
Ok(config) => config,
|
||||
Err(error) => {
|
||||
error!(?error, "Failed to load config file");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let services = Services::new(config.clone(), admin_token).await?;
|
||||
|
||||
install_database_logger(services.db.clone());
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ use warpgate_common::helpers::fs::secure_file;
|
|||
use warpgate_common::{WarpgateConfig, WarpgateConfigStore};
|
||||
|
||||
pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
|
||||
if secure {
|
||||
secure_file(path).context("Could not secure config")?;
|
||||
}
|
||||
|
||||
let mut store: serde_yaml::Value = Config::builder()
|
||||
.add_source(File::from(path))
|
||||
.add_source(Environment::with_prefix("WARPGATE"))
|
||||
|
|
@ -22,6 +18,10 @@ pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
|
|||
.try_deserialize()
|
||||
.context("Could not parse YAML")?;
|
||||
|
||||
if secure {
|
||||
secure_file(path).context("Could not secure config")?;
|
||||
}
|
||||
|
||||
check_and_migrate_config(&mut store);
|
||||
|
||||
let store: WarpgateConfigStore =
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue