feat(kubernetes): add kubernetes tool (#1929)

Co-authored-by: oussama Dahmaz <dahmaz@MacBook-Pro-de-odahmaz.local>
This commit is contained in:
oussama Dahmaz
2025-03-09 17:46:15 +01:00
committed by GitHub
parent f6f0d7c72b
commit f19aa29607
67 changed files with 3897 additions and 67 deletions

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="cm.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="33.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="771"
inkscape:window-x="1"
inkscape:window-y="0"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g3349"
transform="translate(0.11778981,0.45794291)">
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 8.236948,6.2914262 5.825001,0"
id="path876" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 5.723058,6.2914262 1.45834,0"
id="path880" />
<a
id="a3346">
<path
id="path884"
d="m 10.353619,8.4080928 3.70833,0"
inkscape:connector-curvature="0"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:export-xdpi="376.57999"
inkscape:export-ydpi="376.57999" />
</a>
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 7.839728,8.4080928 1.4583305,0"
id="path888" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 10.353619,10.52476 3.70833,0"
id="path892" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 7.839728,10.52476 1.4583305,0"
id="path896" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 8.236948,12.641428 5.825001,0"
id="path900" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 5.723058,12.641428 1.45834,0"
id="path904" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="ing.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="28.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
id="path7709"
pointer-events="none"
d="m 12.75799,13.997178 -2.270701,0 -4.9209009,-6.1558617 -1.42366,0 0,-2.0149069 2.31473,0 4.9230119,6.1558536 1.37752,0 0,-1.593474 3.119869,2.599882 -3.119869,2.601983 z m -2.47616,-4.7552751 1.09864,-1.3754256 1.37752,0 0,1.593475 3.119869,-2.5998829 -3.119869,-2.601983 0,1.593483 -2.270701,0 -1.4571904,1.8241102 z m -3.5979219,1.3649431 -1.11752,1.400578 -1.42366,0 0,2.014915 2.31473,0 1.4781699,-1.849278 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.20966817" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="ns.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="23.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<rect
y="6.5793304"
x="6.1734986"
height="6.6900792"
width="7.6735892"
id="rect8790"
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:0.80000001, 0.4;stroke-dashoffset:3.44000006;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="node.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="16.847496"
inkscape:cy="33.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccscccccccccccccccc"
id="path1994"
d="M 9.9921803,5.0101759 C 9.8538505,5.0057759 5.9970805,6.902049 5.9466504,6.999295 c -0.12117,0.233682 -0.9989,4.281827 -0.94731,4.369074 0.03,0.05065 0.66219,0.851861 1.40458,1.780327 l 1.3498201,1.688014 2.2211901,9.31e-4 2.2216404,9.31e-4 1.41321,-1.765731 1.41365,-1.765228 -0.49479,-2.1685759 C 14.256441,7.946271 14.012981,6.950423 13.988111,6.926433 13.918611,6.859553 10.067151,5.0126389 9.9921803,5.0102179 Z m 0.1961407,0.947753 0.90893,0.2635771 -0.90893,0.263576 -0.9089209,-0.263576 z m -0.9089209,0.36452 0.8511209,0.2532261 -0.004,1.183289 -0.8468109,-0.469347 z m 1.8178509,0 0,0.9671681 -0.84679,0.469347 -0.004,-1.183289 z M 8.8997705,7.937127 9.8087101,8.2007027 8.8997705,8.4642797 7.9908504,8.2007027 Z m 2.2087005,0 0.90894,0.2635757 -0.90894,0.263577 -0.90893,-0.263577 z m -3.1176206,0.3645197 0.8511202,0.252792 -0.004,1.1832908 -0.8468199,-0.468915 z m 1.8178597,0 0,0.9671678 -0.8468098,0.468915 -0.004,-1.1832908 z m 0.3908309,0 0.85113,0.252792 -0.004,1.1832908 -0.84682,-0.468915 z m 1.81787,0 0,0.9671678 -0.84682,0.468915 -0.004,-1.1832908 z m -3.4496605,1.515028 c 0.2706299,0.0096 0.0611,0.2819093 0.3684101,0.4279353 0.3277495,0.155764 0.3953995,-0.235354 0.6013395,0.06341 0.20599,0.298766 -0.18339,0.223239 -0.15443,0.584957 0.029,0.361718 0.40165,0.224858 0.24589,0.552606 -0.15575,0.327746 -0.28532,-0.04764 -0.5840895,0.158317 -0.2987401,0.205957 0.006,0.460208 -0.35546,0.489192 -0.3617401,0.02898 -0.1015001,-0.270447 -0.42924,-0.426208 -0.32775,-0.155765 -0.3953801,0.234921 -0.6013402,-0.06385 -0.2059599,-0.298767 0.1838299,-0.22281 0.15485,-0.584528 -0.029,-0.361718 -0.4016499,-0.224859 -0.24587,-0.552604 0.1557501,-0.327749 0.2848801,0.04764 0.5836502,-0.15832 0.2987898,-0.205956 -0.006,-0.4602083 0.3559099,-0.4891903 0.022499,-0.0018 0.0424,-0.0023 0.0604,-0.0018 z m 2.3359605,0.3627943 c 0.48335,0.01358 0.0146,0.467218 0.45596,0.664766 0.44144,0.197548 0.46714,-0.454103 0.79937,-0.102669 0.33221,0.351432 -0.31997,0.34064 -0.14753,0.792455 0.17243,0.451813 0.65163,0.0092 0.63802,0.49264 -0.0137,0.483411 -0.46723,0.01456 -0.66477,0.455977 -0.19755,0.441412 0.4541,0.467143 0.10266,0.799357 -0.35141,0.332212 -0.34021,-0.319974 -0.79202,-0.147534 -0.45183,0.172437 -0.009,0.65161 -0.49265,0.638019 -0.48339,-0.01358 -0.0146,-0.467216 -0.45596,-0.664764 -0.4414105,-0.197551 -0.4675805,0.454102 -0.7997909,0.102669 -0.3322097,-0.351431 0.3199804,-0.340209 0.14754,-0.792025 -0.17245,-0.451815 -0.6516296,-0.0092 -0.6380295,-0.492642 0.013699,-0.483408 0.4672095,-0.01499 0.6647795,-0.456405 0.1975204,-0.441414 -0.45411,-0.467143 -0.10269,-0.799357 0.3514505,-0.332213 0.3406505,0.319971 0.7924609,0.147534 0.45184,-0.17244 0.009,-0.651611 0.49265,-0.638021 z m -2.3148207,0.253655 c -0.2936499,9e-5 -0.5316098,0.238249 -0.5314498,0.531898 7.99e-5,0.293481 0.2379701,0.531377 0.5314498,0.531467 0.2936602,1.59e-4 0.5318202,-0.23781 0.5319,-0.531467 1.601e-4,-0.293825 -0.2380699,-0.532057 -0.5319,-0.531898 z m 2.2643607,0.480564 c -0.58689,-1.96e-4 -1.0627109,0.475614 -1.0625209,1.062502 5e-5,0.586719 0.4758009,1.062267 1.0625209,1.062071 0.58654,-5.8e-5 1.06201,-0.475531 1.06206,-1.062071 1.9e-4,-0.586708 -0.47535,-1.062444 -1.06206,-1.062502 z"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#eeeeee;stroke-width:0;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:11.23642349;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="pod.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="33.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g3341"
transform="translate(0.12766661,0.35147801)">
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="M 6.2617914,7.036086 9.8826317,5.986087 13.503462,7.036086 9.8826317,8.086087 Z"
id="path910" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 6.2617914,7.43817 0,3.852778 3.3736103,1.868749 0.0167,-4.713193 z"
id="path912" />
<path
inkscape:export-ydpi="376.57999"
inkscape:export-xdpi="376.57999"
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 13.503462,7.43817 0,3.852778 -3.37361,1.868749 -0.0167,-4.713193 z"
id="path914" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="secret.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="28.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g3347"
transform="translate(0.05710921,0.77487342)">
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 10.414299,8.0912253 3.708331,0"
id="path922" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79400003;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 8.2976282,5.9745582 5.8250018,0"
id="path930" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 5.7837382,5.9745582 1.45834,0"
id="path934" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:#326ce5;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 7.7183782,8.4321603 0,0 c 0,-1.1667021 1.0110897,-2.1125011 2.2583305,-2.1125011 1.2472403,0 2.2583303,0.945799 2.2583303,2.1125011 l -1.05624,0 0,0 c 0,-0.583351 -0.5382,-1.0562501 -1.2020903,-1.0562501 -0.6638904,0 -1.2020808,0.4728991 -1.2020808,1.0562501 z"
id="path936" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 10.414299,10.207893 3.708331,0"
id="path942" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 8.2976282,12.32456 5.8250018,0"
id="path950" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 5.7837382,12.32456 1.45834,0"
id="path954" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:#326ce5;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 7.1622182,8.3691063 5.6166608,0 0,3.6583337 -5.6166608,0 z"
id="path956" />
<circle
r="0.55515254"
cy="10.198272"
cx="9.9705448"
id="path7280"
style="opacity:1;fill:#326ce5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.26478875;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:0.52957746, 0.26478873;stroke-dashoffset:5.01860619;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="svc.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-2.090004"
inkscape:cy="28.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g3345"
transform="translate(0.09238801,0.66897746)">
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 4.4949896,11.260826 2.9083311,0 0,2.041667 -2.9083311,0 z"
id="path964" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 8.4637407,11.260826 2.9083303,0 0,2.041667 -2.9083303,0 z"
id="path966" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 12.432491,11.260826 2.90833,0 0,2.041667 -2.90833,0 z"
id="path968" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 7.6137407,5.2082921 4.6083303,0 0,2.041667 -4.6083303,0 z"
id="path970" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 9.9179005,7.2499601 0,2.005449 -3.966671,0 0,2.0028859"
id="path978" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.52899998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 9.9179005,7.2499601 0,2.005449 3.9666705,0 0,2.0028859"
id="path986" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 9.9095538,7.2512251 0,2.005449 0.0167,0 0,2.0028859"
id="path982" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18.035334mm"
height="17.500378mm"
viewBox="0 0 18.035334 17.500378"
version="1.1"
id="svg13826"
inkscape:version="0.91 r13725"
sodipodi:docname="pv.svg">
<defs
id="defs13820" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-12.090004"
inkscape:cy="28.752239"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="775"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata13823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.99262638,-1.174181)">
<g
id="g70"
transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)">
<path
inkscape:export-ydpi="250.55"
inkscape:export-xdpi="250.55"
inkscape:export-filename="new.png"
inkscape:connector-curvature="0"
id="path3055"
d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z"
style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3054-2-9"
d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g3341"
transform="translate(-0.18983289,0.49258906)">
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 5.5709614,7.9105849 0,0 c 0,0.621121 2.0725401,1.124639 4.6291706,1.124639 2.556609,0 4.629159,-0.503518 4.629159,-1.124639 l 0,3.0423911 c 0,0.62112 -2.07255,1.124638 -4.629159,1.124638 -2.5566305,0 -4.6291706,-0.503518 -4.6291706,-1.124638 z"
id="path1114" />
<path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:square;stroke-miterlimit:10"
inkscape:connector-curvature="0"
d="m 5.5709614,7.9105849 0,0 c 0,-0.621119 2.0725401,-1.124637 4.6291706,-1.124637 2.556609,0 4.629159,0.503518 4.629159,1.124637 l 0,0 c 0,0.621121 -2.07255,1.124639 -4.629159,1.124639 -2.5566305,0 -4.6291706,-0.503518 -4.6291706,-1.124639 z"
id="path1116" />
<path
style="fill:none;fill-rule:evenodd;stroke:#326ce5;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1"
inkscape:connector-curvature="0"
d="m 14.829291,7.9105849 0,0 c 0,0.621121 -2.07255,1.124639 -4.629159,1.124639 -2.5566205,0 -4.6291706,-0.503518 -4.6291706,-1.124639 l 0,0 c 0,-0.621119 2.0725501,-1.124637 4.6291706,-1.124637 2.556609,0 4.629159,0.503518 4.629159,1.124637 l 0,3.0423911 c 0,0.62112 -2.07255,1.124638 -4.629159,1.124638 -2.5566205,0 -4.6291706,-0.503518 -4.6291706,-1.124638 l 0,-3.0423911"
id="path1120" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -28,6 +28,7 @@ import {
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { createDocumentationLink } from "@homarr/definitions";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { MainHeader } from "~/components/layout/header";
@@ -113,7 +114,13 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
label: t("items.tools.items.docker"),
icon: IconBrandDocker,
href: "/manage/tools/docker",
hidden: !session?.user.permissions.includes("admin"),
hidden: !(session?.user.permissions.includes("admin") && env.ENABLE_DOCKER),
},
{
label: t("items.tools.items.kubernetes"),
icon: IconBox,
href: "/manage/tools/kubernetes",
hidden: !(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES),
},
{
label: t("items.tools.items.api"),

View File

@@ -3,6 +3,7 @@ import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
@@ -10,7 +11,7 @@ import { DockerTable } from "./docker-table";
export default async function DockerPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
if (!(session?.user.permissions.includes("admin") && env.ENABLE_DOCKER)) {
notFound();
}

View File

@@ -0,0 +1,76 @@
"use client";
import { SimpleGrid, Skeleton, Stack, Title } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/db/client";
import type { KubernetesLabelResourceType } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import KubernetesErrorPage from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/error";
import { HeaderCard } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/header-card/header-card";
import { ResourceGauge } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/resource-gauge/resource-gauge";
import { ResourceTile } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/resource-tile/resource-tile";
export function ClusterDashboard() {
const t = useI18n();
const {
data: clusterData,
isLoading: isClusterLoading,
isError: isClusterError,
} = clientApi.kubernetes.cluster.getCluster.useQuery();
const {
data: resourceCountsData,
isLoading: isResourceCountsLoading,
isError: isResourceCountsError,
} = clientApi.kubernetes.cluster.getClusterResourceCounts.useQuery();
return (
<Stack bg="var(--mantine-color-body)">
<Title>{t("kubernetes.cluster.title")}</Title>
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3 }}>
{isClusterError ? (
Array.from({ length: 3 }).map(() => <KubernetesErrorPage key={createId()} />)
) : isClusterLoading ? (
Array.from({ length: 3 }).map(() => <Skeleton key={createId()} height={65} />)
) : (
<>
<HeaderCard headerType={"providers"} value={clusterData ? clusterData.providers : ""} />
<HeaderCard headerType={"version"} value={clusterData ? clusterData.kubernetesVersion : ""} />
<HeaderCard headerType={"architecture"} value={clusterData ? clusterData.architecture : ""} />
</>
)}
</SimpleGrid>
<Title>{t("kubernetes.cluster.capacity.title")}</Title>
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3 }}>
{isClusterError
? Array.from({ length: 3 }).map(() => <KubernetesErrorPage key={createId()} />)
: isClusterLoading
? Array.from({ length: 3 }).map(() => <Skeleton key={createId()} height={200} />)
: clusterData?.capacity.map((capacity) => (
<ResourceGauge kubernetesCapacity={capacity} key={capacity.type} />
))}
</SimpleGrid>
<Title>{t("kubernetes.cluster.resources.title")}</Title>
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3 }}>
{isResourceCountsError
? Array.from({ length: 8 }).map(() => <KubernetesErrorPage key={createId()} />)
: isResourceCountsLoading
? Array.from({ length: 8 }).map(() => <Skeleton key={createId()} height={100} />)
: resourceCountsData?.map((clusterResourceCount) => (
<ResourceTile
count={clusterResourceCount.count}
label={clusterResourceCount.label as KubernetesLabelResourceType}
key={clusterResourceCount.label}
/>
))}
</SimpleGrid>
</Stack>
);
}

View File

@@ -0,0 +1,27 @@
"use client";
import Link from "next/link";
import { Anchor, Center, Stack, Text } from "@mantine/core";
import { IconCubeOff } from "@tabler/icons-react";
import { useI18n } from "@homarr/translation/client";
export default function KubernetesErrorPage() {
const t = useI18n();
return (
<Center>
<Stack align="center">
<IconCubeOff size={48} stroke={1.5} />
<Stack align="center" gap="xs">
<Text size="lg" fw={500}>
{t("kubernetes.error.internalServerError")}
</Text>
<Anchor size="sm" component={Link} href="/manage/tools/logs">
{t("common.action.checkLogs")}
</Anchor>
</Stack>
</Stack>
</Center>
);
}

View File

@@ -0,0 +1,31 @@
.header {
direction: inherit;
position: relative;
overflow: hidden;
padding: var(--mantine-spacing-xs);
padding-left: calc(var(--mantine-spacing-xs) * 2);
@mixin hover {
box-shadow: var(--mantine-shadow-md);
}
&::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 6px;
background-image: linear-gradient(0, var(--mantine-color-blue-4), var(--mantine-color-blue-9));
}
@mixin rtl {
padding-left: var(--mantine-spacing-xs);
padding-right: calc(var(--mantine-spacing-xs) * 2);
&::before {
left: unset;
right: 0;
}
}
}

View File

@@ -0,0 +1,44 @@
import { Card, Flex, Text, ThemeIcon } from "@mantine/core";
import { isLocaleRTL } from "@homarr/translation";
import { useCurrentLocale, useI18n } from "@homarr/translation/client";
import { HeaderIcon } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/header-card/header-icon";
import classes from "./header-card.module.css";
export type HeaderTypes = "providers" | "version" | "architecture";
interface HeaderCardProps {
headerType: HeaderTypes;
value: string;
}
export function HeaderCard(props: HeaderCardProps) {
const t = useI18n();
const currentLocale = useCurrentLocale();
const isRTL = isLocaleRTL(currentLocale);
return (
<Card className={classes.header}>
<Flex align="center" justify={isRTL ? "space-between" : "flex-start"} gap="md" direction={"row"}>
<ThemeIcon
size="xl"
radius="md"
variant="gradient"
gradient={{
deg: 0,
from: "var(--mantine-color-blue-4)",
to: "var(--mantine-color-blue-9)",
}}
>
<HeaderIcon type={props.headerType} />
</ThemeIcon>
<Text size="xl" fw={500} dir={isRTL ? "rtl" : "ltr"}>
{isRTL
? `${props.value} : ${t(`kubernetes.cluster.${props.headerType}`)}`
: `${t(`kubernetes.cluster.${props.headerType}`)} : ${props.value}`}
</Text>
</Flex>
</Card>
);
}

View File

@@ -0,0 +1,18 @@
import { IconBrandGit, IconCloudShare, IconGeometry } from "@tabler/icons-react";
import type { HeaderTypes } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/header-card/header-card";
interface HeaderIconProps {
type: HeaderTypes;
}
export function HeaderIcon({ type }: HeaderIconProps) {
switch (type) {
case "providers":
return <IconCloudShare size={28} stroke={1.5} />;
case "version":
return <IconBrandGit size={28} stroke={1.5} />;
default:
return <IconGeometry size={28} stroke={1.5} />;
}
}

View File

@@ -0,0 +1,20 @@
.paper {
position: relative;
overflow: visible;
padding: var(--mantine-spacing-xl);
padding-top: calc(var(--mantine-spacing-xl) * 1.5 + 20px);
}
.icon {
position: absolute;
top: -20px;
left: calc(50% - 30px);
border: groove white;
}
.title {
font-family:
Greycliff CF,
var(--mantine-font-family);
line-height: 1;
}

View File

@@ -0,0 +1,90 @@
import React from "react";
import { Group, Paper, Progress, Text, ThemeIcon } from "@mantine/core";
import type { KubernetesCapacity } from "@homarr/definitions";
import { isLocaleRTL } from "@homarr/translation";
import { useCurrentLocale, useI18n } from "@homarr/translation/client";
import { ResourceIcon } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/resource-gauge/resource-icon";
import classes from "./resource-gauge.module.css";
interface KubernetesResourceGaugeProps {
kubernetesCapacity: KubernetesCapacity;
}
export function ResourceGauge(props: KubernetesResourceGaugeProps) {
const t = useI18n();
const currentLocale = useCurrentLocale();
const isRTL = Boolean(isLocaleRTL(currentLocale));
return (
<Paper radius="md" withBorder className={classes.paper} mt={20}>
<ThemeIcon className={classes.icon} size={60} radius={60} bg={"#326ce5"}>
<ResourceIcon type={props.kubernetesCapacity.type} />
</ThemeIcon>
<Text ta="center" fw={700} className={classes.title}>
{props.kubernetesCapacity.type}
</Text>
{props.kubernetesCapacity.resourcesStats.map((stat) => {
const isReserved = stat.type === "Reserved";
const labelKey = isReserved
? "kubernetes.cluster.capacity.resource.reserved"
: "kubernetes.cluster.capacity.resource.used";
return (
<div key={stat.percentageValue}>
<Group justify={"space-between"} mt="xs">
<Text fz="sm" c="dimmed">
{isRTL ? (
<>
{stat.capacityUnit && (
<Text component="span" mr={4}>
{stat.capacityUnit}
</Text>
)}
<Text component="span">
{stat.maxUsedValue} / {stat.usedValue}{" "}
</Text>
<Text component="span" fw={500}>
{t(labelKey)}
</Text>
</>
) : (
<>
<Text component="span" fw={500}>
{t(labelKey)}
</Text>
<Text component="span">
{" "}
{stat.usedValue} / {stat.maxUsedValue}{" "}
</Text>
{stat.capacityUnit && (
<Text component="span" ml={4}>
{stat.capacityUnit}
</Text>
)}
</>
)}
</Text>
<Text fz="sm" c="dimmed">
{isRTL ? `%${stat.percentageValue}` : `${stat.percentageValue}%`}
</Text>
</Group>
<Progress value={stat.percentageValue} mt={5} color={getProgressBarColor(stat.percentageValue)} />
</div>
);
})}
</Paper>
);
}
function getProgressBarColor(value: number): string {
if (value > 50 && value < 65) {
return "yellow";
} else if (value >= 65) {
return "red";
}
return "blue";
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import { IconCpu, IconCube, IconDeviceDesktopAnalytics } from "@tabler/icons-react";
import type { KubernetesCapacityType } from "@homarr/definitions";
const resourceIcons = {
CPU: IconCpu,
Memory: IconDeviceDesktopAnalytics,
Pods: IconCube,
} satisfies Record<KubernetesCapacityType, React.ComponentType<{ size: number; stroke: number }>>;
interface ResourceIconProps {
type: KubernetesCapacityType;
size?: number;
stroke?: number;
}
export const ResourceIcon: React.FC<ResourceIconProps> = ({ type, size = 32, stroke = 1.5 }) => {
const Icon = resourceIcons[type];
return <Icon size={size} stroke={stroke} />;
};

View File

@@ -0,0 +1,10 @@
.cardContainer {
transition:
box-shadow 150ms ease,
transform 100ms ease;
@mixin hover {
box-shadow: var(--mantine-shadow-md);
transform: scale(1.02);
}
}

View File

@@ -0,0 +1,37 @@
import React from "react";
import Image from "next/image";
import Link from "next/link";
import { Card, Group, Text } from "@mantine/core";
import { IconArrowRight } from "@tabler/icons-react";
import type { KubernetesLabelResourceType } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import classes from "./resource-tile.module.css";
interface ResourceTileProps {
count: number;
label: KubernetesLabelResourceType;
}
export function ResourceTile(props: ResourceTileProps) {
const t = useI18n();
return (
<Card
withBorder
component={Link}
href={`/manage/tools/kubernetes/${props.label}`}
className={classes.cardContainer}
>
<Group justify="space-between" wrap="nowrap">
<Image src={`/images/kubernetes/${props.label}.svg`} alt={props.label} width={64} height={64} />
<Group gap="xs">
<Text size="xl" fw={700} tt="capitalize">
{props.count} {t(`kubernetes.cluster.resources.${props.label}`)}
</Text>
<IconArrowRight />
</Group>
</Group>
</Card>
);
}

View File

@@ -0,0 +1,74 @@
"use client";
import React from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesBaseResource } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface ConfigMapsTableComponentProps {
initialConfigMaps: RouterOutputs["kubernetes"]["configMaps"]["getConfigMaps"];
}
const createColumns = (
t: ScopedTranslationFunction<"kubernetes.configmaps">,
): MRT_ColumnDef<KubernetesBaseResource>[] => [
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
enableClickToCopy: true,
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function ConfigmapsTable(initialData: ConfigMapsTableComponentProps) {
const tConfigMaps = useScopedI18n("kubernetes.configmaps");
const { data } = clientApi.kubernetes.configMaps.getConfigMaps.useQuery(undefined, {
initialData: initialData.initialConfigMaps,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tConfigMaps("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tConfigMaps),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,30 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { ConfigmapsTable } from "~/app/[locale]/manage/tools/kubernetes/configmaps/configmaps-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function ConfigMapsPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const configMaps = await api.kubernetes.configMaps.getConfigMaps();
const tConfigMaps = await getScopedI18n("kubernetes.configmaps");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tConfigMaps("label")}</Title>
<ConfigmapsTable initialConfigMaps={configMaps} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,107 @@
"use client";
import React from "react";
import { Anchor, Flex } from "@mantine/core";
import { IconArrowRight } from "@tabler/icons-react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/db/client";
import type { KubernetesIngress } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface IngressesTableComponentProps {
initialIngresses: RouterOutputs["kubernetes"]["ingresses"]["getIngresses"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.ingresses">): MRT_ColumnDef<KubernetesIngress>[] => [
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
enableClickToCopy: true,
},
{
accessorKey: "className",
header: t("field.className.label"),
enableClickToCopy: true,
},
{
accessorKey: "rulesAndPaths",
header: t("field.rulesAndPaths.label"),
Cell({ cell }) {
const getAbsoluteUrl = (host: string) =>
host.startsWith("http://") || host.startsWith("https://") ? host : `https://${host}`;
return (
<>
{cell.row.original.rulesAndPaths.map((ruleAndPaths) => (
<div key={ruleAndPaths.host}>
<Flex align="flex-end">
<Anchor href={getAbsoluteUrl(ruleAndPaths.host)} target="_blank">
{getAbsoluteUrl(ruleAndPaths.host)}
</Anchor>
<IconArrowRight size={22} stroke={2} />
</Flex>
{ruleAndPaths.paths.map((path) => (
<div key={createId()}>
{path.serviceName}:{path.port}
</div>
))}
</div>
))}
</>
);
},
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function IngressesTable(initialData: IngressesTableComponentProps) {
const tIngresses = useScopedI18n("kubernetes.ingresses");
const { data } = clientApi.kubernetes.ingresses.getIngresses.useQuery(undefined, {
initialData: initialData.initialIngresses,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tIngresses("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tIngresses),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { IngressesTable } from "~/app/[locale]/manage/tools/kubernetes/ingresses/ingresses-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function NamespacesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const ingresses = await api.kubernetes.ingresses.getIngresses();
const tIngresses = await getScopedI18n("kubernetes.ingresses");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tIngresses("label")}</Title>
<IngressesTable initialIngresses={ingresses} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,91 @@
"use client";
import React from "react";
import { Badge, rem } from "@mantine/core";
import { IconCircleDashedCheck, IconHeartBroken } from "@tabler/icons-react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesNamespace } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface NamespacesTableComponentProps {
initialNamespaces: RouterOutputs["kubernetes"]["namespaces"]["getNamespaces"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.namespaces">): MRT_ColumnDef<KubernetesNamespace>[] => [
{
accessorKey: "status",
header: t("field.state.label"),
Cell({ cell }) {
const checkIcon = <IconCircleDashedCheck style={{ width: rem(12), height: rem(12) }} />;
const downIcon = <IconHeartBroken style={{ width: rem(12), height: rem(12) }} />;
const badgeKubernetesNamespaceStatusColor = cell.row.original.status === "Active" ? "green" : "yellow";
const badgeKubernetesNamespaceStatusIcon = cell.row.original.status === "Active" ? checkIcon : downIcon;
return (
<Badge
leftSection={badgeKubernetesNamespaceStatusIcon}
color={badgeKubernetesNamespaceStatusColor}
variant="light"
>
{cell.row.original.status}
</Badge>
);
},
},
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function NamespacesTable(initialData: NamespacesTableComponentProps) {
const tNamespaces = useScopedI18n("kubernetes.namespaces");
const { data } = clientApi.kubernetes.namespaces.getNamespaces.useQuery(undefined, {
initialData: initialData.initialNamespaces,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tNamespaces("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tNamespaces),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { NamespacesTable } from "~/app/[locale]/manage/tools/kubernetes/namespaces/namespaces-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function NamespacesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const namespaces = await api.kubernetes.namespaces.getNamespaces();
const tNamespaces = await getScopedI18n("kubernetes.namespaces");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tNamespaces("label")}</Title>
<NamespacesTable initialNamespaces={namespaces} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,132 @@
"use client";
import React from "react";
import { Badge, rem, RingProgress, Text } from "@mantine/core";
import { IconCircleDashedCheck, IconHeartBroken } from "@tabler/icons-react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesNode } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface NodesListComponentProps {
initialNodes: RouterOutputs["kubernetes"]["nodes"]["getNodes"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.nodes">): MRT_ColumnDef<KubernetesNode>[] => [
{
accessorKey: "status",
header: t("field.state.label"),
Cell({ cell }) {
const checkIcon = <IconCircleDashedCheck style={{ width: rem(12), height: rem(12) }} />;
const downIcon = <IconHeartBroken style={{ width: rem(12), height: rem(12) }} />;
const badgeKubernetesNodeStatusColor = cell.row.original.status === "Ready" ? "green" : "red";
const badgeKubernetesNodeStatusIcon = cell.row.original.status === "Ready" ? checkIcon : downIcon;
return (
<Badge leftSection={badgeKubernetesNodeStatusIcon} color={badgeKubernetesNodeStatusColor} variant="light">
{cell.row.original.status}
</Badge>
);
},
},
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "allocatableCpuPercentage",
header: t("field.cpu.label"),
Cell({ cell }) {
return getRingProgress(cell.row.original.allocatableCpuPercentage);
},
},
{
accessorKey: "allocatableRamPercentage",
header: t("field.memory.label"),
Cell({ cell }) {
return getRingProgress(cell.row.original.allocatableRamPercentage);
},
},
{
accessorKey: "operatingSystem",
header: t("field.operatingSystem.label"),
},
{
accessorKey: "podsCount",
header: t("field.pods.label"),
},
{
accessorKey: "architecture",
header: t("field.architecture.label"),
},
{
accessorKey: "kubernetesVersion",
header: t("field.kubernetesVersion.label"),
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function NodesTable(initialData: NodesListComponentProps) {
const tNodes = useScopedI18n("kubernetes.nodes");
const { data } = clientApi.kubernetes.nodes.getNodes.useQuery(undefined, {
initialData: initialData.initialNodes,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tNodes("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tNodes),
});
return <MantineReactTable table={table} />;
}
function getRingProgress(value: number) {
return (
<RingProgress
size={70}
roundCaps
thickness={7}
sections={[{ value, color: "blue" }]}
label={
<Text c="blue" fw={400} ta="center" size="md">
{value}%
</Text>
}
/>
);
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { NodesTable } from "~/app/[locale]/manage/tools/kubernetes/nodes/nodes-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function NodesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const nodes = await api.kubernetes.nodes.getNodes();
const tNodes = await getScopedI18n("kubernetes.nodes");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tNodes("label")}</Title>
<NodesTable initialNodes={nodes} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,21 @@
import { notFound } from "next/navigation";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { ClusterDashboard } from "~/app/[locale]/manage/tools/kubernetes/cluster-dashboard/cluster-dashboard";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function KubernetesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
return (
<>
<DynamicBreadcrumb />
<ClusterDashboard />
</>
);
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { PodsTable } from "~/app/[locale]/manage/tools/kubernetes/pods/pods-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function PodsPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const pods = await api.kubernetes.pods.getPods();
const tPods = await getScopedI18n("kubernetes.pods");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tPods("label")}</Title>
<PodsTable initialPods={pods} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,82 @@
"use client";
import React from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesPod } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface PodsTableComponentProps {
initialPods: RouterOutputs["kubernetes"]["pods"]["getPods"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.pods">): MRT_ColumnDef<KubernetesPod>[] => [
{
accessorKey: "name",
header: t("field.name.label"),
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
},
{
accessorKey: "image",
header: t("field.image.label"),
},
{
accessorKey: "applicationType",
header: t("field.applicationType.label"),
},
{
accessorKey: "status",
header: t("field.status.label"),
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function PodsTable(initialData: PodsTableComponentProps) {
const tPods = useScopedI18n("kubernetes.pods");
const { data } = clientApi.kubernetes.pods.getPods.useQuery(undefined, {
initialData: initialData.initialPods,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true, expanded: true },
mantineSearchTextInputProps: {
placeholder: tPods("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
enableGrouping: true,
columns: createColumns(tPods),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { SecretsTable } from "~/app/[locale]/manage/tools/kubernetes/secrets/secrets-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function SecretsPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const secrets = await api.kubernetes.secrets.getSecrets();
const tSecrets = await getScopedI18n("kubernetes.secrets");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tSecrets("label")}</Title>
<SecretsTable initialSecrets={secrets} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,77 @@
"use client";
import React from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesSecret } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface SecretsTableComponentProps {
initialSecrets: RouterOutputs["kubernetes"]["secrets"]["getSecrets"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.secrets">): MRT_ColumnDef<KubernetesSecret>[] => [
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
enableClickToCopy: true,
},
{
accessorKey: "type",
header: t("field.type.label"),
enableClickToCopy: true,
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function SecretsTable(initialData: SecretsTableComponentProps) {
const tSecrets = useScopedI18n("kubernetes.secrets");
const { data } = clientApi.kubernetes.secrets.getSecrets.useQuery(undefined, {
initialData: initialData.initialSecrets,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tSecrets("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tSecrets),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { ServicesTable } from "~/app/[locale]/manage/tools/kubernetes/services/services-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function ServicesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const services = await api.kubernetes.services.getServices();
const tServices = await getScopedI18n("kubernetes.services");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tServices("label")}</Title>
<ServicesTable initialServices={services} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,95 @@
"use client";
import React from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/db/client";
import type { KubernetesService } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface ServicesTableComponentProps {
initialServices: RouterOutputs["kubernetes"]["services"]["getServices"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.services">): MRT_ColumnDef<KubernetesService>[] => [
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
enableClickToCopy: true,
},
{
accessorKey: "type",
header: t("field.type.label"),
},
{
accessorKey: "ports",
header: t("field.ports.label"),
Cell({ cell }) {
return cell.row.original.ports?.map((port) => <div key={createId()}>{port}</div>);
},
},
{
accessorKey: "targetPorts",
header: t("field.targetPorts.label"),
Cell({ cell }) {
return cell.row.original.targetPorts?.map((targetPort) => <div key={createId()}>{targetPort}</div>);
},
},
{
accessorKey: "clusterIP",
header: t("field.clusterIP.label"),
enableClickToCopy: true,
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function ServicesTable(initialData: ServicesTableComponentProps) {
const tServices = useScopedI18n("kubernetes.services");
const { data } = clientApi.kubernetes.services.getServices.useQuery(undefined, {
initialData: initialData.initialServices,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tServices("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tServices),
});
return <MantineReactTable table={table} />;
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/docker/env";
import { getScopedI18n } from "@homarr/translation/server";
import { VolumesTable } from "~/app/[locale]/manage/tools/kubernetes/volumes/volumes-table";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
export default async function VolumesPage() {
const session = await auth();
if (!(session?.user.permissions.includes("admin") && env.ENABLE_KUBERNETES)) {
notFound();
}
const volumes = await api.kubernetes.volumes.getVolumes();
const tVolumes = await getScopedI18n("kubernetes.volumes");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tVolumes("label")}</Title>
<VolumesTable initialVolumes={volumes} />
</Stack>
</>
);
}

View File

@@ -0,0 +1,101 @@
"use client";
import React from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { KubernetesVolume } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
dayjs.extend(relativeTime);
interface VolumesTableComponentProps {
initialVolumes: RouterOutputs["kubernetes"]["volumes"]["getVolumes"];
}
const createColumns = (t: ScopedTranslationFunction<"kubernetes.volumes">): MRT_ColumnDef<KubernetesVolume>[] => [
{
accessorKey: "status",
header: t("field.status.label"),
},
{
accessorKey: "name",
header: t("field.name.label"),
enableClickToCopy: true,
},
{
accessorKey: "namespace",
header: t("field.namespace.label"),
enableClickToCopy: true,
},
{
accessorKey: "storage",
header: t("field.storage.label"),
},
{
accessorKey: "storageClassName",
header: t("field.storageClassName.label"),
enableClickToCopy: true,
},
{
accessorKey: "volumeMode",
header: t("field.volumeMode.label"),
},
{
accessorKey: "volumeName",
header: t("field.volumeName.label"),
enableClickToCopy: true,
},
{
accessorKey: "accessModes",
header: t("field.accessModes.label"),
Cell({ cell }) {
return cell.row.original.accessModes.map((accessMode) => <div key={accessMode}>{accessMode}</div>);
},
},
{
accessorKey: "creationTimestamp",
header: t("field.creationTimestamp.label"),
Cell: ({ row }) => dayjs(row.original.creationTimestamp).fromNow(false),
},
];
export function VolumesTable(initialData: VolumesTableComponentProps) {
const tVolumes = useScopedI18n("kubernetes.volumes");
const { data } = clientApi.kubernetes.volumes.getVolumes.useQuery(undefined, {
initialData: initialData.initialVolumes,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const table = useTranslatedMantineReactTable({
data,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: true,
positionToolbarAlertBanner: "top",
enableTableFooter: false,
enableBottomToolbar: false,
positionGlobalFilter: "right",
initialState: { density: "xs", showGlobalFilter: true },
mantineSearchTextInputProps: {
placeholder: tVolumes("table.search", { count: data.length }),
style: { minWidth: 300 },
autoFocus: true,
},
columns: createColumns(tVolumes),
});
return <MantineReactTable table={table} />;
}