Ver Fonte

Merge pull request #60 from lllllllillllllillll/dev

v0.40
lllllllillllllillll há 1 ano atrás
pai
commit
c7d79b296c
69 ficheiros alterados com 14893 adições e 3730 exclusões
  1. 5 0
      .dockerignore
  2. 3 8
      .gitignore
  3. 24 0
      CHANGELOG.md
  4. 3 15
      Dockerfile
  5. 12 6
      README.md
  6. 64 35
      components/containerCard.js
  7. 893 0
      components/modal.js
  8. 371 0
      components/permissions_modal.js
  9. 1 1
      controllers/apps.js
  10. 283 8
      controllers/dashboard.js
  11. 34 10
      controllers/images.js
  12. 2 0
      controllers/login.js
  13. 33 12
      controllers/networks.js
  14. 1 1
      controllers/register.js
  15. 32 0
      controllers/supporters.js
  16. 9 0
      controllers/variables.js
  17. 42 8
      controllers/volumes.js
  18. 55 28
      database/models.js
  19. 6 3
      docker-compose.yaml
  20. 28 29
      functions/install.js
  21. 5 2
      functions/uninstall.js
  22. 46 798
      package-lock.json
  23. 7 17
      package.json
  24. 4 0
      public/css/meters.css
  25. 0 0
      public/images/avatars/burns.jpg
  26. 0 0
      public/images/avatars/duffman.png
  27. 0 0
      public/images/avatars/frank.jpg
  28. 0 0
      public/images/avatars/moe.jpg
  29. 0 0
      public/images/avatars/moleman.png
  30. 0 0
      public/images/avatars/poochie.jpg
  31. 0 0
      public/images/avatars/rus.jpg
  32. 0 0
      public/images/avatars/skinner.jpg
  33. 0 0
      public/images/dweebui.svg
  34. BIN
      public/images/logo.png
  35. 355 0
      public/js/htmx-sse.js
  36. 0 0
      public/js/htmx.min.js
  37. 0 141
      public/js/main.js
  38. 0 0
      public/static/logo-sm-black.svg
  39. 0 0
      public/static/logo-sm-white.svg
  40. 41 21
      router/index.js
  41. BIN
      screenshots/dashboard1.png
  42. BIN
      screenshots/login.png
  43. 51 294
      server.js
  44. 5357 0
      templates/foss.json
  45. 5930 0
      templates/templates-bak.json
  46. 8 399
      templates/templates.json
  47. 0 144
      views/account.ejs
  48. 131 0
      views/account.html
  49. 7 7
      views/apps.html
  50. 0 1011
      views/dashboard.ejs
  51. 282 0
      views/dashboard.html
  52. 2 2
      views/footer.html
  53. 0 188
      views/images.ejs
  54. 116 0
      views/images.html
  55. 18 7
      views/login.html
  56. 16 5
      views/navbar.html
  57. 0 188
      views/networks.ejs
  58. 131 0
      views/networks.html
  59. 2 2
      views/portal.html
  60. 10 12
      views/register.html
  61. 0 136
      views/settings.ejs
  62. 120 0
      views/settings.html
  63. 14 0
      views/sidebar.html
  64. 85 0
      views/supporters.html
  65. 2 2
      views/syslogs.html
  66. 2 2
      views/users.html
  67. 120 0
      views/variables.html
  68. 0 188
      views/volumes.ejs
  69. 130 0
      views/volumes.html

+ 5 - 0
.dockerignore

@@ -0,0 +1,5 @@
+**/db.sqlite
+**/node_modules
+**/appdata
+.gitignore
+**/screenshots

+ 3 - 8
.gitignore

@@ -1,8 +1,3 @@
-**/node_modules/
-**/database.sqlite
-**/appdata/
-.github
-test
-.dockerignore
-.gitignore
-docker-compose.yaml
+**/db.sqlite
+**/node_modules
+**/appdata

+ 24 - 0
CHANGELOG.md

@@ -1,3 +1,27 @@
+## v0.40 (Feb 26th 2024) - HTMX rewrite
+* Pages rewritten to use HTMX.
+* Removed Socket.io.
+* Changed view files to *.HTML instead of *.EJS.
+* Removed "USER root" from Dockerfile.
+* Express sessions configured to use memorystore.
+* Improved chart rendering.
+* Improvements to container charts.
+* Created Variables page.
+* Created Supporters page.
+* Ability to remove images, volumes, or networks.
+* Fixed list.js sorting.
+* Fixed apps.js page navigation.
+* Removed stackfiles from templates.json and updated some icons.
+* New logo.
+* Improved handling of Docker events.
+* Improved dashboard responsiveness.
+* Updated server metrics styles.
+* Container cards display pending action.
+* Container charts only rendered if container running.
+* Created permissions modal.
+* Podman support (untested).
+* Started a new template for FOSS apps.
+
 ## v0.20 (Jan 20th 2024) - The rewrite. Jumping all the way to v0.20.
 * Changed to ES6 imports.
 * Cleaned up file structure and code layout.

+ 3 - 15
Dockerfile

@@ -1,19 +1,7 @@
 FROM node:21-alpine
-
 ENV NODE_ENV=production
-
 WORKDIR /app
-
-RUN --mount=type=bind,source=package.json,target=package.json \
-    --mount=type=bind,source=package-lock.json,target=package-lock.json \
-    --mount=type=cache,target=/root/.npm \
-    npm ci --omit=dev
-
-
-USER root
-
-COPY . .
-
+COPY . /app
+RUN npm install
 EXPOSE 8000
-
-CMD ["node", "server.js"]
+CMD node server.js

+ 12 - 6
README.md

@@ -1,7 +1,7 @@
 # DweebUI
 DweebUI is a web interface for managing Docker, with a zero-config dashboard for controlling and monitoring your containers.
 
-Alpha v0.20 ( :fire: Experimental :fire: )
+Alpha v0.40 ( :fire: Experimental :fire: )
 
    
 [:warning: DweebUI is a management interface and should not be directly exposed to the internet :warning:](https://github.com/lllllllillllllillll/DweebUI/wiki/Exposing-DweebUI-to-the-Internet)
@@ -10,7 +10,7 @@ Alpha v0.20 ( :fire: Experimental :fire: )
 [![GitHub Activity](https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
 [![Docker Pulls](https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui)](https://hub.docker.com/repository/docker/lllllllillllllillll/dweebui)
 [![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
-[![GitHub License](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/lllllllillllllillll)
+[![Coffee](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/lllllllillllllillll)
 
 * This is a personal project I started to get more familiar with Javascript and Node.js.
 * Some UI elements are placeholders and every version may have breaking changes.
@@ -31,8 +31,9 @@ Alpha v0.20 ( :fire: Experimental :fire: )
 * [x] Dashboard provides server metrics, container metrics, and container controls, on a single page.
 * [x] View container logs.
 * [ ] Update containers (planned).
-* [ ] Manage your Docker networks, images, and volumes (in development).
+* [x] Manage your Docker networks, images, and volumes.
 * [x] Light/Dark Mode.
+* [x] Mobile Friendly.
 * [x] Easy to install app templates.
 * [x] Multi-User built-in.
 * [ ] Permissions system (in development).
@@ -41,6 +42,7 @@ Alpha v0.20 ( :fire: Experimental :fire: )
 * [x] Templates.json maintains compatability with Portainer, allowing you to use the template without needing to use DweebUI.
 * [x] Automatically persists data in docker volumes if bind mount isn't used.
 * [ ] Preset variables (planned).
+* [ ] Themes (planned).
 
 
 ## Setup
@@ -51,9 +53,8 @@ version: "3.9"
 services:
   dweebui:
     container_name: dweebui
-    image: lllllllillllllillll/dweebui:v0.20
+    image: lllllllillllllillll/dweebui:v0.40
     environment:
-      NODE_ENV: production
       PORT: 8000
       SECRET: MrWiskers
     restart: unless-stopped
@@ -61,7 +62,11 @@ services:
       - 8000:8000
     volumes:
       - dweebui:/app
+      # Docker socket
       - /var/run/docker.sock:/var/run/docker.sock
+      # Podman socket
+      #- /run/podman/podman.sock:/var/run/docker.sock
+
     networks:
       - dweebui_net
 
@@ -91,4 +96,5 @@ Compose setup:
 
 ## Supporters
 
-* MM (Patreon)
+* MM (Patreon)
+* PD (Buymeacoffee)

+ 64 - 35
components/containerCard.js

@@ -1,32 +1,29 @@
-// export for app.js
 export const containerCard = (data) => {
   
   let { name, service, state, external_port, internal_port, ports, link } = data;
   let wrapped = name;
-  let chart = name;
+  let disable = "";
+  let chartName = name.replace(/-/g, '');
 
-  if (name.length > 13) {
-    wrapped = name.slice(0, 10) + '...';
-  }
-
-  //disable controls for a docker container depending on its name
-  let actions = "";
-  if (name.startsWith('dweebui')) {
-    actions = 'disabled=""';
-  }
+  // shorten long names
+  if (name.length > 13) { wrapped = name.slice(0, 10) + '...'; }
+  // disable buttons for dweebui
+  if (name.startsWith('dweebui')) { disable = 'disabled=""'; }
 
   if ( external_port == undefined ) { external_port = 0; }
   if ( internal_port == undefined ) { internal_port = 0; }
 
-
   let state_indicator = 'green';
   if (state == 'exited') {
-    state = 'stopped';
-    state_indicator = 'red';
+      state = 'stopped';
+      state_indicator = 'red';
   } else if (state == 'paused') {
-    state_indicator = 'orange';
+      state_indicator = 'orange';
   }
 
+  let noChart = 'hx-swap="none"';
+  if (state == 'running') { noChart = ''; }
+
   let ports_data = [];
   if (ports) {
     ports_data = ports;
@@ -49,7 +46,7 @@ export const containerCard = (data) => {
 
 
   return `
-    <div class="col-sm-6 col-lg-3 deleteme">
+    <div class="col-sm-6 col-lg-3 pt-1">
       <div class="card">
         <div class="card-body">
           <div class="card-stamp card-stamp-sm">
@@ -60,16 +57,16 @@ export const containerCard = (data) => {
             <div class="ms-auto lh-1">
               <div class="card-actions btn-actions">
                 <div class="card-actions btn-actions">
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="start" class="btn-action" title="Start" ${actions}><!-- player-play -->
+                  <button class="btn-action" title="Start" data-hx-post="/start" data-hx-trigger="click" data-hx-target="#${name}state" name="${name}" id="${state}" ${disable}><!-- player-play -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-play" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 4v16l13 -8z"></path></svg>
                   </button>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="stop" class="btn-action" title="Stop" ${actions}><!-- player-stop -->
+                  <button class="btn-action" title="Stop" data-hx-post="/stop" data-hx-trigger="click" data-hx-target="#${name}state" name="${name}" id="${state}" ${disable}><!-- player-stop -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-stop" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 5m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z"></path></svg>
                   </button>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="pause" class="btn-action" title="Pause" ${actions}><!-- player-pause -->
+                  <button class="btn-action" title="Pause" data-hx-post="/pause" data-hx-trigger="click" data-hx-target="#${name}state" name="${name}" id="${state}" ${disable}><!-- player-pause -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-pause" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path></svg>
                   </button>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="restart" class="btn-action" title="Restart" ${actions}><!-- reload -->
+                  <button class="btn-action" title="Restart" data-hx-post="/restart" data-hx-trigger="click" data-hx-target="#${name}state" name="${name}" id="${state}" ${disable}><!-- reload -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-reload" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M19.933 13.041a8 8 0 1 1 -9.925 -8.788c3.899 -1 7.935 1.007 9.425 4.747"></path><path d="M20 4v5h-5"></path></svg>                          
                   </button>
                   <div class="dropdown">
@@ -77,11 +74,11 @@ export const containerCard = (data) => {
                       <svg xmlns="http://www.w3.org/2000/svg" class="" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><circle cx="12" cy="12" r="1"></circle><circle cx="12" cy="19" r="1"></circle><circle cx="12" cy="5" r="1"></circle></svg>
                     </a>
                     <div class="dropdown-menu dropdown-menu-end">
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" id="details" data-bs-toggle="modal" data-bs-target="#details_modal" href="#">Details</button>
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="logs" data-bs-toggle="modal" data-bs-target="#log_view" href="#">Logs</button>
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="edit" href="#">Edit</button>
-                      <button class="dropdown-item text-primary" onclick="clicked(this)" name="${name}" id="update" href="#">Update</button>
-                      <button class="dropdown-item text-danger" onclick="clicked(this)" name="${name}" id="remove" data-bs-toggle="modal" data-bs-target="#${name}_uninstall_modal" href="#">Remove</button>
+                      <button class="dropdown-item text-secondary" name="${name}" data-hx-get="/modal" data-hx-target="#modals-here" data-hx-trigger="click" data-bs-toggle="modal" data-bs-target="#modals-here">Details</button>
+                      <button class="dropdown-item text-secondary" name="${name}" id="logs" data-hx-get="/logs" data-hx-target="#logView" data-bs-toggle="modal" data-bs-target="#log_view">Logs</button>
+                      <button class="dropdown-item text-secondary" name="${name}" id="edit">Edit</button>
+                      <button class="dropdown-item text-primary" name="${name}" id="update" disabled="">Update</button>
+                      <button class="dropdown-item text-danger" name="${name}" id="remove" data-bs-toggle="modal" data-bs-target="#${name}_uninstall_modal" href="#">Remove</button>
                     </div>
                   </div>
                   <div class="dropdown">
@@ -89,9 +86,9 @@ export const containerCard = (data) => {
                       <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-eye" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /> <path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" /> </svg>
                     </a>
                     <div class="dropdown-menu dropdown-menu-end">
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="hide" value="hide">Hide</button>
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="resetView" value="resetView">Reset View</button>
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="permissions" value="permissions" data-bs-toggle="modal" data-bs-target="#${name}_permissions">Permissions</button>
+                      <button class="dropdown-item text-secondary" data-hx-post="/hide" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="hide" value="hide">Hide</button>
+                      <button class="dropdown-item text-secondary" data-hx-post="/reset" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="reset" value="reset">Reset View</button>
+                      <button class="dropdown-item text-secondary" name="${name}" id="permissions" data-hx-get="/modal" data-hx-target="#modals-here" data-hx-trigger="click" data-bs-toggle="modal" data-bs-target="#modals-here">Permissions</button>
                     </div>
                   </div>
                 </div>
@@ -105,13 +102,27 @@ export const containerCard = (data) => {
               </a>
             </div>
             <div class="ms-auto">
-              <span class="text-${state_indicator} align-items-center lh-1">
-                <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
-                ${state}
-              </span>
+              <label id="${name}state">
+                <span class="text-${state_indicator} align-items-center lh-1">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
+                    ${state}
+                </span>
+              </label>
             </div>
           </div>
-          <div id="${chart}_chart" class="chart-sm"></div>
+          
+          <script>
+              var ${chartName}chart = new ApexCharts(document.querySelector("#${chartName}_chart"), options);
+          </script>
+
+          <div class="chart-sm">
+            <div id="${chartName}_chart" data-hx-trigger="load, every 2s" data-hx-get="/chart" name="${chartName}" ${noChart}>
+              <script>
+                ${chartName}chart.render();
+              </script>
+            </div>
+            
+          </div>
         </div>
       </div>
     </div>
@@ -206,18 +217,35 @@ export const containerCard = (data) => {
 
             <div class="mb-2">
               <div class="divide-y">
+                
+                <div class="row">
+                  <div class="col-9">
+                    <div class="d-flex align-items-center">
+                      User:
+                      <select class="form-select ms-2">
+                        <option value="john_doe" selected hidden>John Doe</option>
+                        <option value="john_doe">John Doe</option>
+                        <option value="jane_doe">Jane Doe</option>
+                      </select>
+                    </div>
+                  </div>
+                </div>
+
                 <div class="row">
                   <div class="col-9">
                     <label class="row text-start">
-                      <span class="col">Install</span>
+                      <span class="col">
+                        View
+                      </span>
                     </label>
                   </div>
                   <div class="col-3">
                     <label class="form-check form-check-single form-switch text-end">
-                      <input class="form-check-input" type="checkbox" name="remove_volumes">
+                      <input class="form-check-input" type="checkbox" name="remove_image">
                     </label>
                   </div>
                 </div>
+
                 <div class="row">
                   <div class="col-9">
                     <label class="row text-start">
@@ -232,6 +260,7 @@ export const containerCard = (data) => {
                     </label>
                   </div>
                 </div>
+
                 <div class="row">
                   <div class="col-9">
                     <label class="row text-start">

+ 893 - 0
components/modal.js

@@ -0,0 +1,893 @@
+export const modal = (data) => {
+  
+  let { name, state, image } = data;
+
+  let ports_data = [];
+
+  for (let i = 0; i < 12; i++) {
+
+    let port_check = "checked";
+    let external = i;
+    let internal = i;
+    let protocol = "tcp";
+
+    ports_data.push({
+      check: port_check,
+      external: external,
+      internal: internal,
+      protocol: protocol
+    });
+  }
+  
+
+  let volumes_data = [];
+  
+    for (let i = 0; i < 12; i++) {
+
+      let vol_check = "checked";
+      let bind = i;
+      let container = i;
+      let readwrite = "rw";
+
+      volumes_data.push({
+        check: vol_check,
+        bind: bind,
+        container: container,
+        readwrite: readwrite
+      });
+    }
+  
+
+
+  let env_data = [];
+  
+    for (let i = 0; i < 12; i++) {
+
+      let env_check = "checked";
+      let env_name = i;
+      let env_default = i;
+
+      env_data.push({
+        check: env_check,
+        name: env_name,
+        default: env_default
+      });
+    }
+  
+
+
+  let label_data = [];
+  
+    for (let i = 0; i < 12; i++) {
+
+      let label_check = "checked";
+      let label_name = i;
+      let label_default = i;
+      
+      label_data.push({
+        check: label_check,
+        name: label_name,
+        value: label_default
+      });
+    }
+  
+
+
+  let modal = `
+  <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h5 class="modal-title">Details</h5>
+      </div>
+      <div class="modal-body">
+        <pre class="text-secondary">note</pre>
+          <form action="" id="details_modal" method="POST">
+            <div class="row mb-3 align-items-end">
+              <div class="col-lg-6">
+                <label class="form-label">Container Name: </label>
+                <input type="text" class="form-control" name="service_name" value="${name}" hidden/>
+                <input type="text" class="form-control" name="name" value="${name}"/>
+              </div>
+              <div class="col-lg-3">
+                <label class="form-label">Image: </label>
+                <input type="text" class="form-control" name="image" value="${image}"/>
+              </div>
+              <div class="col-lg-3">
+                <label class="form-label">Restart Policy: </label>
+                <select class="form-select" name="restart_policy" value="">
+                  <option value="1">unless-stopped</option>
+                  <option value="2">on-failure</option>
+                  <option value="3">never</option>
+                  <option value="4">always</option>
+                </select>
+              </div>
+            </div>
+            <label class="form-label">Network Mode</label>
+            <div class="form-selectgroup-boxes row mb-3">
+              <div class="col">
+                  <label class="form-selectgroup-item">
+                    <input type="radio" name="report-type" value="1" class="form-selectgroup-input">
+                    <span class="form-selectgroup-label d-flex align-items-center p-3">
+                      <span class="me-3">
+                        <span class="form-selectgroup-check"></span>
+                      </span>
+                      <span class="form-selectgroup-label-content">
+                        <span class="form-selectgroup-title strong mb-1">Host Network</span>
+                        <span class="d-block text-secondary">Same as host. No isolation. ex.127.0.0.1</span>
+                      </span>
+                    </span>
+                  </label>
+                </div>
+                <div class="col">
+                  <label class="form-selectgroup-item">
+                    <input type="radio" name="report-type" class="form-selectgroup-input">
+                    <span class="form-selectgroup-label d-flex align-items-center p-3">
+                      <span class="me-3">
+                        <span class="form-selectgroup-check"></span>
+                      </span>
+                      <span class="form-selectgroup-label-content">
+                        <span class="form-selectgroup-title strong mb-1">Bridge Network</span>
+                        <span class="d-block text-secondary">Containers can communicate using names.</span>
+                      </span>
+                    </span>
+                  </label>
+                </div>
+                <div class="col">
+                <label class="form-selectgroup-item">
+                <input type="radio" name="report-type" class="form-selectgroup-input">
+                <span class="form-selectgroup-label d-flex align-items-center p-3">
+                  <span class="me-3">
+                    <span class="form-selectgroup-check"></span>
+                  </span>
+                  <span class="form-selectgroup-label-content">
+                    <span class="form-selectgroup-title strong mb-1">Docker Network</span>
+                    <span class="d-block text-secondary">Isolated on the docker network. ex.172.0.34.2</span>
+                  </span>
+                </span>
+                </label>
+              </div>
+            </div>
+            
+            <div class="accordion" id="modal-accordion">
+                        <div class="accordion-item">
+                          <h2 class="accordion-header" id="heading-1">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-1" aria-expanded="false">
+                              Ports
+                            </button>
+                          </h2>
+                          <div id="collapse-1" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                            <div class="accordion-body pt-0">
+            
+
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_0_check" type="checkbox" ${ports_data[0].check}>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">External Port</label>
+                                  <input type="text" class="form-control" name="port_0_external" value="${ports_data[0].external}"/>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">Internal Port</label>
+                                  <input type="text" class="form-control" name="port_0_internal" value="${ports_data[0].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <label class="form-label">Protocol</label>
+                                  <select class="form-select" name="port_0_protocol">
+                                    <option value="${ports_data[0].protocol}" selected hidden>${ports_data[0].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_1_check" type="checkbox" ${ports_data[1].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_1_external" value="${ports_data[1].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_1_internal" value="${ports_data[1].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_1_protocol">
+                                    <option value="${ports_data[1].protocol}" selected hidden>${ports_data[1].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_2_check" type="checkbox" ${ports_data[2].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_2_external" value="${ports_data[2].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_2_internal" value="${ports_data[2].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_2_protocol">
+                                    <option value="${ports_data[2].protocol}" selected hidden>${ports_data[2].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_3_check" type="checkbox" ${ports_data[3].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_3_external" value="${ports_data[3].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_3_internal" value="${ports_data[3].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_3_protocol">
+                                    <option value="${ports_data[3].protocol}" selected hidden>${ports_data[3].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_4_check" type="checkbox" ${ports_data[4].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_4_external" value="${ports_data[4].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_4_internal" value="${ports_data[4].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_4_protocol">
+                                    <option value="${ports_data[4].protocol}" selected hidden>${ports_data[4].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_5_check" type="checkbox" ${ports_data[5].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_5_external" value="${ports_data[5].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_5_internal" value="${ports_data[5].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_5_protocol">
+                                    <option value="${ports_data[5].protocol}" selected hidden>${ports_data[5].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+            
+                            </div>
+                          </div>
+                        </div>
+                        <div class="accordion-item">
+                          <h2 class="accordion-header" id="heading-2">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-2" aria-expanded="false">
+                              Volumes
+                            </button>
+                          </h2>
+                          <div id="collapse-2" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                            <div class="accordion-body pt-0">
+            
+            
+                            <div class="row mb-1 align-items-end">
+                            <div class="col-auto">
+                              <input class="form-check-input" name="volume_0_check" type="checkbox" ${volumes_data[0].check}>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_0_bind" value="${volumes_data[0].bind}"/>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_0_container" value="${volumes_data[0].container}"/>
+                            </div>
+                            <div class="col-lg-2">
+                              <select class="form-select" name="volume_0_readwrite">
+                                <option value="${volumes_data[0].readwrite}" selected hidden>${volumes_data[0].readwrite}</option>
+                                <option value="rw">rw</option>
+                                <option value="ro">ro</option>
+                              </select>
+                            </div>
+                          </div>
+            
+                          <div class="row mb-1 align-items-end">
+                            <div class="col-auto">
+                              <input class="form-check-input" name="volume_1_check" type="checkbox" ${volumes_data[1].check}>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_1_bind" value="${volumes_data[1].bind}"/>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_1_container" value="${volumes_data[1].container}"/>
+                            </div>
+                            <div class="col-lg-2">
+                              <select class="form-select" name="volume_1_readwrite">
+                                <option value="${volumes_data[1].readwrite}" selected hidden>${volumes_data[1].readwrite}</option>
+                                <option value="rw">rw</option>
+                                <option value="ro">ro</option>
+                              </select>
+                            </div>
+                          </div>
+            
+                          <div class="row mb-1 align-items-end">
+                            <div class="col-auto">
+                              <input class="form-check-input" name="volume_2_check" type="checkbox" ${volumes_data[2].check}>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_2_bind" value="${volumes_data[2].bind}"/>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_2_container" value="${volumes_data[2].container}"/>
+                            </div>
+                            <div class="col-lg-2">
+                              <select class="form-select" name="volume_2_readwrite">
+                                <option value="${volumes_data[2].readwrite}" selected hidden>${volumes_data[2].readwrite}</option>
+                                <option value="rw">rw</option>
+                                <option value="ro">ro</option>
+                              </select>
+                            </div>
+                          </div>
+            
+                          <div class="row mb-1 align-items-end">
+                            <div class="col-auto">
+                              <input class="form-check-input" name="volume_3_check" type="checkbox" ${volumes_data[3].check}>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_3_bind" value="${volumes_data[3].bind}"/>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_3_container" value="${volumes_data[3].container}"/>
+                            </div>
+                            <div class="col-lg-2">
+                              <select class="form-select" name="volume_3_readwrite">
+                                <option value="${volumes_data[3].readwrite}" selected hidden>${volumes_data[3].readwrite}</option>
+                                <option value="rw">rw</option>
+                                <option value="ro">ro</option>
+                              </select>
+                            </div>
+                          </div>
+            
+                          <div class="row mb-1 align-items-end">
+                            <div class="col-auto">
+                              <input class="form-check-input" name="volume_4_check" type="checkbox" ${volumes_data[4].check}>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_4_bind" value="${volumes_data[4].bind}"/>
+                            </div>
+                            <div class="col">
+                              <input type="text" class="form-control" name="volume_4_container" value="${volumes_data[4].container}"/>
+                            </div>
+                            <div class="col-lg-2">
+                              <select class="form-select" name="volume_4_readwrite">
+                                <option value="${volumes_data[4].readwrite}" selected hidden>${volumes_data[4].readwrite}</option>
+                                <option value="rw">rw</option>
+                                <option value="ro">ro</option>
+                              </select>
+                            </div>
+                          </div>
+            
+                          <div class="row mb-1 align-items-end">
+                          <div class="col-auto">
+                            <input class="form-check-input" name="volume_5_check" type="checkbox" ${volumes_data[5].check}>
+                          </div>
+                          <div class="col">
+                            <input type="text" class="form-control" name="volume_5_bind" value="${volumes_data[5].bind}"/>
+                          </div>
+                          <div class="col">
+                            <input type="text" class="form-control" name="volume_5_container" value="${volumes_data[5].container}"/>
+                          </div>
+                          <div class="col-lg-2">
+                            <select class="form-select" name="volume_5_readwrite">
+                              <option value="${volumes_data[5].readwrite}" selected hidden>${volumes_data[5].readwrite}</option>
+                              <option value="rw">rw</option>
+                              <option value="ro">ro</option>
+                            </select>
+                          </div>
+                        </div>
+            
+            
+                            </div>
+                          </div>
+                        </div>
+                        <div class="accordion-item">
+                          <h2 class="accordion-header" id="heading-3">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-3" aria-expanded="false">
+                              Environment Variables
+                            </button>
+                          </h2>
+                          <div id="collapse-3" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                            <div class="accordion-body pt-0">
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_0_check" ${env_data[0].check}>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">Variable</label>
+                                  <input type="text" class="form-control" name="env_0_name" value="${env_data[0].name}"/>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">Value</label>
+                                  <input type="text" class="form-control" name="env_0_default" value="${env_data[0].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_1_check" ${env_data[1].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_1_name" value="${env_data[1].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_1_default" value="${env_data[1].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_2_check" ${env_data[2].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_2_name" value="${env_data[2].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_2_default" value="${env_data[2].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_3_check" ${env_data[3].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_3_name" value="${env_data[3].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_3_default" value="${env_data[3].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_4_check" ${env_data[4].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_4_name" value="${env_data[4].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_4_default" value="${env_data[4].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_5_check" ${env_data[5].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_5_name" value="${env_data[5].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_5_default" value="${env_data[5].default}"/>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_6_check" ${env_data[6].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_6_name" value="${env_data[6].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_6_default" value="${env_data[6].default}"/>
+                                </div>
+                              </div>
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_7_check" ${env_data[7].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_7_name" value="${env_data[7].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_7_default" value="${env_data[7].default}"/>
+                                </div>
+                              </div>
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_8_check" ${env_data[8].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_8_name" value="${env_data[8].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_8_default" value="${env_data[8].default}"/>
+                                </div>
+                              </div>
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_9_check" ${env_data[9].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_9_name" value="${env_data[9].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_9_default" value="${env_data[9].default}"/>
+                                </div>
+                              </div>
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_10_check" ${env_data[10].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_10_name" value="${env_data[10].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_10_default" value="${env_data[10].default}"/>
+                                </div>
+                              </div>
+            
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" type="checkbox" name="env_11_check" ${env_data[11].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_11_name" value="${env_data[11].name}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="env_11_default" value="${env_data[11].default}"/>
+                                </div>
+                              </div>
+            
+            
+            
+            
+                            </div>
+                          </div>
+                        </div>
+                        <div class="accordion-item">
+                          <h2 class="accordion-header" id="heading-4">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-4" aria-expanded="false">
+                              Labels
+                            </button>
+                          </h2>
+                          <div id="collapse-4" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                            <div class="accordion-body pt-0">
+            
+            
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_0_check" ${label_data[0].check}>
+                              </div>
+                              <div class="col">
+                                <label class="form-label">Variable</label>
+                                <input type="text" class="form-control" name="label_0_name" value="${label_data[0].name}"/>
+                              </div>
+                              <div class="col">
+                                <label class="form-label">Value</label>
+                                <input type="text" class="form-control" name="label_0_value" value="${label_data[0].value}"/>
+                              </div>
+                            </div>
+            
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_1_check" ${label_data[1].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_1_name" value="${label_data[1].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_1_value" value="${label_data[1].value}"/>
+                              </div>
+                            </div>
+            
+                              
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_2_check" ${label_data[2].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_2_name" value="${label_data[2].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_2_value" value="${label_data[2].value}"/>
+                              </div>
+                            </div>
+            
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_3_check" ${label_data[3].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_3_name" value="${label_data[3].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_3_value" value="${label_data[3].value}"/>
+                              </div>
+                            </div>
+            
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_4_check" ${label_data[4].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_4_name" value="${label_data[4].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_4_value" value="${label_data[4].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_5_check" ${label_data[5].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_5_name" value="${label_data[5].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_5_value" value="${label_data[5].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_6_check" ${label_data[6].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_6_name" value="${label_data[6].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_6_value" value="${label_data[6].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_7_check" ${label_data[7].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_7_name" value="${label_data[7].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_7_value" value="${label_data[7].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_8_check" ${label_data[8].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_8_name" value="${label_data[8].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_8_value" value="${label_data[8].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_9_check" ${label_data[9].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_9_name" value="${label_data[9].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_9_value" value="${label_data[9].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_10_check" ${label_data[10].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_10_name" value="${label_data[10].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_10_value" value="${label_data[10].value}"/>
+                              </div>
+                            </div>
+
+                            <div class="row mb-1 align-items-end">
+                              <div class="col-auto">
+                                <input class="form-check-input" type="checkbox" name="label_11_check" ${label_data[11].check}>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_11_name" value="${label_data[11].name}"/>
+                              </div>
+                              <div class="col">
+                                <input type="text" class="form-control" name="label_11_value" value="${label_data[11].value}"/>
+                              </div>
+                            </div>
+            
+            
+                            </div>
+                          </div>
+                        </div>
+
+
+                        <div class="accordion-item">
+                          <h2 class="accordion-header" id="heading-5">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-5" aria-expanded="false">
+                              Extras
+                            </button>
+                          </h2>
+                          <div id="collapse-5" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                            <div class="accordion-body pt-0">
+            
+
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_0_check" type="checkbox" ${ports_data[0].check}>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">External Port</label>
+                                  <input type="text" class="form-control" name="port_0_external" value="${ports_data[0].external}"/>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">Internal Port</label>
+                                  <input type="text" class="form-control" name="port_0_internal" value="${ports_data[0].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <label class="form-label">Protocol</label>
+                                  <select class="form-select" name="port_0_protocol">
+                                    <option value="${ports_data[0].protocol}" selected hidden>${ports_data[0].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_1_check" type="checkbox" ${ports_data[1].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_1_external" value="${ports_data[1].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_1_internal" value="${ports_data[1].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_1_protocol">
+                                    <option value="${ports_data[1].protocol}" selected hidden>${ports_data[1].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_2_check" type="checkbox" ${ports_data[2].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_2_external" value="${ports_data[2].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_2_internal" value="${ports_data[2].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_2_protocol">
+                                    <option value="${ports_data[2].protocol}" selected hidden>${ports_data[2].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_3_check" type="checkbox" ${ports_data[3].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_3_external" value="${ports_data[3].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_3_internal" value="${ports_data[3].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_3_protocol">
+                                    <option value="${ports_data[3].protocol}" selected hidden>${ports_data[3].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="port_4_check" type="checkbox" ${ports_data[4].check}>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_4_external" value="${ports_data[4].external}"/>
+                                </div>
+                                <div class="col">
+                                  <input type="text" class="form-control" name="port_4_internal" value="${ports_data[4].internal}"/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <select class="form-select" name="port_4_protocol">
+                                    <option value="${ports_data[4].protocol}" selected hidden>${ports_data[4].protocol}</option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+                              <div class="row mb-1 align-items-end">
+                                <div class="col-auto">
+                                  <input class="form-check-input" name="" type="checkbox" >
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">External Port</label>
+                                  <input type="text" class="form-control" name="" value=""/>
+                                </div>
+                                <div class="col">
+                                  <label class="form-label">Internal Port</label>
+                                  <input type="text" class="form-control" name="" value=""/>
+                                </div>
+                                <div class="col-lg-2">
+                                  <label class="form-label">Protocol</label>
+                                  <select class="form-select" name="">
+                                    <option value="" selected hidden></option>
+                                    <option value="tcp">tcp</option>
+                                    <option value="udp">udp</option>
+                                  </select>
+                                </div>
+                              </div>
+            
+            
+                            </div>
+                          </div>
+                        </div>
+
+
+
+                      </div>
+            
+            
+                      
+                      </form>
+
+
+      </div>
+      <div class="modal-footer">
+          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
+      </div>
+    </div>
+  </div>`;
+
+  return modal;
+}

+ 371 - 0
components/permissions_modal.js

@@ -0,0 +1,371 @@
+export const permissionsModal = (data) => {
+  
+
+  let modal = `
+        <div class="modal-dialog modal-sm modal-dialog-centered modal-dialog-scrollables">
+          <div class="modal-content">
+            <div class="modal-header">
+              <h5 class="modal-title">Permissions</h5>
+            </div>
+            <div class="modal-body">
+              <form action="" id="permissions_modal" method="POST">
+            
+              <div class="accordion" id="modal-accordion">
+
+
+                <div class="accordion-item mb-3" style="border: 1px solid grey;">
+                  <h2 class="accordion-header" id="heading-1">
+                    <button class="accordion-button collapsed row" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-1" aria-expanded="false">
+                      <span class="avatar avatar-sm bg-green-lt col-3 text-start">JD</span>
+                        <div class="col text-end" style="margin-right: 10px;">Jane Doe</div>
+                    </button>
+                  </h2>
+                  <div id="collapse-1" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                    <div class="accordion-body pt-0">
+
+
+                      <div class="">
+                        <div class="">
+
+                          <div class="row mb-3">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  All
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select" onclick="selectAll('select')">
+                              </label>
+                            </div>
+                          </div>
+          
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Uninstall
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+          
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Edit
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Upgrade
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Start
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Stop
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Pause
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Restart
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+
+                          <div class="row mb-4">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Logs
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="select">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <button class="btn" type="submit" formaction="/updatePermissions">Update</button>
+                          </div>
+
+
+                        </div>
+                      </div>
+
+                    </div>
+                  </div>
+                </div>
+
+
+                <div class="accordion-item mb-3" style="border: 1px solid grey;">
+                  <h2 class="accordion-header" id="heading-2">
+                    <button class="accordion-button collapsed row" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-2" aria-expanded="false">
+                      <span class="avatar avatar-sm bg-cyan-lt col-3 text-start">JD</span>
+                        <div class="col text-end" style="margin-right: 10px;">John Doe</div>
+                    </button>
+                  </h2>
+                  <div id="collapse-2" class="accordion-collapse collapse" data-bs-parent="#modal-accordion">
+                    <div class="accordion-body pt-0">
+
+
+                      <div class="">
+                        <div class="">
+
+                          <div class="row mb-3">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  All
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_image">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  View
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_image">
+                              </label>
+                            </div>
+                          </div>
+          
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Uninstall
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_image">
+                              </label>
+                            </div>
+                          </div>
+          
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Edit
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Upgrade
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Start
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Stop
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Pause
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Restart
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+
+                          <div class="row mb-2">
+                            <div class="col-9">
+                              <label class="row text-start">
+                                <span class="col">
+                                  Logs
+                                </span>
+                              </label>
+                            </div>
+                            <div class="col-3">
+                              <label class="form-check form-check-single form-switch text-end">
+                                <input class="form-check-input" type="checkbox" name="remove_backups">
+                              </label>
+                            </div>
+                          </div>
+
+
+                        </div>
+                      </div>
+
+                    </div>
+                  </div>
+                </div>
+
+
+              </div>
+              </form>
+            </div>
+            <div class="modal-footer">
+              <div class="row">
+              
+                <div class="col">
+                  <button type="button" class="btn btn-danger" data-bs-dismiss="modal" disabled="">Reset</button>
+                </div>
+                <div class="col">
+                  <button type="button" class="btn btn-primary" data-bs-dismiss="modal">Update</button>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>`;
+
+    return modal;
+}

+ 1 - 1
controllers/apps.js

@@ -1,7 +1,7 @@
 import { readFileSync } from 'fs';
 import { appCard } from '../components/appCard.js';
 
-let templatesJSON = readFileSync('./templates.json');
+let templatesJSON = readFileSync('./templates/templates.json');
 let templates = JSON.parse(templatesJSON).templates;
 
 templates = templates.sort((a, b) => {

+ 283 - 8
controllers/dashboard.js

@@ -1,23 +1,298 @@
+import { Readable } from 'stream';
+import { Permission, Container } from '../database/models.js';
+import { modal } from '../components/modal.js';
+import { permissionsModal } from '../components/permissions_modal.js';
+import { setEvent, cpu, ram, tx, rx, disk, docker } from '../server.js';
+import { dockerContainerStats } from 'systeminformation';
+import { containerCard } from '../components/containerCard.js';
 
-export const Dashboard = (req, res) => {
-
+let [ hidden, cardList ] = [ '', '' ];
 
+export const Dashboard = (req, res) => {
     res.render("dashboard", {
         name: req.session.user,
         role: req.session.role,
         avatar: req.session.avatar,
     });
+}
 
+export const Logs = (req, res) => {
+    let name = req.header('hx-trigger-name');
+    function containerLogs (data) {
+        return new Promise((resolve, reject) => {
+            let logString = '';
+            var options = {
+                follow: false,
+                stdout: true,
+                stderr: false,
+                timestamps: false
+            };
+            var containerName = docker.getContainer(data);
+            containerName.logs(options, function (err, stream) {
+                if (err) { reject(err); return; }
+                const readableStream = Readable.from(stream);
+                readableStream.on('data', function (chunk) {
+                    logString += chunk.toString('utf8');
+                });
+                readableStream.on('end', function () {
+                    resolve(logString);
+                });
+            });
+        });
+    };
+    containerLogs(name).then((data) => {
+        res.send(`<pre>${data}</pre> `)
+    });
 }
 
-export const searchDashboard = (req, res) => {
+export const Modal = async (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let id = req.header('hx-trigger');
+    if (id == 'permissions') {
+        let containerPermissions = await Permission.findAll({ where: {containerName: name}});
+        let form = permissionsModal();
+        res.send(form);
+        return;
+    }
+    let containerId = docker.getContainer(name);
+    let containerInfo = await containerId.inspect();
+    let ports_list = [];
+    try {
+    for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
+        let ports = {
+            check: 'checked',
+            external: value[0].HostPort,
+            internal: key.split('/')[0],
+            protocol: key.split('/')[1]
+        }
+        ports_list.push(ports);
+    }
+    } catch {}
+    let external_port = ports_list[0]?.external || 0;
+    let internal_port = ports_list[0]?.internal || 0;
+
+    let container_info = {
+        name: containerInfo.Name.slice(1),
+        state: containerInfo.State.Status,
+        image: containerInfo.Config.Image,
+        external_port: external_port,
+        internal_port: internal_port,
+        ports: ports_list,
+        link: 'localhost',
+    }
+    let form = modal(container_info);
+    res.send(form);
+}
 
-    console.log(req.params);
+export const Stats = async (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let color = req.header('hx-trigger');
+    let value = 0;
+    switch (name) {
+        case 'CPU': value = cpu;
+            break;
+        case 'RAM': value = ram;
+            break;
+        case 'TX': value = tx;
+            break;
+        case 'RX': value = rx;
+            break;
+        case 'DISK': value = disk;
+            break;
+    }
+    let info = `<div class="font-weight-medium">
+                    <label class="cpu-text mb-1">${name} ${value}%</label>
+                </div>
+                <div class="cpu-bar meter animate ${color}">
+                    <span style="width:${value}%"><span></span></span>
+                </div>`;
+    res.send(info);
+}
+
+
+export const Hide = async (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let exists = await Container.findOne({ where: {name: name}});
+    if (!exists) {
+        const newContainer = await Container.create({ name: name, visibility: false, });
+    } else {
+        exists.update({ visibility: false });
+    }
+    setEvent(true, 'docker');
+    res.send("ok");
+}
+
+export const Reset = async (req, res) => {
+    Container.update({ visibility: true }, { where: {} });
+    setEvent(true, 'docker');
+    res.send("ok");
+}
+
+
+let stats = {};
+export const Chart = async (req, res) => {
+    let name = req.header('hx-trigger-name');
+    // create an empty array if it doesn't exist
+    if (!stats[name]) {
+        stats[name] = { cpuArray: Array(15).fill(0), ramArray: Array(15).fill(0) };
+    }
+    // get the stats
+    const info = await dockerContainerStats(name);
+    // update the arrays
+    stats[name].cpuArray.push(Math.round(info[0].cpuPercent));
+    stats[name].ramArray.push(Math.round(info[0].memPercent));
+    // slice them down to the last 15 values
+    stats[name].cpuArray = stats[name].cpuArray.slice(-15);
+    stats[name].ramArray = stats[name].ramArray.slice(-15);
+    // replace the chart with the new data
+    let chart = `
+        <script>
+            ${name}chart.updateSeries([{
+                data: [${stats[name].cpuArray}]
+            }, {
+                data: [${stats[name].ramArray}]
+            }])
+        </script>`
+    res.send(chart);
+}
+
+// Get hidden containers
+async function getHidden() {
+    hidden = await Container.findAll({ where: {visibility:false}});
+    hidden = hidden.map((container) => container.name);
+}
+
+// Create list of docker containers cards
+async function containerCards() {
+    let list = '';
+    const allContainers = await docker.listContainers({ all: true });
+    for (const container of allContainers) {
+        if (!hidden.includes(container.Names[0].slice(1))) {
+
+            let imageVersion = container.Image.split('/');
+            let service = imageVersion[imageVersion.length - 1].split(':')[0];
+            let containerId = docker.getContainer(container.Id);
+            let containerInfo = await containerId.inspect();
+            let ports_list = [];
+            try {
+            for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
+                let ports = {
+                    check: 'checked',
+                    external: value[0].HostPort,
+                    internal: key.split('/')[0],
+                    protocol: key.split('/')[1]
+                }
+                ports_list.push(ports);
+            }
+            } catch {}
+
+            let external_port = ports_list[0]?.external || 0;
+            let internal_port = ports_list[0]?.internal || 0;
+
+            let container_info = {
+                name: container.Names[0].slice(1),
+                service: service,
+                id: container.Id,
+                state: container.State,
+                image: container.Image,
+                external_port: external_port,
+                internal_port: internal_port,
+                ports: ports_list,
+                link: 'localhost',
+            }
+            let card = containerCard(container_info);
+            list += card;
+        }
+    }
+    cardList = list;
+}
+
+export const Containers = async (req, res) => {
+    await getHidden();
+    await containerCards();
+    res.send(cardList);
+}
+
+
+function status (state) {
+    let status = `<span class="text-yellow align-items-center lh-1">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
+                    ${state}
+                </span>`;
+    return status;
+}
+
+export const Start = (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let state = req.header('hx-trigger');
+    if (state == 'stopped') {
+        var containerName = docker.getContainer(name);
+        containerName.start();
+    } else if (state == 'paused') {
+        var containerName = docker.getContainer(name);
+        containerName.unpause();
+    }
+
+    res.send(status('starting'));
+}
+
+export const Stop = (req, res) => {   
+    let name = req.header('hx-trigger-name');
+    let state = req.header('hx-trigger');
+    if (state != 'stopped') {
+        var containerName = docker.getContainer(name);
+        containerName.stop();
+    }
+    res.send(status('stopping'));
+}
+
+export const Pause = (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let state = req.header('hx-trigger');
+    if (state == 'running') {
+        var containerName = docker.getContainer(name);
+        containerName.pause();
+    } else if (state == 'paused') {
+        var containerName = docker.getContainer(name);
+        containerName.unpause();
+    }
+    res.send(status('pausing'));
+}
+
+export const Restart = (req, res) => {   
+    let name = req.header('hx-trigger-name');
+    var containerName = docker.getContainer(name);
+    containerName.restart();
+    res.send(status('restarting'));
+}
+
+export const Installs = async (req, res) => {
+
+    let name = req.header('hx-trigger-name');
+    let all_containers = '';
+
+    await docker.listContainers({ all: true }).then(containers => {
+        containers.forEach(container => {
+            if (container.Names[0].slice(1) == name) {
+                return;
+            }
+        });
+
+        let install_info = {
+            name: name,
+            service: 'Service Name',
+            id: '',
+            state: '',
+            image: '',
+            external_port: 0,
+            internal_port: 0,
+            ports: '',
+            link: 'localhost',
+        }
+        let card = containerCard(install_info);
+        res.send(card);
 
-    res.render("dashboard", {
-        name: req.session.user,
-        role: req.session.role,
-        avatar: req.session.avatar,
     });
 
+    
 }

+ 34 - 10
controllers/images.js

@@ -8,13 +8,13 @@ export const Images = async function(req, res) {
     <thead>
         <tr>
             <th class="w-1"><input class="form-check-input m-0 align-middle" name="select" type="checkbox" aria-label="Select all" onclick="selectAll()"></th>
-            <th><button class="table-sort" data-sort="sort-name">Name</button></th>
-            <th><button class="table-sort" data-sort="sort-city">ID</button></th>
-            <th><button class="table-sort" data-sort="sort-type">Tag</button></th>
-            <th><button class="table-sort" data-sort="sort-score">Status</button></th>
-            <th><button class="table-sort" data-sort="sort-date">Created</button></th>
-            <th><button class="table-sort" data-sort="sort-quantity">Size</button></th>
-            <th><button class="table-sort" data-sort="sort-progress">Action</button></th>
+            <th><label class="table-sort" data-sort="sort-name">Name</label></th>
+            <th><label class="table-sort" data-sort="sort-city">ID</label></th>
+            <th><label class="table-sort" data-sort="sort-type">Tag</label></th>
+            <th><label class="table-sort" data-sort="sort-score">Status</label></th>
+            <th><label class="table-sort" data-sort="sort-date">Created</label></th>
+            <th><label class="table-sort" data-sort="sort-quantity">Size</label></th>
+            <th><label class="table-sort" data-sort="sort-progress">Action</label></th>
         </tr>
     </thead>
     <tbody class="table-tbody">`
@@ -30,11 +30,11 @@ export const Images = async function(req, res) {
 
         let details = `
             <tr>
-                <td><input class="form-check-input m-0 align-middle" name="select" value="" type="checkbox" aria-label="Select"></td>
+                <td><input class="form-check-input m-0 align-middle" name="select" value="${images[i].Id}" type="checkbox" aria-label="Select"></td>
                 <td class="sort-name">${images[i].RepoTags}</td>
                 <td class="sort-city">${images[i].Id}</td>
-                <td class="sort-type">Latest</td>
-                <td class="sort-score text-green">In use</td>
+                <td class="sort-type"> - </td>
+                <td class="sort-score text-green"> - </td>
                 <td class="sort-date" data-date="1628122643">${created}</td>
                 <td class="sort-quantity">${size} MB</td>
                 <td class="text-end"><a class="btn" href="#">Details</a></td>
@@ -53,4 +53,28 @@ export const Images = async function(req, res) {
         image_count: images.length
     });
 
+}
+
+
+
+export const removeImage = async function(req, res) {
+    let images = req.body.select;
+
+    if (typeof(images) == 'string') {
+        images = [images];
+    }
+
+    for (let i = 0; i < images.length; i++) {
+        
+        if (images[i] != 'on') {
+            try {
+                console.log(`Removing image: ${images[i]}`);
+                let image = docker.getImage(images[i]);
+                await image.remove();
+            } catch (error) {
+                console.log(`Unable to remove image: ${images[i]}`);
+            }
+        }
+    }
+    res.redirect("/images");
 }

+ 2 - 0
controllers/login.js

@@ -1,6 +1,8 @@
 import { User, Syslog } from '../database/models.js';
 import bcrypt from 'bcrypt';
 
+
+
 export const Login = function(req,res){
     if(req.session.user){
         res.redirect("/logout");

+ 33 - 12
controllers/networks.js

@@ -9,29 +9,27 @@ export const Networks = async function(req, res) {
         <thead>
             <tr>
                 <th class="w-1"><input class="form-check-input m-0 align-middle" name="select" type="checkbox" aria-label="Select all" onclick="selectAll()"></th>
-                <th><button class="table-sort" data-sort="sort-name">Name</button></th>
-                <th><button class="table-sort" data-sort="sort-city">ID</button></th>
-                <th><button class="table-sort" data-sort="sort-score">Status</button></th>
-                <th><button class="table-sort" data-sort="sort-date">Created</button></th>
-                <th><button class="table-sort" data-sort="sort-progress">Action</button></th>
+                <th><label class="table-sort" data-sort="sort-name">Name</label></th>
+                <th><label class="table-sort" data-sort="sort-city">ID</label></th>
+                <th><label class="table-sort" data-sort="sort-score">Status</label></th>
+                <th><label class="table-sort" data-sort="sort-date">Created</label></th>
+                <th><label class="table-sort" data-sort="sort-progress">Action</label></th>
             </tr>
         </thead>
-    <tbody class="table-tbody">`
+        <tbody class="table-tbody">`
 
 
     for (let i = 0; i < networks.length; i++) {
 
         // let date = new Date(images[i].Created * 1000);
         // let created = date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
-
-    
-
+        
         let details = `
             <tr>
-                <td><input class="form-check-input m-0 align-middle" name="select" value="" type="checkbox" aria-label="Select"></td>
+                <td><input class="form-check-input m-0 align-middle" name="select" value="${networks[i].Id}" type="checkbox" aria-label="Select"></td>
                 <td class="sort-name">${networks[i].Name}</td>
                 <td class="sort-city">${networks[i].Id}</td>
-                <td class="sort-score text-green">In use</td>
+                <td class="sort-score text-green"> - </td>
                 <td class="sort-date" data-date="1628122643">${networks[i].Created}</td>
                 <td class="text-end"><a class="btn" href="#">Details</a></td>
             </tr>`
@@ -40,7 +38,6 @@ export const Networks = async function(req, res) {
     
     network_list += `</tbody>`
 
-    
     res.render("networks", {
         name: req.session.user,
         role: req.session.role,
@@ -48,5 +45,29 @@ export const Networks = async function(req, res) {
         network_list: network_list,
         network_count: networks.length
     });
+}
+
+
 
+
+export const removeNetwork = async function(req, res) {
+    let networks = req.body.select;
+
+    if (typeof(networks) == 'string') {
+        networks = [networks];
+    }
+
+    for (let i = 0; i < networks.length; i++) {
+        
+        if (networks[i] != 'on') {
+            try {
+                console.log(`Removing network: ${networks[i]}`);
+                let network = docker.getNetwork(networks[i]);
+                await network.remove();
+            } catch (error) {
+                console.log(`Unable to remove network: ${networks[i]}`);
+            }
+        }
+    }
+    res.redirect("/networks");
 }

+ 1 - 1
controllers/register.js

@@ -55,7 +55,7 @@ export const submitRegister = async function(req,res){
                     password: bcrypt.hashSync(password,10),
                     role: await userRole(),
                     group: 'all',
-                    avatar: `<img src="img/avatars/${avatar}">`,
+                    avatar: `<img src="/images/avatars/${avatar}">`,
                     lastLogin: newLogin,
                 });
 

+ 32 - 0
controllers/supporters.js

@@ -0,0 +1,32 @@
+import { User } from "../database/models.js";
+
+export const Supporters = async (req, res) => {
+    
+    if (!req.session.UUID) return res.redirect("/login");
+
+    let user = await User.findOne({ where: { UUID: req.session.UUID }});
+    
+
+    res.render("supporters", {
+        first_name: user.name,
+        last_name: user.name,
+        name: user.name,
+        id: user.id,
+        email: user.email,
+        role: user.role,
+        avatar: user.avatar,
+    });
+
+
+}
+
+
+let thanks = 0;
+export const Thanks = async (req, res) => {
+    thanks++;
+    let data = thanks.toString();
+    if (thanks > 999) {
+        data = 'Did you really click 1000 times?!';
+    }
+    res.send(data);
+}

+ 9 - 0
controllers/variables.js

@@ -0,0 +1,9 @@
+
+export const Variables = (req, res) => {
+
+    res.render("variables", {
+        name: req.session.user,
+        role: req.session.role,
+        avatar: req.session.avatar,
+    });
+}

+ 42 - 8
controllers/volumes.js

@@ -10,12 +10,12 @@ export const Volumes = async function(req, res) {
     <thead>
         <tr>
             <th class="w-1"><input class="form-check-input m-0 align-middle" name="select" type="checkbox" aria-label="Select all" onclick="selectAll()"></th>
-            <th><button class="table-sort" data-sort="sort-name">Name</button></th>
-            <th><button class="table-sort" data-sort="sort-city">Mount point</button></th>
-            <th><button class="table-sort" data-sort="sort-score">Status</button></th>
-            <th><button class="table-sort" data-sort="sort-date">Created</button></th>
-            <th><button class="table-sort" data-sort="sort-quantity">Size</button></th>
-            <th><button class="table-sort" data-sort="sort-progress">Action</button></th>
+            <th><label class="table-sort" data-sort="sort-name">Name</label></th>
+            <th><label class="table-sort" data-sort="sort-city">Mount point</label></th>
+            <th><label class="table-sort" data-sort="sort-score">Status</label></th>
+            <th><label class="table-sort" data-sort="sort-date">Created</label></th>
+            <th><label class="table-sort" data-sort="sort-quantity">Size</label></th>
+            <th><label class="table-sort" data-sort="sort-progress">Action</label></th>
         </tr>
     </thead>
     <tbody class="table-tbody">`
@@ -44,10 +44,10 @@ export const Volumes = async function(req, res) {
     
         let details = `
         <tr>
-            <td><input class="form-check-input m-0 align-middle" name="select" value="" type="checkbox" aria-label="Select"></td>
+            <td><input class="form-check-input m-0 align-middle" name="select" value="${name}" type="checkbox" aria-label="Select"></td>
             <td class="sort-name">${name}</td>
             <td class="sort-city">${mount}</td>
-            <td class="sort-score text-green">In use</td>
+            <td class="sort-score text-green"> - </td>
             <td class="sort-date" data-date="1628122643">${volume.CreatedAt}</td>
             <td class="sort-quantity">MB</td>
             <td class="text-end"><a class="btn" href="#">Details</a></td>
@@ -67,4 +67,38 @@ export const Volumes = async function(req, res) {
         volume_count: volumes.length
     });
 
+}
+
+export const createVolume = async function(req, res) {
+    
+    let name = req.body.name;
+
+    docker.createVolume({
+        Name: name
+    });
+    res.redirect("/volumes");
+}
+
+
+export const removeVolume = async function(req, res) {
+    let volumes = req.body.select;
+    
+    if (typeof(volumes) == 'string') {
+        volumes = [volumes];
+    }
+
+    for (let i = 0; i < volumes.length; i++) {
+        
+        if (volumes[i] != 'on') {
+            try {
+                console.log(`Removing volume: ${volumes[i]}`);
+                let volume = docker.getVolume(volumes[i]);
+                await volume.remove();
+            } catch (error) {
+                console.log(`Unable to remove volume: ${volumes[i]}`);
+            }
+        }
+    }
+
+    res.redirect("/volumes");
 }

+ 55 - 28
database/models.js

@@ -1,17 +1,9 @@
 import { Sequelize, DataTypes } from 'sequelize';
 
-// let SQLITE_PASS = process.env.SQLITE_PASS || 'some_long_elaborate_password';
-
-// export const sequelize = new Sequelize('dweebui', 'dweebui', SQLITE_PASS, { 
-//   dialect: 'sqlite',
-//   dialectModulePath: '@journeyapps/sqlcipher',
-//   storage: './database/database.sqlite',
-//   logging: false,
-// });
 
 export const sequelize = new Sequelize({ 
   dialect: 'sqlite',
-  storage: './database/database.sqlite',
+  storage: './database/db.sqlite',
   logging: false,
 });
 
@@ -55,25 +47,60 @@ export const User = sequelize.define('User', {
 });
 
 export const Container = sequelize.define('Container', {
-  id: {
-    type: DataTypes.INTEGER,
-    autoIncrement: true,
-    primaryKey: true
-  },
-  name: {
-    type: DataTypes.STRING,
-    allowNull: false
-  },
-  visibility: {
-    type: DataTypes.STRING
-  },
-  size: {
-    type: DataTypes.STRING
-  },
-  group: {
-    type: DataTypes.STRING
-  }
-});
+    id: {
+        type: DataTypes.INTEGER,
+        autoIncrement: true,
+        primaryKey: true
+    },
+    name: {
+        type: DataTypes.STRING,
+        allowNull: false
+    },
+    visibility: {
+        type: DataTypes.STRING
+    },
+    service: {
+        type: DataTypes.STRING
+    },
+    state: {
+        type: DataTypes.STRING
+    },
+    image: {
+        type: DataTypes.STRING
+    },
+    external_port: {
+        type: DataTypes.STRING
+    },
+    internal_port: {
+        type: DataTypes.STRING
+    },
+    ports: {
+        type: DataTypes.STRING
+    },
+    volumes: {
+        type: DataTypes.STRING
+    },
+    environment_variables: {
+        type: DataTypes.STRING
+    },
+    labels: {
+        type: DataTypes.STRING
+    },
+    IPv4: {
+        type: DataTypes.STRING
+    },
+    style: {
+        type: DataTypes.STRING
+    },
+    cpu: {
+        // store the last 15 values from dockerContainerStats
+        type: DataTypes.STRING
+    },
+    ram: {
+        // store the last 15 values from dockerContainerStats
+        type: DataTypes.STRING
+    },
+  });
 
 export const Permission = sequelize.define('Permission', {
   id: {

+ 6 - 3
docker-compose.yaml

@@ -2,9 +2,8 @@ version: "3.9"
 services:
   dweebui:
     container_name: dweebui
-    image: lllllllillllllillll/dweebui:v0.20
+    image: lllllllillllllillll/dweebui:v0.40
     environment:
-      NODE_ENV: production
       PORT: 8000
       SECRET: MrWiskers
     restart: unless-stopped
@@ -12,7 +11,11 @@ services:
       - 8000:8000
     volumes:
       - dweebui:/app
+      # Docker socket
       - /var/run/docker.sock:/var/run/docker.sock
+      # Podman socket
+      #- /run/podman/podman.sock:/var/run/docker.sock
+
     networks:
       - dweebui_net
 
@@ -21,4 +24,4 @@ volumes:
 
 networks:
   dweebui_net:
-    driver: bridge
+    driver: bridge

+ 28 - 29
functions/install.js

@@ -9,8 +9,6 @@ import { containerCard } from "../components/containerCard.js";
 // This entire page hurts to look at. 
 export const Install = async (req, res) => {
 
-        console.log(req.app.locals.installCard);
-
         let data = req.body;
 
         let { service_name, name, image, command_check, command, net_mode, restart_policy } = data;        
@@ -170,39 +168,40 @@ export const Install = async (req, res) => {
             try {   
                 mkdirSync(`./appdata/${name}`, { recursive: true });
                 writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
-
-            } catch { console.log('error creating directory or compose file') }
-
-            var compose = new DockerodeCompose(docker, `./appdata/${name}/docker-compose.yml`, `${name}`);
-
-            (async () => {
-                try {
+                var compose = new DockerodeCompose(docker, `./appdata/${name}/docker-compose.yml`, `${name}`);
+
+            } catch { 
+                const syslog = await Syslog.create({
+                    user: req.session.user,
+                    email: null,
+                    event: "App Installation",
+                    message: `${name} installation failed - error creating directory or compose file : ${err}`,
+                    ip: req.socket.remoteAddress
+                });
+             }
+
+            try {
+                (async () => {
                     await compose.pull();
-                    await compose.up().then(() => {
-                        const syslog = Syslog.create({
-                            user: req.session.user,
-                            email: null,
-                            event: "App Installation",
-                            message: `${name} installed successfully`,
-                            ip: req.socket.remoteAddress
-                        });
-                    });
-                } catch (err) {
-                    console.error(err);
+                    await compose.up();
+
                     const syslog = await Syslog.create({
                         user: req.session.user,
                         email: null,
                         event: "App Installation",
-                        message: `${name} installation failed: ${err}`,
+                        message: `${name} installed successfully`,
                         ip: req.socket.remoteAddress
-                    });
-                }
-            })();
+                    });  
+                })();
+            } catch (err) {
+                const syslog = await Syslog.create({
+                    user: req.session.user,
+                    email: null,
+                    event: "App Installation",
+                    message: `${name} installation failed: ${err}`,
+                    ip: req.socket.remoteAddress
+                });
+            }
         }
-
-
     res.redirect('/');
-
 }
-
-

+ 5 - 2
functions/uninstall.js

@@ -6,17 +6,20 @@ export const Uninstall = async (req, res) => {
 
     let { confirm, service_name } = req.body;
 
+    console.log(`Uninstalling ${service_name}...`);
+
     if (confirm == 'Yes') {
+
         var containerName = docker.getContainer(`${service_name}`);
         try {
             await containerName.stop();
         } catch {
             console.log(`Error stopping ${service_name} container`);
         }
+
         try {
-            await containerName.remove();
+            containerName.remove();
             
-
             const syslog = await Syslog.create({
                 user: req.session.user,
                 email: null,

Diff do ficheiro suprimidas por serem muito extensas
+ 46 - 798
package-lock.json


+ 7 - 17
package.json

@@ -1,37 +1,27 @@
 {
   "name": "dweebui",
   "version": "1.0.0",
-  "description": "A web UI for Docker",
+  "description": "",
   "main": "server.js",
   "type": "module",
   "scripts": {
-    "test": "mocha --require @babel/register"
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "start": "node server.js"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "@babel/register": "^7.23.7",
-    "@socket.io/admin-ui": "^0.5.1",
     "bcrypt": "^5.1.1",
-    "chai": "^5.0.0",
-    "compression": "^1.7.4",
-    "cors": "^2.8.5",
     "dockerode": "^4.0.2",
     "dockerode-compose": "^1.4.0",
     "ejs": "^3.1.9",
     "express": "^4.18.2",
-    "express-rate-limit": "^7.1.5",
-    "express-session": "^1.17.3",
-    "helmet": "^7.1.0",
+    "express-session": "^1.18.0",
     "js-yaml": "^4.1.0",
-    "mocha": "^10.2.0",
-    "sequelize": "^6.35.2",
-    "sinon": "^17.0.1",
-    "socket.io": "^4.7.4",
+    "memorystore": "^1.6.7",
+    "sequelize": "^6.37.1",
     "sqlite3": "^5.1.7",
-    "stream": "^0.0.2",
-    "supertest": "^6.3.3",
-    "systeminformation": "^5.21.22"
+    "systeminformation": "^5.22.0"
   }
 }

+ 4 - 0
public/css/meters.css

@@ -83,6 +83,10 @@
   .blue > span {
     background-image: linear-gradient(#2478f5, #22017e);
   }
+
+  .purple > span {
+    background-image: linear-gradient(#bd14d3, #670370);
+  }
   
   .nostripes > span > span,
   .nostripes > span::after {

+ 0 - 0
public/img/avatars/burns.jpg → public/images/avatars/burns.jpg


+ 0 - 0
public/img/avatars/duffman.png → public/images/avatars/duffman.png


+ 0 - 0
public/img/avatars/frank.jpg → public/images/avatars/frank.jpg


+ 0 - 0
public/img/avatars/moe.jpg → public/images/avatars/moe.jpg


+ 0 - 0
public/img/avatars/moleman.png → public/images/avatars/moleman.png


+ 0 - 0
public/img/avatars/poochie.jpg → public/images/avatars/poochie.jpg


+ 0 - 0
public/img/avatars/rus.jpg → public/images/avatars/rus.jpg


+ 0 - 0
public/img/avatars/skinner.jpg → public/images/avatars/skinner.jpg


+ 0 - 0
public/static/logo.svg → public/images/dweebui.svg


BIN
public/images/logo.png


+ 355 - 0
public/js/htmx-sse.js

@@ -0,0 +1,355 @@
+/*
+Server Sent Events Extension
+============================
+This extension adds support for Server Sent Events to htmx.  See /www/extensions/sse.md for usage instructions.
+
+*/
+
+(function() {
+
+	/** @type {import("../htmx").HtmxInternalApi} */
+	var api;
+
+	htmx.defineExtension("sse", {
+
+		/**
+		 * Init saves the provided reference to the internal HTMX API.
+		 * 
+		 * @param {import("../htmx").HtmxInternalApi} api 
+		 * @returns void
+		 */
+		init: function(apiRef) {
+			// store a reference to the internal API.
+			api = apiRef;
+
+			// set a function in the public API for creating new EventSource objects
+			if (htmx.createEventSource == undefined) {
+				htmx.createEventSource = createEventSource;
+			}
+		},
+
+		/**
+		 * onEvent handles all events passed to this extension.
+		 * 
+		 * @param {string} name 
+		 * @param {Event} evt 
+		 * @returns void
+		 */
+		onEvent: function(name, evt) {
+
+			switch (name) {
+
+				case "htmx:beforeCleanupElement":
+					var internalData = api.getInternalData(evt.target)
+					// Try to remove remove an EventSource when elements are removed
+					if (internalData.sseEventSource) {
+						internalData.sseEventSource.close();
+					}
+
+					return;
+
+				// Try to create EventSources when elements are processed
+				case "htmx:afterProcessNode":
+					ensureEventSourceOnElement(evt.target);
+					registerSSE(evt.target);
+			}
+		}
+	});
+
+	///////////////////////////////////////////////
+	// HELPER FUNCTIONS
+	///////////////////////////////////////////////
+
+
+	/**
+	 * createEventSource is the default method for creating new EventSource objects.
+	 * it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed.
+	 * 
+	 * @param {string} url 
+	 * @returns EventSource
+	 */
+	function createEventSource(url) {
+		return new EventSource(url, { withCredentials: true });
+	}
+
+	function splitOnWhitespace(trigger) {
+		return trigger.trim().split(/\s+/);
+	}
+
+	function getLegacySSEURL(elt) {
+		var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
+		if (legacySSEValue) {
+			var values = splitOnWhitespace(legacySSEValue);
+			for (var i = 0; i < values.length; i++) {
+				var value = values[i].split(/:(.+)/);
+				if (value[0] === "connect") {
+					return value[1];
+				}
+			}
+		}
+	}
+
+	function getLegacySSESwaps(elt) {
+		var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
+		var returnArr = [];
+		if (legacySSEValue != null) {
+			var values = splitOnWhitespace(legacySSEValue);
+			for (var i = 0; i < values.length; i++) {
+				var value = values[i].split(/:(.+)/);
+				if (value[0] === "swap") {
+					returnArr.push(value[1]);
+				}
+			}
+		}
+		return returnArr;
+	}
+
+	/**
+	 * registerSSE looks for attributes that can contain sse events, right 
+	 * now hx-trigger and sse-swap and adds listeners based on these attributes too
+	 * the closest event source
+	 *
+	 * @param {HTMLElement} elt
+	 */
+	function registerSSE(elt) {
+		// Find closest existing event source
+		var sourceElement = api.getClosestMatch(elt, hasEventSource);
+		if (sourceElement == null) {
+			// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
+			return null; // no eventsource in parentage, orphaned element
+		}
+
+		// Set internalData and source
+		var internalData = api.getInternalData(sourceElement);
+		var source = internalData.sseEventSource;
+
+		// Add message handlers for every `sse-swap` attribute
+		queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
+
+			var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
+			if (sseSwapAttr) {
+				var sseEventNames = sseSwapAttr.split(",");
+			} else {
+				var sseEventNames = getLegacySSESwaps(child);
+			}
+
+			for (var i = 0; i < sseEventNames.length; i++) {
+				var sseEventName = sseEventNames[i].trim();
+				var listener = function(event) {
+
+					// If the source is missing then close SSE
+					if (maybeCloseSSESource(sourceElement)) {
+						return;
+					}
+
+					// If the body no longer contains the element, remove the listener
+					if (!api.bodyContains(child)) {
+						source.removeEventListener(sseEventName, listener);
+					}
+
+					// swap the response into the DOM and trigger a notification
+					swap(child, event.data);
+					api.triggerEvent(elt, "htmx:sseMessage", event);
+				};
+
+				// Register the new listener
+				api.getInternalData(child).sseEventListener = listener;
+				source.addEventListener(sseEventName, listener);
+			}
+		});
+
+		// Add message handlers for every `hx-trigger="sse:*"` attribute
+		queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
+
+			var sseEventName = api.getAttributeValue(child, "hx-trigger");
+			if (sseEventName == null) {
+				return;
+			}
+
+			// Only process hx-triggers for events with the "sse:" prefix
+			if (sseEventName.slice(0, 4) != "sse:") {
+				return;
+			}
+			
+			// remove the sse: prefix from here on out
+			sseEventName = sseEventName.substr(4);
+
+			var listener = function() {
+				if (maybeCloseSSESource(sourceElement)) {
+					return
+				}
+
+				if (!api.bodyContains(child)) {
+					source.removeEventListener(sseEventName, listener);
+				}
+			}
+		});
+	}
+
+	/**
+	 * ensureEventSourceOnElement creates a new EventSource connection on the provided element.
+	 * If a usable EventSource already exists, then it is returned.  If not, then a new EventSource
+	 * is created and stored in the element's internalData.
+	 * @param {HTMLElement} elt
+	 * @param {number} retryCount
+	 * @returns {EventSource | null}
+	 */
+	function ensureEventSourceOnElement(elt, retryCount) {
+
+		if (elt == null) {
+			return null;
+		}
+
+		// handle extension source creation attribute
+		queryAttributeOnThisOrChildren(elt, "sse-connect").forEach(function(child) {
+			var sseURL = api.getAttributeValue(child, "sse-connect");
+			if (sseURL == null) {
+				return;
+			}
+
+			ensureEventSource(child, sseURL, retryCount);
+		});
+
+		// handle legacy sse, remove for HTMX2
+		queryAttributeOnThisOrChildren(elt, "hx-sse").forEach(function(child) {
+			var sseURL = getLegacySSEURL(child);
+			if (sseURL == null) {
+				return;
+			}
+
+			ensureEventSource(child, sseURL, retryCount);
+		});
+
+	}
+
+	function ensureEventSource(elt, url, retryCount) {
+		var source = htmx.createEventSource(url);
+
+		source.onerror = function(err) {
+
+			// Log an error event
+			api.triggerErrorEvent(elt, "htmx:sseError", { error: err, source: source });
+
+			// If parent no longer exists in the document, then clean up this EventSource
+			if (maybeCloseSSESource(elt)) {
+				return;
+			}
+
+			// Otherwise, try to reconnect the EventSource
+			if (source.readyState === EventSource.CLOSED) {
+				retryCount = retryCount || 0;
+				var timeout = Math.random() * (2 ^ retryCount) * 500;
+				window.setTimeout(function() {
+					ensureEventSourceOnElement(elt, Math.min(7, retryCount + 1));
+				}, timeout);
+			}
+		};
+
+		source.onopen = function(evt) {
+			api.triggerEvent(elt, "htmx:sseOpen", { source: source });
+		}
+
+		api.getInternalData(elt).sseEventSource = source;
+	}
+
+	/**
+	 * maybeCloseSSESource confirms that the parent element still exists.
+	 * If not, then any associated SSE source is closed and the function returns true.
+	 * 
+	 * @param {HTMLElement} elt 
+	 * @returns boolean
+	 */
+	function maybeCloseSSESource(elt) {
+		if (!api.bodyContains(elt)) {
+			var source = api.getInternalData(elt).sseEventSource;
+			if (source != undefined) {
+				source.close();
+				// source = null
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
+	 * 
+	 * @param {HTMLElement} elt 
+	 * @param {string} attributeName 
+	 */
+	function queryAttributeOnThisOrChildren(elt, attributeName) {
+
+		var result = [];
+
+		// If the parent element also contains the requested attribute, then add it to the results too.
+		if (api.hasAttribute(elt, attributeName)) {
+			result.push(elt);
+		}
+
+		// Search all child nodes that match the requested attribute
+		elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) {
+			result.push(node);
+		});
+
+		return result;
+	}
+
+	/**
+	 * @param {HTMLElement} elt
+	 * @param {string} content 
+	 */
+	function swap(elt, content) {
+
+		api.withExtensions(elt, function(extension) {
+			content = extension.transformResponse(content, null, elt);
+		});
+
+		var swapSpec = api.getSwapSpecification(elt);
+		var target = api.getTarget(elt);
+		var settleInfo = api.makeSettleInfo(elt);
+
+		api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo);
+
+		settleInfo.elts.forEach(function(elt) {
+			if (elt.classList) {
+				elt.classList.add(htmx.config.settlingClass);
+			}
+			api.triggerEvent(elt, 'htmx:beforeSettle');
+		});
+
+		// Handle settle tasks (with delay if requested)
+		if (swapSpec.settleDelay > 0) {
+			setTimeout(doSettle(settleInfo), swapSpec.settleDelay);
+		} else {
+			doSettle(settleInfo)();
+		}
+	}
+
+	/**
+	 * doSettle mirrors much of the functionality in htmx that 
+	 * settles elements after their content has been swapped.
+	 * TODO: this should be published by htmx, and not duplicated here
+	 * @param {import("../htmx").HtmxSettleInfo} settleInfo 
+	 * @returns () => void
+	 */
+	function doSettle(settleInfo) {
+
+		return function() {
+			settleInfo.tasks.forEach(function(task) {
+				task.call();
+			});
+
+			settleInfo.elts.forEach(function(elt) {
+				if (elt.classList) {
+					elt.classList.remove(htmx.config.settlingClass);
+				}
+				api.triggerEvent(elt, 'htmx:afterSettle');
+			});
+		}
+	}
+
+	function hasEventSource(node) {
+		return api.getInternalData(node).sseEventSource != null;
+	}
+
+})();

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
public/js/htmx.min.js


+ 0 - 141
public/js/main.js

@@ -1,141 +0,0 @@
-socket.on('connect', () => { 
-  console.log('connected'); 
-  //clear localStorage (because of code in old versions)
-  localStorage.clear();
-});
-
-// Server metrics
-const cpuText = document.getElementById('cpu-text');
-const cpuBar = document.getElementById('cpu-bar');
-const ramText = document.getElementById('ram-text');
-const ramBar = document.getElementById('ram-bar');
-const netText = document.getElementById('net-text');
-const netBar = document.getElementById('net-bar');
-const diskText = document.getElementById('disk-text');
-const diskBar = document.getElementById('disk-bar');
-
-// Container cards
-const dockerCards = document.getElementById('cards');
-
-// Container logs
-const logViewer = document.getElementById('logView');
-
-// Server metrics
-socket.on('metrics', (data) => {
-    let [cpu, ram, tx, rx, disk] = data;
-
-    cpuText.innerHTML = `<span>CPU ${cpu} %</span>`;
-    if (cpu < 7 ) { cpu = 7; }
-    cpuBar.innerHTML = `<span style="width: ${cpu}%"><span></span></span>`;
-    
-    ramText.innerHTML = `<span>RAM ${ram} %</span>`;
-    if (ram < 7 ) { ram = 7; }
-    ramBar.innerHTML = `<span style="width: ${ram}%"><span></span></span>`;
-
-    tx = Math.round(tx / 1024 / 1024);
-    rx = Math.round(rx / 1024 / 1024);
-
-    netText.innerHTML = `<span>Down: ${rx}MB</span><span>  Up: ${tx}MB</span>`;
-    netBar.innerHTML = `<span style="width: 50%"><span></span></span>`;
-
-    diskText.innerHTML = `<span>DISK ${disk} %</span>`;
-    if (disk < 7 ) { disk = 7; }
-    diskBar.innerHTML = `<span style="width: ${disk}%"><span></span></span>`;
-});
-
-// Container cards
-socket.on('containers', (data) => {
-    let deleteMeElements = document.querySelectorAll('.deleteme');
-    deleteMeElements.forEach((element) => {
-      element.parentNode.removeChild(element);
-    });
-    dockerCards.insertAdjacentHTML("afterend", data);
-});
-
-
-function drawCharts(name, cpuArray, ramArray) {
-  let element = document.querySelector(`${name}`);
-
-  let chart = new ApexCharts(element, {
-    chart: {
-      type: "line",
-      height: 40.0,
-      sparkline: {
-        enabled: true
-      },
-      animations: {
-        enabled: false
-      }
-    },
-    fill: {
-      opacity: 1
-    },
-    stroke: {
-      width: [2, 1],
-      dashArray: [0, 3],
-      lineCap: "round",
-      curve: "smooth"
-    },
-    series: [{
-      name: "CPU",
-      data: cpuArray
-    }, {
-      name: "RAM",
-      data: ramArray
-    }],
-    tooltip: {
-      enabled: false
-    },
-    grid: {
-      strokeDashArray: 4
-    },
-    xaxis: {
-      labels: {
-        padding: 0
-      },
-      tooltip: {
-        enabled: false
-      }
-    },
-    yaxis: {
-      labels: {
-        padding: 4
-      }
-    },
-    colors: [tabler.getColor("primary"), tabler.getColor("gray-600")],
-    legend: {
-      show: false
-    }
-  })
-  chart.render();
-}
-
-// Buttons functions
-function clicked(button) {
-  socket.emit('clicked', {name: button.name, id: button.id, value: button.value});
-}
-
-
-socket.on('containerStats', (data) => {
-  let containerStats = data;
-  
-  for (const [name, statsArray] of Object.entries(containerStats)) {
-
-    let cpuArray = statsArray.cpuArray;
-    let ramArray = statsArray.ramArray;
-
-    let chart = document.getElementById(`${name}_chart`);
-    if (chart) {
-      chart.innerHTML = '';
-      drawCharts(`#${name}_chart`, cpuArray, ramArray);
-    } else {
-      console.log(`Chart element with id ${name}_chart not found in the DOM`);
-    }
-  }
-
-});
-
-
-socket.on('logs', (data) => {
-  logViewer.innerHTML = `<pre>${data}</pre>`;
-});

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
public/static/logo-sm-black.svg


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
public/static/logo-sm-white.svg


+ 41 - 21
router/index.js

@@ -4,21 +4,19 @@ export const router = express.Router();
 // Controllers
 import { Login, submitLogin, Logout } from "../controllers/login.js";
 import { Register, submitRegister } from "../controllers/register.js";
-import { Dashboard, searchDashboard } from "../controllers/dashboard.js";
+import { Dashboard, Start, Stop, Pause, Restart, Logs, Modal, Stats, Hide, Reset, Chart, Containers, Installs } from "../controllers/dashboard.js";
 import { Apps, appSearch } from "../controllers/apps.js";
 import { Users } from "../controllers/users.js";
-import { Images } from "../controllers/images.js";
+import { Images, removeImage } from "../controllers/images.js";
+import { Networks, removeNetwork } from "../controllers/networks.js";
+import { Volumes, removeVolume } from "../controllers/volumes.js";
 import { Account } from "../controllers/account.js";
+import { Variables } from "../controllers/variables.js";
 import { Settings } from "../controllers/settings.js";
-import { Networks } from "../controllers/networks.js";
-import { Volumes } from "../controllers/volumes.js";
+import { Supporters, Thanks } from "../controllers/supporters.js";
 import { Syslogs } from "../controllers/syslogs.js";
 import { Portal } from "../controllers/portal.js"
 
-/// Functions
-import { Install } from "../functions/install.js"
-import { Uninstall } from "../functions/uninstall.js"
-
 // Auth middleware
 const auth = (req, res, next) => {
     if (req.session.role == "admin") {
@@ -28,22 +26,29 @@ const auth = (req, res, next) => {
     }
 };
 
-// Routes
-router.get("/login", Login);
-router.post("/login", submitLogin);
-router.get("/logout", Logout);
-
-router.get("/register", Register);
-router.post("/register", submitRegister);  
-
-
+// Admin routes
 router.get("/", auth, Dashboard);
-router.post("/", auth, searchDashboard);
+router.get("/containers", auth, Containers);
+router.post("/start", auth, Start);
+router.post("/stop", auth, Stop);
+router.post("/pause", auth, Pause);
+router.post("/restart", auth, Restart);
+router.get("/logs", auth, Logs);
+router.get ("/modal", auth, Modal);
+router.get("/stats", auth, Stats);
+router.post("/hide", auth, Hide);
+router.post("/reset", auth, Reset);
+router.get("/chart", auth, Chart);
+router.get("/installs", auth, Installs);
 
 router.get("/images", auth, Images);
+router.post("/removeImage", auth, removeImage);
+
 router.get("/volumes", auth, Volumes);
+router.post("/removeVolume", auth, removeVolume);
+
 router.get("/networks", auth, Networks);
-router.get("/portal", Portal)
+router.post("/removeNetwork", auth, removeNetwork);
 
 router.get("/apps", auth, Apps);
 router.get("/apps/:page", auth, Apps);
@@ -52,10 +57,25 @@ router.post("/apps", auth, appSearch);
 router.get("/users", auth, Users);
 router.get("/syslogs", auth, Syslogs);
 
+router.get("/variables", auth, Variables);
+router.get("/settings", auth, Settings);
 
+// User routes
+router.get("/portal", Portal);
 router.get("/account", Account);
-router.get("/settings", auth, Settings);
+router.get("/supporters", Supporters);
+router.post("/thank", Thanks);
+
+router.get("/login", Login);
+router.post("/login", submitLogin);
+router.get("/register", Register);
+router.post("/register", submitRegister);  
+router.get("/logout", Logout);
+
 
 // Functions
+import { Install } from "../functions/install.js"
+import { Uninstall } from "../functions/uninstall.js"
+
 router.post("/install", auth, Install);
-router.post("/uninstall", auth, Uninstall);
+router.post("/uninstall", auth, Uninstall);

BIN
screenshots/dashboard1.png


BIN
screenshots/login.png


+ 51 - 294
server.js

@@ -1,44 +1,24 @@
 import express from 'express';
 import session from 'express-session';
-import compression from 'compression';
-import helmet from 'helmet';
+import memorystore from 'memorystore';
+import ejs from 'ejs';
 import Docker from 'dockerode';
-import cors from 'cors';
-import { Readable } from 'stream';
-import { rateLimit } from 'express-rate-limit';
-import { instrument } from '@socket.io/admin-ui'
 import { router } from './router/index.js';
-import { createServer } from 'node:http';
-import { Server } from 'socket.io';
-import { sequelize, Container } from './database/models.js';
-import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
-import { containerCard } from './components/containerCard.js';
+import { sequelize } from './database/models.js';
+import { currentLoad, mem, networkStats, fsSize } from 'systeminformation';
 
-export const app = express();
-const server = createServer(app);
-const port = process.env.PORT || 8000;
 export var docker = new Docker();
-let [cpu, ram, tx, rx, disk] = [0, 0, 0, 0, 0];
-let [hidden, clicked, dockerEvents] = ['', false, ''];
-let metricsInterval, cardsInterval, graphsInterval;
-let cardList = '';
-const statsArray = {};
+export { setEvent, cpu, ram, tx, rx, disk }
 
-// Socket.io admin ui
-export const io = new Server(server, { 
-    cors: {
-        origin: ['http://localhost:8000', 'https://admin.socket.io'],
-        methods: ['GET', 'POST'],
-        credentials: true
-    } 
-});
-instrument(io, {
-    auth: false,
-    readonly: true
-});
+const app = express();
+const MemoryStore = memorystore(session);
+const port = process.env.PORT || 8000;
+let [ cpu, ram, tx, rx, disk ] = [0, 0, 0, 0, 0];
+let [ event, eventType ] = [false, 'docker'];
 
 // Session middleware
 const sessionMiddleware = session({
+    store: new MemoryStore({ checkPeriod: 86400000 }), // Prune expired entries every 24h
     secret: "keyboard cat", 
     resave: false, 
     saveUninitialized: false, 
@@ -49,46 +29,36 @@ const sessionMiddleware = session({
     }
 });
 
-// Make session data available to socket.io
-io.engine.use(sessionMiddleware); 
-
-// Rate limiter
-const limiter = rateLimit({
-	windowMs: 5 * 60 * 1000, // 5 minutes
-	limit: 50, // Limit each IP to 50 requests per `window`.
-	standardHeaders: 'draft-7',
-	legacyHeaders: false,
-})
-
 // Express middleware
-app.set('view engine', 'ejs');
+app.set('view engine', 'html');
+app.engine('html', ejs.renderFile);
 app.use([
-    compression(),
-    cors(),
-    helmet({contentSecurityPolicy: false}),
-    express.static("public"),
+    express.static('public'),
     express.json(),
     express.urlencoded({ extended: true }),
     sessionMiddleware,
-    router,
-    limiter
+    router
 ]);
 
 // Initialize server
-server.listen(port, async () => {
+app.listen(port, async () => {
     async function init() {
-        try { await sequelize.authenticate().then(() => { console.log('[Connected to DB]') }); } 
-            catch { console.log('[Could not connect to DB]'); }
-        try { await sequelize.sync().then(() => { console.log('[Models Synced]') }); } 
-            catch { console.log('[Could not Sync Models]', error); }
-        await getHidden();
-        containerCards();
-    }
-    await init();
-    app.emit("appStarted");
-    console.log(`\nServer listening on http://localhost:${port}`);
+        try { await sequelize.authenticate().then(
+            () => { console.log('DB Connection: ✔️') }); }
+            catch { console.log('DB Connection: ❌'); }
+        try { await sequelize.sync().then( // check out that formatting
+            () => { console.log('Synced Models: ✔️') }); }
+            catch { console.log('Synced Models: ❌'); } }
+        await init().then(() => { 
+            console.log(`Listening on http://localhost:${port}`);
+    });
 });
 
+function setEvent(value, type) {
+    event = value;
+    eventType = type;
+}
+
 // Server metrics
 let serverMetrics = async () => {
     currentLoad().then(data => { 
@@ -105,239 +75,26 @@ let serverMetrics = async () => {
         disk = data[0].use; 
     });
 }
-
-// List docker containers
-let containerCards = async () => {
-    let list = '';
-    const allContainers = await docker.listContainers({ all: true });
-    for (const container of allContainers) {
-        if (!hidden.includes(container.Names[0].slice(1))) {
-
-            let imageVersion = container.Image.split('/');
-            let service = imageVersion[imageVersion.length - 1].split(':')[0];
-            let containerId = docker.getContainer(container.Id);
-            let containerInfo = await containerId.inspect();
-            let ports_list = [];
-            try {
-            for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
-                let ports = {
-                    check: 'checked',
-                    external: value[0].HostPort,
-                    internal: key.split('/')[0],
-                    protocol: key.split('/')[1]
-                }
-                ports_list.push(ports);
-            }
-            } catch {}
-
-            let external_port = ports_list[0]?.external || 0;
-            let internal_port = ports_list[0]?.internal || 0;
-
-            let container_info = {
-                name: container.Names[0].slice(1),
-                service: service,
-                id: container.Id,
-                state: container.State,
-                image: container.Image,
-                external_port: external_port,
-                internal_port: internal_port,
-                ports: ports_list,
-                link: 'localhost',
-            }
-            let card = containerCard(container_info);
-            list += card;
-        }
-    }
-    cardList = list;
-}
-
-// Container metrics
-let containerStats = async () => {
-    const data = await docker.listContainers({ all: true });
-    for (const container of data) {
-        if (!hidden.includes(container.Names[0].slice(1))) {
-            const stats = await dockerContainerStats(container.Id);
-            const name = container.Names[0].slice(1);
-
-            if (!statsArray[name]) {
-                statsArray[name] = {
-                    cpuArray: Array(15).fill(0),
-                    ramArray: Array(15).fill(0)
-                };
-            }
-            statsArray[name].cpuArray.push(Math.round(stats[0].cpuPercent));
-            statsArray[name].ramArray.push(Math.round(stats[0].memPercent));
-
-            statsArray[name].cpuArray = statsArray[name].cpuArray.slice(-15);
-            statsArray[name].ramArray = statsArray[name].ramArray.slice(-15);
+setInterval(serverMetrics, 1000);
+
+let sent_list = '';
+router.get('/sse_event', (req, res) => {
+    res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', });
+    let eventCheck = setInterval(async() => {
+        let all_containers = '';
+        await docker.listContainers({ all: true }).then(containers => {
+            containers.forEach(container => {
+                all_containers += `${container.Names}: ${container.State}\n`;
+            });
+        });
+        if ((all_containers != sent_list) || (event == true)) {
+            sent_list = all_containers;
+            event = false;
+            res.write(`event: ${eventType}\n`);
+            res.write(`data: there was an event!\n\n`);
         }
-    }
-}
-
-// Store docker events 
-docker.getEvents((err, stream) => {
-    if (err) throw err;
-    stream.on('data', (chunk) => {
-        dockerEvents += chunk.toString('utf8');
+    }, 1000);
+    req.on('close', () => {
+        clearInterval(eventCheck);
     });
-});
-
-// Check for docker events
-setInterval(async () => {
-    if (dockerEvents != '') {
-        await getHidden();
-        await containerCards();
-        dockerEvents = '';
-    }
-}, 1000);
-
-// Get hidden containers
-async function getHidden() {
-    hidden = await Container.findAll({ where: {visibility:false}});
-    hidden = hidden.map((container) => container.name);
-}
-
-// Socket.io
-io.on('connection', (socket) => {
-    let sessionData = socket.request.session;
-    let sent = '';
-    if (sessionData.user != undefined) {
-        console.log(`${sessionData.user} connected from ${socket.handshake.headers.host}`);
-        
-        // Start intervals if not already started
-        if (!metricsInterval) {
-            serverMetrics();
-            metricsInterval = setInterval(serverMetrics, 1000);
-            console.log('Metrics interval started');
-        }
-        if (!cardsInterval) {
-            containerCards();
-            cardsInterval = setInterval(containerCards, 1000);
-            console.log('Cards interval started');
-        }
-        if (!graphsInterval) {
-            containerStats();
-            graphsInterval = setInterval(containerStats, 1000);
-            console.log('Graphs interval started');
-        }
-
-        setInterval(() => {
-            socket.emit('metrics', [cpu, ram, tx, rx, disk]);
-            if (sent != cardList) {
-                sent = cardList;
-                socket.emit('containers', cardList);
-            }
-            socket.emit('containerStats', statsArray);
-        }, 1000);
-
-
-        // Client input        
-        socket.on('clicked', (data) => {
-            let { name, id, value } = data;
-            console.log(`${sessionData.user} clicked: ${id} ${value} ${name}`);
-            if (clicked == true) { return; } clicked = true;
-
-            // View container logs
-            if (id == 'logs'){
-                function containerLogs (data) {
-                    return new Promise((resolve, reject) => {
-                        let logString = '';
-                        var options = {
-                            follow: false,
-                            stdout: true,
-                            stderr: false,
-                            timestamps: false
-                        };
-                        var containerName = docker.getContainer(data);
-                        containerName.logs(options, function (err, stream) {
-                            if (err) { reject(err); return; }
-                            const readableStream = Readable.from(stream);
-                            readableStream.on('data', function (chunk) {
-                                logString += chunk.toString('utf8');
-                            });
-                            readableStream.on('end', function () {
-                                resolve(logString);
-                            });
-                        });
-                    });
-                };
-                containerLogs(name).then((data) => {
-                    socket.emit('logs', data);
-                });
-            }
-
-            // start, stop, pause, restart container
-            if (id == 'start' || id == 'stop' || id == 'pause' || id == 'restart'){
-                var containerName = docker.getContainer(name);
-        
-                if ((id == 'start') && (value == 'stopped')) {
-                    containerName.start();
-                } else if ((id == 'start') && (value == 'paused')) {
-                    containerName.unpause();
-                } else if ((id == 'stop') && (value != 'stopped')) {
-                    containerName.stop();
-                } else if ((id == 'pause') && (value == 'running')) {
-                    containerName.pause();
-                } else if ((id == 'pause') && (value == 'paused')) {
-                    containerName.unpause();
-                } else if (id == 'restart') {
-                    containerName.restart();
-                }
-            }
-
-            // hide container
-            if (id == 'hide') {
-                async function hideContainer() {
-                    let containerExists = await Container.findOne({ where: {name: name}});
-                    if(!containerExists) {
-                        const newContainer = await Container.create({ name: name, visibility: false, });
-                        getHidden();
-                    } else {
-                        containerExists.update({ visibility: false });
-                        getHidden();
-                    }
-                    
-                }
-                hideContainer();
-            }
-
-            // unhide containers
-            if (id == 'resetView') {
-                Container.update({ visibility: true }, { where: {} });
-                getHidden();
-            }
-                
-            clicked = false;
-        });
-
-        socket.on('disconnect', () => {
-            console.log(`${sessionData.user} disconnected`);
-            socket.disconnect();
-            // clear intervals if no users are connected
-            if (io.engine.clientsCount == 0) {
-                clearInterval(metricsInterval);
-                clearInterval(cardsInterval);
-                clearInterval(graphsInterval);
-                metricsInterval = null;
-                cardsInterval = null;
-                graphsInterval = null;
-                console.log('All intervals cleared');
-            }
-        });
-    } else {
-        console.log('Missing session data');
-    }
-});
-
-
-
-
-
-
-
-
-// let link = '';
-// networkInterfaces().then(data => {
-//     link = data[0].ip4;
-// });
-
+});

+ 5357 - 0
templates/foss.json

@@ -0,0 +1,5357 @@
+{
+  "version": "2",
+  "templates": [
+    {
+      "type": 1,
+      "name": "heimdall",
+      "title": "Heimdall",
+      "note": "",
+      "description": "Heimdall is a way to organise all those links to your most used web sites and web applications in a simple way. <a href='https://hub.docker.com/r/linuxserver/heimdall/' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/heimdall.png",
+      "image": "lscr.io/linuxserver/heimdall:latest",
+      "categories": [
+        "Dashboard"
+      ],
+      "ports": [
+        "8001:80/tcp",
+        "4001:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/heimdall",
+          "container": "/config"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "homepage",
+      "title": "Homepage",
+      "description": "A modern (fully static, fast), secure (fully proxied), highly customizable application dashboard with integrations for more than 25 services and translations for over 15 languages. Easily configured via YAML files (or discovery via docker labels). <a href='https://github.com/benphelps/homepage/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homepage.png",
+      "image": "ghcr.io/benphelps/homepage:latest",
+      "categories": [
+        "Dashboard"
+      ],
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/homepage",
+          "container": "/app/config"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock:ro"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "jackett",
+      "title": "Jackett",
+      "description": "Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. <a href='https://github.com/Jackett/Jackett/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jackett.png",
+      "image": "linuxserver/jackett:latest",
+      "categories": [
+        "Downloaders",
+        "Tools"
+      ],
+      "ports": [
+        "9117:9117/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/jackett",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/downloads"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Librespeed",
+      "name": "librespeed",
+      "note": "",
+      "description": "Librespeed is a very lightweight speed test implemented in Javascript, using XMLHttpRequest and Web Workers. No Flash, No Java, No Websocket, No Bullshit. <a href='https://github.com/librespeed/speedtest/'>Github</a>",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/librespeed.png",
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "image": "adolfintel/speedtest",
+      "ports": [
+        "81:81/tcp"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "description": "Specify a timezone to use for example Europe/Amsterdam"
+        },
+        {
+          "name": "MODE",
+          "label": "MODE",
+          "default": "standalone",
+          "description": "Set the mode."
+        },
+        {
+          "name": "PASSWORD",
+          "label": "PASSWORD",
+          "default": "SOMEPASSWORD",
+          "description": "Password to access the stats page. If not set, stats page will not allow accesses."
+        },
+        {
+          "name": "WEBPORT",
+          "label": "WEBPORT",
+          "default": "81",
+          "description": "Allows choosing a custom port for the included web server."
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "name": "ombi",
+      "title": "Ombi",
+      "description": "Ombi allows you to host your own Plex Request and user management system. If you are sharing your Plex server with other users, allow them to request new content using an easy to manage interface. . [There is no official Ombi docker image. This one is created and maintained by <a href='https://hub.docker.com/r/linuxserver/ombi/' target='_blank'>linuxserver.io</a>]",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/ombi.png",
+      "image": "linuxserver/ombi:latest",
+      "categories": [
+        "Tools"
+      ],
+      "ports": [
+        "3579:3579/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/ombi",
+          "container": "/config"
+        },
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime:ro"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "overseerr",
+      "title": "Overseerr",
+      "description": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem. <a href='https://overseerr.dev/' target='_blank'>Official Site</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/overseerr.png",
+      "image": "sctx/overseerr:latest",
+      "categories": [
+        "Multimedia",
+        "Tools"
+      ],
+      "ports": [
+        "5055/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/overseerr",
+          "container": "/app/config"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime:ro",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "prowlarr",
+      "title": "Prowlarr",
+      "description": "Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. ",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/prowlarr.png",
+      "image": "ghcr.io/linuxserver/prowlarr:develop",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "9696/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/prowlarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/etc/localtime:ro",
+          "container": "/etc/localtime:ro",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "radarr",
+      "title": "Radarr",
+      "note": "There is no official Radarr docker image. This one is created and maintained by <a href='https://hotio.dev/containers/radarr/' target='_blank'>Hotio.dev</a>",
+      "description": "Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. <a href='https://radarr.video/#downloads-v3-docker' target='_blank'>Official Site</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/radarr.png",
+      "image": "ghcr.io/hotio/radarr",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "network": "AppBridge",
+      "ports": [
+        "7878:7878/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/radarr",
+          "container": "/config"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "readarr",
+      "title": "Readarr",
+      "description": "Readarr is an ebook and audiobook collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new books from your favorite authors and will grab, sort, and rename them.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/readarr.png",
+      "image": "ghcr.io/linuxserver/readarr:nightly",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "8787/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/readarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/media/downloads/ebooks",
+          "container": "/downloads"
+        },
+        {
+          "container": "/books",
+          "bind": "/media/storage/ebooks"
+        },
+        {
+          "container": "/blackhole",
+          "bind": "/media/temp/blackhole/ebooks"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "scrutiny",
+      "title": "Scrutiny",
+      "description": "WebUI for smartd S.M.A.R.T monitoring",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/scrutiny.png",
+      "image": "analogj/scrutiny:latest",
+      "categories": [
+        "Monitoring"
+      ],
+      "ports": [
+        "8080/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/scrutiny/config/",
+          "bind": "/opt/mediadepot/apps/scrutiny"
+        },
+        {
+          "container": "/run/udev",
+          "bind": "/run/udev",
+          "readonly": true
+        }
+      ],
+      "env": [],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.scrutiny.loadbalancer.server.port",
+          "value": "8080"
+        },
+        {
+          "name": "traefik.http.routers.scrutiny.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.scrutiny.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "sonarr",
+      "title": "Sonarr",
+      "description": "Sonarr is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/sonarr.png",
+      "image": "linuxserver/sonarr:latest",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "8989/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/opt/mediadepot/apps/sonarr"
+        },
+        {
+          "container": "/downloads",
+          "bind": "/media/storage/downloads/tvshows"
+        },
+        {
+          "container": "/tv",
+          "bind": "/media/storage/tvshows"
+        },
+        {
+          "container": "/blackhole",
+          "bind": "/media/temp/blackhole/tvshows"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.sonarr.loadbalancer.server.port",
+          "value": "8989"
+        },
+        {
+          "name": "traefik.http.routers.sonarr.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.sonarr.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "tautulli",
+      "title": "Tautulli",
+      "description": "A Python based monitoring and tracking tool for Plex Media Server.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/tautulli.png",
+      "image": "linuxserver/tautulli:latest",
+      "categories": [
+        "MediaServer:Other",
+        "Tools"
+      ],
+      "ports": [
+        "8181/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/opt/mediadepot/apps/tautulli"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.tautulli.loadbalancer.server.port",
+          "value": "8181"
+        },
+        {
+          "name": "traefik.http.routers.tautulli.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.tautulli.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "watchtower",
+      "title": "Watchtower",
+      "description": "Automatically update running Docker containers",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/watchtower.png",
+      "image": "containrrr/watchtower:latest",
+      "command": "--cleanup --label-enable",
+      "categories": [
+        "Monitoring"
+      ],
+      "volumes": [
+        {
+          "container": "/var/run/docker.sock",
+          "bind": "/var/run/docker.sock"
+        }
+      ],
+      "env": []
+    },
+    {
+      "type": 1,
+      "name": "wizarr",
+      "title": "Wizarr",
+      "description": "Wizarr is an advanced user invitation and management system for Jellyfin, Plex, Emby etc. ",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wizarr.png",
+      "image": "ghcr.io/wizarrrr/wizarr",
+      "categories": [
+        "Arr"
+      ],
+      "ports": [
+        "5690/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wizarr",
+          "container": "/data/database"
+        },
+        {
+          "bind": "/etc/localtime:ro",
+          "container": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "APP_URL",
+          "label": "APP_URL",
+          "default": "https://wizarr.domain.com"
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.wizarr.loadbalancer.server.port",
+          "value": "5690"
+        },
+        {
+          "name": "traefik.http.routers.wizarr.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.wizarr.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, \u00ef\u00bf\u00bdTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/transmission:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/transmission.png",
+      "platform": "linux",
+      "ports": [
+        "9091/tcp",
+        "51413/tcp",
+        "51413/udp"
+      ],
+      "title": "Transmission",
+      "name": "transmission",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/transmission",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/downloads"
+        },
+        {
+          "container": "/watch"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Video"
+      ],
+      "description": "Headless installation of Kodi.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/kodi-headless:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/kodi.png",
+      "platform": "linux",
+      "ports": [
+        "8080/tcp",
+        "9777/udp"
+      ],
+      "title": "Kodi-Headless",
+      "name": "kodi-headless",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config/.kodi"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Backup:",
+        "Cloud",
+        "Other",
+        "Tools"
+      ],
+      "description": "Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it's transmitted over the Internet.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/syncthing:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/syncthing.png",
+      "platform": "linux",
+      "ports": [
+        "8384/tcp",
+        "21027/udp",
+        "22000/tcp"
+      ],
+      "title": "Syncthing",
+      "name": "syncthing",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/sync"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mysql",
+      "title": "MySQL",
+      "note": "",
+      "description": "MySQL is the world's most popular open source database. With its proven performance, reliability and ease-of-use, MySQL has become the leading database choice for web-based applications, covering the entire range from personal projects and websites, via e-commerce and information services, all the way to high profile web properties including Facebook, Twitter, YouTube, Yahoo! and many more. <a href='https://www.mysql.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mysql' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mysql.png",
+      "image": "mysql",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mysql",
+          "container": "/var/lib/mysql",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "MYSQL_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MySQL",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "Deluge is a lightweight, Free Software, cross-platform BitTorrent client providing: Full Encryption, WebUI, Plugin System, Much more...",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "lscr.io/linuxserver/deluge:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/deluge.png",
+      "platform": "linux",
+      "title": "Deluge",
+      "name": "deluge",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        }
+      ],
+      "ports": [
+        "8112/tcp",
+        "6881/tcp",
+        "6881/udp"
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Proxy"
+      ],
+      "description": "Nginx is a web server with a strong focus on high concurrency, performance and low memory usage. It can also act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/nginx:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nginx.png",
+      "platform": "linux",
+      "ports": [
+        "80/tcp",
+        "443/tcp"
+      ],
+      "title": "Nginx",
+      "name": "nginx",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "description": "Lidarr is a music collection manager for Usenet and BitTorrent users.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/lidarr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/lidarr.png",
+      "platform": "linux",
+      "ports": [
+        "8686/tcp"
+      ],
+      "title": "Lidarr",
+      "name": "lidarr",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        },
+        {
+          "container": "/music"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "The qBittorrent project aims to provide an open-source software alternative to \u00ef\u00bf\u00bdTorrent. qBittorrent is based on the Qt toolkit and libtorrent-rasterbar library.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/qbittorrent:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/qbittorrent.png",
+      "platform": "linux",
+      "ports": [
+        "6881/tcp",
+        "6881/udp",
+        "8080/tcp"
+      ],
+      "title": "qbittorrent",
+      "name": "qbittorrent",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "OpenVPN Access Server is a full featured secure network tunneling VPN software solution that integrates OpenVPN server capabilities, enterprise management capabilities, simplified OpenVPN Connect UI, and OpenVPN Client software packages that accommodate Windows, MAC, Linux, Android, and iOS environments.",
+      "env": [
+        {
+          "label": "INTERFACE",
+          "name": "INTERFACE",
+          "set": "eth0"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/openvpn-as:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/openvpn.png",
+      "platform": "linux",
+      "ports": [
+        "943/tcp",
+        "9443/tcp",
+        "1194/udp"
+      ],
+      "name": "openvpn-as",
+      "title": "OpenVPN Access Server",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "Server version of minetest, a free, open source alternative to minecraft.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/minetest:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/minetest.png",
+      "platform": "linux",
+      "ports": [
+        "30000/udp"
+      ],
+      "title": "Minetest",
+      "name": "minetest",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config/.minetest"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Media"
+      ],
+      "description": "Airsonic is a free, web-based media streamer, providing ubiqutious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "CONTEXT_PATH",
+          "name": "CONTEXT_PATH",
+          "set": "airsonic"
+        },
+        {
+          "label": "JAVA_OPTS",
+          "name": "JAVA_OPTS",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/airsonic:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/airsonic.png",
+      "platform": "linux",
+      "ports": [
+        "4040/tcp"
+      ],
+      "name": "airsonic",
+      "title": "Airsonic",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/music"
+        },
+        {
+          "container": "/playlists"
+        },
+        {
+          "container": "/podcasts"
+        },
+        {
+          "container": "/media"
+        },
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Web"
+      ],
+      "description": "This container sets up an Nginx webserver and reverse proxy with php support and a built-in letsencrypt client that automates free SSL server certificate generation and renewal processes. It also contains fail2ban for intrusion prevention.\r\n  \r\n  Before running this container, make sure that the url and subdomains are properly forwarded to this container's host.\r\n  \r\n  - Port 443 on the internet side of the router should be forwarded to this container's port 443.\r\n  - If you need a dynamic dns provider, you can use the free provider duckdns.org where the url will be yoursubdomain.duckdns.org and the subdomains    can be www,ftp,cloud\r\n  - The container detects changes to url and subdomains, revokes existing certs and generates new ones during start. \r\n  - It also detects changes to the DHLEVEL parameter and replaces the dhparams file.\r\n  \r\n  - If you'd like to password protect your sites, you can use htpasswd. Run the following command on your host to generate the htpasswd file docker exec -it letsencrypt htpasswd -c /config/nginx/.htpasswd &lt;username&gt;",
+      "env": [
+        {
+          "label": "EMAIL",
+          "name": "EMAIL",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "URL",
+          "name": "URL",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "SUBDOMAINS",
+          "name": "SUBDOMAINS",
+          "set": "www,"
+        },
+        {
+          "label": "ONLY_SUBDOMAINS",
+          "name": "ONLY_SUBDOMAINS",
+          "set": "false"
+        },
+        {
+          "label": "DHLEVEL",
+          "name": "DHLEVEL",
+          "set": "2048"
+        },
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "VALIDATION",
+          "name": "VALIDATION",
+          "set": "http"
+        },
+        {
+          "label": "DNSPLUGIN",
+          "name": "DNSPLUGIN",
+          "set": "http"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/letsencrypt:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/letsencrypt.png",
+      "platform": "linux",
+      "ports": [
+        "80/tcp",
+        "443/tcp"
+      ],
+      "title": "Let's Encrypt",
+      "name": "letsencrypt",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Cloud",
+        "Productivity",
+        "Tools",
+        "Other",
+        "Web"
+      ],
+      "description": "Nextcloud is an open source, self-hosted file sync and communication app platform. Access and sync your files, contacts, calendars and communicate and collaborate across your devices. You decide what happens with your data, where it is and who can access it!",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/nextcloud:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nextcloud.png",
+      "platform": "linux",
+      "ports": [
+        "443/tcp"
+      ],
+      "title": "Nextcloud",
+      "name": "nextcloud",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Apprise-api",
+      "name": "apprise-api",
+      "note": "",
+      "description": "Apprise-api takes advantage of Apprise through your network with a user-friendly API. * Send notifications to more then 65+ services. * An incredibly lightweight gateway to Apprise. * A production ready micro-service at your disposal. Apprise API was designed to easily fit into existing (and new) eco-systems that are looking for a simple notification solution.",
+      "categories": [
+        "Tools",
+        "Development"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/apprise-api.png",
+      "image": "linuxserver/apprise-api:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "8000:8000/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/apprise-api"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Audacity",
+      "name": "audacity",
+      "note": "",
+      "description": "Audacity is an easy-to-use, multi-track audio editor and recorder. Developed by a group of volunteers as open source. (https://www.audacityteam.org/)",
+      "platform": "linux",
+      "categories": [
+        "Media"
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/audacity.png",
+      "image": "linuxserver/audacity:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/audacity/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Bazarr",
+      "name": "bazarr",
+      "note": "",
+      "description": "Bazarr is a companion application to Sonarr and Radarr. It can manage and download subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.",
+      "categories": [
+        "Multimedia",
+        "Downloader",
+        "Arr"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/bazarr.png",
+      "image": "linuxserver/bazarr:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "6767:6767/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/bazarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/home/docker/bazarr/movies",
+          "container": "/movies"
+        },
+        {
+          "container": "/tv",
+          "bind": "/home/docker/bazarr/tv"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Changedetection.io",
+      "name": "changedetection.io",
+      "note": "",
+      "description": "Changedetection.io provides free, open-source web page monitoring, notification and change detection. (https://github.com/dgtlmoon/changedetection.io)",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/changedetection.io.png",
+      "image": "linuxserver/changedetection.io:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "BASE_URL",
+          "label": "BASE_URL",
+          "default": "",
+          "description": "Specify the full URL (including protocol) when running behind a reverse proxy"
+        }
+      ],
+      "ports": [
+        "5000:5000/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/changedetection.io"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Code-server",
+      "name": "code-server",
+      "note": "",
+      "description": "Code-server is VS Code running on a remote server, accessible through the browser. - Code on your Chromebook, tablet, and laptop with a consistent dev environment. - If you have a Windows or Mac workstation, more easily develop for Linux. - Take advantage of large cloud servers to speed up tests, compilations, downloads, and more. - Preserve battery life when you're on the go. - All intensive computation runs on your server. - You're no longer running excess instances of Chrome. (https://coder.com)",
+      "categories": [
+        "Development"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/code-server.png",
+      "image": "linuxserver/code-server:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000"
+
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "PASSWORD",
+          "label": "PASSWORD",
+          "default": "password",
+          "description": "Optional web gui password, if `PASSWORD` or `HASHED_PASSWORD` is not provided, there will be no auth."
+        },
+        {
+          "name": "HASHED_PASSWORD",
+          "label": "HASHED_PASSWORD",
+          "default": "",
+          "description": "Optional web gui password, overrides `PASSWORD`, instructions on how to create it is below."
+        },
+        {
+          "name": "SUDO_PASSWORD",
+          "label": "SUDO_PASSWORD",
+          "default": "password",
+          "description": "If this optional variable is set, user will have sudo access in the code-server terminal with the specified password."
+        },
+        {
+          "name": "SUDO_PASSWORD_HASH",
+          "label": "SUDO_PASSWORD_HASH",
+          "default": "",
+          "description": "Optionally set sudo password via hash (takes priority over `SUDO_PASSWORD` var). Format is `$type$salt$hashed`."
+        },
+        {
+          "name": "PROXY_DOMAIN",
+          "label": "PROXY_DOMAIN",
+          "default": "code-server.mydomain",
+          "description": "If this optional variable is set, this domain will be proxied for subdomain proxying. See [Documentation](https://github.com/cdr/code-server/blob/master/docs/FAQ.md#sub-domains)"
+        },
+        {
+          "name": "DEFAULT_WORKSPACE",
+          "label": "DEFAULT_WORKSPACE",
+          "default": "/config/workspace",
+          "description": "If this optional variable is set, code-server will open this directory by default"
+        }
+      ],
+      "ports": [
+        "8443:8443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/code-server/config",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Dokuwiki",
+      "name": "dokuwiki",
+      "note": "",
+      "description": "Dokuwiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database. It is loved by users for its clean and readable syntax. The ease of maintenance, backup and integration makes it an administrator's favorite. Built in access controls and authentication connectors make DokuWiki especially useful in the enterprise context and the large number of plugins contributed by its vibrant community allow for a broad range of use cases beyond a traditional wiki.  (https://www.dokuwiki.org/dokuwiki/)",
+      "categories": [
+        "Productivity",
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dokuwiki.png",
+      "image": "linuxserver/dokuwiki:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/dokuwiki"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Doplarr",
+      "name": "doplarr",
+      "note": "",
+      "description": "Doplarr (https://github.com/kiranshila/Doplarr) is an *arr request bot for Discord.'",
+      "categories": [
+        "Arr"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/doplarr.png",
+      "image": "linuxserver/doplarr:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "DISCORD__TOKEN",
+          "label": "DISCORD__TOKEN",
+          "default": "",
+          "description": "Specify your discord bot token."
+        },
+        {
+          "name": "OVERSEERR__API",
+          "label": "OVERSEERR__API",
+          "default": "",
+          "description": "Specify your Overseerr API key. Leave blank if using Radarr/Sonarr."
+        },
+        {
+          "name": "OVERSEERR__URL",
+          "label": "OVERSEERR__URL",
+          "default": "http://localhost:5055",
+          "description": "Specify your Overseerr URL. Leave blank if using Radarr/Sonarr."
+        },
+        {
+          "name": "RADARR__API",
+          "label": "RADARR__API",
+          "default": "",
+          "description": "Specify your Radarr API key. Leave blank if using Overseerr."
+        },
+        {
+          "name": "RADARR__URL",
+          "label": "RADARR__URL",
+          "default": "http://localhost:7878",
+          "description": "Specify your Radarr URL. Leave blank if using Overseerr."
+        },
+        {
+          "name": "SONARR__API",
+          "label": "SONARR__API",
+          "default": "",
+          "description": "Specify your Sonarr API key. Leave blank if using Overseerr."
+        },
+        {
+          "name": "SONARR__URL",
+          "label": "SONARR__URL",
+          "default": "http://localhost:8989",
+          "description": "Specify your Sonarr URL. Leave blank if using Overseerr."
+        },
+        {
+          "name": "DISCORD__MAX_RESULTS",
+          "label": "DISCORD__MAX_RESULTS",
+          "default": "25",
+          "description": "Sets the maximum size of the search results selection"
+        },
+        {
+          "name": "DISCORD__REQUESTED_MSG_STYLE",
+          "label": "DISCORD__REQUESTED_MSG_STYLE",
+          "default": ":plain",
+          "description": "Sets the style of the request alert message. One of `:plain` `:embed` `:none`"
+        },
+        {
+          "name": "SONARR__QUALITY_PROFILE",
+          "label": "SONARR__QUALITY_PROFILE",
+          "default": "",
+          "description": "The name of the quality profile to use by default for Sonarr"
+        },
+        {
+          "name": "RADARR__QUALITY_PROFILE",
+          "label": "RADARR__QUALITY_PROFILE",
+          "default": "",
+          "description": "The name of the quality profile to use by default for Radarr"
+        },
+        {
+          "name": "SONARR__ROOTFOLDER",
+          "label": "SONARR__ROOTFOLDER",
+          "default": "",
+          "description": "The root folder to use by default for Sonarr"
+        },
+        {
+          "name": "RADARR__ROOTFOLDER",
+          "label": "RADARR__ROOTFOLDER",
+          "default": "",
+          "description": "The root folder to use by default for Radarr"
+        },
+        {
+          "name": "SONARR__LANGUAGE_PROFILE",
+          "label": "SONARR__LANGUAGE_PROFILE",
+          "default": "",
+          "description": "The name of the language profile to use by default for Sonarr"
+        },
+        {
+          "name": "OVERSEERR__DEFAULT_ID",
+          "label": "OVERSEERR__DEFAULT_ID",
+          "default": "",
+          "description": "The Overseerr user id to use by default if there is no associated discord account for the requester"
+        },
+        {
+          "name": "PARTIAL_SEASONS",
+          "label": "PARTIAL_SEASONS",
+          "default": "true",
+          "description": "Sets whether users can request partial seasons."
+        },
+        {
+          "name": "LOG_LEVEL",
+          "label": "LOG_LEVEL",
+          "default": ":info",
+          "description": "The log level for the logging backend. This can be changed for debugging purposes. One of trace `:debug` `:info` `:warn` `:error` `:fatal` `:report`"
+        },
+        {
+          "name": "JAVA_OPTS",
+          "label": "JAVA_OPTS",
+          "default": "",
+          "description": "For passing additional java options."
+        }
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/doplarr"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "EmbyStat",
+      "name": "embystat",
+      "note": "",
+      "description": "[Embystat](https://github.com/mregni/EmbyStat) is a personal web server that can calculate all kinds of statistics from your (local) Emby server. Just install this on your server and let him calculate all kinds of fun stuff.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/embystat.png",
+      "image": "linuxserver/embystat:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "6555:6555/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/embystat"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Emulatorjs",
+      "name": "emulatorjs",
+      "note": "",
+      "description": "Emulatorjs - In browser web based emulation portable to nearly any device for many retro consoles. A mix of emulators is used between Libretro and EmulatorJS.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/emulatorjs.png",
+      "image": "linuxserver/emulatorjs:latest",
+      "categories": [
+        "Gaming"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "SUBFOLDER",
+          "label": "SUBFOLDER",
+          "default": "/",
+          "description": "Specify a subfolder for reverse proxies IE '/FOLDER/'"
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "80:80/tcp",
+        "4001:4001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/emulatorjs"
+        },
+        {
+          "container": "/data",
+          "bind": "/home/docker/emulatorjs/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Fail2ban",
+      "name": "fail2ban",
+      "note": "",
+      "description": "Fail2ban is a daemon to ban hosts that cause multiple authentication errors.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/fail2ban.png",
+      "image": "linuxserver/fail2ban:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "VERBOSITY",
+          "label": "VERBOSITY",
+          "default": "-vv",
+          "description": "Set the container log verbosity. Valid options are -v, -vv, -vvv, -vvvv, or leaving the value blank or not setting the variable."
+        }
+      ],
+      "ports": [
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/fail2ban/config"
+        },
+        {
+          "container": "/var/log:ro",
+          "bind": "/home/docker/fail2ban/var/log:ro"
+        },
+        {
+          "container": "/remotelogs/airsonic:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/airsonic:ro"
+        },
+        {
+          "container": "/remotelogs/apache2:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/apache2:ro"
+        },
+        {
+          "container": "/remotelogs/authelia:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/authelia:ro"
+        },
+        {
+          "container": "/remotelogs/emby:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/emby:ro"
+        },
+        {
+          "container": "/remotelogs/filebrowser:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/filebrowser:ro"
+        },
+        {
+          "container": "/remotelogs/homeassistant:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/homeassistant:ro"
+        },
+        {
+          "container": "/remotelogs/lighttpd:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/lighttpd:ro"
+        },
+        {
+          "container": "/remotelogs/nextcloud:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nextcloud:ro"
+        },
+        {
+          "container": "/remotelogs/nginx:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nginx:ro"
+        },
+        {
+          "container": "/remotelogs/nzbget:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nzbget:ro"
+        },
+        {
+          "container": "/remotelogs/overseerr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/overseerr:ro"
+        },
+        {
+          "container": "/remotelogs/prowlarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/prowlarr:ro"
+        },
+        {
+          "container": "/remotelogs/radarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/radarr:ro"
+        },
+        {
+          "container": "/remotelogs/sabnzbd:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/sabnzbd:ro"
+        },
+        {
+          "container": "/remotelogs/sonarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/sonarr:ro"
+        },
+        {
+          "container": "/remotelogs/unificontroller:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/unificontroller:ro"
+        },
+        {
+          "container": "/remotelogs/vaultwarden:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/vaultwarden:ro"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Homeassistant",
+      "name": "homeassistant",
+      "note": "",
+      "description": "Home Assistant Core is an open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server.",
+      "categories": [
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homeassistant.png",
+      "image": "linuxserver/homeassistant:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "8123:8123/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/homeassistant"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Jellyfin",
+      "name": "jellyfin",
+      "note": "",
+      "description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jellyfin.png",
+      "image": "linuxserver/jellyfin:latest",
+      "categories": [
+        "Media Server",
+        "LDAP"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "JELLYFIN_PublishedServerUrl",
+          "label": "JELLYFIN_PublishedServerUrl",
+          "default": "192.168.0.5",
+          "description": "Set the autodiscovery response domain or IP address."
+        }
+      ],
+      "network": "AppBridge",
+      "ports": [
+        "8096:8096/tcp",
+        "8920:8920/tcp",
+        "7359:7359/udp",
+        "1900:1900/udp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/jellyfin",
+          "container": "/config"
+        },
+        {
+          "bind": "/media/tvshows",
+          "container": "/data/tvshows"
+        },
+        {
+          "bind": "/media/movies",
+          "container": "/data/movies"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Kasm",
+      "name": "kasm",
+      "note": "",
+      "description": "Kasm workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services. Kasm uses devops-enabled Containerized Desktop Infrastructure (CDI) to create on-demand, disposable, docker containers that are accessible via web browser. Example use-cases include Remote Browser Isolation (RBI), Data Loss Prevention (DLP), Desktop as a Service (DaaS), Secure Remote Access Services (RAS), and Open Source Intelligence (OSINT) collections. The rendering of the graphical-based containers is powered by the open-source project [KasmVNC](https://www.kasmweb.com/kasmvnc.html?utm_campaign=LinuxServer&utm_source=kasmvnc).",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/kasm.png",
+      "image": "linuxserver/kasm:latest",
+      "privileged": true,
+      "env": [
+        {
+          "name": "KASM_PORT",
+          "label": "KASM_PORT",
+          "default": "443",
+          "description": "Specify the port you bind to the outside for Kasm Workspaces."
+        },
+        {
+          "name": "DOCKER_HUB_USERNAME",
+          "label": "DOCKER_HUB_USERNAME",
+          "default": "USER",
+          "description": "Optionally specify a DockerHub Username to pull private images."
+        },
+        {
+          "name": "DOCKER_HUB_PASSWORD",
+          "label": "DOCKER_HUB_PASSWORD",
+          "default": "PASS",
+          "description": "Optionally specify a DockerHub password to pull private images."
+        },
+        {
+          "name": "DOCKER_MTU",
+          "label": "DOCKER_MTU",
+          "default": "1500",
+          "description": "Optionally specify the mtu options passed to dockerd."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/kasm/opt",
+          "container": "/opt"
+        },
+        {
+          "bind": "/home/docker/kasm/profiles",
+          "container": "/profiles"
+        },
+        {
+          "bind": "/dev/input",
+          "container": "/dev/input"
+        },
+        {
+          "bind": "/run/udev/data",
+          "container": "/run/udev/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Libreoffice",
+      "name": "libreoffice",
+      "note": "",
+      "description": "LibreOffice is a free and powerful office suite, and a successor to OpenOffice.org (commonly known as OpenOffice). Its clean interface and feature-rich tools help you unleash your creativity and enhance your productivity.",
+      "categories": [
+        "Productivity",
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/libreoffice.png",
+      "image": "linuxserver/libreoffice:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/libreoffice/config",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Mastodon",
+      "name": "mastodon",
+      "note": "",
+      "description": "Mastodon is a free, open-source social network server based on ActivityPub where users can follow friends and discover new ones. (https://github.com/mastodon/mastodon/)",
+      "categories": [
+        "Communication"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mastodon.png",
+      "image": "linuxserver/mastodon:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "LOCAL_DOMAIN",
+          "label": "LOCAL_DOMAIN",
+          "default": "example.com",
+          "description": "This is the unique identifier of your server in the network. It cannot be safely changed later."
+        },
+        {
+          "name": "REDIS_HOST",
+          "label": "REDIS_HOST",
+          "default": "redis",
+          "description": "Redis server hostname"
+        },
+        {
+          "name": "REDIS_PORT",
+          "label": "REDIS_PORT",
+          "default": "6379",
+          "description": "Redis port"
+        },
+        {
+          "name": "DB_HOST",
+          "label": "DB_HOST",
+          "default": "db",
+          "description": "Postgres database hostname"
+        },
+        {
+          "name": "DB_USER",
+          "label": "DB_USER",
+          "default": "mastodon",
+          "description": "Postgres username"
+        },
+        {
+          "name": "DB_NAME",
+          "label": "DB_NAME",
+          "default": "mastodon",
+          "description": "Postgres db name"
+        },
+        {
+          "name": "DB_PASS",
+          "label": "DB_PASS",
+          "default": "mastodon",
+          "description": "Postgres password"
+        },
+        {
+          "name": "DB_PORT",
+          "label": "DB_PORT",
+          "default": "5432",
+          "description": "Portgres port"
+        },
+        {
+          "name": "ES_ENABLED",
+          "label": "ES_ENABLED",
+          "default": "false",
+          "description": "Enable or disable Elasticsearch (requires a separate ES instance)"
+        },
+        {
+          "name": "SECRET_KEY_BASE",
+          "label": "SECRET_KEY_BASE",
+          "default": "",
+          "description": "Browser session secret. Changing it will break all active browser sessions."
+        },
+        {
+          "name": "OTP_SECRET",
+          "label": "OTP_SECRET",
+          "default": "",
+          "description": "MFA secret. Changing it will break two-factor authentication."
+        },
+        {
+          "name": "VAPID_PRIVATE_KEY",
+          "label": "VAPID_PRIVATE_KEY",
+          "default": "",
+          "description": "Push notification private key. Changing it will break push notifications."
+        },
+        {
+          "name": "VAPID_PUBLIC_KEY",
+          "label": "VAPID_PUBLIC_KEY",
+          "default": "",
+          "description": "Push notification public key. Changing it will break push notifications."
+        },
+        {
+          "name": "SMTP_SERVER",
+          "label": "SMTP_SERVER",
+          "default": "mail.example.com",
+          "description": "SMTP server for email notifications"
+        },
+        {
+          "name": "SMTP_PORT",
+          "label": "SMTP_PORT",
+          "default": "25",
+          "description": "SMTP server port"
+        },
+        {
+          "name": "SMTP_LOGIN",
+          "label": "SMTP_LOGIN",
+          "default": "",
+          "description": "SMTP username"
+        },
+        {
+          "name": "SMTP_PASSWORD",
+          "label": "SMTP_PASSWORD",
+          "default": "",
+          "description": "SMTP password"
+        },
+        {
+          "name": "SMTP_FROM_ADDRESS",
+          "label": "SMTP_FROM_ADDRESS",
+          "default": "notifications@example.com",
+          "description": "From address for emails send from Mastodon"
+        },
+        {
+          "name": "S3_ENABLED",
+          "label": "S3_ENABLED",
+          "default": "false",
+          "description": "Enable or disable S3 storage of uploaded files"
+        },
+        {
+          "name": "WEB_DOMAIN",
+          "label": "WEB_DOMAIN",
+          "default": "mastodon.example.com",
+          "description": "This can be set if you want your server identifier to be different to the subdomain hosting Mastodon. See [https://docs.joinmastodon.org/admin/config/#basic](https://docs.joinmastodon.org/admin/config/#basic)"
+        },
+        {
+          "name": "ES_HOST",
+          "label": "ES_HOST",
+          "default": "es",
+          "description": "Elasticsearch server hostname"
+        },
+        {
+          "name": "ES_PORT",
+          "label": "ES_PORT",
+          "default": "9200",
+          "description": "Elasticsearch port"
+        },
+        {
+          "name": "ES_USER",
+          "label": "ES_USER",
+          "default": "elastic",
+          "description": "Elasticsearch username"
+        },
+        {
+          "name": "ES_PASS",
+          "label": "ES_PASS",
+          "default": "elastic",
+          "description": "Elasticsearch password"
+        },
+        {
+          "name": "S3_BUCKET",
+          "label": "S3_BUCKET",
+          "default": "",
+          "description": "S3 bucket hostname"
+        },
+        {
+          "name": "AWS_ACCESS_KEY_ID",
+          "label": "AWS_ACCESS_KEY_ID",
+          "default": "",
+          "description": "S3 bucket access key ID"
+        },
+        {
+          "name": "AWS_SECRET_ACCESS_KEY",
+          "label": "AWS_SECRET_ACCESS_KEY",
+          "default": "",
+          "description": "S3 bucket secret access key"
+        },
+        {
+          "name": "S3_ALIAS_HOST",
+          "label": "S3_ALIAS_HOST",
+          "default": "",
+          "description": "Alternate hostname for object fetching if you are front the S3 connections."
+        },
+        {
+          "name": "SIDEKIQ_ONLY",
+          "label": "SIDEKIQ_ONLY",
+          "default": "false",
+          "description": "Only run the sidekiq service in this container instance. For large scale instances that need better queue handling."
+        },
+        {
+          "name": "SIDEKIQ_QUEUE",
+          "label": "SIDEKIQ_QUEUE",
+          "default": "",
+          "description": "The name of the sidekiq queue to run in this container. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-queues)."
+        },
+        {
+          "name": "SIDEKIQ_DEFAULT",
+          "label": "SIDEKIQ_DEFAULT",
+          "default": "false",
+          "description": "Set to `true` on the main container if you're running additional sidekiq instances. It will run the `default` queue."
+        },
+        {
+          "name": "SIDEKIQ_THREADS",
+          "label": "SIDEKIQ_THREADS",
+          "default": "5",
+          "description": "The number of threads for sidekiq to use. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-threads)."
+        },
+        {
+          "name": "DB_POOL",
+          "label": "DB_POOL",
+          "default": "5",
+          "description": "The size of the DB connection pool, must be *at least* the same as `SIDEKIQ_THREADS`. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-threads)."
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/mastodon/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Phpmyadmin",
+      "name": "phpmyadmin",
+      "note": "",
+      "description": "Phpmyadmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB.",
+      "categories": [
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/phpmyadmin.png",
+      "image": "linuxserver/phpmyadmin:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "PMA_ARBITRARY",
+          "label": "PMA_ARBITRARY",
+          "default": "1",
+          "description": "Set to `1` to allow you to connect to any server. Setting to `0` will only allow you to connect to specified hosts (See Application Setup)"
+        },
+        {
+          "name": "PMA_ABSOLUTE_URI",
+          "label": "PMA_ABSOLUTE_URI",
+          "default": "https://phpmyadmin.example.com",
+          "description": "Set the URL you will use to access the web frontend"
+        }
+      ],
+      "ports": [
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/phpmyadmin/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Pidgin",
+      "name": "pidgin",
+      "note": "",
+      "description": "Pidgin is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pidgin.png",
+      "image": "linuxserver/pidgin:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/pidgin",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Remmina",
+      "name": "remmina",
+      "note": "",
+      "description": "Remmina is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/remmina.png",
+      "image": "linuxserver/remmina:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/remmina/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Sqlitebrowser",
+      "name": "sqlitebrowser",
+      "note": "",
+      "description": "DB Browser for SQLite is a high quality, visual, open source tool to create, design, and edit database files compatible with SQLite.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/sqlitebrowser.png",
+      "image": "linuxserver/sqlitebrowser:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/sqlitebrowser/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Swag",
+      "name": "swag",
+      "note": "",
+      "description": "SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt\u2122) sets up an Nginx webserver and reverse proxy with php support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/linuxserverio.png",
+      "image": "linuxserver/swag:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "URL",
+          "label": "URL",
+          "default": "yourdomain.url",
+          "description": "Top url you have control over (`customdomain.com` if you own it, or `customsubdomain.ddnsprovider.com` if dynamic dns)."
+        },
+        {
+          "name": "VALIDATION",
+          "label": "VALIDATION",
+          "default": "http",
+          "description": "Certbot validation method to use, options are `http` or `dns` (`dns` method also requires `DNSPLUGIN` variable set)."
+        },
+        {
+          "name": "SUBDOMAINS",
+          "label": "SUBDOMAINS",
+          "default": "www,",
+          "description": "Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only)"
+        },
+        {
+          "name": "CERTPROVIDER",
+          "label": "CERTPROVIDER",
+          "default": "",
+          "description": "Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt."
+        },
+        {
+          "name": "DNSPLUGIN",
+          "label": "DNSPLUGIN",
+          "default": "cloudflare",
+          "description": "Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `godaddy`, `google`, `google-domains`, `he`, `hetzner`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`."
+        },
+        {
+          "name": "PROPAGATION",
+          "label": "PROPAGATION",
+          "default": "",
+          "description": "Optionally override (in seconds) the default propagation time for the dns plugins."
+        },
+        {
+          "name": "EMAIL",
+          "label": "EMAIL",
+          "default": "",
+          "description": "Optional e-mail address used for cert expiration notifications (Required for ZeroSSL)."
+        },
+        {
+          "name": "ONLY_SUBDOMAINS",
+          "label": "ONLY_SUBDOMAINS",
+          "default": "false",
+          "description": "If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true`"
+        },
+        {
+          "name": "EXTRA_DOMAINS",
+          "label": "EXTRA_DOMAINS",
+          "default": "",
+          "description": "Additional fully qualified domain names (comma separated, no spaces) ie. `extradomain.com,subdomain.anotherdomain.org,*.anotherdomain.org`"
+        },
+        {
+          "name": "STAGING",
+          "label": "STAGING",
+          "default": "false",
+          "description": "Set to `true` to retrieve certs in staging mode. Rate limits will be much higher, but the resulting cert will not pass the browser's security test. Only to be used for testing purposes."
+        }
+      ],
+      "ports": [
+        "443:443/tcp",
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/swag",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wikijs",
+      "name": "wikijs",
+      "note": "Setup mysql, postgres, mariadb, mssql database first, or use the default sqlite.",
+      "description": "Wikijs is a modern, lightweight and powerful wiki app built on NodeJS. (https://wiki.js.org/)",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wikijs.png",
+      "image": "linuxserver/wikijs:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "DB_TYPE",
+          "label": "DB_TYPE",
+          "default": "sqlite",
+          "description": "Type of database (mysql, postgres, mariadb, mssql or sqlite)"
+        },
+        {
+          "name": "DB_HOST",
+          "label": "DB_HOST",
+          "default": "",
+          "description": "Hostname or IP of the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_PORT",
+          "label": "DB_PORT",
+          "default": "",
+          "description": "Port of the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_NAME",
+          "label": "DB_NAME",
+          "default": "",
+          "description": "Database name (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_USER",
+          "label": "DB_USER",
+          "default": "",
+          "description": "Username to connect to the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_PASS",
+          "label": "DB_PASS",
+          "default": "",
+          "description": "Password to connect to the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wikijs/config",
+          "container": "/config"
+        },
+        {
+          "bind": "/home/docker/wikijs/data",
+          "container": "/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wireguard",
+      "name": "wireguard",
+      "note": "",
+      "description": "WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireguard.png",
+      "image": "linuxserver/wireguard:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "SERVERURL",
+          "label": "SERVERURL",
+          "default": "wireguard.domain.com",
+          "description": "External IP or domain name for docker host. Used in server mode. If set to `auto`, the container will try to determine and set the external IP automatically"
+        },
+        {
+          "name": "SERVERPORT",
+          "label": "SERVERPORT",
+          "default": "51820",
+          "description": "External port for docker host. Used in server mode."
+        },
+        {
+          "name": "PEERS",
+          "label": "PEERS",
+          "default": "1",
+          "description": "Number of peers to create confs for. Required for server mode. Can also be a list of names: `myPC,myPhone,myTablet` (alphanumeric only)"
+        },
+        {
+          "name": "PEERDNS",
+          "label": "PEERDNS",
+          "default": "auto",
+          "description": "DNS server set in peer/client configs (can be set as `8.8.8.8`). Used in server mode. Defaults to `auto`, which uses wireguard docker host's DNS via included CoreDNS forward."
+        },
+        {
+          "name": "INTERNAL_SUBNET",
+          "label": "INTERNAL_SUBNET",
+          "default": "10.13.13.0",
+          "description": "Internal subnet for the wireguard and server and peers (only change if it clashes). Used in server mode."
+        },
+        {
+          "name": "ALLOWEDIPS",
+          "label": "ALLOWEDIPS",
+          "default": "0.0.0.0/0",
+          "description": "The IPs/Ranges that the peers will be able to reach using the VPN connection. If not specified the default value is: '0.0.0.0/0, ::0/0' This will cause ALL traffic to route through the VPN, if you want split tunneling, set this to only the IPs you would like to use the tunnel AND the ip of the server's WG ip, such as 10.13.13.1."
+        },
+        {
+          "name": "PERSISTENTKEEPALIVE_PEERS",
+          "label": "PERSISTENTKEEPALIVE_PEERS",
+          "default": "",
+          "description": "Set to `all` or a list of comma separated peers (ie. `1,4,laptop`) for the wireguard server to send keepalive packets to listed peers every 25 seconds. Useful if server is accessed via domain name and has dynamic IP. Used only in server mode."
+        },
+        {
+          "name": "LOG_CONFS",
+          "label": "LOG_CONFS",
+          "default": "true",
+          "description": "Generated QR codes will be displayed in the docker log. Set to `false` to skip log output."
+        }
+      ],
+      "ports": [
+        "51820:51820/udp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wireguard/config",
+          "container": "/config"
+        },
+        {
+          "bind": "/lib/modules",
+          "container": "/lib/modules"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wireshark",
+      "name": "wireshark",
+      "note": "",
+      "description": "Wireshark is the world's foremost and widely-used network protocol analyzer. It lets you see what's happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireshark.png",
+      "image": "linuxserver/wireshark:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wireshark",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "description": "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases",
+      "env": [
+        {
+          "default": "/home/node/trilium-data",
+          "label": "TRILIUM_DATA_DIR",
+          "name": "TRILIUM_DATA_DIR"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "image": "zadam/trilium:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/trilium.png",
+      "name": "trilium",
+      "platform": "linux",
+      "ports": [
+        "3388:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Trilium",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/trilium-data",
+          "container": "/home/node/trilium-data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Helps you organize your self-hosted services by making them accessible from a single place.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dashy.png",
+      "name": "dashy",
+      "platform": "linux",
+      "image": "lissy93/dashy:latest",
+      "title": "Dashy",
+      "restart_policy": "unless-stopped",
+      "type": 3,
+      "ports": [
+        "4000:80/tcp"
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Flame is self-hosted startpage for your server. Its design is inspired (heavily) by SUI. Flame is very easy to setup and use. With built-in editors, it allows you to setup your very own application hub in no time - no file editing necessary.",
+      "image": "pawelmalak/flame",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/flame.png",
+      "name": "flame-dashboard",
+      "platform": "linux",
+      "ports": [
+        "5005:5005/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Flame-Dashboard",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/flame-dashboard",
+          "container": "/app/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Authentication"
+      ],
+      "description": "An open-source authentication and authorization server providing 2-factor authentication and single sign-on (SSO) for your applications via a web portal.",
+      "env": [
+        {
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "authelia/authelia:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authelia.png",
+      "name": "authelia",
+      "note": "Requires a configuration.yml file in order to work. Documentation is available <a href='https://docs.authelia.com/deployment/deployment-ha'>here</a>.",
+      "platform": "linux",
+      "ports": [
+        "9091:9091/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Authelia",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/authelia",
+          "container": "/etc/authelia/"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Security"
+      ],
+      "description": "This is a Bitwarden server API implementation written in Rust compatible with upstream Bitwarden clients*, perfect for self-hosted deployment where running the official resource-heavy service might not be ideal.",
+      "image": "vaultwarden/server:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/vaultwarden.png",
+      "name": "vaultwarden",
+      "note": "This project is not associated with the Bitwarden project nor 8bit Solutions LLC.",
+      "platform": "linux",
+      "ports": [
+        "80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Vaultwarden",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/vaultwarden",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "description": "A clientless remote desktop gateway.",
+      "image": "oznu/guacamole:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/guacamole.png",
+      "name": "guacamole",
+      "note": "The default login will be guacadmin/guacadmin. It is common practice to add a new admin user and remove the default user for Guacamole.",
+      "platform": "linux",
+      "ports": [
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Guacamole",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/guacamole",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "A dead simple static HOMepage for your servER to keep your s ervices on hand, from a simple yaml configuration file.",
+      "image": "b4bz/homer:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homer.png",
+      "name": "homer",
+      "note": "This container requires a yml file within the config volume. See the documentation here https://github.com/bastienwirtz/homer",
+      "platform": "linux",
+      "ports": [
+        "8902:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Homer",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homer/assets",
+          "container": "/www/assets"
+        },
+        {
+          "bind": "/home/docker/homer/config",
+          "container": "/www/config.yml"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Monitoring"
+      ],
+      "description": "Create agents that monitor and act on your behalf.",
+      "image": "huginn/huginn:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/huginn.png",
+      "name": "huginn",
+      "platform": "linux",
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Huginn",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/huginn",
+          "container": "/var/lib/mysql"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Security"
+      ],
+      "description": "Nginx Proxy Manager enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.",
+      "image": "jc21/nginx-proxy-manager",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nginx-proxy-manager.png",
+      "name": "nginx-proxy-manager",
+      "platform": "linux",
+      "env": [
+        {
+          "label": "DB_SQLITE_FILE",
+          "name": "DB_SQLITE_FILE",
+          "default": "/data/database.sqlite"
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "81:81/tcp",
+        "443:443/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Nginx Proxy Manager",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/nginx-proxy/data",
+          "container": "/data"
+        },
+        {
+          "bind": "/home/docker/nginx-proxy/letsencrypt",
+          "container": "/etc/letsencrypt"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "ownCloud is a self-hosted file sync and share server. It provides access to your data through a web interface, sync clients or WebDAV while providing a platform to view, sync and share across devices easily\u2014all under your control. ownCloud\u2019s open architecture is extensible via a simple but powerful API for applications and plugins and it works with any storage.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "label": "OWNCLOUD_DOMAIN",
+          "name": "OWNCLOUD_DOMAIN"
+        },
+        {
+          "label": "DB_PASSWORD",
+          "name": "DB_PASSWORD"
+        },
+        {
+          "label": "ADMIN_USERNAME",
+          "name": "ADMIN_USERNAME"
+        },
+        {
+          "label": "ADMIN_PASSWORD",
+          "name": "ADMIN_PASSWORD"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/owncloud.png",
+      "name": "owncloud",
+      "note": "The database user is owncloud and the database is owncloud.",
+      "platform": "linux",
+      "image": "owncloud/server:latest",
+      "title": "Owncloud",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "A Linux network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole.",
+      "image": "pihole/pihole:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pihole.png",
+      "name": "pihole",
+      "note": "When the installation is complete, navigate to your.ip.goes.here:1010/admin. Follow the article <a href='https://medium.com/@niktrix/getting-rid-of-systemd-resolved-consuming-port-53-605f0234f32f'>here</a> if you run into issues binding to port 53.",
+      "platform": "linux",
+      "ports": [
+        "53:53/tcp",
+        "53:53/udp",
+        "67:67/udp",
+        "1010:80/tcp",
+        "4443:443/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Pi-Hole",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/pihole",
+          "container": "/etc/pihole"
+        },
+        {
+          "bind": "/home/docker/pihole/DNS",
+          "container": "/etc/dnsmasq.d"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "description": "This container contains OpenVPN and Transmission with a configuration where Transmission is running only when OpenVPN has an active tunnel. It bundles configuration files for many popular VPN providers to make the setup easier.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "MULLVAD",
+          "description": "https://haugene.github.io/docker-transmission-openvpn/supported-providers/",
+          "label": "OPENVPN_PROVIDER",
+          "name": "OPENVPN_PROVIDER"
+        },
+        {
+          "default": "",
+          "label": "OPENVPN_USERNAME",
+          "name": "OPENVPN_USERNAME"
+        },
+        {
+          "default": "",
+          "label": "OPENVPN_PASSWORD",
+          "name": "OPENVPN_PASSWORD"
+        },
+        {
+          "default": "192.168.0.0/24",
+          "label": "LOCAL_NETWORK",
+          "name": "LOCAL_NETWORK"
+        }
+      ],
+      "image": "haugene/transmission-openvpn:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/transmission.png",
+      "name": "transmission-openvpn",
+      "note": "List of supported providers available <a href='https://haugene.github.io/docker-transmission-openvpn/supported-providers'/>here</a>.",
+      "platform": "linux",
+      "ports": [
+        "9091:9091/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Transmission-OpenVPN",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/data"
+        },
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Self-hosted, ad-free, privacy-respecting Google metasearch engine.",
+      "image": "benbusby/whoogle-search:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/whoogle-search.png",
+      "name": "whoogle",
+      "platform": "linux",
+      "ports": [
+        "5001:5000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Whoogle",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/whoogle",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Yacht is a web interface for managing docker containers with an emphasis on templating to provide 1 click deployments. Think of it like a decentralized app store for servers that anyone can make packages for.",
+      "image": "selfhostedpro/yacht:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/yacht.png",
+      "name": "yacht",
+      "platform": "linux",
+      "ports": [
+        "8001:8000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Yacht",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/yacht",
+          "container": "/config"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "categories": [
+        "Tools"
+      ],
+      "title": "Paperless-ng",
+      "name": "paperless-ng",
+      "note": "",
+      "description": "Paperless-ng is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.'",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/paperless-ng.png",
+      "image": "linuxserver/paperless-ng:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for GroupID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for UserID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "Specify a timezone to use for example Europe/Amsterdam"
+        },
+        {
+          "name": "REDIS_URL",
+          "label": "REDIS_URL",
+          "default": "",
+          "description": "Specify an external redis instance to use. Can optionally include a port (`redis:6379`) and/or db (`redis/foo`). If left blank or not included, will use a built-in redis instance. If changed after initial setup will also require manual modification of /config/settings.py"
+        }
+      ],
+      "ports": [
+        "8000:8000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/paperless-ng/config",
+          "container": "/config"
+        },
+        {
+          "container": "/data",
+          "bind": "/home/docker/paperless-ng/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Multimedia"
+      ],
+      "description": "YoutubeDL-Material is a Material Design frontend for youtube-dl. It's coded using Angular 9 for the frontend, and Node.js on the backend.",
+      "image": "tzahi12345/youtubedl-material:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/youtubedl.png",
+      "name": "youtubedl-material",
+      "platform": "linux",
+      "ports": [
+        "17442:17442/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "YouTubeDL-Material",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/ytdlm",
+          "container": "/app/appdata"
+        },
+        {
+          "bind": "/home/docker/ytdlm/video",
+          "container": "/app/video"
+        },
+        {
+          "bind": "/home/docker/ytdlm/subscriptions",
+          "container": "/app/subscriptions"
+        },
+        {
+          "bind": "/home/docker/ytdlm/users",
+          "container": "/app/users"
+        },
+        {
+          "bind": "/home/docker/ytdlm/audio",
+          "container": "/app/audio"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools",
+        "Gaming"
+      ],
+      "description": "C# application with primary purpose of farming Steam cards from multiple accounts simultaneously.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "justarchi/archisteamfarm:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/archisteamfarm.png",
+      "name": "archisteamfarm",
+      "platform": "linux",
+      "ports": [
+        "1242:1242/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "ArchiSteamFarm",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/archiSteamFarm/config",
+          "container": "/app/config"
+        },
+        {
+          "bind": "/home/docker/archiSteamFarm/plugins",
+          "container": "/app/plugins"
+        },
+        {
+          "bind": "/home/docker/archiSteamFarm/logs",
+          "container": "/app/logs"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "ArchiveBox is a powerful, self-hosted internet archiving solution to collect, save, and view sites you want to preserve offline.",
+      "env": [
+        {
+          "default": "*",
+          "label": "ALLOWED_HOSTS",
+          "name": "ALLOWED_HOSTS"
+        },
+        {
+          "default": "750m",
+          "label": "MEDIA_MAX_SIZE",
+          "name": "MEDIA_MAX_SIZE"
+        },
+        {
+          "default": "true",
+          "label": "PUBLIC_INDEX",
+          "name": "PUBLIC_INDEX"
+        },
+        {
+          "default": "true",
+          "label": "PUBLIC_SNAPSHOTS",
+          "name": "PUBLIC_SNAPSHOTS"
+        },
+        {
+          "default": "false",
+          "label": "PUBLIC_ADD_VIEW",
+          "name": "PUBLIC_ADD_VIEW"
+        }
+      ],
+      "image": "archivebox/archivebox:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/archivebox.png",
+      "name": "archivebox",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "8002:8000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Archivebox",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/archivebox",
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "Caddy is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go. ",
+      "image": "caddy:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/caddy.png",
+      "name": "caddy",
+      "note": "Create firewall rule first: 'sudo ufw allow http' and 'sudo ufw allow https'. Add port 2019/tcp for admin endpoint",
+      "platform": "linux",
+      "network": "host",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp",
+        "443:443/udp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Caddy",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "caddy",
+          "container": "/data"
+        },
+        {
+          "bind": "caddy",
+          "container": "/config"
+        },
+        {
+          "bind": "caddyfiles",
+          "container": "/etc/caddy"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Security",
+        "Tools"
+      ],
+      "description": "ClamAV is an open source antivirus engine for detecting trojans, viruses, malware & other malicious threats.",
+      "image": "clamav/clamav",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/clamav.png",
+      "name": "clamav",
+      "platform": "linux",
+      "ports": [
+        "3310:3310/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "ClamAV",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/etc/timezone",
+          "container": "/etc/timezone:ro"
+        },
+        {
+          "bind": "/home/docker/clamav/virus_definitions",
+          "container": "/var/lib/clamav"
+        },
+        {
+          "bind": "/home/docker",
+          "container": "/scandir"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Arr",
+        "Tools"
+      ],
+      "description": "FlareSolverr is a proxy server to bypass Cloudflare and DDoS-GUARD protection.",
+      "env": [
+        {
+          "default": "info",
+          "label": "LOG_LEVEL",
+          "name": "LOG_LEVEL"
+        },
+        {
+          "default": "false",
+          "label": "LOG_HTML",
+          "name": "LOG_HTML"
+        },
+        {
+          "default": "none",
+          "label": "CAPTCHA_SOLVER",
+          "name": "CAPTCHA_SOLVER"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "ghcr.io/flaresolverr/flaresolverr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/flaresolverr.png",
+      "name": "flaresolverr",
+      "platform": "linux",
+      "ports": [
+        "8191:8191/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "FlareSolverr",
+      "type": 1,
+      "note": ""
+    },
+    {
+      "categories": [
+        "CMS"
+      ],
+      "description": "Ghost is a free and open source blogging platform written in JavaScript and distributed under the MIT License, designed to simplify the process of online publishing for individual bloggers as well as online publications.",
+      "env": [
+        {
+          "default": "development",
+          "label": "NODE_ENV",
+          "name": "NODE_ENV"
+        },
+        {
+          "default": "http://localhost/",
+          "label": "url",
+          "name": "url"
+        }
+      ],
+      "image": "ghost:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/ghost.png",
+      "name": "ghost",
+      "platform": "linux",
+      "ports": [
+        "2368:2368/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Ghost",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/ghost",
+          "container": "/var/lib/ghost/content"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Dashboard",
+        "Web",
+        "Other"
+      ],
+      "description": "Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.",
+      "note": "",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "ghcr.io/ajnart/homarr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homarr.png",
+      "name": "homarr-secured",
+      "platform": "linux",
+      "ports": [
+        "7575:7575/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Homarr-Secured",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homarr/configs",
+          "container": "/app/data/configs"
+        },
+        {
+          "bind": "/home/docker/homarr/icons",
+          "container": "/app/public/icons"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Homebridge allows you to integrate with smart home devices that do not natively support HomeKit. There are over 2,000 Homebridge plugins supporting thousands of different smart accessories.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1",
+          "label": "HOMEBRIDGE_CONFIG_UI",
+          "name": "HOMEBRIDGE_CONFIG_UI"
+        },
+        {
+          "default": "8581",
+          "label": "HOMEBRIDGE_CONFIG_UI_PORT",
+          "name": "HOMEBRIDGE_CONFIG_UI_PORT"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "homebridge/homebridge:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/homebridge.png",
+      "name": "homebridge",
+      "network": "host",
+      "note": "",
+      "platform": "linux",
+      "privileged": true,
+      "restart_policy": "unless-stopped",
+      "title": "Homebridge",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homebridge",
+          "container": "/homebridge"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Tools"
+      ],
+      "description": "JDownloader 2 is a free, open-source download management tool with a huge community of developers that makes downloading as easy and fast as it should be. Users can start, stop or pause downloads, set bandwith limitations, auto-extract archives and much more. It's an easy-to-extend framework that can save hours of your valuable time every day!. <a href='https://hub.docker.com/r/jlesage/jdownloader-2'>Docker Hub</a>",
+      "env": [
+        {
+          "default": "",
+          "label": "MYJD_DEVICE_NAME",
+          "name": "MYJD_DEVICE_NAME"
+        },
+        {
+          "default": "",
+          "label": "MYJD_USER",
+          "name": "MYJD_USER"
+        },
+        {
+          "default": "",
+          "label": "MYJD_PASSWORD",
+          "name": "MYJD_PASSWORD"
+        }
+      ],
+      "image": "jlesage/jdownloader-2",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/jdownloader.png",
+      "name": "jdownloader",
+      "platform": "linux",
+      "ports": [
+        "5800:5800/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "JDownloader",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/jdownloader",
+          "container": "/opt/JDownloader/app/cfg"
+        },
+        {
+          "bind": "/media",
+          "container": "/opt/JDownloader/Downloads"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "An alternative private front-end to Reddit",
+      "image": "libreddit/libreddit:armv7",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/libreddit.png",
+      "name": "libreddit",
+      "platform": "linux",
+      "ports": [
+        "8088:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Libreddit",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/libreddit",
+          "container": "/config"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Authentication",
+        "LDAP"
+      ],
+      "description": "This project is a lightweight authentication server that provides an opinionated, simplified LDAP interface for authentication.",
+      "env": [
+        {
+          "default": "somesecretjwt",
+          "label": "LLDAP_JWT_SECRET",
+          "name": "LLDAP_JWT_SECRET"
+        },
+        {
+          "default": "someadminpassword",
+          "label": "LLDAP_LDAP_USER_PASS",
+          "name": "LLDAP_LDAP_USER_PASS"
+        },
+        {
+          "default": "dc=example,dc=com",
+          "label": "LLDAP_LDAP_BASE_DN",
+          "name": "LLDAP_LDAP_BASE_DN"
+        }
+      ],
+      "image": "nitnelave/lldap:stable-debian",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/lldap.png",
+      "name": "lldap",
+      "platform": "linux",
+      "ports": [
+        "3890:3890/tcp",
+        "17170:17170/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "LLDAP",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/lldap",
+          "container": "/data"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A self-hosted recipe manager and meal planner",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "default": "2",
+          "label": "WEB_CONCURRENCY",
+          "name": "WEB_CONCURRENCY"
+        },
+        {
+          "default": "8",
+          "label": "MAX_WORKERS",
+          "name": "MAX_WORKERS"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_PUBLIC",
+          "name": "RECIPE_PUBLIC"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_SHOW_NUTRITION",
+          "name": "RECIPE_SHOW_NUTRITION"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_SHOW_ASSETS",
+          "name": "RECIPE_SHOW_ASSETS"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_LANDSCAPE_VIEW",
+          "name": "RECIPE_LANDSCAPE_VIEW"
+        },
+        {
+          "default": "false",
+          "label": "RECIPE_DISABLE_COMMENTS",
+          "name": "RECIPE_DISABLE_COMMENTS"
+        },
+        {
+          "default": "false",
+          "label": "RECIPE_DISABLE_AMOUNT",
+          "name": "RECIPE_DISABLE_AMOUNT"
+        }
+      ],
+      "image": "hkotel/mealie:v0.4.3",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mealie.png",
+      "name": "mealie",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "9925:80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Mealie",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/mealie",
+          "container": "/app/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools",
+        "Gaming"
+      ],
+      "description": "This docker image provides a Minecraft Server that will automatically download the latest stable version at startup. You can also run/upgrade to any specific version or the latest snapshot. See the Versions section below for more information.",
+      "env": [
+        {
+          "default": "TRUE",
+          "label": "EULA",
+          "name": "EULA"
+        }
+      ],
+      "image": "itzg/minecraft-server:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/minecraft.png",
+      "name": "minecraft",
+      "platform": "linux",
+      "ports": [
+        "25565:25565/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Minecraft Server",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/minecraft-data",
+          "container": "/data"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Networking",
+        "Monitoring"
+      ],
+      "description": "Troubleshoot slowdowns and anomalies in your infrastructure with thousands of per-second metrics, meaningful visualizations, and insightful health alarms with zero configuration.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "DOCKER_USR",
+          "name": "DOCKER_USR"
+        },
+        {
+          "default": "1000",
+          "label": "DOCKER_GRP",
+          "name": "DOCKER_GRP"
+        }
+      ],
+      "image": "netdata/netdata:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/netdata.png",
+      "name": "netdata",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "19999:19999/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Netdata",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/netdata/netdataconfig",
+          "container": "/etc/netdata"
+        },
+        {
+          "bind": "/home/docker/netdata/netdatalib",
+          "container": "/var/lib/netdata"
+        },
+        {
+          "bind": "/etc/passwd",
+          "container": "/host/etc/passwd:ro"
+        },
+        {
+          "bind": "/etc/group",
+          "container": "/host/etc/group:ro"
+        },
+        {
+          "bind": "/proc",
+          "container": "/host/proc:ro"
+        },
+        {
+          "bind": "/sys",
+          "container": "/host/sys:ro"
+        },
+        {
+          "bind": "/etc/os-release",
+          "container": "/host/etc/os-release:ro"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "Organizr allows you to setup Tabs that will be loaded all in one webpage. You can then work on your server with ease.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "organizr/organizr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/organizr.png",
+      "name": "organizr-v2",
+      "platform": "linux",
+      "ports": [
+        "7171:80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Organizr v2",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/organizr",
+          "container": "/config"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "type": 1,
+      "name": "postgres",
+      "title": "PostgreSQL",
+      "note": "",
+      "description": "PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance. It was originally named POSTGRES, referring to its origins as a successor to the Ingres database developed at the University of California, Berkeley. <a href='https://www.postgresql.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/postgres' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/postgres.png",
+      "image": "postgres",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/postgres",
+          "container": "/var/lib/postgresql/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "5432",
+          "container": "5432"
+        }
+      ],
+      "env": [
+        {
+          "name": "POSTGRES_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "POSTGRES_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "POSTGRES_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "A docker image with qBittorrent and the Flood UI, also optional WireGuard VPN support. See the official documentation for WireGuard VPN support at https://hotio.dev/containers/qflood/",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "002",
+          "label": "UMASK",
+          "name": "UMASK"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "default": "false",
+          "label": "FLOOD_AUTH",
+          "name": "FLOOD_AUTH"
+        }
+      ],
+      "image": "hotio/qflood:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/qflood.png",
+      "name": "qflood",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "3000:3000/tcp",
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "qFlood",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/qflood",
+          "container": "/config"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/app/qBittorrent/downloads"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A remote desktop software, the open source TeamViewer alternative, works out of the box, no configuration required. You have full control of your data, with no concerns about security.",
+      "env": [
+        {
+          "default": "rustdesk.example.com:21117",
+          "description": "Use your domain with the default 21117 port",
+          "label": "RELAY",
+          "name": "RELAY"
+        },
+        {
+          "default": "1",
+          "description": "if set to \"1\" unencrypted connection will not be accepted",
+          "label": "ENCRYPTED_ONLY",
+          "name": "ENCRYPTED_ONLY"
+        }
+      ],
+      "image": "rustdesk/rustdesk-server-s6:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/rustdesk.png",
+      "name": "rustdesk",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "21115:21115/tcp",
+        "21116:21116/tcp",
+        "21116:21116/udp",
+        "21117:21117/tcp",
+        "21118:21118/tcp",
+        "21119:21119/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "RustDesk",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/rustdesk",
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "Open-Source Privacy-respecting metasearch engine",
+      "env": [
+        {
+          "default": "http://localhost:9017",
+          "label": "BASE_URL",
+          "name": "BASE_URL"
+        },
+        {
+          "default": "my-instance",
+          "label": "INSTANCE_NAME",
+          "name": "INSTANCE_NAME"
+        }
+      ],
+      "image": "searxng/searxng:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/searx.png",
+      "name": "searxng",
+      "platform": "linux",
+      "ports": [
+        "9017:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "SearXNG",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/searxng",
+          "container": "/etc/searxng"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "type": 1,
+      "name": "surrealdb",
+      "title": "SurrealDB",
+      "note": "",
+      "description": "SurrealDB acts as both a database and a modern, realtime, collaborative API backend layer. SurrealDB can run as a single server or in a highly-available, highly-scalable distributed mode - with support for SQL querying from client devices, GraphQL, ACID transactions, WebSocket connections, structured and unstructured data, graph querying, full-text indexing, geospatial querying, and row-by-row permissions-based access. <a href='https://surrealdb.co/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/surrealdb/surrealdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/surrealdb.png",
+      "image": "surrealdb/surrealdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/surrealdb",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "SURREALDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for SurrealDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "SURREALDB_ADMIN_EMAIL",
+          "label": "Admin Email",
+          "description": "Admin email for SurrealDB",
+          "type": "text",
+          "default": "root"
+
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Security"
+      ],
+      "description": "Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them. ",
+      "image": "traefik:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/traefik.png",
+      "name": "traefik",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp",
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "command": "--api.insecure=true --providers.docker",
+      "title": "Traefik",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/traefik/traefik.yml",
+          "container": "/traefik.yml"
+        },
+        {
+          "bind": "/home/docker/traefik/config.yml",
+          "container": "/config.yml"
+        },
+        {
+          "bind": "/home/docker/traefik/acme.json",
+          "container": "/acme.json"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Multimedia"
+      ],
+      "description": "Unmanic is a simple tool for optimising your file library. You can use it to convert your files into a single, uniform format, manage file movements based on timestamps, or execute custom commands against a file based on its file size.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "josh5/unmanic:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/unmanic.png",
+      "name": "unmanic",
+      "platform": "linux",
+      "ports": [
+        "8888:8888/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Unmanic",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/unmanic",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/library"
+        },
+        {
+          "bind": "/var/tmp/unmanic",
+          "container": "/tmp/unmanic"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "Quickly and easily configure Wireguard through a web browser.",
+      "env": [
+        {
+          "default": "example.domain.com",
+          "description": "Set here your DDNS domain",
+          "label": "WG_HOST",
+          "name": "WG_HOST"
+        },
+        {
+          "default": "ENTER AN ADMIN PASSWORD",
+          "description": "Leave blank to access WebUI without loggin",
+          "label": "PASSWORD",
+          "name": "PASSWORD"
+        },
+        {
+          "default": "51820",
+          "label": "WG_PORT",
+          "name": "WG_PORT"
+        },
+        {
+          "default": "1.1.1.1",
+          "label": "WG_DEFAULT_DNS",
+          "name": "WG_DEFAULT_DNS"
+        },
+        {
+          "default": "10.8.0.x",
+          "label": "WG_DEFAULT_ADDRESS",
+          "name": "WG_DEFAULT_ADDRESS"
+        },
+        {
+          "default": "0.0.0.0/0, ::/0",
+          "label": "WG_ALLOWED_IPS",
+          "name": "WG_ALLOWED_IPS"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireguard.png",
+      "name": "wg-easy",
+      "platform": "linux",
+      "image": "weejewel/wg-easy",
+      "title": "WireGuard-Easy",
+      "type": 3,
+      "note": ""
+    },
+    {
+      "categories": [
+        "CMS"
+      ],
+      "description": "WordPress is a web content management system. It was originally created as a tool to publish blogs but has evolved to support publishing other web content, including more traditional websites, mailing lists and Internet forum, media galleries, membership sites, learning management systems and online stores.",
+      "env": [
+        {
+          "default": "db",
+          "label": "Port or host of database server",
+          "name": "WORDPRESS_DB_HOST"
+        },
+        {
+          "default": "exampleuser",
+          "label": "Database user name",
+          "name": "WORDPRESS_DB_USER"
+        },
+        {
+          "default": "examplepass",
+          "label": "Database password for user",
+          "name": "WORDPRESS_DB_PASSWORD"
+        },
+        {
+          "default": "exampledb",
+          "label": "Database name",
+          "name": "WORDPRESS_DB_NAME"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wordpress.png",
+      "note": "Create a mysql or mariadb database for the container first.",
+      "platform": "linux",
+      "image": "wordpress:latest",
+      "title": "Wordpress",
+      "name": "wordpress",
+      "type": 3,
+      "restart_policy": "unless-stopped",
+      "ports": [
+        "8080:80/tcp"
+      ],
+      "network": "AppBridge",
+      "volumes": [
+        {
+          "bind": "/home/docker/wordpress",
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Gaming"
+      ],
+      "description": "McMyAdmin 2 is the leading web control panel and administration console for Minecraft servers.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "linuxserver/mcmyadmin2:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mcmyadmin2.png",
+      "name": "mcmyadmin2",
+      "platform": "linux",
+      "ports": [
+        "8080:8080/tcp",
+        "25565:25565/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "McMyAdmin 2",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/minecraft"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A one-of-a-kind resume builder that's not out to get your data. Completely secure, customizable, portable, open-source and free forever.",
+      "image": "amruthpillai/reactive-resume:latest",
+      "logo": "https://raw.githubusercontent.com/SelfhostedPro/selfhosted_templates/master/Images/reactiveresume.png",
+      "name": "reactive-resume",
+      "platform": "linux",
+      "ports": [
+        "80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Reactive-Resume",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/reactiveresume",
+          "container": "/usr/src/app"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "VPN",
+        "Tools"
+      ],
+      "description": "This container contains OpenVPN and Deluge with a configuration where Deluge is running only when OpenVPN has an active tunnel. It bundles configuration files for many popular VPN providers to make the setup easier.",
+      "env": [
+        {
+          "default": "1001",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1001",
+          "label": "PGID",
+          "name": "PUID"
+        },
+        {
+          "default": "MULLVAD",
+          "description": "see https://github.com/sgtsquiggs/docker-deluge-openvpn",
+          "label": "OPENVPN_PROVIDER",
+          "name": "OPENVPN_PROVIDER"
+        },
+        {
+          "label": "OPENVPN_USERNAME",
+          "name": "OPENVPN_USERNAME"
+        },
+        {
+          "label": "OPENVPN_PASSWORD",
+          "name": "OPENVPN_PASSWORD"
+        }
+      ],
+      "image": "sgtsquiggs/deluge-openvpn:latest",
+      "name": "deluge-openvpn",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/deluge.png",
+      "platform": "linux",
+      "ports": [
+        "8112:8112/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Deluge OpenVPN",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/downloads"
+        },
+        {
+          "bind": "/home/docker/delugeopenvpn/config",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Multimedia"
+      ],
+      "note": "",
+      "description": "Tdarr is a popular conditional transcoding application for processing large (or small) media libraries. The application comes in the form of a click-to-run web-app, which you run on your own device and access through a web browser.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/tdarr.png",
+      "name": "tdarr",
+      "platform": "linux",
+      "image": "ghcr.io/haveagitgat/tdarr",
+      "title": "Tdarr",
+      "type": 3,
+      "volumes": [
+        {
+          "bind": "/home/docker/tdarr/configs",
+          "container": "/app/configs"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/downloads"
+        }
+      ],
+      "ports": [
+        "8265:8265/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Finance"
+      ],
+      "description": "Cryptofolio is an open-source, and self-hosted solution for tracking your cryptocurrency holdings. It features a web interface, an Android mobile app, and a cross-platform desktop application for Windows, macOS, and Linux.",
+      "image": "xtrendence/cryptofolio:latest",
+      "logo": "https://i.imgur.com/5v8lzea.png",
+      "name": "cryptofolio",
+      "platform": "linux",
+      "ports": [
+        "7280:80/tcp"
+      ],
+      "restart_policy": "always",
+      "title": "Cryptofolio",
+      "type": 1
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "An easy to use Status Page for your websites and applications. Statping will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page.",
+      "image": "statping/statping:latest",
+      "logo": "https://raw.githubusercontent.com/xneo1/portainer_templates/master/Images/statping.png",
+      "name": "statping",
+      "platform": "linux",
+      "ports": [
+        "4040:8080/tcp"
+      ],
+      "restart_policy": "always",
+      "title": "Statping",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/statping",
+          "container": "/app"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "influxdb",
+      "title": "InfluxDB",
+      "note": "",
+      "description": "InfluxDB is an open source time series database developed by InfluxData. It is written in Go and optimized for fast, high-availability storage and retrieval of time series data in fields such as operations monitoring, application metrics, Internet of Things sensor data, and real-time analytics. <a href='https://www.influxdata.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/influxdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/influxdb.png",
+      "image": "influxdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/influxdb",
+          "container": "/var/lib/influxdb",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "8086",
+          "container": "8086"
+        }
+      ],
+      "env": [
+        {
+          "name": "INFLUXDB_ADMIN_USER",
+          "label": "Admin User",
+          "description": "Admin user for InfluxDB",
+          "type": "text",
+          "default": "admin"
+        },
+        {
+          "name": "INFLUXDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for InfluxDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "INFLUXDB_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "mydb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Media Server",
+        "LDAP"
+      ],
+      "description": "Tubearchivist is your self hosted YouTube media server",
+      "note": "Requires a Redis and Elasticsearch database. Tube Archivist needs around 2GB of available memory for a small testing setup and around 4GB of available memory for a mid to large sized installation. Minimal with dual core with 4 threads, better quad core plus.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/tube-archivist.png",
+      "name": "tubearchivist",
+      "platform": "linux",
+      "image": "bbilly1/tubearchivist",
+      "title": "Tubearchivist",
+      "type": 3,
+      "ports": [
+        "8080:8080/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/media/youtube",
+          "container": "/youtube"
+        }
+      ],
+      "env": [
+        {
+          "name": "INFLUXDB_ADMIN_USER",
+          "label": "Admin User",
+          "description": "Admin user for InfluxDB",
+          "type": "text",
+          "default": "admin"
+        },
+        {
+          "name": "INFLUXDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for InfluxDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "INFLUXDB_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "mydb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Finance"
+      ],
+      "description": "Actual is a local-first personal finance tool. It is 100% free and open-source. It has a synchronization element so that all your changes can move between devices without any heavy lifting.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/actual.png",
+      "name": "actual-server",
+      "platform": "linux",
+      "image": "actualbudget/actual-server:latest",
+      "restart_policy": "unless-stopped",
+      "title": "Actual-Server",
+      "ports": [
+        "5006:5006/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/actual",
+          "container": "/data"
+        }
+      ],
+      "type": 3
+    },
+    {
+      "categories": [
+        "Development"
+      ],
+      "description": "Appsmith (www.appsmith.com) is the first open-source low code tool that helps developers build dashboards and admin panels very quickly.",
+      "logo": "https://cdn-images.himalayas.app/vr60veq4neiptamhqm6qxwi3toi3",
+      "name": "appsmith",
+      "platform": "linux",
+      "image": "appsmith/appsmith-ce",
+      "title": "Appsmith",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/appsmith",
+          "container": "/appsmith-stacks"
+        }
+      ],
+      "type": 3
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "note": "Requires a MySQL database. Create a database first.",
+      "description": "Leantime is an open source project management system for small teams and startups written in PHP, Javascript using MySQL. https://leantime.io",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/leantime.png",
+      "name": "leantime",
+      "platform": "linux",
+      "image": "leantime/leantime:latest",
+      "title": "Leantime",
+      "type": 3,
+      "ports": [
+        "80:80/tcp"
+      ],
+      "env": [
+        {
+          "name": "MYSQL_HOST",
+          "label": "Database Host",
+          "description": "Database host",
+          "type": "text",
+          "default": "mysql_leantime"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "Database User",
+          "description": "Database user",
+          "type": "text",
+          "default": "leantime_user"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Database Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "choose.a.long.random.password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database Name",
+          "description": "Database name",
+          "type": "text",
+          "default": "leantime_database"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "Jellyseerr is a free and open source software application for managing requests for your media library. It is a a fork of Overseerr built to bring support for Jellyfin & Emby media servers!. <a href='https://github.com/Fallenbagel/jellyseerr/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jellyseerr.png",
+      "name": "jellyseerr",
+      "note": "For Emby : add the environment variable JELLYFIN_TYPE=emby . (this will allow you to use the play button)",
+      "platform": "linux",
+      "image": "fallenbagel/jellyseerr:latest",
+      "title": "Jellyseerr",
+      "type": 3,
+      "volumes": [
+        {
+          "bind": "/home/docker/jellyseerr",
+          "container": "/app/config"
+        }
+      ],
+      "ports": [
+        "5055:5055/tcp"
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Dashdot is a modern server dashboard, running on the latest tech, designed with glassmorphism in mind. It is intended to be used for smaller VPS and private servers.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dashdot.png",
+      "name": "dashdot",
+      "platform": "linux",
+      "image": "mauricenino/dashdot",
+      "title": "Dashdot",
+      "type": 3,
+      "ports": [
+        "80:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/",
+          "container": "/mnt/host:ro"
+        }
+      ],
+      "privileged": true
+    },
+    {
+      "name": "nocodb",
+      "title": "NocoDB",
+      "note": "",
+      "description": "NocoDB is a free, open-source, self-hosted, no-code platform to make database driven application. <a href='https://www.nocodb.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/nocodb/nocodb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/nocodb.png",
+      "image": "nocodb/nocodb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/nocodb",
+          "container": "/var/lib/nocodb",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "8080",
+          "container": "8080"
+        }
+      ],
+      "env": [
+        {
+          "name": "NOCODB_DB_HOST",
+          "label": "Database Host",
+          "description": "Database host",
+          "type": "text",
+          "default": "mysql"
+        },
+        {
+          "name": "NOCODB_DB_PORT",
+          "label": "Database Port",
+          "description": "Database port",
+          "type": "text",
+          "default": "3306"
+        },
+        {
+          "name": "NOCODB_DB_USER",
+          "label": "Database User",
+          "description": "Database user",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "NOCODB_DB_PASSWORD",
+          "label": "Database Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "NOCODB_DB_NAME",
+          "label": "Database Name",
+          "description": "Database name",
+          "type": "text",
+          "default": "nocodb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "AdGuard Home is a network-wide software for blocking ads & tracking. After you set it up, it\u2019ll cover ALL your home devices, and you don\u2019t need any client-side software for that. With the rise of Internet-Of-Things and connected devices, it becomes more and more important to be able to control your whole network.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "label": "CONTEXT_PATH",
+          "name": "CONTEXT_PATH",
+          "set": "adguard home"
+        }
+      ],
+      "note": "DNS-over-HTTPS: [80:80/TCP] [443:443/TCP] [443:443/UDP] [3000:3000/TCP] [DEFAULT]. DNS: [53:53/TCP] [53:53/UDP]. Admin Panel: [3000:3000/TCP]. DHCP: [67:67/UDP] [68:68/TCP] [68:68/UDP]. DNS-over-TLS: [853:853/TCP]. DNS-over-QUIC: [784:784/UDP] [853:853/UDP] [8853:8853/UDP]. DNSCrypt: [5443:5443/TCP] [5443:5443/UDP]",
+      "image": "adguard/adguardhome:latest",
+      "logo": "https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Images/adguard.png",
+      "name": "adguard",
+      "platform": "linux",
+      "ports": [
+        "53:53/tcp",
+        "53:53/udp",
+        "67:67/udp",
+        "68:68/tcp",
+        "68:68/udp",
+        "80:80/tcp",
+        "443:443/tcp",
+        "853:853/tcp",
+        "3000:3000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Adguard",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/portainer/Files/AppData/Adguard/Workdir",
+          "container": "/opt/adguardhome/work"
+        },
+        {
+          "bind": "/portainer/Files/AppData/Adguard/Conf",
+          "container": "/opt/adguardhome/conf"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mongodb",
+      "title": "MongoDB",
+      "note": "",
+      "description": "MongoDB is a source-available cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with optional schemas. <a href='https://www.mongodb.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mongo' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/mongodb.png",
+      "image": "mongo",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mongodb",
+          "container": "/data/db",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "27017",
+          "container": "27017"
+        }
+      ],
+      "env": [
+        {
+          "name": "MONGO_INITDB_ROOT_USERNAME",
+          "label": "Root Username",
+          "description": "Root username for MongoDB",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "MONGO_INITDB_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MongoDB",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "cratedb",
+      "title": "CrateDB",
+      "note": "",
+      "description": "CrateDB is a distributed SQL database that combines SQL and search in a way that's simple to scale. It is designed to be highly available, horizontally scalable, and easy to use. <a href='https://crate.io/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/crate' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/cratedb.png",
+      "image": "crate",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/cratedb",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "4200",
+          "container": "4200"
+        },
+        {
+          "host": "4300",
+          "container": "4300"
+        }
+      ],
+      "env": [
+        {
+          "name": "CRATE_HEAP_SIZE",
+          "label": "Heap Size",
+          "description": "Heap size for CrateDB",
+          "type": "text",
+          "default": "2g"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "elasticsearch",
+      "title": "Elasticsearch",
+      "note": "",
+      "description": "Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents. <a href='https://www.elastic.co/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/elasticsearch' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/elasticsearch.png",
+      "image": "elasticsearch",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/elasticsearch",
+          "container": "/usr/share/elasticsearch/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "9200",
+          "container": "9200"
+        },
+        {
+          "host": "9300",
+          "container": "9300"
+        }
+      ],
+      "env": [
+        {
+          "name": "discovery.type",
+          "label": "Discovery Type",
+          "description": "Discovery type for Elasticsearch",
+          "type": "text",
+          "default": "single-node"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "SQL Server",
+      "name": "mssql",
+      "description": "Official image for Microsoft SQL Server based on Ubuntu 20.04. <a href=\"https://hub.docker.com/_/microsoft-mssql-server\" target=\"_blank\">Docker Hub</a>",
+      "categories": [
+        "Database"
+      ],
+      "platform": "linux",
+      "note": "Requires at least 2GB of RAM. Make sure to assign enough memory to the Docker VM if you're running on Docker for Mac or Windows. Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
+      "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
+      "image": "mcr.microsoft.com/mssql/server:2022-latest",
+      "ports": [
+        "1433/tcp"
+      ],
+      "env": [
+        {
+          "name": "ACCEPT_EULA",
+          "default": "Y",
+          "preset": true
+        },
+        {
+          "name": "MSSQL_SA_PASSWORD",
+          "label": "MSSQL_SA_PASSWORD",
+          "default": "YOUR_STRONG_PASSWORD"
+        },
+        {
+          "name": "MSSQL_PID",
+          "label": "MSSQL_PID",
+          "default": "Developer"
+        }
+      ]
+    },
+    {
+      "name": "redis",
+      "title": "Redis",
+      "note": "",
+      "description": "Redis is an in-memory data structure project implementing a distributed, in-memory key-value database with optional durability. Redis supports different kinds of abstract data structures, such as strings, lists, maps, sets, sorted sets, HyperLogLogs, bitmaps, streams, and spatial indexes. <a href='https://redis.io/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/redis' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/redis.png",
+      "image": "redis",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/redis",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "6379",
+          "container": "6379"
+        }
+      ],
+      "env": [
+        {
+          "name": "REDIS_PASSWORD",
+          "label": "Password",
+          "description": "Password for Redis",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Joomla",
+      "name": "joomla",
+      "description": "Joomla! is an award-winning content management system (CMS), which enables you to build web sites and powerful online applications.",
+      "categories": [
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/joomla.png",
+      "image": "joomla:latest",
+      "env": [
+        {
+          "name": "JOOMLA_DB_HOST",
+          "label": "MySQL database host",
+          "type": "container"
+        },
+        {
+          "name": "JOOMLA_DB_PASSWORD",
+          "label": "Database password"
+        }
+      ],
+      "ports": [
+        "80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Drupal",
+      "name": "drupal",
+      "description": "Open-source content management framework",
+      "categories": [
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/drupal.png",
+      "image": "drupal:latest",
+      "ports": [
+        "80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "cockroachdb",
+      "title": "CockroachDB",
+      "note": "",
+      "description": "CockroachDB is a distributed SQL database built on a transactional and strongly-consistent key-value store. It scales horizontally; survives disk, machine, rack, and even datacenter failures with minimal latency disruption and no manual intervention; supports strongly-consistent ACID transactions; and provides a familiar SQL API for structuring, manipulating, and querying data. <a href='https://www.cockroachlabs.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/cockroachdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/cockroachdb.png",
+      "image": "cockroachdb/cockroach",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/cockroachdb",
+          "container": "/cockroach/cockroach-data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "26257",
+          "container": "26257"
+        },
+        {
+          "host": "8080",
+          "container": "8080"
+        }
+      ],
+      "env": [
+        {
+          "name": "COCKROACH_CHANNEL",
+          "label": "Channel",
+          "description": "CockroachDB channel",
+          "type": "text",
+          "default": "stable"
+        },
+        {
+          "name": "COCKROACH_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "COCKROACH_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mariadb",
+      "title": "MariaDB",
+      "note": "",
+      "description": "MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system (RDBMS), intended to remain free and open-source software under the GNU General Public License. Development is led by some of the original developers of MySQL, who forked it due to concerns over its acquisition by Oracle Corporation in 2009. <a href='https://mariadb.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mariadb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mariadb.png",
+      "image": "mariadb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mariadb",
+          "container": "/var/lib/mysql",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "MYSQL_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MariaDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "couchdb",
+      "title": "CouchDB",
+      "note": "",
+      "description": "Apache CouchDB is open source database software that focuses on ease of use and having a scalable architecture. It has a document-oriented NoSQL database architecture and is implemented in the concurrency-oriented language Erlang; it uses JSON to store data, JavaScript as its query language using MapReduce, and HTTP for an API. <a href='https://couchdb.apache.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/couchdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/couchdb.png",
+      "image": "couchdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/couchdb",
+          "container": "/opt/couchdb/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "5984",
+          "container": "5984"
+        }
+      ],
+      "env": [
+        {
+          "name": "COUCHDB_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "COUCHDB_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    }
+  ]
+}

+ 5930 - 0
templates/templates-bak.json

@@ -0,0 +1,5930 @@
+{
+  "version": "2",
+  "templates": [
+    {
+      "type": 1,
+      "name": "heimdall",
+      "title": "Heimdall",
+      "note": "",
+      "description": "Heimdall is a way to organise all those links to your most used web sites and web applications in a simple way. <a href='https://hub.docker.com/r/linuxserver/heimdall/' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/heimdall.png",
+      "image": "lscr.io/linuxserver/heimdall:latest",
+      "categories": [
+        "Dashboard"
+      ],
+      "ports": [
+        "8001:80/tcp",
+        "4001:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/heimdall",
+          "container": "/config"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "homepage",
+      "title": "Homepage",
+      "description": "A modern (fully static, fast), secure (fully proxied), highly customizable application dashboard with integrations for more than 25 services and translations for over 15 languages. Easily configured via YAML files (or discovery via docker labels). <a href='https://github.com/benphelps/homepage/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homepage.png",
+      "image": "ghcr.io/benphelps/homepage:latest",
+      "categories": [
+        "Dashboard"
+      ],
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/homepage",
+          "container": "/app/config"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock:ro"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "jackett",
+      "title": "Jackett",
+      "description": "Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. <a href='https://github.com/Jackett/Jackett/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jackett.png",
+      "image": "linuxserver/jackett:latest",
+      "categories": [
+        "Downloaders",
+        "Tools"
+      ],
+      "ports": [
+        "9117:9117/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/jackett",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/downloads"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Librespeed",
+      "name": "librespeed",
+      "note": "",
+      "description": "Librespeed is a very lightweight speed test implemented in Javascript, using XMLHttpRequest and Web Workers. No Flash, No Java, No Websocket, No Bullshit. <a href='https://github.com/librespeed/speedtest/'>Github</a>",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/librespeed.png",
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "image": "adolfintel/speedtest",
+      "ports": [
+        "81:81/tcp"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "description": "Specify a timezone to use for example Europe/Amsterdam"
+        },
+        {
+          "name": "MODE",
+          "label": "MODE",
+          "default": "standalone",
+          "description": "Set the mode."
+        },
+        {
+          "name": "PASSWORD",
+          "label": "PASSWORD",
+          "default": "SOMEPASSWORD",
+          "description": "Password to access the stats page. If not set, stats page will not allow accesses."
+        },
+        {
+          "name": "WEBPORT",
+          "label": "WEBPORT",
+          "default": "81",
+          "description": "Allows choosing a custom port for the included web server."
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "name": "ombi",
+      "title": "Ombi",
+      "description": "Ombi allows you to host your own Plex Request and user management system. If you are sharing your Plex server with other users, allow them to request new content using an easy to manage interface. . [There is no official Ombi docker image. This one is created and maintained by <a href='https://hub.docker.com/r/linuxserver/ombi/' target='_blank'>linuxserver.io</a>]",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/ombi.png",
+      "image": "linuxserver/ombi:latest",
+      "categories": [
+        "Tools"
+      ],
+      "ports": [
+        "3579:3579/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/ombi",
+          "container": "/config"
+        },
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime:ro"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "overseerr",
+      "title": "Overseerr",
+      "description": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem. <a href='https://overseerr.dev/' target='_blank'>Official Site</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/overseerr.png",
+      "image": "sctx/overseerr:latest",
+      "categories": [
+        "Multimedia",
+        "Tools"
+      ],
+      "ports": [
+        "5055/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/overseerr",
+          "container": "/app/config"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime:ro",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "plex",
+      "title": "Plex",
+      "description": "Plex organizes your video, music, and photo collections and streams them to all of your screens.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/plex.png",
+      "image": "linuxserver/plex:latest",
+      "network": "host",
+      "categories": [
+        "Media Server",
+        "Paid"
+      ],
+      "privileged": true,
+      "ports": [],
+      "volumes": [
+        {
+          "bind": "/home/docker/plex",
+          "container": "/config"
+        },
+        {
+          "bind": "/media/tvshows",
+          "container": "/data/tvshows"
+        },
+        {
+          "bind": "/media/movies",
+          "container": "/data/movies"
+        },
+        {
+          "bind": "/media/music",
+          "container": "/data/music"
+        },
+        {
+          "container": "/transcode"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        },
+        {
+          "name": "VERSION",
+          "label": "VERSION",
+          "default": "latest"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "prowlarr",
+      "title": "Prowlarr",
+      "description": "Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. ",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/prowlarr.png",
+      "image": "ghcr.io/linuxserver/prowlarr:develop",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "9696/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/prowlarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/etc/localtime:ro",
+          "container": "/etc/localtime:ro",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "radarr",
+      "title": "Radarr",
+      "note": "There is no official Radarr docker image. This one is created and maintained by <a href='https://hotio.dev/containers/radarr/' target='_blank'>Hotio.dev</a>",
+      "description": "Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. <a href='https://radarr.video/#downloads-v3-docker' target='_blank'>Official Site</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/radarr.png",
+      "image": "ghcr.io/hotio/radarr",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "network": "AppBridge",
+      "ports": [
+        "7878:7878/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/radarr",
+          "container": "/config"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "readarr",
+      "title": "Readarr",
+      "description": "Readarr is an ebook and audiobook collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new books from your favorite authors and will grab, sort, and rename them.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/readarr.png",
+      "image": "ghcr.io/linuxserver/readarr:nightly",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "8787/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/readarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/media/downloads/ebooks",
+          "container": "/downloads"
+        },
+        {
+          "container": "/books",
+          "bind": "/media/storage/ebooks"
+        },
+        {
+          "container": "/blackhole",
+          "bind": "/media/temp/blackhole/ebooks"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "scrutiny",
+      "title": "Scrutiny",
+      "description": "WebUI for smartd S.M.A.R.T monitoring",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/scrutiny.png",
+      "image": "analogj/scrutiny:latest",
+      "categories": [
+        "Monitoring"
+      ],
+      "ports": [
+        "8080/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/scrutiny/config/",
+          "bind": "/opt/mediadepot/apps/scrutiny"
+        },
+        {
+          "container": "/run/udev",
+          "bind": "/run/udev",
+          "readonly": true
+        }
+      ],
+      "env": [],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.scrutiny.loadbalancer.server.port",
+          "value": "8080"
+        },
+        {
+          "name": "traefik.http.routers.scrutiny.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.scrutiny.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "sonarr",
+      "title": "Sonarr",
+      "description": "Sonarr is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/sonarr.png",
+      "image": "linuxserver/sonarr:latest",
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "ports": [
+        "8989/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/opt/mediadepot/apps/sonarr"
+        },
+        {
+          "container": "/downloads",
+          "bind": "/media/storage/downloads/tvshows"
+        },
+        {
+          "container": "/tv",
+          "bind": "/media/storage/tvshows"
+        },
+        {
+          "container": "/blackhole",
+          "bind": "/media/temp/blackhole/tvshows"
+        },
+        {
+          "container": "/etc/localtime",
+          "bind": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.sonarr.loadbalancer.server.port",
+          "value": "8989"
+        },
+        {
+          "name": "traefik.http.routers.sonarr.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.sonarr.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "tautulli",
+      "title": "Tautulli",
+      "description": "A Python based monitoring and tracking tool for Plex Media Server.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/tautulli.png",
+      "image": "linuxserver/tautulli:latest",
+      "categories": [
+        "MediaServer:Other",
+        "Tools"
+      ],
+      "ports": [
+        "8181/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/opt/mediadepot/apps/tautulli"
+        }
+      ],
+      "env": [
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "preset": true
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles"
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.tautulli.loadbalancer.server.port",
+          "value": "8181"
+        },
+        {
+          "name": "traefik.http.routers.tautulli.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.tautulli.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "watchtower",
+      "title": "Watchtower",
+      "description": "Automatically update running Docker containers",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/watchtower.png",
+      "image": "containrrr/watchtower:latest",
+      "command": "--cleanup --label-enable",
+      "categories": [
+        "Monitoring"
+      ],
+      "volumes": [
+        {
+          "container": "/var/run/docker.sock",
+          "bind": "/var/run/docker.sock"
+        }
+      ],
+      "env": []
+    },
+    {
+      "type": 1,
+      "name": "wizarr",
+      "title": "Wizarr",
+      "description": "Wizarr is an advanced user invitation and management system for Jellyfin, Plex, Emby etc. ",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wizarr.png",
+      "image": "ghcr.io/wizarrrr/wizarr",
+      "categories": [
+        "Arr"
+      ],
+      "ports": [
+        "5690/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wizarr",
+          "container": "/data/database"
+        },
+        {
+          "bind": "/etc/localtime:ro",
+          "container": "/etc/localtime",
+          "readonly": true
+        }
+      ],
+      "env": [
+        {
+          "name": "APP_URL",
+          "label": "APP_URL",
+          "default": "https://wizarr.domain.com"
+        }
+      ],
+      "labels": [
+        {
+          "name": "traefik.enable",
+          "value": "true"
+        },
+        {
+          "name": "traefik.http.services.wizarr.loadbalancer.server.port",
+          "value": "5690"
+        },
+        {
+          "name": "traefik.http.routers.wizarr.entrypoints",
+          "value": "websecure"
+        },
+        {
+          "name": "traefik.http.routers.wizarr.tls.certresolver",
+          "value": "mydnschallenge"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, \u00ef\u00bf\u00bdTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/transmission:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/transmission.png",
+      "platform": "linux",
+      "ports": [
+        "9091/tcp",
+        "51413/tcp",
+        "51413/udp"
+      ],
+      "title": "Transmission",
+      "name": "transmission",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/transmission",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/downloads"
+        },
+        {
+          "container": "/watch"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Video"
+      ],
+      "description": "Headless installation of Kodi.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/kodi-headless:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/kodi.png",
+      "platform": "linux",
+      "ports": [
+        "8080/tcp",
+        "9777/udp"
+      ],
+      "title": "Kodi-Headless",
+      "name": "kodi-headless",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config/.kodi"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Backup:",
+        "Cloud",
+        "Other",
+        "Tools"
+      ],
+      "description": "Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it's transmitted over the Internet.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/syncthing:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/syncthing.png",
+      "platform": "linux",
+      "ports": [
+        "8384/tcp",
+        "21027/udp",
+        "22000/tcp"
+      ],
+      "title": "Syncthing",
+      "name": "syncthing",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/sync"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mysql",
+      "title": "MySQL",
+      "note": "",
+      "description": "MySQL is the world's most popular open source database. With its proven performance, reliability and ease-of-use, MySQL has become the leading database choice for web-based applications, covering the entire range from personal projects and websites, via e-commerce and information services, all the way to high profile web properties including Facebook, Twitter, YouTube, Yahoo! and many more. <a href='https://www.mysql.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mysql' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mysql.png",
+      "image": "mysql",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mysql",
+          "container": "/var/lib/mysql",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "MYSQL_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MySQL",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "Deluge is a lightweight, Free Software, cross-platform BitTorrent client providing: Full Encryption, WebUI, Plugin System, Much more...",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "lscr.io/linuxserver/deluge:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/deluge.png",
+      "platform": "linux",
+      "title": "Deluge",
+      "name": "deluge",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        }
+      ],
+      "ports": [
+        "8112/tcp",
+        "6881/tcp",
+        "6881/udp"
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Proxy"
+      ],
+      "description": "Nginx is a web server with a strong focus on high concurrency, performance and low memory usage. It can also act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/nginx:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nginx.png",
+      "platform": "linux",
+      "ports": [
+        "80/tcp",
+        "443/tcp"
+      ],
+      "title": "Nginx",
+      "name": "nginx",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Arr"
+      ],
+      "description": "Lidarr is a music collection manager for Usenet and BitTorrent users.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/lidarr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/lidarr.png",
+      "platform": "linux",
+      "ports": [
+        "8686/tcp"
+      ],
+      "title": "Lidarr",
+      "name": "lidarr",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        },
+        {
+          "container": "/music"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "The qBittorrent project aims to provide an open-source software alternative to \u00ef\u00bf\u00bdTorrent. qBittorrent is based on the Qt toolkit and libtorrent-rasterbar library.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/qbittorrent:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/qbittorrent.png",
+      "platform": "linux",
+      "ports": [
+        "6881/tcp",
+        "6881/udp",
+        "8080/tcp"
+      ],
+      "title": "qbittorrent",
+      "name": "qbittorrent",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/downloads"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "OpenVPN Access Server is a full featured secure network tunneling VPN software solution that integrates OpenVPN server capabilities, enterprise management capabilities, simplified OpenVPN Connect UI, and OpenVPN Client software packages that accommodate Windows, MAC, Linux, Android, and iOS environments.",
+      "env": [
+        {
+          "label": "INTERFACE",
+          "name": "INTERFACE",
+          "set": "eth0"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/openvpn-as:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/openvpn.png",
+      "platform": "linux",
+      "ports": [
+        "943/tcp",
+        "9443/tcp",
+        "1194/udp"
+      ],
+      "name": "openvpn-as",
+      "title": "OpenVPN Access Server",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "Server version of minetest, a free, open source alternative to minecraft.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/minetest:latest",
+      "logo": "https://raw.githubusercontent.com/linuxserver/beta-templates/master/lsiodev/img/minetest-icon.png",
+      "platform": "linux",
+      "ports": [
+        "30000/udp"
+      ],
+      "title": "Minetest",
+      "name": "minetest",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config/.minetest"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Media"
+      ],
+      "description": "Airsonic is a free, web-based media streamer, providing ubiqutious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "CONTEXT_PATH",
+          "name": "CONTEXT_PATH",
+          "set": "airsonic"
+        },
+        {
+          "label": "JAVA_OPTS",
+          "name": "JAVA_OPTS",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/airsonic:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/airsonic.png",
+      "platform": "linux",
+      "ports": [
+        "4040/tcp"
+      ],
+      "name": "airsonic",
+      "title": "Airsonic",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/music"
+        },
+        {
+          "container": "/playlists"
+        },
+        {
+          "container": "/podcasts"
+        },
+        {
+          "container": "/media"
+        },
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Web"
+      ],
+      "description": "This container sets up an Nginx webserver and reverse proxy with php support and a built-in letsencrypt client that automates free SSL server certificate generation and renewal processes. It also contains fail2ban for intrusion prevention.\r\n  \r\n  Before running this container, make sure that the url and subdomains are properly forwarded to this container's host.\r\n  \r\n  - Port 443 on the internet side of the router should be forwarded to this container's port 443.\r\n  - If you need a dynamic dns provider, you can use the free provider duckdns.org where the url will be yoursubdomain.duckdns.org and the subdomains    can be www,ftp,cloud\r\n  - The container detects changes to url and subdomains, revokes existing certs and generates new ones during start. \r\n  - It also detects changes to the DHLEVEL parameter and replaces the dhparams file.\r\n  \r\n  - If you'd like to password protect your sites, you can use htpasswd. Run the following command on your host to generate the htpasswd file docker exec -it letsencrypt htpasswd -c /config/nginx/.htpasswd &lt;username&gt;",
+      "env": [
+        {
+          "label": "EMAIL",
+          "name": "EMAIL",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "URL",
+          "name": "URL",
+          "set": "-Xms256m -Xmx512m"
+        },
+        {
+          "label": "SUBDOMAINS",
+          "name": "SUBDOMAINS",
+          "set": "www,"
+        },
+        {
+          "label": "ONLY_SUBDOMAINS",
+          "name": "ONLY_SUBDOMAINS",
+          "set": "false"
+        },
+        {
+          "label": "DHLEVEL",
+          "name": "DHLEVEL",
+          "set": "2048"
+        },
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "VALIDATION",
+          "name": "VALIDATION",
+          "set": "http"
+        },
+        {
+          "label": "DNSPLUGIN",
+          "name": "DNSPLUGIN",
+          "set": "http"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/letsencrypt:latest",
+      "logo": "https://raw.githubusercontent.com/thesugarat/portainer_templates-1/master/Images/letsencrypt.png",
+      "platform": "linux",
+      "ports": [
+        "80/tcp",
+        "443/tcp"
+      ],
+      "title": "Let's Encrypt",
+      "name": "letsencrypt",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Cloud",
+        "Productivity",
+        "Tools",
+        "Other",
+        "Web"
+      ],
+      "description": "Nextcloud is an open source, self-hosted file sync and communication app platform. Access and sync your files, contacts, calendars and communicate and collaborate across your devices. You decide what happens with your data, where it is and who can access it!",
+      "env": [
+        {
+          "label": "PUID",
+          "name": "PUID",
+          "set": "1000"
+        },
+        {
+          "label": "PGID",
+          "name": "PGID",
+          "set": "1000"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ",
+          "set": "America/Chicago"
+        }
+      ],
+      "image": "linuxserver/nextcloud:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nextcloud.png",
+      "platform": "linux",
+      "ports": [
+        "443/tcp"
+      ],
+      "title": "Nextcloud",
+      "name": "nextcloud",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/config"
+        },
+        {
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Apprise-api",
+      "name": "apprise-api",
+      "note": "",
+      "description": "Apprise-api takes advantage of Apprise through your network with a user-friendly API. * Send notifications to more then 65+ services. * An incredibly lightweight gateway to Apprise. * A production ready micro-service at your disposal. Apprise API was designed to easily fit into existing (and new) eco-systems that are looking for a simple notification solution.",
+      "categories": [
+        "Tools",
+        "Development"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/apprise-api.png",
+      "image": "linuxserver/apprise-api:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "8000:8000/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/apprise-api"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Audacity",
+      "name": "audacity",
+      "note": "",
+      "description": "Audacity is an easy-to-use, multi-track audio editor and recorder. Developed by a group of volunteers as open source. (https://www.audacityteam.org/)",
+      "platform": "linux",
+      "categories": [
+        "Media"
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/audacity.png",
+      "image": "linuxserver/audacity:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/audacity/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Bazarr",
+      "name": "bazarr",
+      "note": "",
+      "description": "Bazarr is a companion application to Sonarr and Radarr. It can manage and download subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.",
+      "categories": [
+        "Multimedia",
+        "Downloader",
+        "Arr"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/bazarr.png",
+      "image": "linuxserver/bazarr:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "6767:6767/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/bazarr",
+          "container": "/config"
+        },
+        {
+          "bind": "/home/docker/bazarr/movies",
+          "container": "/movies"
+        },
+        {
+          "container": "/tv",
+          "bind": "/home/docker/bazarr/tv"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Changedetection.io",
+      "name": "changedetection.io",
+      "note": "",
+      "description": "Changedetection.io provides free, open-source web page monitoring, notification and change detection. (https://github.com/dgtlmoon/changedetection.io)",
+      "platform": "linux",
+      "logo": "https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/changedetection-icon.png",
+      "image": "linuxserver/changedetection.io:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "BASE_URL",
+          "label": "BASE_URL",
+          "default": "",
+          "description": "Specify the full URL (including protocol) when running behind a reverse proxy"
+        }
+      ],
+      "ports": [
+        "5000:5000/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/changedetection.io"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Code-server",
+      "name": "code-server",
+      "note": "",
+      "description": "Code-server is VS Code running on a remote server, accessible through the browser. - Code on your Chromebook, tablet, and laptop with a consistent dev environment. - If you have a Windows or Mac workstation, more easily develop for Linux. - Take advantage of large cloud servers to speed up tests, compilations, downloads, and more. - Preserve battery life when you're on the go. - All intensive computation runs on your server. - You're no longer running excess instances of Chrome. (https://coder.com)",
+      "categories": [
+        "Development"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/code-server.png",
+      "image": "linuxserver/code-server:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000"
+
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "America/Los_Angeles",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "PASSWORD",
+          "label": "PASSWORD",
+          "default": "password",
+          "description": "Optional web gui password, if `PASSWORD` or `HASHED_PASSWORD` is not provided, there will be no auth."
+        },
+        {
+          "name": "HASHED_PASSWORD",
+          "label": "HASHED_PASSWORD",
+          "default": "",
+          "description": "Optional web gui password, overrides `PASSWORD`, instructions on how to create it is below."
+        },
+        {
+          "name": "SUDO_PASSWORD",
+          "label": "SUDO_PASSWORD",
+          "default": "password",
+          "description": "If this optional variable is set, user will have sudo access in the code-server terminal with the specified password."
+        },
+        {
+          "name": "SUDO_PASSWORD_HASH",
+          "label": "SUDO_PASSWORD_HASH",
+          "default": "",
+          "description": "Optionally set sudo password via hash (takes priority over `SUDO_PASSWORD` var). Format is `$type$salt$hashed`."
+        },
+        {
+          "name": "PROXY_DOMAIN",
+          "label": "PROXY_DOMAIN",
+          "default": "code-server.mydomain",
+          "description": "If this optional variable is set, this domain will be proxied for subdomain proxying. See [Documentation](https://github.com/cdr/code-server/blob/master/docs/FAQ.md#sub-domains)"
+        },
+        {
+          "name": "DEFAULT_WORKSPACE",
+          "label": "DEFAULT_WORKSPACE",
+          "default": "/config/workspace",
+          "description": "If this optional variable is set, code-server will open this directory by default"
+        }
+      ],
+      "ports": [
+        "8443:8443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/code-server/config",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Dokuwiki",
+      "name": "dokuwiki",
+      "note": "",
+      "description": "Dokuwiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database. It is loved by users for its clean and readable syntax. The ease of maintenance, backup and integration makes it an administrator's favorite. Built in access controls and authentication connectors make DokuWiki especially useful in the enterprise context and the large number of plugins contributed by its vibrant community allow for a broad range of use cases beyond a traditional wiki.  (https://www.dokuwiki.org/dokuwiki/)",
+      "categories": [
+        "Productivity",
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dokuwiki.png",
+      "image": "linuxserver/dokuwiki:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/dokuwiki"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Doplarr",
+      "name": "doplarr",
+      "note": "",
+      "description": "Doplarr (https://github.com/kiranshila/Doplarr) is an *arr request bot for Discord.'",
+      "categories": [
+        "Arr"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/doplarr.png",
+      "image": "linuxserver/doplarr:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "DISCORD__TOKEN",
+          "label": "DISCORD__TOKEN",
+          "default": "",
+          "description": "Specify your discord bot token."
+        },
+        {
+          "name": "OVERSEERR__API",
+          "label": "OVERSEERR__API",
+          "default": "",
+          "description": "Specify your Overseerr API key. Leave blank if using Radarr/Sonarr."
+        },
+        {
+          "name": "OVERSEERR__URL",
+          "label": "OVERSEERR__URL",
+          "default": "http://localhost:5055",
+          "description": "Specify your Overseerr URL. Leave blank if using Radarr/Sonarr."
+        },
+        {
+          "name": "RADARR__API",
+          "label": "RADARR__API",
+          "default": "",
+          "description": "Specify your Radarr API key. Leave blank if using Overseerr."
+        },
+        {
+          "name": "RADARR__URL",
+          "label": "RADARR__URL",
+          "default": "http://localhost:7878",
+          "description": "Specify your Radarr URL. Leave blank if using Overseerr."
+        },
+        {
+          "name": "SONARR__API",
+          "label": "SONARR__API",
+          "default": "",
+          "description": "Specify your Sonarr API key. Leave blank if using Overseerr."
+        },
+        {
+          "name": "SONARR__URL",
+          "label": "SONARR__URL",
+          "default": "http://localhost:8989",
+          "description": "Specify your Sonarr URL. Leave blank if using Overseerr."
+        },
+        {
+          "name": "DISCORD__MAX_RESULTS",
+          "label": "DISCORD__MAX_RESULTS",
+          "default": "25",
+          "description": "Sets the maximum size of the search results selection"
+        },
+        {
+          "name": "DISCORD__REQUESTED_MSG_STYLE",
+          "label": "DISCORD__REQUESTED_MSG_STYLE",
+          "default": ":plain",
+          "description": "Sets the style of the request alert message. One of `:plain` `:embed` `:none`"
+        },
+        {
+          "name": "SONARR__QUALITY_PROFILE",
+          "label": "SONARR__QUALITY_PROFILE",
+          "default": "",
+          "description": "The name of the quality profile to use by default for Sonarr"
+        },
+        {
+          "name": "RADARR__QUALITY_PROFILE",
+          "label": "RADARR__QUALITY_PROFILE",
+          "default": "",
+          "description": "The name of the quality profile to use by default for Radarr"
+        },
+        {
+          "name": "SONARR__ROOTFOLDER",
+          "label": "SONARR__ROOTFOLDER",
+          "default": "",
+          "description": "The root folder to use by default for Sonarr"
+        },
+        {
+          "name": "RADARR__ROOTFOLDER",
+          "label": "RADARR__ROOTFOLDER",
+          "default": "",
+          "description": "The root folder to use by default for Radarr"
+        },
+        {
+          "name": "SONARR__LANGUAGE_PROFILE",
+          "label": "SONARR__LANGUAGE_PROFILE",
+          "default": "",
+          "description": "The name of the language profile to use by default for Sonarr"
+        },
+        {
+          "name": "OVERSEERR__DEFAULT_ID",
+          "label": "OVERSEERR__DEFAULT_ID",
+          "default": "",
+          "description": "The Overseerr user id to use by default if there is no associated discord account for the requester"
+        },
+        {
+          "name": "PARTIAL_SEASONS",
+          "label": "PARTIAL_SEASONS",
+          "default": "true",
+          "description": "Sets whether users can request partial seasons."
+        },
+        {
+          "name": "LOG_LEVEL",
+          "label": "LOG_LEVEL",
+          "default": ":info",
+          "description": "The log level for the logging backend. This can be changed for debugging purposes. One of trace `:debug` `:info` `:warn` `:error` `:fatal` `:report`"
+        },
+        {
+          "name": "JAVA_OPTS",
+          "label": "JAVA_OPTS",
+          "default": "",
+          "description": "For passing additional java options."
+        }
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/doplarr"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Emby",
+      "name": "emby",
+      "note": "",
+      "description": "Emby organizes video, music, live TV, and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone emby Media Server.",
+      "categories": [
+        "Media Server",
+        "Paid"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/emby.png",
+      "image": "linuxserver/emby:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "8096:8096/tcp",
+        "8920:8920/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/emby/config"
+        },
+        {
+          "container": "/data/tvshows",
+          "bind": "/home/docker/emby/data/tvshows"
+        },
+        {
+          "container": "/data/movies",
+          "bind": "/home/docker/emby/data/movies"
+        },
+        {
+          "container": "/opt/vc/lib",
+          "bind": "/opt/vc/lib"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "EmbyStat",
+      "name": "embystat",
+      "note": "",
+      "description": "[Embystat](https://github.com/mregni/EmbyStat) is a personal web server that can calculate all kinds of statistics from your (local) Emby server. Just install this on your server and let him calculate all kinds of fun stuff.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/embystat-logo.png",
+      "image": "linuxserver/embystat:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "6555:6555/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/embystat"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Emulatorjs",
+      "name": "emulatorjs",
+      "note": "",
+      "description": "Emulatorjs - In browser web based emulation portable to nearly any device for many retro consoles. A mix of emulators is used between Libretro and EmulatorJS.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/emulatorjs.png",
+      "image": "linuxserver/emulatorjs:latest",
+      "categories": [
+        "Gaming"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "SUBFOLDER",
+          "label": "SUBFOLDER",
+          "default": "/",
+          "description": "Specify a subfolder for reverse proxies IE '/FOLDER/'"
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "80:80/tcp",
+        "4001:4001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/emulatorjs"
+        },
+        {
+          "container": "/data",
+          "bind": "/home/docker/emulatorjs/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Fail2ban",
+      "name": "fail2ban",
+      "note": "",
+      "description": "Fail2ban is a daemon to ban hosts that cause multiple authentication errors.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/fail2ban.png",
+      "image": "linuxserver/fail2ban:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "VERBOSITY",
+          "label": "VERBOSITY",
+          "default": "-vv",
+          "description": "Set the container log verbosity. Valid options are -v, -vv, -vvv, -vvvv, or leaving the value blank or not setting the variable."
+        }
+      ],
+      "ports": [
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/fail2ban/config"
+        },
+        {
+          "container": "/var/log:ro",
+          "bind": "/home/docker/fail2ban/var/log:ro"
+        },
+        {
+          "container": "/remotelogs/airsonic:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/airsonic:ro"
+        },
+        {
+          "container": "/remotelogs/apache2:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/apache2:ro"
+        },
+        {
+          "container": "/remotelogs/authelia:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/authelia:ro"
+        },
+        {
+          "container": "/remotelogs/emby:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/emby:ro"
+        },
+        {
+          "container": "/remotelogs/filebrowser:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/filebrowser:ro"
+        },
+        {
+          "container": "/remotelogs/homeassistant:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/homeassistant:ro"
+        },
+        {
+          "container": "/remotelogs/lighttpd:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/lighttpd:ro"
+        },
+        {
+          "container": "/remotelogs/nextcloud:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nextcloud:ro"
+        },
+        {
+          "container": "/remotelogs/nginx:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nginx:ro"
+        },
+        {
+          "container": "/remotelogs/nzbget:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/nzbget:ro"
+        },
+        {
+          "container": "/remotelogs/overseerr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/overseerr:ro"
+        },
+        {
+          "container": "/remotelogs/prowlarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/prowlarr:ro"
+        },
+        {
+          "container": "/remotelogs/radarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/radarr:ro"
+        },
+        {
+          "container": "/remotelogs/sabnzbd:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/sabnzbd:ro"
+        },
+        {
+          "container": "/remotelogs/sonarr:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/sonarr:ro"
+        },
+        {
+          "container": "/remotelogs/unificontroller:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/unificontroller:ro"
+        },
+        {
+          "container": "/remotelogs/vaultwarden:ro",
+          "bind": "/home/docker/fail2ban/remotelogs/vaultwarden:ro"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Homeassistant",
+      "name": "homeassistant",
+      "note": "",
+      "description": "Home Assistant Core is an open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server.",
+      "categories": [
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homeassistant.png",
+      "image": "linuxserver/homeassistant:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "8123:8123/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/homeassistant"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Jellyfin",
+      "name": "jellyfin",
+      "note": "",
+      "description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jellyfin.png",
+      "image": "linuxserver/jellyfin:latest",
+      "categories": [
+        "Media Server",
+        "LDAP"
+      ],
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "JELLYFIN_PublishedServerUrl",
+          "label": "JELLYFIN_PublishedServerUrl",
+          "default": "192.168.0.5",
+          "description": "Set the autodiscovery response domain or IP address."
+        }
+      ],
+      "network": "AppBridge",
+      "ports": [
+        "8096:8096/tcp",
+        "8920:8920/tcp",
+        "7359:7359/udp",
+        "1900:1900/udp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/jellyfin",
+          "container": "/config"
+        },
+        {
+          "bind": "/media/tvshows",
+          "container": "/data/tvshows"
+        },
+        {
+          "bind": "/media/movies",
+          "container": "/data/movies"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Kasm",
+      "name": "kasm",
+      "note": "",
+      "description": "Kasm workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services. Kasm uses devops-enabled Containerized Desktop Infrastructure (CDI) to create on-demand, disposable, docker containers that are accessible via web browser. Example use-cases include Remote Browser Isolation (RBI), Data Loss Prevention (DLP), Desktop as a Service (DaaS), Secure Remote Access Services (RAS), and Open Source Intelligence (OSINT) collections. The rendering of the graphical-based containers is powered by the open-source project [KasmVNC](https://www.kasmweb.com/kasmvnc.html?utm_campaign=LinuxServer&utm_source=kasmvnc).",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/kasm.png",
+      "image": "linuxserver/kasm:latest",
+      "privileged": true,
+      "env": [
+        {
+          "name": "KASM_PORT",
+          "label": "KASM_PORT",
+          "default": "443",
+          "description": "Specify the port you bind to the outside for Kasm Workspaces."
+        },
+        {
+          "name": "DOCKER_HUB_USERNAME",
+          "label": "DOCKER_HUB_USERNAME",
+          "default": "USER",
+          "description": "Optionally specify a DockerHub Username to pull private images."
+        },
+        {
+          "name": "DOCKER_HUB_PASSWORD",
+          "label": "DOCKER_HUB_PASSWORD",
+          "default": "PASS",
+          "description": "Optionally specify a DockerHub password to pull private images."
+        },
+        {
+          "name": "DOCKER_MTU",
+          "label": "DOCKER_MTU",
+          "default": "1500",
+          "description": "Optionally specify the mtu options passed to dockerd."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/kasm/opt",
+          "container": "/opt"
+        },
+        {
+          "bind": "/home/docker/kasm/profiles",
+          "container": "/profiles"
+        },
+        {
+          "bind": "/dev/input",
+          "container": "/dev/input"
+        },
+        {
+          "bind": "/run/udev/data",
+          "container": "/run/udev/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Libreoffice",
+      "name": "libreoffice",
+      "note": "",
+      "description": "LibreOffice is a free and powerful office suite, and a successor to OpenOffice.org (commonly known as OpenOffice). Its clean interface and feature-rich tools help you unleash your creativity and enhance your productivity.",
+      "categories": [
+        "Productivity",
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/libreoffice.png",
+      "image": "linuxserver/libreoffice:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/libreoffice/config",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Mastodon",
+      "name": "mastodon",
+      "note": "",
+      "description": "Mastodon is a free, open-source social network server based on ActivityPub where users can follow friends and discover new ones. (https://github.com/mastodon/mastodon/)",
+      "categories": [
+        "Communication"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mastodon.png",
+      "image": "linuxserver/mastodon:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "LOCAL_DOMAIN",
+          "label": "LOCAL_DOMAIN",
+          "default": "example.com",
+          "description": "This is the unique identifier of your server in the network. It cannot be safely changed later."
+        },
+        {
+          "name": "REDIS_HOST",
+          "label": "REDIS_HOST",
+          "default": "redis",
+          "description": "Redis server hostname"
+        },
+        {
+          "name": "REDIS_PORT",
+          "label": "REDIS_PORT",
+          "default": "6379",
+          "description": "Redis port"
+        },
+        {
+          "name": "DB_HOST",
+          "label": "DB_HOST",
+          "default": "db",
+          "description": "Postgres database hostname"
+        },
+        {
+          "name": "DB_USER",
+          "label": "DB_USER",
+          "default": "mastodon",
+          "description": "Postgres username"
+        },
+        {
+          "name": "DB_NAME",
+          "label": "DB_NAME",
+          "default": "mastodon",
+          "description": "Postgres db name"
+        },
+        {
+          "name": "DB_PASS",
+          "label": "DB_PASS",
+          "default": "mastodon",
+          "description": "Postgres password"
+        },
+        {
+          "name": "DB_PORT",
+          "label": "DB_PORT",
+          "default": "5432",
+          "description": "Portgres port"
+        },
+        {
+          "name": "ES_ENABLED",
+          "label": "ES_ENABLED",
+          "default": "false",
+          "description": "Enable or disable Elasticsearch (requires a separate ES instance)"
+        },
+        {
+          "name": "SECRET_KEY_BASE",
+          "label": "SECRET_KEY_BASE",
+          "default": "",
+          "description": "Browser session secret. Changing it will break all active browser sessions."
+        },
+        {
+          "name": "OTP_SECRET",
+          "label": "OTP_SECRET",
+          "default": "",
+          "description": "MFA secret. Changing it will break two-factor authentication."
+        },
+        {
+          "name": "VAPID_PRIVATE_KEY",
+          "label": "VAPID_PRIVATE_KEY",
+          "default": "",
+          "description": "Push notification private key. Changing it will break push notifications."
+        },
+        {
+          "name": "VAPID_PUBLIC_KEY",
+          "label": "VAPID_PUBLIC_KEY",
+          "default": "",
+          "description": "Push notification public key. Changing it will break push notifications."
+        },
+        {
+          "name": "SMTP_SERVER",
+          "label": "SMTP_SERVER",
+          "default": "mail.example.com",
+          "description": "SMTP server for email notifications"
+        },
+        {
+          "name": "SMTP_PORT",
+          "label": "SMTP_PORT",
+          "default": "25",
+          "description": "SMTP server port"
+        },
+        {
+          "name": "SMTP_LOGIN",
+          "label": "SMTP_LOGIN",
+          "default": "",
+          "description": "SMTP username"
+        },
+        {
+          "name": "SMTP_PASSWORD",
+          "label": "SMTP_PASSWORD",
+          "default": "",
+          "description": "SMTP password"
+        },
+        {
+          "name": "SMTP_FROM_ADDRESS",
+          "label": "SMTP_FROM_ADDRESS",
+          "default": "notifications@example.com",
+          "description": "From address for emails send from Mastodon"
+        },
+        {
+          "name": "S3_ENABLED",
+          "label": "S3_ENABLED",
+          "default": "false",
+          "description": "Enable or disable S3 storage of uploaded files"
+        },
+        {
+          "name": "WEB_DOMAIN",
+          "label": "WEB_DOMAIN",
+          "default": "mastodon.example.com",
+          "description": "This can be set if you want your server identifier to be different to the subdomain hosting Mastodon. See [https://docs.joinmastodon.org/admin/config/#basic](https://docs.joinmastodon.org/admin/config/#basic)"
+        },
+        {
+          "name": "ES_HOST",
+          "label": "ES_HOST",
+          "default": "es",
+          "description": "Elasticsearch server hostname"
+        },
+        {
+          "name": "ES_PORT",
+          "label": "ES_PORT",
+          "default": "9200",
+          "description": "Elasticsearch port"
+        },
+        {
+          "name": "ES_USER",
+          "label": "ES_USER",
+          "default": "elastic",
+          "description": "Elasticsearch username"
+        },
+        {
+          "name": "ES_PASS",
+          "label": "ES_PASS",
+          "default": "elastic",
+          "description": "Elasticsearch password"
+        },
+        {
+          "name": "S3_BUCKET",
+          "label": "S3_BUCKET",
+          "default": "",
+          "description": "S3 bucket hostname"
+        },
+        {
+          "name": "AWS_ACCESS_KEY_ID",
+          "label": "AWS_ACCESS_KEY_ID",
+          "default": "",
+          "description": "S3 bucket access key ID"
+        },
+        {
+          "name": "AWS_SECRET_ACCESS_KEY",
+          "label": "AWS_SECRET_ACCESS_KEY",
+          "default": "",
+          "description": "S3 bucket secret access key"
+        },
+        {
+          "name": "S3_ALIAS_HOST",
+          "label": "S3_ALIAS_HOST",
+          "default": "",
+          "description": "Alternate hostname for object fetching if you are front the S3 connections."
+        },
+        {
+          "name": "SIDEKIQ_ONLY",
+          "label": "SIDEKIQ_ONLY",
+          "default": "false",
+          "description": "Only run the sidekiq service in this container instance. For large scale instances that need better queue handling."
+        },
+        {
+          "name": "SIDEKIQ_QUEUE",
+          "label": "SIDEKIQ_QUEUE",
+          "default": "",
+          "description": "The name of the sidekiq queue to run in this container. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-queues)."
+        },
+        {
+          "name": "SIDEKIQ_DEFAULT",
+          "label": "SIDEKIQ_DEFAULT",
+          "default": "false",
+          "description": "Set to `true` on the main container if you're running additional sidekiq instances. It will run the `default` queue."
+        },
+        {
+          "name": "SIDEKIQ_THREADS",
+          "label": "SIDEKIQ_THREADS",
+          "default": "5",
+          "description": "The number of threads for sidekiq to use. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-threads)."
+        },
+        {
+          "name": "DB_POOL",
+          "label": "DB_POOL",
+          "default": "5",
+          "description": "The size of the DB connection pool, must be *at least* the same as `SIDEKIQ_THREADS`. See [notes](https://docs.joinmastodon.org/admin/scaling/#sidekiq-threads)."
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/mastodon/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Phpmyadmin",
+      "name": "phpmyadmin",
+      "note": "",
+      "description": "Phpmyadmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB.",
+      "categories": [
+        "Tools"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/phpmyadmin.png",
+      "image": "linuxserver/phpmyadmin:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "PMA_ARBITRARY",
+          "label": "PMA_ARBITRARY",
+          "default": "1",
+          "description": "Set to `1` to allow you to connect to any server. Setting to `0` will only allow you to connect to specified hosts (See Application Setup)"
+        },
+        {
+          "name": "PMA_ABSOLUTE_URI",
+          "label": "PMA_ABSOLUTE_URI",
+          "default": "https://phpmyadmin.example.com",
+          "description": "Set the URL you will use to access the web frontend"
+        }
+      ],
+      "ports": [
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/phpmyadmin/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Pidgin",
+      "name": "pidgin",
+      "note": "",
+      "description": "Pidgin is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pidgin.png",
+      "image": "linuxserver/pidgin:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/pidgin",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Remmina",
+      "name": "remmina",
+      "note": "",
+      "description": "Remmina is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/remmina.png",
+      "image": "linuxserver/remmina:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/remmina/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Sqlitebrowser",
+      "name": "sqlitebrowser",
+      "note": "",
+      "description": "DB Browser for SQLite is a high quality, visual, open source tool to create, design, and edit database files compatible with SQLite.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/sqlitebrowser.png",
+      "image": "linuxserver/sqlitebrowser:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/config",
+          "bind": "/home/docker/sqlitebrowser/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Swag",
+      "name": "swag",
+      "note": "",
+      "description": "SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt\u2122) sets up an Nginx webserver and reverse proxy with php support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.",
+      "platform": "linux",
+      "logo": "https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/swag.gif",
+      "image": "linuxserver/swag:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "URL",
+          "label": "URL",
+          "default": "yourdomain.url",
+          "description": "Top url you have control over (`customdomain.com` if you own it, or `customsubdomain.ddnsprovider.com` if dynamic dns)."
+        },
+        {
+          "name": "VALIDATION",
+          "label": "VALIDATION",
+          "default": "http",
+          "description": "Certbot validation method to use, options are `http` or `dns` (`dns` method also requires `DNSPLUGIN` variable set)."
+        },
+        {
+          "name": "SUBDOMAINS",
+          "label": "SUBDOMAINS",
+          "default": "www,",
+          "description": "Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only)"
+        },
+        {
+          "name": "CERTPROVIDER",
+          "label": "CERTPROVIDER",
+          "default": "",
+          "description": "Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt."
+        },
+        {
+          "name": "DNSPLUGIN",
+          "label": "DNSPLUGIN",
+          "default": "cloudflare",
+          "description": "Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `godaddy`, `google`, `google-domains`, `he`, `hetzner`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`."
+        },
+        {
+          "name": "PROPAGATION",
+          "label": "PROPAGATION",
+          "default": "",
+          "description": "Optionally override (in seconds) the default propagation time for the dns plugins."
+        },
+        {
+          "name": "EMAIL",
+          "label": "EMAIL",
+          "default": "",
+          "description": "Optional e-mail address used for cert expiration notifications (Required for ZeroSSL)."
+        },
+        {
+          "name": "ONLY_SUBDOMAINS",
+          "label": "ONLY_SUBDOMAINS",
+          "default": "false",
+          "description": "If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true`"
+        },
+        {
+          "name": "EXTRA_DOMAINS",
+          "label": "EXTRA_DOMAINS",
+          "default": "",
+          "description": "Additional fully qualified domain names (comma separated, no spaces) ie. `extradomain.com,subdomain.anotherdomain.org,*.anotherdomain.org`"
+        },
+        {
+          "name": "STAGING",
+          "label": "STAGING",
+          "default": "false",
+          "description": "Set to `true` to retrieve certs in staging mode. Rate limits will be much higher, but the resulting cert will not pass the browser's security test. Only to be used for testing purposes."
+        }
+      ],
+      "ports": [
+        "443:443/tcp",
+        "80:80/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/swag",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wikijs",
+      "name": "wikijs",
+      "note": "Setup mysql, postgres, mariadb, mssql database first, or use the default sqlite.",
+      "description": "Wikijs is a modern, lightweight and powerful wiki app built on NodeJS. (https://wiki.js.org/)",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wikijs.png",
+      "image": "linuxserver/wikijs:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "DB_TYPE",
+          "label": "DB_TYPE",
+          "default": "sqlite",
+          "description": "Type of database (mysql, postgres, mariadb, mssql or sqlite)"
+        },
+        {
+          "name": "DB_HOST",
+          "label": "DB_HOST",
+          "default": "",
+          "description": "Hostname or IP of the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_PORT",
+          "label": "DB_PORT",
+          "default": "",
+          "description": "Port of the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_NAME",
+          "label": "DB_NAME",
+          "default": "",
+          "description": "Database name (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_USER",
+          "label": "DB_USER",
+          "default": "",
+          "description": "Username to connect to the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        },
+        {
+          "name": "DB_PASS",
+          "label": "DB_PASS",
+          "default": "",
+          "description": "Password to connect to the database (For PostgreSQL, MySQL, MariaDB and MSSQL)"
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wikijs/config",
+          "container": "/config"
+        },
+        {
+          "bind": "/home/docker/wikijs/data",
+          "container": "/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wireguard",
+      "name": "wireguard",
+      "note": "",
+      "description": "WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireguard.png",
+      "image": "linuxserver/wireguard:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1000",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "1000",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        },
+        {
+          "name": "SERVERURL",
+          "label": "SERVERURL",
+          "default": "wireguard.domain.com",
+          "description": "External IP or domain name for docker host. Used in server mode. If set to `auto`, the container will try to determine and set the external IP automatically"
+        },
+        {
+          "name": "SERVERPORT",
+          "label": "SERVERPORT",
+          "default": "51820",
+          "description": "External port for docker host. Used in server mode."
+        },
+        {
+          "name": "PEERS",
+          "label": "PEERS",
+          "default": "1",
+          "description": "Number of peers to create confs for. Required for server mode. Can also be a list of names: `myPC,myPhone,myTablet` (alphanumeric only)"
+        },
+        {
+          "name": "PEERDNS",
+          "label": "PEERDNS",
+          "default": "auto",
+          "description": "DNS server set in peer/client configs (can be set as `8.8.8.8`). Used in server mode. Defaults to `auto`, which uses wireguard docker host's DNS via included CoreDNS forward."
+        },
+        {
+          "name": "INTERNAL_SUBNET",
+          "label": "INTERNAL_SUBNET",
+          "default": "10.13.13.0",
+          "description": "Internal subnet for the wireguard and server and peers (only change if it clashes). Used in server mode."
+        },
+        {
+          "name": "ALLOWEDIPS",
+          "label": "ALLOWEDIPS",
+          "default": "0.0.0.0/0",
+          "description": "The IPs/Ranges that the peers will be able to reach using the VPN connection. If not specified the default value is: '0.0.0.0/0, ::0/0' This will cause ALL traffic to route through the VPN, if you want split tunneling, set this to only the IPs you would like to use the tunnel AND the ip of the server's WG ip, such as 10.13.13.1."
+        },
+        {
+          "name": "PERSISTENTKEEPALIVE_PEERS",
+          "label": "PERSISTENTKEEPALIVE_PEERS",
+          "default": "",
+          "description": "Set to `all` or a list of comma separated peers (ie. `1,4,laptop`) for the wireguard server to send keepalive packets to listed peers every 25 seconds. Useful if server is accessed via domain name and has dynamic IP. Used only in server mode."
+        },
+        {
+          "name": "LOG_CONFS",
+          "label": "LOG_CONFS",
+          "default": "true",
+          "description": "Generated QR codes will be displayed in the docker log. Set to `false` to skip log output."
+        }
+      ],
+      "ports": [
+        "51820:51820/udp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wireguard/config",
+          "container": "/config"
+        },
+        {
+          "bind": "/lib/modules",
+          "container": "/lib/modules"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "type": 1,
+      "title": "Wireshark",
+      "name": "wireshark",
+      "note": "",
+      "description": "Wireshark is the world's foremost and widely-used network protocol analyzer. It lets you see what's happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions.",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireshark.png",
+      "image": "linuxserver/wireshark:latest",
+      "network": "host",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for UserID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for GroupID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)."
+        }
+      ],
+      "ports": [
+        "3000:3000/tcp",
+        "3001:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/wireshark",
+          "container": "/config"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "categories": [
+        "Authentication"
+      ],
+      "note": "recommend requirements: a host with at least 2 CPU cores and 2 GB of RAM",
+      "description": "authentik is an open-source Identity Provider focused on flexibility and versatility. You can use authentik in an existing environment to add support for new protocols, implement sign-up/recovery/etc. in your application so you don't have to deal with it, and many other things.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authentik.png",
+      "name": "authentik",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/authentik.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Authentik",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "description": "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases",
+      "env": [
+        {
+          "default": "/home/node/trilium-data",
+          "label": "TRILIUM_DATA_DIR",
+          "name": "TRILIUM_DATA_DIR"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "image": "zadam/trilium:latest",
+      "logo": "https://www.saashub.com/images/app/service_logos/55/2901389fab77/large.png?1561117248",
+      "name": "trilium",
+      "platform": "linux",
+      "ports": [
+        "3388:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Trilium",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/trilium-data",
+          "container": "/home/node/trilium-data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Communication"
+      ],
+      "description": "Rocket.Chat is an open-source fully customizable communications platform developed in JavaScript for organizations with high standards of data protection.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/rocketchat.png",
+      "note": "Rocket.Chat Server Container",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/rocketchat.yml",
+        "url": "https://github.com/mycroftwilde/portainer_templates"
+      },
+      "title": "Rocket Chat",
+      "name": "rocketchat",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "Joplin is an open-source note-taking app",
+      "env": [
+        {
+          "default": "22300",
+          "label": "PORT",
+          "name": "PORT"
+        },
+        {
+          "default": "http://joplin.yourdomain.tld:22300",
+          "label": "URL",
+          "name": "URL"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/laurent22/joplin/master/Assets/SquareIcon512.png",
+      "note": "Joplin is an open-source note-taking app",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/joplin.yml",
+        "url": "https://github.com/mycroftwilde/portainer_templates"
+      },
+      "title": "Joplin",
+      "name": "joplin",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Helps you organize your self-hosted services by making them accessible from a single place.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dashy.png",
+      "name": "dashy",
+      "platform": "linux",
+      "image": "lissy93/dashy:latest",
+      "title": "Dashy",
+      "restart_policy": "unless-stopped",
+      "type": 3,
+      "ports": [
+        "4000:80/tcp"
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Flame is self-hosted startpage for your server. Its design is inspired (heavily) by SUI. Flame is very easy to setup and use. With built-in editors, it allows you to setup your very own application hub in no time - no file editing necessary.",
+      "image": "pawelmalak/flame",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/flame.png",
+      "name": "flame-dashboard",
+      "platform": "linux",
+      "ports": [
+        "5005:5005/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Flame-Dashboard",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/flame-dashboard",
+          "container": "/app/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Authentication"
+      ],
+      "description": "An open-source authentication and authorization server providing 2-factor authentication and single sign-on (SSO) for your applications via a web portal.",
+      "env": [
+        {
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "authelia/authelia:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authelia.png",
+      "name": "authelia",
+      "note": "Requires a configuration.yml file in order to work. Documentation is available <a href='https://docs.authelia.com/deployment/deployment-ha'>here</a>.",
+      "platform": "linux",
+      "ports": [
+        "9091:9091/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Authelia",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/authelia",
+          "container": "/etc/authelia/"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Security"
+      ],
+      "description": "This is a Bitwarden server API implementation written in Rust compatible with upstream Bitwarden clients*, perfect for self-hosted deployment where running the official resource-heavy service might not be ideal.",
+      "image": "vaultwarden/server:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/vaultwarden.png",
+      "name": "vaultwarden",
+      "note": "This project is not associated with the Bitwarden project nor 8bit Solutions LLC.",
+      "platform": "linux",
+      "ports": [
+        "80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Vaultwarden",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/vaultwarden",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "description": "A clientless remote desktop gateway.",
+      "image": "oznu/guacamole:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/guacamole.png",
+      "name": "guacamole",
+      "note": "The default login will be guacadmin/guacadmin. It is common practice to add a new admin user and remove the default user for Guacamole.",
+      "platform": "linux",
+      "ports": [
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Guacamole",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/guacamole",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "A dead simple static HOMepage for your servER to keep your s ervices on hand, from a simple yaml configuration file.",
+      "image": "b4bz/homer:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homer.png",
+      "name": "homer",
+      "note": "This container requires a yml file within the config volume. See the documentation here https://github.com/bastienwirtz/homer",
+      "platform": "linux",
+      "ports": [
+        "8902:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Homer",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homer/assets",
+          "container": "/www/assets"
+        },
+        {
+          "bind": "/home/docker/homer/config",
+          "container": "/www/config.yml"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Monitoring"
+      ],
+      "description": "Create agents that monitor and act on your behalf.",
+      "image": "huginn/huginn:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/huginn.png",
+      "name": "huginn",
+      "platform": "linux",
+      "ports": [
+        "3000:3000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Huginn",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/huginn",
+          "container": "/var/lib/mysql"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Security"
+      ],
+      "description": "Nginx Proxy Manager enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.",
+      "image": "jc21/nginx-proxy-manager",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nginx-proxy-manager.png",
+      "name": "nginx-proxy-manager",
+      "platform": "linux",
+      "env": [
+        {
+          "label": "DB_SQLITE_FILE",
+          "name": "DB_SQLITE_FILE",
+          "default": "/data/database.sqlite"
+        }
+      ],
+      "ports": [
+        "80:80/tcp",
+        "81:81/tcp",
+        "443:443/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Nginx Proxy Manager",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/nginx-proxy/data",
+          "container": "/data"
+        },
+        {
+          "bind": "/home/docker/nginx-proxy/letsencrypt",
+          "container": "/etc/letsencrypt"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "ownCloud is a self-hosted file sync and share server. It provides access to your data through a web interface, sync clients or WebDAV while providing a platform to view, sync and share across devices easily\u2014all under your control. ownCloud\u2019s open architecture is extensible via a simple but powerful API for applications and plugins and it works with any storage.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "label": "OWNCLOUD_DOMAIN",
+          "name": "OWNCLOUD_DOMAIN"
+        },
+        {
+          "label": "DB_PASSWORD",
+          "name": "DB_PASSWORD"
+        },
+        {
+          "label": "ADMIN_USERNAME",
+          "name": "ADMIN_USERNAME"
+        },
+        {
+          "label": "ADMIN_PASSWORD",
+          "name": "ADMIN_PASSWORD"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/owncloud.png",
+      "name": "owncloud",
+      "note": "The database user is owncloud and the database is owncloud.",
+      "platform": "linux",
+      "image": "owncloud/server:latest",
+      "title": "Owncloud",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "A Linux network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole.",
+      "image": "pihole/pihole:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pihole.png",
+      "name": "pihole",
+      "note": "When the installation is complete, navigate to your.ip.goes.here:1010/admin. Follow the article <a href='https://medium.com/@niktrix/getting-rid-of-systemd-resolved-consuming-port-53-605f0234f32f'>here</a> if you run into issues binding to port 53.",
+      "platform": "linux",
+      "ports": [
+        "53:53/tcp",
+        "53:53/udp",
+        "67:67/udp",
+        "1010:80/tcp",
+        "4443:443/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Pi-Hole",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/pihole",
+          "container": "/etc/pihole"
+        },
+        {
+          "bind": "/home/docker/pihole/DNS",
+          "container": "/etc/dnsmasq.d"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Tools"
+      ],
+      "description": "This container contains OpenVPN and Transmission with a configuration where Transmission is running only when OpenVPN has an active tunnel. It bundles configuration files for many popular VPN providers to make the setup easier.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "MULLVAD",
+          "description": "https://haugene.github.io/docker-transmission-openvpn/supported-providers/",
+          "label": "OPENVPN_PROVIDER",
+          "name": "OPENVPN_PROVIDER"
+        },
+        {
+          "default": "",
+          "label": "OPENVPN_USERNAME",
+          "name": "OPENVPN_USERNAME"
+        },
+        {
+          "default": "",
+          "label": "OPENVPN_PASSWORD",
+          "name": "OPENVPN_PASSWORD"
+        },
+        {
+          "default": "192.168.0.0/24",
+          "label": "LOCAL_NETWORK",
+          "name": "LOCAL_NETWORK"
+        }
+      ],
+      "image": "haugene/transmission-openvpn:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/transmission.png",
+      "name": "transmission-openvpn",
+      "note": "List of supported providers available <a href='https://haugene.github.io/docker-transmission-openvpn/supported-providers'/>here</a>.",
+      "platform": "linux",
+      "ports": [
+        "9091:9091/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Transmission-OpenVPN",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/data"
+        },
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Self-hosted, ad-free, privacy-respecting Google metasearch engine.",
+      "image": "benbusby/whoogle-search:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/whoogle-search.png",
+      "name": "whoogle",
+      "platform": "linux",
+      "ports": [
+        "5001:5000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Whoogle",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/whoogle",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Yacht is a web interface for managing docker containers with an emphasis on templating to provide 1 click deployments. Think of it like a decentralized app store for servers that anyone can make packages for.",
+      "image": "selfhostedpro/yacht:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/yacht.png",
+      "name": "yacht",
+      "platform": "linux",
+      "ports": [
+        "8001:8000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Yacht",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/yacht",
+          "container": "/config"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "categories": [
+        "Tools"
+      ],
+      "title": "Paperless-ng",
+      "name": "paperless-ng",
+      "note": "",
+      "description": "Paperless-ng is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.'",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/paperless-ng.png",
+      "image": "linuxserver/paperless-ng:latest",
+      "env": [
+        {
+          "name": "PUID",
+          "label": "PUID",
+          "default": "1024",
+          "description": "for GroupID"
+        },
+        {
+          "name": "PGID",
+          "label": "PGID",
+          "default": "100",
+          "description": "for UserID"
+        },
+        {
+          "name": "TZ",
+          "label": "TZ",
+          "default": "Europe/Amsterdam",
+          "description": "Specify a timezone to use for example Europe/Amsterdam"
+        },
+        {
+          "name": "REDIS_URL",
+          "label": "REDIS_URL",
+          "default": "",
+          "description": "Specify an external redis instance to use. Can optionally include a port (`redis:6379`) and/or db (`redis/foo`). If left blank or not included, will use a built-in redis instance. If changed after initial setup will also require manual modification of /config/settings.py"
+        }
+      ],
+      "ports": [
+        "8000:8000/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/paperless-ng/config",
+          "container": "/config"
+        },
+        {
+          "container": "/data",
+          "bind": "/home/docker/paperless-ng/data"
+        }
+      ],
+      "restart_policy": "unless-stopped"
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Multimedia"
+      ],
+      "description": "YoutubeDL-Material is a Material Design frontend for youtube-dl. It's coded using Angular 9 for the frontend, and Node.js on the backend.",
+      "image": "tzahi12345/youtubedl-material:latest",
+      "logo": "https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Images/ytdlm.png",
+      "name": "youtubedl-material",
+      "platform": "linux",
+      "ports": [
+        "17442:17442/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "YouTubeDL-Material",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/ytdlm",
+          "container": "/app/appdata"
+        },
+        {
+          "bind": "/home/docker/ytdlm/video",
+          "container": "/app/video"
+        },
+        {
+          "bind": "/home/docker/ytdlm/subscriptions",
+          "container": "/app/subscriptions"
+        },
+        {
+          "bind": "/home/docker/ytdlm/users",
+          "container": "/app/users"
+        },
+        {
+          "bind": "/home/docker/ytdlm/audio",
+          "container": "/app/audio"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools",
+        "Gaming"
+      ],
+      "description": "C# application with primary purpose of farming Steam cards from multiple accounts simultaneously.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "justarchi/archisteamfarm:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/archisteamfarm.png",
+      "name": "archisteamfarm",
+      "platform": "linux",
+      "ports": [
+        "1242:1242/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "ArchiSteamFarm",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/archiSteamFarm/config",
+          "container": "/app/config"
+        },
+        {
+          "bind": "/home/docker/archiSteamFarm/plugins",
+          "container": "/app/plugins"
+        },
+        {
+          "bind": "/home/docker/archiSteamFarm/logs",
+          "container": "/app/logs"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "ArchiveBox is a powerful, self-hosted internet archiving solution to collect, save, and view sites you want to preserve offline.",
+      "env": [
+        {
+          "default": "*",
+          "label": "ALLOWED_HOSTS",
+          "name": "ALLOWED_HOSTS"
+        },
+        {
+          "default": "750m",
+          "label": "MEDIA_MAX_SIZE",
+          "name": "MEDIA_MAX_SIZE"
+        },
+        {
+          "default": "true",
+          "label": "PUBLIC_INDEX",
+          "name": "PUBLIC_INDEX"
+        },
+        {
+          "default": "true",
+          "label": "PUBLIC_SNAPSHOTS",
+          "name": "PUBLIC_SNAPSHOTS"
+        },
+        {
+          "default": "false",
+          "label": "PUBLIC_ADD_VIEW",
+          "name": "PUBLIC_ADD_VIEW"
+        }
+      ],
+      "image": "archivebox/archivebox:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/archivebox.png",
+      "name": "archivebox",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "8002:8000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Archivebox",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/archivebox",
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "Caddy is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go. ",
+      "image": "caddy:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/caddy.png",
+      "name": "caddy",
+      "note": "Create firewall rule first: 'sudo ufw allow http' and 'sudo ufw allow https'. Add port 2019/tcp for admin endpoint",
+      "platform": "linux",
+      "network": "host",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp",
+        "443:443/udp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Caddy",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "caddy",
+          "container": "/data"
+        },
+        {
+          "bind": "caddy",
+          "container": "/config"
+        },
+        {
+          "bind": "caddyfiles",
+          "container": "/etc/caddy"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Security",
+        "Tools"
+      ],
+      "description": "ClamAV is an open source antivirus engine for detecting trojans, viruses, malware & other malicious threats.",
+      "image": "clamav/clamav",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/clamav.png",
+      "name": "clamav",
+      "platform": "linux",
+      "ports": [
+        "3310:3310/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "ClamAV",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/etc/timezone",
+          "container": "/etc/timezone:ro"
+        },
+        {
+          "bind": "/home/docker/clamav/virus_definitions",
+          "container": "/var/lib/clamav"
+        },
+        {
+          "bind": "/home/docker",
+          "container": "/scandir"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Arr",
+        "Tools"
+      ],
+      "description": "FlareSolverr is a proxy server to bypass Cloudflare and DDoS-GUARD protection.",
+      "env": [
+        {
+          "default": "info",
+          "label": "LOG_LEVEL",
+          "name": "LOG_LEVEL"
+        },
+        {
+          "default": "false",
+          "label": "LOG_HTML",
+          "name": "LOG_HTML"
+        },
+        {
+          "default": "none",
+          "label": "CAPTCHA_SOLVER",
+          "name": "CAPTCHA_SOLVER"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "ghcr.io/flaresolverr/flaresolverr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/flaresolverr.png",
+      "name": "flaresolverr",
+      "platform": "linux",
+      "ports": [
+        "8191:8191/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "FlareSolverr",
+      "type": 1,
+      "note": ""
+    },
+    {
+      "categories": [
+        "Gaming",
+        "Paid"
+      ],
+      "description": "This docker image provides the FoundryVTT system for hosting your own virtual table top games.",
+      "env": [
+        {
+          "default": "John",
+          "label": "Foundry Account Name",
+          "name": "FOUNDRY_USERNAME"
+        },
+        {
+          "default": "password",
+          "label": "Foundry Password",
+          "name": "FOUNDRY_PASSWORD"
+        },
+        {
+          "default": "changeme",
+          "label": "Instance Admin Password",
+          "name": "FOUNDRY_ADMIN_KEY"
+        },
+        {
+          "default": "true",
+          "label": "CONTAINER_PRESERVE_CONFIG",
+          "name": "CONTAINER_PRESERVE_CONFIG"
+        }
+      ],
+      "image": "felddy/foundryvtt:release",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/foundryvtt.png",
+      "name": "foundryVTT",
+      "platform": "linux",
+      "ports": [
+        "30000:30000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "FoundryVTT Server",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/foundryvtt",
+          "container": "/data"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "CMS"
+      ],
+      "description": "Ghost is a free and open source blogging platform written in JavaScript and distributed under the MIT License, designed to simplify the process of online publishing for individual bloggers as well as online publications.",
+      "env": [
+        {
+          "default": "development",
+          "label": "NODE_ENV",
+          "name": "NODE_ENV"
+        },
+        {
+          "default": "http://localhost/",
+          "label": "url",
+          "name": "url"
+        }
+      ],
+      "image": "ghost:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/ghost.png",
+      "name": "ghost",
+      "platform": "linux",
+      "ports": [
+        "2368:2368/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Ghost",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/ghost",
+          "container": "/var/lib/ghost/content"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Dashboard",
+        "Web",
+        "Other"
+      ],
+      "description": "Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.",
+      "note": "",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "ghcr.io/ajnart/homarr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/homarr.png",
+      "name": "homarr-secured",
+      "platform": "linux",
+      "ports": [
+        "7575:7575/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Homarr-Secured",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homarr/configs",
+          "container": "/app/data/configs"
+        },
+        {
+          "bind": "/home/docker/homarr/icons",
+          "container": "/app/public/icons"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "Homebridge allows you to integrate with smart home devices that do not natively support HomeKit. There are over 2,000 Homebridge plugins supporting thousands of different smart accessories.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1",
+          "label": "HOMEBRIDGE_CONFIG_UI",
+          "name": "HOMEBRIDGE_CONFIG_UI"
+        },
+        {
+          "default": "8581",
+          "label": "HOMEBRIDGE_CONFIG_UI_PORT",
+          "name": "HOMEBRIDGE_CONFIG_UI_PORT"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        }
+      ],
+      "image": "homebridge/homebridge:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/homebridge.png",
+      "name": "homebridge",
+      "network": "host",
+      "note": "",
+      "platform": "linux",
+      "privileged": true,
+      "restart_policy": "unless-stopped",
+      "title": "Homebridge",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/homebridge",
+          "container": "/homebridge"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Tools"
+      ],
+      "description": "JDownloader 2 is a free, open-source download management tool with a huge community of developers that makes downloading as easy and fast as it should be. Users can start, stop or pause downloads, set bandwith limitations, auto-extract archives and much more. It's an easy-to-extend framework that can save hours of your valuable time every day!. <a href='https://hub.docker.com/r/jlesage/jdownloader-2'>Docker Hub</a>",
+      "env": [
+        {
+          "default": "",
+          "label": "MYJD_DEVICE_NAME",
+          "name": "MYJD_DEVICE_NAME"
+        },
+        {
+          "default": "",
+          "label": "MYJD_USER",
+          "name": "MYJD_USER"
+        },
+        {
+          "default": "",
+          "label": "MYJD_PASSWORD",
+          "name": "MYJD_PASSWORD"
+        }
+      ],
+      "image": "jlesage/jdownloader-2",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/jdownloader.png",
+      "name": "jdownloader",
+      "platform": "linux",
+      "ports": [
+        "5800:5800/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "JDownloader",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/jdownloader",
+          "container": "/opt/JDownloader/app/cfg"
+        },
+        {
+          "bind": "/media",
+          "container": "/opt/JDownloader/Downloads"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "An alternative private front-end to Reddit",
+      "image": "libreddit/libreddit:armv7",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/libreddit.png",
+      "name": "libreddit",
+      "platform": "linux",
+      "ports": [
+        "8088:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Libreddit",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/libreddit",
+          "container": "/config"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Authentication",
+        "LDAP"
+      ],
+      "description": "This project is a lightweight authentication server that provides an opinionated, simplified LDAP interface for authentication.",
+      "env": [
+        {
+          "default": "somesecretjwt",
+          "label": "LLDAP_JWT_SECRET",
+          "name": "LLDAP_JWT_SECRET"
+        },
+        {
+          "default": "someadminpassword",
+          "label": "LLDAP_LDAP_USER_PASS",
+          "name": "LLDAP_LDAP_USER_PASS"
+        },
+        {
+          "default": "dc=example,dc=com",
+          "label": "LLDAP_LDAP_BASE_DN",
+          "name": "LLDAP_LDAP_BASE_DN"
+        }
+      ],
+      "image": "nitnelave/lldap:stable-debian",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/lldap.png",
+      "name": "lldap",
+      "platform": "linux",
+      "ports": [
+        "3890:3890/tcp",
+        "17170:17170/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "LLDAP",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/lldap",
+          "container": "/data"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A self-hosted recipe manager and meal planner",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "default": "2",
+          "label": "WEB_CONCURRENCY",
+          "name": "WEB_CONCURRENCY"
+        },
+        {
+          "default": "8",
+          "label": "MAX_WORKERS",
+          "name": "MAX_WORKERS"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_PUBLIC",
+          "name": "RECIPE_PUBLIC"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_SHOW_NUTRITION",
+          "name": "RECIPE_SHOW_NUTRITION"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_SHOW_ASSETS",
+          "name": "RECIPE_SHOW_ASSETS"
+        },
+        {
+          "default": "true",
+          "label": "RECIPE_LANDSCAPE_VIEW",
+          "name": "RECIPE_LANDSCAPE_VIEW"
+        },
+        {
+          "default": "false",
+          "label": "RECIPE_DISABLE_COMMENTS",
+          "name": "RECIPE_DISABLE_COMMENTS"
+        },
+        {
+          "default": "false",
+          "label": "RECIPE_DISABLE_AMOUNT",
+          "name": "RECIPE_DISABLE_AMOUNT"
+        }
+      ],
+      "image": "hkotel/mealie:v0.4.3",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mealie.png",
+      "name": "mealie",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "9925:80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Mealie",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/mealie",
+          "container": "/app/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools",
+        "Gaming"
+      ],
+      "description": "This docker image provides a Minecraft Server that will automatically download the latest stable version at startup. You can also run/upgrade to any specific version or the latest snapshot. See the Versions section below for more information.",
+      "env": [
+        {
+          "default": "TRUE",
+          "label": "EULA",
+          "name": "EULA"
+        }
+      ],
+      "image": "itzg/minecraft-server:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/minecraft.png",
+      "name": "minecraft",
+      "platform": "linux",
+      "ports": [
+        "25565:25565/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Minecraft Server",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/minecraft-data",
+          "container": "/data"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Networking",
+        "Monitoring"
+      ],
+      "description": "Troubleshoot slowdowns and anomalies in your infrastructure with thousands of per-second metrics, meaningful visualizations, and insightful health alarms with zero configuration.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "DOCKER_USR",
+          "name": "DOCKER_USR"
+        },
+        {
+          "default": "1000",
+          "label": "DOCKER_GRP",
+          "name": "DOCKER_GRP"
+        }
+      ],
+      "image": "netdata/netdata:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/netdata.png",
+      "name": "netdata",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "19999:19999/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Netdata",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/netdata/netdataconfig",
+          "container": "/etc/netdata"
+        },
+        {
+          "bind": "/home/docker/netdata/netdatalib",
+          "container": "/var/lib/netdata"
+        },
+        {
+          "bind": "/etc/passwd",
+          "container": "/host/etc/passwd:ro"
+        },
+        {
+          "bind": "/etc/group",
+          "container": "/host/etc/group:ro"
+        },
+        {
+          "bind": "/proc",
+          "container": "/host/proc:ro"
+        },
+        {
+          "bind": "/sys",
+          "container": "/host/sys:ro"
+        },
+        {
+          "bind": "/etc/os-release",
+          "container": "/host/etc/os-release:ro"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "Organizr allows you to setup Tabs that will be loaded all in one webpage. You can then work on your server with ease.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "organizr/organizr:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/organizr.png",
+      "name": "organizr-v2",
+      "platform": "linux",
+      "ports": [
+        "7171:80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Organizr v2",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/organizr",
+          "container": "/config"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "type": 1,
+      "name": "postgres",
+      "title": "PostgreSQL",
+      "note": "",
+      "description": "PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance. It was originally named POSTGRES, referring to its origins as a successor to the Ingres database developed at the University of California, Berkeley. <a href='https://www.postgresql.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/postgres' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/postgres.png",
+      "image": "postgres",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/postgres",
+          "container": "/var/lib/postgresql/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "5432",
+          "container": "5432"
+        }
+      ],
+      "env": [
+        {
+          "name": "POSTGRES_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "POSTGRES_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "POSTGRES_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "A docker image with qBittorrent and the Flood UI, also optional WireGuard VPN support. See the official documentation for WireGuard VPN support at https://hotio.dev/containers/qflood/",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "default": "002",
+          "label": "UMASK",
+          "name": "UMASK"
+        },
+        {
+          "default": "America/New_York",
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "default": "false",
+          "label": "FLOOD_AUTH",
+          "name": "FLOOD_AUTH"
+        }
+      ],
+      "image": "hotio/qflood:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/qflood.png",
+      "name": "qflood",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "3000:3000/tcp",
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "qFlood",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/qflood",
+          "container": "/config"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/app/qBittorrent/downloads"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A remote desktop software, the open source TeamViewer alternative, works out of the box, no configuration required. You have full control of your data, with no concerns about security.",
+      "env": [
+        {
+          "default": "rustdesk.example.com:21117",
+          "description": "Use your domain with the default 21117 port",
+          "label": "RELAY",
+          "name": "RELAY"
+        },
+        {
+          "default": "1",
+          "description": "if set to \"1\" unencrypted connection will not be accepted",
+          "label": "ENCRYPTED_ONLY",
+          "name": "ENCRYPTED_ONLY"
+        }
+      ],
+      "image": "rustdesk/rustdesk-server-s6:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/rustdesk.png",
+      "name": "rustdesk",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "21115:21115/tcp",
+        "21116:21116/tcp",
+        "21116:21116/udp",
+        "21117:21117/tcp",
+        "21118:21118/tcp",
+        "21119:21119/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "RustDesk",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/rustdesk",
+          "container": "/data"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "Open-Source Privacy-respecting metasearch engine",
+      "env": [
+        {
+          "default": "http://localhost:9017",
+          "label": "BASE_URL",
+          "name": "BASE_URL"
+        },
+        {
+          "default": "my-instance",
+          "label": "INSTANCE_NAME",
+          "name": "INSTANCE_NAME"
+        }
+      ],
+      "image": "searxng/searxng:latest",
+      "logo": "https://raw.githubusercontent.com/pi-hosted/pi-hosted/master/images/searx.png",
+      "name": "searxng",
+      "platform": "linux",
+      "ports": [
+        "9017:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "SearXNG",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/searxng",
+          "container": "/etc/searxng"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "type": 1,
+      "name": "surrealdb",
+      "title": "SurrealDB",
+      "note": "",
+      "description": "SurrealDB acts as both a database and a modern, realtime, collaborative API backend layer. SurrealDB can run as a single server or in a highly-available, highly-scalable distributed mode - with support for SQL querying from client devices, GraphQL, ACID transactions, WebSocket connections, structured and unstructured data, graph querying, full-text indexing, geospatial querying, and row-by-row permissions-based access. <a href='https://surrealdb.co/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/surrealdb/surrealdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/surrealdb.png",
+      "image": "surrealdb/surrealdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/surrealdb",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "SURREALDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for SurrealDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "SURREALDB_ADMIN_EMAIL",
+          "label": "Admin Email",
+          "description": "Admin email for SurrealDB",
+          "type": "text",
+          "default": "root"
+
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Networking",
+        "Security"
+      ],
+      "description": "Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them. ",
+      "image": "traefik:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/traefik.png",
+      "name": "traefik",
+      "note": "",
+      "platform": "linux",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp",
+        "8080:8080/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "command": "--api.insecure=true --providers.docker",
+      "title": "Traefik",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/traefik/traefik.yml",
+          "container": "/traefik.yml"
+        },
+        {
+          "bind": "/home/docker/traefik/config.yml",
+          "container": "/config.yml"
+        },
+        {
+          "bind": "/home/docker/traefik/acme.json",
+          "container": "/acme.json"
+        },
+        {
+          "bind": "/var/run/docker.sock",
+          "container": "/var/run/docker.sock"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Tools",
+        "Multimedia"
+      ],
+      "description": "Unmanic is a simple tool for optimising your file library. You can use it to convert your files into a single, uniform format, manage file movements based on timestamps, or execute custom commands against a file based on its file size.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "josh5/unmanic:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/unmanic.png",
+      "name": "unmanic",
+      "platform": "linux",
+      "ports": [
+        "8888:8888/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Unmanic",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/unmanic",
+          "container": "/config"
+        },
+        {
+          "bind": "/media",
+          "container": "/library"
+        },
+        {
+          "bind": "/var/tmp/unmanic",
+          "container": "/tmp/unmanic"
+        }
+      ],
+      "note": ""
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "Quickly and easily configure Wireguard through a web browser.",
+      "env": [
+        {
+          "default": "example.domain.com",
+          "description": "Set here your DDNS domain",
+          "label": "WG_HOST",
+          "name": "WG_HOST"
+        },
+        {
+          "default": "ENTER AN ADMIN PASSWORD",
+          "description": "Leave blank to access WebUI without loggin",
+          "label": "PASSWORD",
+          "name": "PASSWORD"
+        },
+        {
+          "default": "51820",
+          "label": "WG_PORT",
+          "name": "WG_PORT"
+        },
+        {
+          "default": "1.1.1.1",
+          "label": "WG_DEFAULT_DNS",
+          "name": "WG_DEFAULT_DNS"
+        },
+        {
+          "default": "10.8.0.x",
+          "label": "WG_DEFAULT_ADDRESS",
+          "name": "WG_DEFAULT_ADDRESS"
+        },
+        {
+          "default": "0.0.0.0/0, ::/0",
+          "label": "WG_ALLOWED_IPS",
+          "name": "WG_ALLOWED_IPS"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wireguard.png",
+      "name": "wg-easy",
+      "platform": "linux",
+      "image": "weejewel/wg-easy",
+      "title": "WireGuard-Easy",
+      "type": 3,
+      "note": ""
+    },
+    {
+      "categories": [
+        "CMS"
+      ],
+      "description": "WordPress is a web content management system. It was originally created as a tool to publish blogs but has evolved to support publishing other web content, including more traditional websites, mailing lists and Internet forum, media galleries, membership sites, learning management systems and online stores.",
+      "env": [
+        {
+          "default": "db",
+          "label": "Port or host of database server",
+          "name": "WORDPRESS_DB_HOST"
+        },
+        {
+          "default": "exampleuser",
+          "label": "Database user name",
+          "name": "WORDPRESS_DB_USER"
+        },
+        {
+          "default": "examplepass",
+          "label": "Database password for user",
+          "name": "WORDPRESS_DB_PASSWORD"
+        },
+        {
+          "default": "exampledb",
+          "label": "Database name",
+          "name": "WORDPRESS_DB_NAME"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/wordpress.png",
+      "note": "Create a mysql or mariadb database for the container first.",
+      "platform": "linux",
+      "image": "wordpress:latest",
+      "title": "Wordpress",
+      "name": "wordpress",
+      "type": 3,
+      "restart_policy": "unless-stopped",
+      "ports": [
+        "8080:80/tcp"
+      ],
+      "network": "AppBridge",
+      "volumes": [
+        {
+          "bind": "/home/docker/wordpress",
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Finance",
+        "Productivity"
+      ],
+      "description": "Invoices, Expenses and Tasks built with Laravel and Flutter.",
+      "env": [
+        {
+          "default": "invoice.my.domain",
+          "label": "URL",
+          "name": "URL"
+        },
+        {
+          "label": "APP_KEY",
+          "name": "APP_KEY"
+        },
+        {
+          "label": "TZ",
+          "name": "TZ"
+        },
+        {
+          "label": "DATABASE_PASSWORD",
+          "name": "DATABASE_PASSWORD"
+        },
+        {
+          "label": "MYSQL_ROOT_PASSWORD",
+          "name": "MYSQL_ROOT_PASSWORD"
+        },
+        {
+          "label": "PORT",
+          "name": "PORT"
+        }
+      ],
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/invoiceninja.png",
+      "name": "invoice_ninja",
+      "note": "The database user is invoice_ninja and the database is ninja_db. Please generate an app key following the documentation <a href='https://github.com/invoiceninja/dockerfiles'>here</a>. ",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/invoice-ninja.yml",
+        "url": "https://github.com/SelfhostedPro/selfhosted_templates"
+      },
+      "title": "Invoice Ninja",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Gaming"
+      ],
+      "description": "McMyAdmin 2 is the leading web control panel and administration console for Minecraft servers.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ],
+      "image": "linuxserver/mcmyadmin2:latest",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mcmyadmin2.png",
+      "name": "mcmyadmin2",
+      "platform": "linux",
+      "ports": [
+        "8080:8080/tcp",
+        "25565:25565/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "McMyAdmin 2",
+      "type": 1,
+      "volumes": [
+        {
+          "container": "/minecraft"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "Tools"
+      ],
+      "description": "A one-of-a-kind resume builder that's not out to get your data. Completely secure, customizable, portable, open-source and free forever.",
+      "image": "amruthpillai/reactive-resume:latest",
+      "logo": "https://raw.githubusercontent.com/SelfhostedPro/selfhosted_templates/master/Images/reactiveresume.png",
+      "name": "reactive-resume",
+      "platform": "linux",
+      "ports": [
+        "80/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Reactive-Resume",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/reactiveresume",
+          "container": "/usr/src/app"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other",
+        "VPN",
+        "Tools"
+      ],
+      "description": "This container contains OpenVPN and Deluge with a configuration where Deluge is running only when OpenVPN has an active tunnel. It bundles configuration files for many popular VPN providers to make the setup easier.",
+      "env": [
+        {
+          "default": "1001",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1001",
+          "label": "PGID",
+          "name": "PUID"
+        },
+        {
+          "default": "MULLVAD",
+          "description": "see https://github.com/sgtsquiggs/docker-deluge-openvpn",
+          "label": "OPENVPN_PROVIDER",
+          "name": "OPENVPN_PROVIDER"
+        },
+        {
+          "label": "OPENVPN_USERNAME",
+          "name": "OPENVPN_USERNAME"
+        },
+        {
+          "label": "OPENVPN_PASSWORD",
+          "name": "OPENVPN_PASSWORD"
+        }
+      ],
+      "image": "sgtsquiggs/deluge-openvpn:latest",
+      "name": "deluge-openvpn",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/deluge.png",
+      "platform": "linux",
+      "ports": [
+        "8112:8112/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Deluge OpenVPN",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/etc/localtime",
+          "container": "/etc/localtime"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/downloads"
+        },
+        {
+          "bind": "/home/docker/delugeopenvpn/config",
+          "container": "/config"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Multimedia"
+      ],
+      "note": "",
+      "description": "Tdarr is a popular conditional transcoding application for processing large (or small) media libraries. The application comes in the form of a click-to-run web-app, which you run on your own device and access through a web browser.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/tdarr.png",
+      "name": "tdarr",
+      "platform": "linux",
+      "image": "ghcr.io/haveagitgat/tdarr",
+      "title": "Tdarr",
+      "type": 3,
+      "volumes": [
+        {
+          "bind": "/home/docker/tdarr/configs",
+          "container": "/app/configs"
+        },
+        {
+          "bind": "/portainer/Downloads",
+          "container": "/downloads"
+        }
+      ],
+      "ports": [
+        "8265:8265/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "1000",
+          "label": "PGID",
+          "name": "PGID"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Finance"
+      ],
+      "description": "Cryptofolio is an open-source, and self-hosted solution for tracking your cryptocurrency holdings. It features a web interface, an Android mobile app, and a cross-platform desktop application for Windows, macOS, and Linux.",
+      "image": "xtrendence/cryptofolio:latest",
+      "logo": "https://i.imgur.com/5v8lzea.png",
+      "name": "cryptofolio",
+      "platform": "linux",
+      "ports": [
+        "7280:80/tcp"
+      ],
+      "restart_policy": "always",
+      "title": "Cryptofolio",
+      "type": 1
+    },
+    {
+      "categories": [
+        "Networking"
+      ],
+      "description": "An easy to use Status Page for your websites and applications. Statping will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page.",
+      "image": "statping/statping:latest",
+      "logo": "https://raw.githubusercontent.com/xneo1/portainer_templates/master/Images/statping.png",
+      "name": "statping",
+      "platform": "linux",
+      "ports": [
+        "4040:8080/tcp"
+      ],
+      "restart_policy": "always",
+      "title": "Statping",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/home/docker/statping",
+          "container": "/app"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Downloaders"
+      ],
+      "description": "A docker image with qBittorrent and the Flood UI, also optional WireGuard VPN support.",
+      "logo": "https://hotio.dev/img/image-logos/flood.svg",
+      "name": "flood",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/flood.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Flood",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Multimedia",
+        "Productivity"
+      ],
+      "description": "PhotoPrism is an AI-powered app for browsing, organizing & sharing your photo collection. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.| Copy as Custom stack and EDIT environment variables.",
+      "logo": "https://photoprism.app/static/img/logo.svg",
+      "name": "photoprism",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/photoprism.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Photoprism",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Multimedia",
+        "AI"
+      ],
+      "description": "Immich is a high performance self-hosted photo and video backup solution.",
+      "logo": "https://github.com/immich-app/immich/raw/main/design/immich-logo.svg",
+      "name": "immich",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/immich.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Immich",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "The recipe manager that allows you to manage your ever growing collection of digital recipes.",
+      "logo": "https://docs.tandoor.dev/logo_color.svg",
+      "name": "tandoor",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/tandoor.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Tandoor",
+      "type": 3
+    },
+    {
+      "type": 1,
+      "name": "influxdb",
+      "title": "InfluxDB",
+      "note": "",
+      "description": "InfluxDB is an open source time series database developed by InfluxData. It is written in Go and optimized for fast, high-availability storage and retrieval of time series data in fields such as operations monitoring, application metrics, Internet of Things sensor data, and real-time analytics. <a href='https://www.influxdata.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/influxdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/influxdb.png",
+      "image": "influxdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/influxdb",
+          "container": "/var/lib/influxdb",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "8086",
+          "container": "8086"
+        }
+      ],
+      "env": [
+        {
+          "name": "INFLUXDB_ADMIN_USER",
+          "label": "Admin User",
+          "description": "Admin user for InfluxDB",
+          "type": "text",
+          "default": "admin"
+        },
+        {
+          "name": "INFLUXDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for InfluxDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "INFLUXDB_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "mydb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Media Server"
+      ],
+      "description": "Your media enjoyed through a minimal lightweight media server.",
+      "logo": "https://github.com/midarrlabs/midarr-server/raw/master/priv/static/logo.svg",
+      "name": "midarr",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/midarr.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Midarr",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Development"
+      ],
+      "description": "Appwrite is a self-hosted backend-as-a-service platform that provides developers with all the core APIs required to build any application.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/appwrite.png",
+      "name": "appwrite",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/appwrite.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Appwrite",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Media Server",
+        "LDAP"
+      ],
+      "description": "Tubearchivist is your self hosted YouTube media server",
+      "note": "Requires a Redis and Elasticsearch database. Tube Archivist needs around 2GB of available memory for a small testing setup and around 4GB of available memory for a mid to large sized installation. Minimal with dual core with 4 threads, better quad core plus.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/tube-archivist.png",
+      "name": "tubearchivist",
+      "platform": "linux",
+      "image": "bbilly1/tubearchivist",
+      "title": "Tubearchivist",
+      "type": 3,
+      "ports": [
+        "8080:8080/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/media/youtube",
+          "container": "/youtube"
+        }
+      ],
+      "env": [
+        {
+          "name": "INFLUXDB_ADMIN_USER",
+          "label": "Admin User",
+          "description": "Admin user for InfluxDB",
+          "type": "text",
+          "default": "admin"
+        },
+        {
+          "name": "INFLUXDB_ADMIN_PASSWORD",
+          "label": "Admin Password",
+          "description": "Admin password for InfluxDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "INFLUXDB_DB",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "mydb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Finance"
+      ],
+      "description": "Actual is a local-first personal finance tool. It is 100% free and open-source. It has a synchronization element so that all your changes can move between devices without any heavy lifting.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/actual.png",
+      "name": "actual-server",
+      "platform": "linux",
+      "image": "actualbudget/actual-server:latest",
+      "restart_policy": "unless-stopped",
+      "title": "Actual-Server",
+      "ports": [
+        "5006:5006/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/actual",
+          "container": "/data"
+        }
+      ],
+      "type": 3
+    },
+    {
+      "categories": [
+        "Downloaders",
+        "Multimedia"
+      ],
+      "description": "Autobrr is the modern download automation tool for torrents.",
+      "logo": "https://autobrr.com/img/logo.png",
+      "name": "autobrr",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/autobrr.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Autobrr",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Monitoring"
+      ],
+      "description": "Glances is an open-source system cross-platform monitoring tool. It allows real-time monitoring of various aspects of your system such as CPU, memory, disk, network usage etc.",
+      "logo": "https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-responsive-webdesign.png",
+      "name": "glances",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/glances.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Glances",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Development",
+        "Low Code",
+        "No code"
+      ],
+      "description": "Tooljet is an Open-source low-code application development platform for building and deploying business applications.",
+      "logo": "https://uploads-ssl.webflow.com/6266634263b9179f76b2236e/63aaa161e3b3be42ec50eb6f_Logomark.svg",
+      "name": "tooljet",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/tooljet.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Tooljet",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Productivity",
+        "Development",
+        "Low-code",
+        "No-code"
+      ],
+      "description": "Budibase allows no-code users to build apps quickly, with more functionality available with a little bit of inline code.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/budibase.png",
+      "name": "budibase",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/budibase.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Budibase",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Development"
+      ],
+      "description": "Appsmith (www.appsmith.com) is the first open-source low code tool that helps developers build dashboards and admin panels very quickly.",
+      "logo": "https://cdn-images.himalayas.app/vr60veq4neiptamhqm6qxwi3toi3",
+      "name": "appsmith",
+      "platform": "linux",
+      "image": "appsmith/appsmith-ce",
+      "title": "Appsmith",
+      "ports": [
+        "80:80/tcp",
+        "443:443/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/appsmith",
+          "container": "/appsmith-stacks"
+        }
+      ],
+      "type": 3
+    },
+    {
+      "categories": [
+        "Networking",
+        "Monitoring"
+      ],
+      "description": "Lazytainer monitors network traffic to containers. If there is traffic, the container runs, otherwise the container is stopped/paused.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/lazytainer.png",
+      "name": "lazytainer",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/lazytainer.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Lazytainer",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Finance"
+      ],
+      "description": "I hate money is a web application made to ease shared budget management. It keeps track of who bought what, when, and for whom; and helps to settle the bills.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/ihatemoney.png",
+      "name": "ihatemoney",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/ihatemoney.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "I hate money",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Media Server",
+        "LDAP"
+      ],
+      "description": "Fireshare: Share your game clips, videos, or other media via unique links.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/fireshare.png",
+      "name": "fireshare",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/fireshare.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Fireshare",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "description": "Xwiki s a free wiki software platform written in Java with a design emphasis on extensibility. XWiki is an enterprise wiki.",
+      "logo": "https://upload.wikimedia.org/wikipedia/commons/e/e2/Logo-xwikiorange.svg",
+      "name": "xwiki",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/xwiki.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Xwiki",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "note": "Requires a MySQL database. Create a database first.",
+      "description": "Leantime is an open source project management system for small teams and startups written in PHP, Javascript using MySQL. https://leantime.io",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/leantime.png",
+      "name": "leantime",
+      "platform": "linux",
+      "image": "leantime/leantime:latest",
+      "title": "Leantime",
+      "type": 3,
+      "ports": [
+        "80:80/tcp"
+      ],
+      "env": [
+        {
+          "name": "MYSQL_HOST",
+          "label": "Database Host",
+          "description": "Database host",
+          "type": "text",
+          "default": "mysql_leantime"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "Database User",
+          "description": "Database user",
+          "type": "text",
+          "default": "leantime_user"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Database Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "choose.a.long.random.password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database Name",
+          "description": "Database name",
+          "type": "text",
+          "default": "leantime_database"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Other"
+      ],
+      "description": "Jellyseerr is a free and open source software application for managing requests for your media library. It is a a fork of Overseerr built to bring support for Jellyfin & Emby media servers!. <a href='https://github.com/Fallenbagel/jellyseerr/'>Github</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/jellyseerr.png",
+      "name": "jellyseerr",
+      "note": "For Emby : add the environment variable JELLYFIN_TYPE=emby . (this will allow you to use the play button)",
+      "platform": "linux",
+      "image": "fallenbagel/jellyseerr:latest",
+      "title": "Jellyseerr",
+      "type": 3,
+      "volumes": [
+        {
+          "bind": "/home/docker/jellyseerr",
+          "container": "/app/config"
+        }
+      ],
+      "ports": [
+        "5055:5055/tcp"
+      ]
+    },
+    {
+      "categories": [
+        "Productivity"
+      ],
+      "description": "Trudesk is an Open Source Help Desk Software and Ticketing System",
+      "logo": "https://trudesk.io/wp-content/uploads/2019/10/logo-med.png",
+      "name": "trudesk",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/trudesk.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Trudesk",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Dashboard"
+      ],
+      "description": "Dashdot is a modern server dashboard, running on the latest tech, designed with glassmorphism in mind. It is intended to be used for smaller VPS and private servers.",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dashdot.png",
+      "name": "dashdot",
+      "platform": "linux",
+      "image": "mauricenino/dashdot",
+      "title": "Dashdot",
+      "type": 3,
+      "ports": [
+        "80:3001/tcp"
+      ],
+      "volumes": [
+        {
+          "bind": "/",
+          "container": "/mnt/host:ro"
+        }
+      ],
+      "privileged": true
+    },
+    {
+      "categories": [
+        "Productivity",
+        "Development",
+        "No-code"
+      ],
+      "description": "Open source no-code database and Airtable alternative.Create your own online database without technical experience. Our user friendly no-code tool gives you the powers of a developer without leaving your browser.",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/baserow.png",
+      "name": "baserow",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/baserow.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "title": "Baserow",
+      "type": 3
+    },
+    {
+      "name": "nocodb",
+      "title": "NocoDB",
+      "note": "",
+      "description": "NocoDB is a free, open-source, self-hosted, no-code platform to make database driven application. <a href='https://www.nocodb.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/nocodb/nocodb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/nocodb.png",
+      "image": "nocodb/nocodb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/nocodb",
+          "container": "/var/lib/nocodb",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "8080",
+          "container": "8080"
+        }
+      ],
+      "env": [
+        {
+          "name": "NOCODB_DB_HOST",
+          "label": "Database Host",
+          "description": "Database host",
+          "type": "text",
+          "default": "mysql"
+        },
+        {
+          "name": "NOCODB_DB_PORT",
+          "label": "Database Port",
+          "description": "Database port",
+          "type": "text",
+          "default": "3306"
+        },
+        {
+          "name": "NOCODB_DB_USER",
+          "label": "Database User",
+          "description": "Database user",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "NOCODB_DB_PASSWORD",
+          "label": "Database Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "NOCODB_DB_NAME",
+          "label": "Database Name",
+          "description": "Database name",
+          "type": "text",
+          "default": "nocodb"
+        }
+      ]
+    },
+    {
+      "categories": [
+        "Analytics",
+        "Tools"
+      ],
+      "description": "Google Analytics alternative that protects your data and your customers' privacy",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/matomo.png",
+      "name": "matomo",
+      "platform": "linux",
+      "repository": {
+        "stackfile": "Template/Stack/matomo.yml",
+        "url": "https://github.com/xneo1/portainer_templates"
+      },
+      "ports": [
+        "8282:80/tcp"
+      ],
+      "title": "Matomo",
+      "type": 3
+    },
+    {
+      "categories": [
+        "Tools"
+      ],
+      "description": "AdGuard Home is a network-wide software for blocking ads & tracking. After you set it up, it\u2019ll cover ALL your home devices, and you don\u2019t need any client-side software for that. With the rise of Internet-Of-Things and connected devices, it becomes more and more important to be able to control your whole network.",
+      "env": [
+        {
+          "default": "1000",
+          "label": "PUID",
+          "name": "PUID"
+        },
+        {
+          "default": "100",
+          "label": "PGID",
+          "name": "PGID"
+        },
+        {
+          "label": "CONTEXT_PATH",
+          "name": "CONTEXT_PATH",
+          "set": "adguard home"
+        }
+      ],
+      "note": "DNS-over-HTTPS: [80:80/TCP] [443:443/TCP] [443:443/UDP] [3000:3000/TCP] [DEFAULT]. DNS: [53:53/TCP] [53:53/UDP]. Admin Panel: [3000:3000/TCP]. DHCP: [67:67/UDP] [68:68/TCP] [68:68/UDP]. DNS-over-TLS: [853:853/TCP]. DNS-over-QUIC: [784:784/UDP] [853:853/UDP] [8853:8853/UDP]. DNSCrypt: [5443:5443/TCP] [5443:5443/UDP]",
+      "image": "adguard/adguardhome:latest",
+      "logo": "https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Images/adguard.png",
+      "name": "adguard",
+      "platform": "linux",
+      "ports": [
+        "53:53/tcp",
+        "53:53/udp",
+        "67:67/udp",
+        "68:68/tcp",
+        "68:68/udp",
+        "80:80/tcp",
+        "443:443/tcp",
+        "853:853/tcp",
+        "3000:3000/tcp"
+      ],
+      "restart_policy": "unless-stopped",
+      "title": "Adguard",
+      "type": 1,
+      "volumes": [
+        {
+          "bind": "/portainer/Files/AppData/Adguard/Workdir",
+          "container": "/opt/adguardhome/work"
+        },
+        {
+          "bind": "/portainer/Files/AppData/Adguard/Conf",
+          "container": "/opt/adguardhome/conf"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mongodb",
+      "title": "MongoDB",
+      "note": "",
+      "description": "MongoDB is a source-available cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with optional schemas. <a href='https://www.mongodb.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mongo' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/mongodb.png",
+      "image": "mongo",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mongodb",
+          "container": "/data/db",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "27017",
+          "container": "27017"
+        }
+      ],
+      "env": [
+        {
+          "name": "MONGO_INITDB_ROOT_USERNAME",
+          "label": "Root Username",
+          "description": "Root username for MongoDB",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "MONGO_INITDB_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MongoDB",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "cratedb",
+      "title": "CrateDB",
+      "note": "",
+      "description": "CrateDB is a distributed SQL database that combines SQL and search in a way that's simple to scale. It is designed to be highly available, horizontally scalable, and easy to use. <a href='https://crate.io/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/crate' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/cratedb.png",
+      "image": "crate",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/cratedb",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "4200",
+          "container": "4200"
+        },
+        {
+          "host": "4300",
+          "container": "4300"
+        }
+      ],
+      "env": [
+        {
+          "name": "CRATE_HEAP_SIZE",
+          "label": "Heap Size",
+          "description": "Heap size for CrateDB",
+          "type": "text",
+          "default": "2g"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "elasticsearch",
+      "title": "Elasticsearch",
+      "note": "",
+      "description": "Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents. <a href='https://www.elastic.co/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/elasticsearch' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/elasticsearch.png",
+      "image": "elasticsearch",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/elasticsearch",
+          "container": "/usr/share/elasticsearch/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "9200",
+          "container": "9200"
+        },
+        {
+          "host": "9300",
+          "container": "9300"
+        }
+      ],
+      "env": [
+        {
+          "name": "discovery.type",
+          "label": "Discovery Type",
+          "description": "Discovery type for Elasticsearch",
+          "type": "text",
+          "default": "single-node"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "SQL Server",
+      "name": "mssql",
+      "description": "Official image for Microsoft SQL Server based on Ubuntu 20.04. <a href=\"https://hub.docker.com/_/microsoft-mssql-server\" target=\"_blank\">Docker Hub</a>",
+      "categories": [
+        "Database"
+      ],
+      "platform": "linux",
+      "note": "Requires at least 2GB of RAM. Make sure to assign enough memory to the Docker VM if you're running on Docker for Mac or Windows. Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
+      "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
+      "image": "mcr.microsoft.com/mssql/server:2022-latest",
+      "ports": [
+        "1433/tcp"
+      ],
+      "env": [
+        {
+          "name": "ACCEPT_EULA",
+          "default": "Y",
+          "preset": true
+        },
+        {
+          "name": "MSSQL_SA_PASSWORD",
+          "label": "MSSQL_SA_PASSWORD",
+          "default": "YOUR_STRONG_PASSWORD"
+        },
+        {
+          "name": "MSSQL_PID",
+          "label": "MSSQL_PID",
+          "default": "Developer"
+        }
+      ]
+    },
+    {
+      "name": "redis",
+      "title": "Redis",
+      "note": "",
+      "description": "Redis is an in-memory data structure project implementing a distributed, in-memory key-value database with optional durability. Redis supports different kinds of abstract data structures, such as strings, lists, maps, sets, sorted sets, HyperLogLogs, bitmaps, streams, and spatial indexes. <a href='https://redis.io/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/redis' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/redis.png",
+      "image": "redis",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/redis",
+          "container": "/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "6379",
+          "container": "6379"
+        }
+      ],
+      "env": [
+        {
+          "name": "REDIS_PASSWORD",
+          "label": "Password",
+          "description": "Password for Redis",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Joomla",
+      "name": "joomla",
+      "description": "Joomla! is an award-winning content management system (CMS), which enables you to build web sites and powerful online applications.",
+      "categories": [
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/joomla.png",
+      "image": "joomla:latest",
+      "env": [
+        {
+          "name": "JOOMLA_DB_HOST",
+          "label": "MySQL database host",
+          "type": "container"
+        },
+        {
+          "name": "JOOMLA_DB_PASSWORD",
+          "label": "Database password"
+        }
+      ],
+      "ports": [
+        "80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "title": "Drupal",
+      "name": "drupal",
+      "description": "Open-source content management framework",
+      "categories": [
+        "CMS"
+      ],
+      "platform": "linux",
+      "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/drupal.png",
+      "image": "drupal:latest",
+      "ports": [
+        "80/tcp"
+      ],
+      "volumes": [
+        {
+          "container": "/var/www/html"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "cockroachdb",
+      "title": "CockroachDB",
+      "note": "",
+      "description": "CockroachDB is a distributed SQL database built on a transactional and strongly-consistent key-value store. It scales horizontally; survives disk, machine, rack, and even datacenter failures with minimal latency disruption and no manual intervention; supports strongly-consistent ACID transactions; and provides a familiar SQL API for structuring, manipulating, and querying data. <a href='https://www.cockroachlabs.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/cockroachdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/cockroachdb.png",
+      "image": "cockroachdb/cockroach",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/cockroachdb",
+          "container": "/cockroach/cockroach-data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "26257",
+          "container": "26257"
+        },
+        {
+          "host": "8080",
+          "container": "8080"
+        }
+      ],
+      "env": [
+        {
+          "name": "COCKROACH_CHANNEL",
+          "label": "Channel",
+          "description": "CockroachDB channel",
+          "type": "text",
+          "default": "stable"
+        },
+        {
+          "name": "COCKROACH_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "root"
+        },
+        {
+          "name": "COCKROACH_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "mariadb",
+      "title": "MariaDB",
+      "note": "",
+      "description": "MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system (RDBMS), intended to remain free and open-source software under the GNU General Public License. Development is led by some of the original developers of MySQL, who forked it due to concerns over its acquisition by Oracle Corporation in 2009. <a href='https://mariadb.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/mariadb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mariadb.png",
+      "image": "mariadb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/mariadb",
+          "container": "/var/lib/mysql",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "3306",
+          "container": "3306"
+        }
+      ],
+      "env": [
+        {
+          "name": "MYSQL_ROOT_PASSWORD",
+          "label": "Root Password",
+          "description": "Root password for MariaDB",
+          "type": "password",
+          "default": "password"
+        },
+        {
+          "name": "MYSQL_DATABASE",
+          "label": "Database",
+          "description": "Database name",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "MYSQL_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "couchdb",
+      "title": "CouchDB",
+      "note": "",
+      "description": "Apache CouchDB is open source database software that focuses on ease of use and having a scalable architecture. It has a document-oriented NoSQL database architecture and is implemented in the concurrency-oriented language Erlang; it uses JSON to store data, JavaScript as its query language using MapReduce, and HTTP for an API. <a href='https://couchdb.apache.org/' target='_blank'>Website</a>. <a href='https://hub.docker.com/_/couchdb' target='_blank'>Docker Hub</a>",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/couchdb.png",
+      "image": "couchdb",
+      "categories": [
+        "Database"
+      ],
+      "volumes": [
+        {
+          "bind": "/home/docker/couchdb",
+          "container": "/opt/couchdb/data",
+          "mode": "rw"
+        }
+      ],
+      "ports" : [
+        {
+          "host": "5984",
+          "container": "5984"
+        }
+      ],
+      "env": [
+        {
+          "name": "COUCHDB_USER",
+          "label": "User",
+          "description": "Database user",
+          "type": "text",
+          "default": "heimdall"
+        },
+        {
+          "name": "COUCHDB_PASSWORD",
+          "label": "Password",
+          "description": "Database password",
+          "type": "password",
+          "default": "password"
+        }
+      ]
+    },
+    {
+      "type": 1,
+      "name": "nvidia-hwa-test",
+      "title": "NVIDIA HWA Test",
+      "note": "",
+      "description": "Nvidia HWA Test is a test container for NVIDIA hardware acceleration. Start the container then check the logs to confirm your output matches the example from this page: <a href='https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/sample-workload.html'>Running a Sample Workload</a>. <a href='https://www.nvidia.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/nvidia/cuda' target='_blank'>Docker Hub</a>",
+      "platform": "linux",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nvidia.png",
+	    "image": "nvidia/cuda:12.2.0-base-ubuntu20.04",
+      "env": [
+        {
+          "name": "DRINODE",
+          "label": "DRINODE",
+          "default": "/dev/dri/renderD128",
+          "description": "Specify the render device (GPU) for the contianer to use."
+        }
+      ],
+      "command": "nvidia-smi"  
+    }
+  ]
+}

+ 8 - 399
templates.json → templates/templates.json

@@ -1128,7 +1128,7 @@
         }
       ],
       "image": "linuxserver/minetest:latest",
-      "logo": "https://raw.githubusercontent.com/linuxserver/beta-templates/master/lsiodev/img/minetest-icon.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/minetest.png",
       "platform": "linux",
       "ports": [
         "30000/udp"
@@ -1260,7 +1260,7 @@
         }
       ],
       "image": "linuxserver/letsencrypt:latest",
-      "logo": "https://raw.githubusercontent.com/thesugarat/portainer_templates-1/master/Images/letsencrypt.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/letsencrypt.png",
       "platform": "linux",
       "ports": [
         "80/tcp",
@@ -1467,7 +1467,7 @@
       "note": "",
       "description": "Changedetection.io provides free, open-source web page monitoring, notification and change detection. (https://github.com/dgtlmoon/changedetection.io)",
       "platform": "linux",
-      "logo": "https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/changedetection-icon.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/changedetection.io.png",
       "image": "linuxserver/changedetection.io:latest",
       "env": [
         {
@@ -1841,7 +1841,7 @@
       "note": "",
       "description": "[Embystat](https://github.com/mregni/EmbyStat) is a personal web server that can calculate all kinds of statistics from your (local) Emby server. Just install this on your server and let him calculate all kinds of fun stuff.",
       "platform": "linux",
-      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/embystat-logo.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/embystat.png",
       "image": "linuxserver/embystat:latest",
       "env": [
         {
@@ -2686,7 +2686,7 @@
       "note": "",
       "description": "SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt\u2122) sets up an Nginx webserver and reverse proxy with php support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.",
       "platform": "linux",
-      "logo": "https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/swag.gif",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/linuxserverio.png",
       "image": "linuxserver/swag:latest",
       "env": [
         {
@@ -2994,38 +2994,6 @@
       ],
       "restart_policy": "unless-stopped"
     },
-    {
-      "categories": [
-        "Authentication"
-      ],
-      "note": "recommend requirements: a host with at least 2 CPU cores and 2 GB of RAM",
-      "description": "authentik is an open-source Identity Provider focused on flexibility and versatility. You can use authentik in an existing environment to add support for new protocols, implement sign-up/recovery/etc. in your application so you don't have to deal with it, and many other things.",
-      "env": [
-        {
-          "default": "1000",
-          "label": "PUID",
-          "name": "PUID"
-        },
-        {
-          "default": "1000",
-          "label": "PGID",
-          "name": "PGID"
-        },
-        {
-          "label": "PORT",
-          "name": "PORT"
-        }
-      ],
-      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authentik.png",
-      "name": "authentik",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/authentik.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Authentik",
-      "type": 3
-    },
     {
       "categories": [
         "Productivity"
@@ -3043,7 +3011,7 @@
         }
       ],
       "image": "zadam/trilium:latest",
-      "logo": "https://www.saashub.com/images/app/service_logos/55/2901389fab77/large.png?1561117248",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/trilium.png",
       "name": "trilium",
       "platform": "linux",
       "ports": [
@@ -3059,50 +3027,6 @@
         }
       ]
     },
-    {
-      "categories": [
-        "Communication"
-      ],
-      "description": "Rocket.Chat is an open-source fully customizable communications platform developed in JavaScript for organizations with high standards of data protection.",
-      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/rocketchat.png",
-      "note": "Rocket.Chat Server Container",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/rocketchat.yml",
-        "url": "https://github.com/mycroftwilde/portainer_templates"
-      },
-      "title": "Rocket Chat",
-      "name": "rocketchat",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Other"
-      ],
-      "description": "Joplin is an open-source note-taking app",
-      "env": [
-        {
-          "default": "22300",
-          "label": "PORT",
-          "name": "PORT"
-        },
-        {
-          "default": "http://joplin.yourdomain.tld:22300",
-          "label": "URL",
-          "name": "URL"
-        }
-      ],
-      "logo": "https://raw.githubusercontent.com/laurent22/joplin/master/Assets/SquareIcon512.png",
-      "note": "Joplin is an open-source note-taking app",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/joplin.yml",
-        "url": "https://github.com/mycroftwilde/portainer_templates"
-      },
-      "title": "Joplin",
-      "name": "joplin",
-      "type": 3
-    },
     {
       "categories": [
         "Dashboard"
@@ -3552,7 +3476,7 @@
       ],
       "description": "YoutubeDL-Material is a Material Design frontend for youtube-dl. It's coded using Angular 9 for the frontend, and Node.js on the backend.",
       "image": "tzahi12345/youtubedl-material:latest",
-      "logo": "https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Images/ytdlm.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/youtubedl.png",
       "name": "youtubedl-material",
       "platform": "linux",
       "ports": [
@@ -4677,50 +4601,6 @@
         }
       ]
     },
-    {
-      "categories": [
-        "Finance",
-        "Productivity"
-      ],
-      "description": "Invoices, Expenses and Tasks built with Laravel and Flutter.",
-      "env": [
-        {
-          "default": "invoice.my.domain",
-          "label": "URL",
-          "name": "URL"
-        },
-        {
-          "label": "APP_KEY",
-          "name": "APP_KEY"
-        },
-        {
-          "label": "TZ",
-          "name": "TZ"
-        },
-        {
-          "label": "DATABASE_PASSWORD",
-          "name": "DATABASE_PASSWORD"
-        },
-        {
-          "label": "MYSQL_ROOT_PASSWORD",
-          "name": "MYSQL_ROOT_PASSWORD"
-        },
-        {
-          "label": "PORT",
-          "name": "PORT"
-        }
-      ],
-      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/invoiceninja.png",
-      "name": "invoice_ninja",
-      "note": "The database user is invoice_ninja and the database is ninja_db. Please generate an app key following the documentation <a href='https://github.com/invoiceninja/dockerfiles'>here</a>. ",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/invoice-ninja.yml",
-        "url": "https://github.com/SelfhostedPro/selfhosted_templates"
-      },
-      "title": "Invoice Ninja",
-      "type": 3
-    },
     {
       "categories": [
         "Gaming"
@@ -4913,68 +4793,6 @@
         }
       ]
     },
-    {
-      "categories": [
-        "Downloaders"
-      ],
-      "description": "A docker image with qBittorrent and the Flood UI, also optional WireGuard VPN support.",
-      "logo": "https://hotio.dev/img/image-logos/flood.svg",
-      "name": "flood",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/flood.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Flood",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Multimedia",
-        "Productivity"
-      ],
-      "description": "PhotoPrism is an AI-powered app for browsing, organizing & sharing your photo collection. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.| Copy as Custom stack and EDIT environment variables.",
-      "logo": "https://photoprism.app/static/img/logo.svg",
-      "name": "photoprism",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/photoprism.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Photoprism",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Multimedia",
-        "AI"
-      ],
-      "description": "Immich is a high performance self-hosted photo and video backup solution.",
-      "logo": "https://github.com/immich-app/immich/raw/main/design/immich-logo.svg",
-      "name": "immich",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/immich.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Immich",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Tools"
-      ],
-      "description": "The recipe manager that allows you to manage your ever growing collection of digital recipes.",
-      "logo": "https://docs.tandoor.dev/logo_color.svg",
-      "name": "tandoor",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/tandoor.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Tandoor",
-      "type": 3
-    },
     {
       "type": 1,
       "name": "influxdb",
@@ -5023,36 +4841,6 @@
         }
       ]
     },
-    {
-      "categories": [
-        "Media Server"
-      ],
-      "description": "Your media enjoyed through a minimal lightweight media server.",
-      "logo": "https://github.com/midarrlabs/midarr-server/raw/master/priv/static/logo.svg",
-      "name": "midarr",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/midarr.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Midarr",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Development"
-      ],
-      "description": "Appwrite is a self-hosted backend-as-a-service platform that provides developers with all the core APIs required to build any application.",
-      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/appwrite.png",
-      "name": "appwrite",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/appwrite.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Appwrite",
-      "type": 3
-    },
     {
       "categories": [
         "Media Server",
@@ -5121,72 +4909,6 @@
       ],
       "type": 3
     },
-    {
-      "categories": [
-        "Downloaders",
-        "Multimedia"
-      ],
-      "description": "Autobrr is the modern download automation tool for torrents.",
-      "logo": "https://autobrr.com/img/logo.png",
-      "name": "autobrr",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/autobrr.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Autobrr",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Monitoring"
-      ],
-      "description": "Glances is an open-source system cross-platform monitoring tool. It allows real-time monitoring of various aspects of your system such as CPU, memory, disk, network usage etc.",
-      "logo": "https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-responsive-webdesign.png",
-      "name": "glances",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/glances.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Glances",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Development",
-        "Low Code",
-        "No code"
-      ],
-      "description": "Tooljet is an Open-source low-code application development platform for building and deploying business applications.",
-      "logo": "https://uploads-ssl.webflow.com/6266634263b9179f76b2236e/63aaa161e3b3be42ec50eb6f_Logomark.svg",
-      "name": "tooljet",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/tooljet.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Tooljet",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Productivity",
-        "Development",
-        "Low-code",
-        "No-code"
-      ],
-      "description": "Budibase allows no-code users to build apps quickly, with more functionality available with a little bit of inline code.",
-      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/budibase.png",
-      "name": "budibase",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/budibase.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Budibase",
-      "type": 3
-    },
     {
       "categories": [
         "Development"
@@ -5209,68 +4931,6 @@
       ],
       "type": 3
     },
-    {
-      "categories": [
-        "Networking",
-        "Monitoring"
-      ],
-      "description": "Lazytainer monitors network traffic to containers. If there is traffic, the container runs, otherwise the container is stopped/paused.",
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/lazytainer.png",
-      "name": "lazytainer",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/lazytainer.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Lazytainer",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Finance"
-      ],
-      "description": "I hate money is a web application made to ease shared budget management. It keeps track of who bought what, when, and for whom; and helps to settle the bills.",
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/ihatemoney.png",
-      "name": "ihatemoney",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/ihatemoney.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "I hate money",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Media Server",
-        "LDAP"
-      ],
-      "description": "Fireshare: Share your game clips, videos, or other media via unique links.",
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/fireshare.png",
-      "name": "fireshare",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/fireshare.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Fireshare",
-      "type": 3
-    },
-    {
-      "categories": [
-        "Productivity"
-      ],
-      "description": "Xwiki s a free wiki software platform written in Java with a design emphasis on extensibility. XWiki is an enterprise wiki.",
-      "logo": "https://upload.wikimedia.org/wikipedia/commons/e/e2/Logo-xwikiorange.svg",
-      "name": "xwiki",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/xwiki.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Xwiki",
-      "type": 3
-    },
     {
       "categories": [
         "Productivity"
@@ -5339,21 +4999,6 @@
         "5055:5055/tcp"
       ]
     },
-    {
-      "categories": [
-        "Productivity"
-      ],
-      "description": "Trudesk is an Open Source Help Desk Software and Ticketing System",
-      "logo": "https://trudesk.io/wp-content/uploads/2019/10/logo-med.png",
-      "name": "trudesk",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/trudesk.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Trudesk",
-      "type": 3
-    },
     {
       "categories": [
         "Dashboard"
@@ -5376,23 +5021,6 @@
       ],
       "privileged": true
     },
-    {
-      "categories": [
-        "Productivity",
-        "Development",
-        "No-code"
-      ],
-      "description": "Open source no-code database and Airtable alternative.Create your own online database without technical experience. Our user friendly no-code tool gives you the powers of a developer without leaving your browser.",
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/baserow.png",
-      "name": "baserow",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/baserow.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "title": "Baserow",
-      "type": 3
-    },
     {
       "name": "nocodb",
       "title": "NocoDB",
@@ -5454,25 +5082,6 @@
         }
       ]
     },
-    {
-      "categories": [
-        "Analytics",
-        "Tools"
-      ],
-      "description": "Google Analytics alternative that protects your data and your customers' privacy",
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/matomo.png",
-      "name": "matomo",
-      "platform": "linux",
-      "repository": {
-        "stackfile": "Template/Stack/matomo.yml",
-        "url": "https://github.com/xneo1/portainer_templates"
-      },
-      "ports": [
-        "8282:80/tcp"
-      ],
-      "title": "Matomo",
-      "type": 3
-    },
     {
       "categories": [
         "Tools"
@@ -5914,7 +5523,7 @@
       "note": "",
       "description": "Nvidia HWA Test is a test container for NVIDIA hardware acceleration. Start the container then check the logs to confirm your output matches the example from this page: <a href='https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/sample-workload.html'>Running a Sample Workload</a>. <a href='https://www.nvidia.com/' target='_blank'>Website</a>. <a href='https://hub.docker.com/r/nvidia/cuda' target='_blank'>Docker Hub</a>",
       "platform": "linux",
-      "logo": "https://avatars.githubusercontent.com/u/1728152",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/nvidia.png",
 	    "image": "nvidia/cuda:12.2.0-base-ubuntu20.04",
       "env": [
         {

+ 0 - 144
views/account.ejs

@@ -1,144 +0,0 @@
-	<!doctype html>
-	<html lang="en">
-	<head>
-		<meta charset="utf-8"/>
-		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-		<title>DweebUI - Account</title>
-		<!-- CSS files -->
-		<link href="/css/tabler.min.css" rel="stylesheet"/>
-		<link href="/css/demo.min.css" rel="stylesheet"/>
-		<style>
-			@import url('/fonts/inter.css');
-			:root {
-			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-			}
-			body {
-			  font-feature-settings: "cv03", "cv04", "cv11";
-			}
-		  </style>
-	</head>
-	<body >
-		<div class="page">
-		<!-- Navbar -->
-		<%- include('navbar.ejs') %>
-		<div class="page-wrapper">
-			<!-- Page header -->
-			<div class="page-header d-print-none">
-				<div class="container-xl">
-					<div class="row g-2 align-items-center">
-					<div class="col">
-						<h2 class="page-title">
-						Account Settings
-						</h2>
-					</div>
-					</div>
-				</div>
-			</div>
-			<!-- Page body -->
-			<div class="page-body">
-			<div class="container-xl">
-				<div class="card">
-				<div class="row g-0">
-					<div class="col-3 d-none d-md-block border-end">
-					<div class="card-body">
-						<h4 class="subheader">Business settings</h4>
-						<div class="list-group list-group-transparent">
-						<a href="/account" class="list-group-item list-group-item-action d-flex align-items-center active">Accounts</a>
-						<!-- <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">My Notifications</a>
-						<a href="#" class="list-group-item list-group-item-action d-flex align-items-center">Connected Apps</a> -->
-						<a href="/settings" class="list-group-item list-group-item-action d-flex align-items-center">Settings</a>
-						<!-- <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">Billing & Invoices</a> -->
-						</div>
-						<h4 class="subheader mt-4">Experience</h4>
-						<div class="list-group list-group-transparent">
-						<a href="#" class="list-group-item list-group-item-action">Credits</a>
-						</div>
-					</div>
-					</div>
-					<div class="col d-flex flex-column">
-					<div class="card-body">
-						<h2 class="mb-4">My Account</h2>
-						<h3 class="card-title">Profile Details</h3>
-						<div class="row align-items-center">
-							<div class="col-auto"><span class="avatar avatar-xl"><%- avatar %></span>
-							</div>
-							<div class="col-auto"><a href="#" class="btn">
-								Change avatar
-								</a>
-							</div>
-							<div class="col-auto"><a href="#" class="btn btn-ghost-danger">
-								Delete avatar
-								</a>
-							</div>
-						</div>
-						<h3 class="card-title mt-4">Profile</h3>
-						<div class="row g-3">
-							<div class="col-md">
-								<div class="form-label">Full Name</div>
-								<input type="text" class="form-control" value="<%= name %>" readonly="<%= name %>">
-							</div>
-							<div class="col-md">
-								<div class="form-label">First Name</div>
-								<input type="text" class="form-control" value="<%= first_name %>" readonly="<%= first_name %>">
-							</div>
-							<div class="col-md">
-								<div class="form-label">Last Name</div>
-								<input type="text" class="form-control" value="<%= last_name %>" readonly="<%= last_name %>">
-							</div>
-						</div>
-						<h3 class="card-title mt-4">Email</h3>
-						<p class="card-subtitle">This contact will be shown to others publicly, so choose it carefully.</p>
-						<div>
-							<div class="row g-2">
-								<div class="col-auto">
-									<input type="text" class="form-control w-auto" value="<%= email %>" readonly="<%= email %>">
-								</div>
-								<div class="col-auto">
-									<a href="#" class="btn">Change</a>
-								</div>
-							</div>
-						</div>
-						<h3 class="card-title mt-4">Password</h3>
-						<p class="card-subtitle">You can set a permanent password if you don't want to use temporary login codes.</p>
-						<div>
-							<a href="#" class="btn">
-								Set new password
-							</a>
-						</div>
-						<h3 class="card-title mt-4">Public profile</h3>
-						<p class="card-subtitle">Making your profile public means that anyone on the Dashkit network will be able to find
-						you.</p>
-						<div>
-							<label class="form-check form-switch form-switch-lg">
-								<input class="form-check-input" type="checkbox" >
-								<span class="form-check-label form-check-label-on">You're currently visible</span>
-								<span class="form-check-label form-check-label-off">You're
-								currently invisible</span>
-							</label>
-						</div>
-					</div>
-					<div class="card-footer bg-transparent mt-auto">
-						<div class="btn-list justify-content-end">
-						<a href="#" class="btn">
-							Cancel
-						</a>
-						<a href="#" class="btn btn-primary">
-							Submit
-						</a>
-						</div>
-					</div>
-					</div>
-				</div>
-				</div>
-			</div>
-			</div>
-			<%- include('footer.ejs') %>
-		</div>
-		</div>
-		<!-- Libs JS -->
-		<!-- Tabler Core -->
-		<script src="/js/tabler.min.js" defer></script>
-		<script src="/js/demo.min.js" defer></script>
-	</body>
-	</html>

+ 131 - 0
views/account.html

@@ -0,0 +1,131 @@
+	<!doctype html>
+	<html lang="en">
+	<head>
+		<meta charset="utf-8"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+		<title>DweebUI - Account</title>
+		<!-- CSS files -->
+		<link href="/css/tabler.min.css" rel="stylesheet"/>
+		<link href="/css/demo.min.css" rel="stylesheet"/>
+		<style>
+			@import url('/fonts/inter.css');
+			:root {
+			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+			}
+			body {
+			  font-feature-settings: "cv03", "cv04", "cv11";
+			}
+		  </style>
+	</head>
+	<body >
+		<div class="page">
+			<!-- Navbar -->
+			<%- include('navbar.html') %>
+			<div class="page-wrapper">
+				<!-- Page header -->
+				<div class="page-header d-print-none">
+					<div class="container-xl">
+						<div class="row g-2 align-items-center">
+						<div class="col">
+							<h2 class="page-title">
+							Settings
+							</h2>
+						</div>
+						</div>
+					</div>
+				</div>
+				<!-- Page body -->
+				<div class="page-body">
+					<div class="container-xl">
+						<div class="card">
+							<div class="row g-0">
+								
+								<%- include('sidebar.html') %>
+								
+								<div class="col d-flex flex-column">
+								<div class="card-body">
+									<h2 class="mb-4">My Account</h2>
+									<h3 class="card-title">Profile Details</h3>
+									<div class="row align-items-center">
+										<div class="col-auto"><span class="avatar avatar-xl"><%- avatar %></span>
+										</div>
+										<div class="col-auto"><a href="#" class="btn">
+											Change avatar
+											</a>
+										</div>
+										<div class="col-auto"><a href="#" class="btn btn-ghost-danger">
+											Delete avatar
+											</a>
+										</div>
+									</div>
+									<h3 class="card-title mt-4">Profile</h3>
+									<div class="row g-3">
+										<div class="col-md">
+											<div class="form-label">Full Name</div>
+											<input type="text" class="form-control" value="<%= name %>" readonly="<%= name %>">
+										</div>
+										<div class="col-md">
+											<div class="form-label">First Name</div>
+											<input type="text" class="form-control" value="<%= first_name %>" readonly="<%= first_name %>">
+										</div>
+										<div class="col-md">
+											<div class="form-label">Last Name</div>
+											<input type="text" class="form-control" value="<%= last_name %>" readonly="<%= last_name %>">
+										</div>
+									</div>
+									<h3 class="card-title mt-4">Email</h3>
+									<p class="card-subtitle">This contact will be shown to others publicly, so choose it carefully.</p>
+									<div>
+										<div class="row g-2">
+											<div class="col-auto">
+												<input type="text" class="form-control w-auto" value="<%= email %>" readonly="<%= email %>">
+											</div>
+											<div class="col-auto">
+												<a href="#" class="btn">Change</a>
+											</div>
+										</div>
+									</div>
+									<h3 class="card-title mt-4">Password</h3>
+									<p class="card-subtitle">You can set a permanent password if you don't want to use temporary login codes.</p>
+									<div>
+										<a href="#" class="btn">
+											Set new password
+										</a>
+									</div>
+									<h3 class="card-title mt-4">Public profile</h3>
+									<p class="card-subtitle">Making your profile public means that anyone on the Dashkit network will be able to find
+									you.</p>
+									<div>
+										<label class="form-check form-switch form-switch-lg">
+											<input class="form-check-input" type="checkbox" >
+											<span class="form-check-label form-check-label-on">You're currently visible</span>
+											<span class="form-check-label form-check-label-off">You're
+											currently invisible</span>
+										</label>
+									</div>
+								</div>
+								<div class="card-footer bg-transparent mt-auto">
+									<div class="btn-list justify-content-end">
+									<a href="#" class="btn">
+										Cancel
+									</a>
+									<a href="#" class="btn btn-primary">
+										Submit
+									</a>
+									</div>
+								</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+				<%- include('footer.html') %>
+			</div>
+		</div>
+		<!-- Libs JS -->
+		<!-- Tabler Core -->
+		<script src="/js/tabler.min.js" defer></script>
+		<script src="/js/demo.min.js" defer></script>
+	</body>
+</html>

+ 7 - 7
views/apps.ejs → views/apps.html

@@ -22,7 +22,7 @@
     <div class="page">
       <!-- Navbar -->
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
       <div class="page-wrapper">
         <!-- Page header -->
@@ -82,11 +82,11 @@
                     prev
                   </a>
                 </li>
-                <li class="page-item"><a class="page-link" href="/apps?page=1">1</a></li>
-                <li class="page-item"><a class="page-link" href="/apps?page=2">2</a></li>
-                <li class="page-item"><a class="page-link" href="/apps?page=3">3</a></li>
-                <li class="page-item"><a class="page-link" href="/apps?page=4">4</a></li>
-                <li class="page-item"><a class="page-link" href="/apps?page=5">5</a></li>
+                <li class="page-item"><a class="page-link" href="/apps/1">1</a></li>
+                <li class="page-item"><a class="page-link" href="/apps/2">2</a></li>
+                <li class="page-item"><a class="page-link" href="/apps/3">3</a></li>
+                <li class="page-item"><a class="page-link" href="/apps/4">4</a></li>
+                <li class="page-item"><a class="page-link" href="/apps/5">5</a></li>
                 <li class="page-item">
                   <a class="page-link" href="<%- next %>">
                     next <!-- Download SVG icon from http://tabler-icons.io/i/chevron-right -->
@@ -98,7 +98,7 @@
           </div>
         </div>
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
 
       </div>
     </div>

+ 0 - 1011
views/dashboard.ejs

@@ -1,1011 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-    <title>DweebUI - Dashboard</title>
-    <!-- CSS files -->
-    <link href="/css/tabler.min.css" rel="stylesheet"/>
-    <link href="/css/meters.css" rel="stylesheet"/>
-    <style>
-      @import url('/fonts/inter.css');
-      :root {
-        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-      }
-      body {
-        font-feature-settings: "cv03", "cv04", "cv11";
-      }
-    </style>
-  </head>
-  <body >
-  <div class="page">
-
-    <%- include('navbar.ejs') %>
-    <div class="page-wrapper">
-
-      <div class="page-body">
-        <div class="container-xl">
-          <div class="row row-deck row-cards">
-            
-            <div class="col-12" id="cards">
-              <div class="row row-cards">
-                
-                <div class="col-sm-6 col-lg-3">
-                  <div class="card card-sm">
-                    <div class="card-body">
-                      <div class="row align-items-center">
-                        <div class="col-auto">
-                          <span class="bg-primary text-white avatar">
-                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cpu" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg>
-                          </span>
-                        </div>
-                        <div class="col">
-                          <div class="font-weight-medium">
-                            <label id="cpu-text" class="cpu-text mb-1" for="cpu">CPU 0%</label>
-                          </div>
-                          <div id="cpu-bar" class="cpu-bar meter animate">
-                            <span style="width: 25%"><span></span></span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="col-sm-6 col-lg-3">
-                  <div class="card card-sm">
-                    <div class="card-body">
-                      <div class="row align-items-center">
-                        <div class="col-auto">
-                          <span class="bg-green text-white avatar">
-                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-container" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 4v.01"></path> <path d="M20 20v.01"></path> <path d="M20 16v.01"></path> <path d="M20 12v.01"></path> <path d="M20 8v.01"></path> <path d="M8 4m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path> <path d="M4 4v.01"></path> <path d="M4 20v.01"></path> <path d="M4 16v.01"></path> <path d="M4 12v.01"></path> <path d="M4 8v.01"></path> </svg>                            
-                          </span>
-                        </div>
-                        <div class="col">
-                          <div class="font-weight-medium">
-                            <label id="ram-text" class="ram-text mb-1" for="ram">RAM 0%</label>
-                          </div>
-                          <div id="ram-bar" class="ram-bar meter animate orange">
-                            <span style="width: 25%"><span></span></span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-                
-                <div class="col-sm-6 col-lg-3">
-                  <div class="card card-sm">
-                    <div class="card-body">
-                      <div class="row align-items-center">
-                        <div class="col-auto">
-                          <span class="bg-twitter text-white avatar">
-                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrows-left-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 17l-18 0"></path> <path d="M6 10l-3 -3l3 -3"></path> <path d="M3 7l18 0"></path> <path d="M18 20l3 -3l-3 -3"></path> </svg>                            
-                          </span>
-                        </div>
-                        <div class="col">
-                          <div class="font-weight-medium">
-                            <label id="net-text" class="net-text mb-1" for="network">Down: 0MB  Up: 0MB</label>
-                          </div>
-                          <div id="net-bar" class="meter animate blue">
-                            <span style="width: 25%"><span></span></span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="col-sm-6 col-lg-3">
-                  <div class="card card-sm">
-                    <div class="card-body">
-                      <div class="row align-items-center">
-                        <div class="col-auto">
-                          <span class="bg-facebook text-white avatar">
-                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-database" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 6m-8 0a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"></path> <path d="M4 6v6a8 3 0 0 0 16 0v-6"></path> <path d="M4 12v6a8 3 0 0 0 16 0v-6"></path></svg>
-                          </span>
-                        </div>
-                        <div class="col">
-                          <div class="font-weight-medium">
-                            <label id="disk-text" class="disk-text mb-1" for="disk">DISK 0%</label>
-                          </div>
-                          <div id="disk-bar" class="meter animate red">
-                            <span style="width: 25%"><span></span></span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-
-
-              </div>
-            </div>
-
-
-            <!-- containerCards get inserted here from public/js/main.js -->
-            
-            <div class="modal modal-blur fade" id="details_modal" tabindex="-1" role="dialog" aria-hidden="true">
-                        <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
-                          <div class="modal-content">
-                            <div class="modal-header">
-                              <h5 class="modal-title">${name}</h5>
-                              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-                            </div>
-                    
-                            
-                            <div class="modal-body">
-                              
-                            <pre class="text-secondary">note</pre>
-                            
-                              <form action="" id="details_modal" method="POST">
-                              
-                              <div class="row mb-3 align-items-end">
-                                
-                                <div class="col-lg-6">
-                                  <label class="form-label">Container Name: </label>
-                                  <input type="text" class="form-control" name="service_name" value="${app_name}" hidden/>
-                                  <input type="text" class="form-control" name="name" value="${app_name}"/>
-                                </div>
-                                <div class="col-lg-3">
-                                  <label class="form-label">Image: </label>
-                                  <input type="text" class="form-control" name="image" value="${image}"/>
-                                </div>
-                                <div class="col-lg-3">
-                                  <label class="form-label">Restart Policy: </label>
-                                  <select class="form-select" name="restart_policy" value="${restart_policy}">
-                                    <option value="1">unless-stopped</option>
-                                    <option value="2">on-failure</option>
-                                    <option value="3">never</option>
-                                    <option value="4">always</option>
-                                  </select>
-                                </div>
-                              </div>
-                    
-                              <label class="form-label">Network Mode</label>
-                                <div class="form-selectgroup-boxes row mb-3">
-                                  <div class="col">
-                                    <label class="form-selectgroup-item">
-                                      <input type="radio" name="report-type" value="1" class="form-selectgroup-input">
-                                      <span class="form-selectgroup-label d-flex align-items-center p-3">
-                                        <span class="me-3">
-                                          <span class="form-selectgroup-check"></span>
-                                        </span>
-                                        <span class="form-selectgroup-label-content">
-                                          <span class="form-selectgroup-title strong mb-1">Host Network</span>
-                                          <span class="d-block text-secondary">Same as host. No isolation. ex.127.0.0.1</span>
-                                        </span>
-                                      </span>
-                                    </label>
-                                  </div>
-                                  <div class="col">
-                                    <label class="form-selectgroup-item">
-                                      <input type="radio" name="report-type" class="form-selectgroup-input">
-                                      <span class="form-selectgroup-label d-flex align-items-center p-3">
-                                        <span class="me-3">
-                                          <span class="form-selectgroup-check"></span>
-                                        </span>
-                                        <span class="form-selectgroup-label-content">
-                                          <span class="form-selectgroup-title strong mb-1">Bridge Network</span>
-                                          <span class="d-block text-secondary">Containers can communicate using names.</span>
-                                        </span>
-                                      </span>
-                                    </label>
-                                  </div>
-                                  <div class="col">
-                                  <label class="form-selectgroup-item">
-                                    <input type="radio" name="report-type" class="form-selectgroup-input">
-                                    <span class="form-selectgroup-label d-flex align-items-center p-3">
-                                      <span class="me-3">
-                                        <span class="form-selectgroup-check"></span>
-                                      </span>
-                                      <span class="form-selectgroup-label-content">
-                                        <span class="form-selectgroup-title strong mb-1">Docker Network</span>
-                                        <span class="d-block text-secondary">Isolated on the docker network. ex.172.0.34.2</span>
-                                      </span>
-                                    </span>
-                                  </label>
-                                </div>
-                              </div>
-        
-        
-        
-        
-                    
-                              <div class="accordion" id="details_accordion">
-                                <div class="accordion-item">
-                                  <h2 class="accordion-header" id="heading-1">
-                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-1" aria-expanded="false">
-                                      Ports
-                                    </button>
-                                  </h2>
-                                  <div id="collapse-1" class="accordion-collapse collapse" data-bs-parent="#details_accordion">
-                                    <div class="accordion-body pt-0">
-                    
-        
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_0_check" type="checkbox" ${ports_data[0].check}>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">External Port</label>
-                                          <input type="text" class="form-control" name="port_0_external" value="${ports_data[0].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">Internal Port</label>
-                                          <input type="text" class="form-control" name="port_0_internal" value="${ports_data[0].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <label class="form-label">Protocol</label>
-                                          <select class="form-select" name="port_0_protocol">
-                                            <option value="${ports_data[0].protocol}" selected hidden>${ports_data[0].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_1_check" type="checkbox" ${ports_data[1].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_1_external" value="${ports_data[1].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_1_internal" value="${ports_data[1].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_1_protocol">
-                                            <option value="${ports_data[1].protocol}" selected hidden>${ports_data[1].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_2_check" type="checkbox" ${ports_data[2].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_2_external" value="${ports_data[2].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_2_internal" value="${ports_data[2].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_2_protocol">
-                                            <option value="${ports_data[2].protocol}" selected hidden>${ports_data[2].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_3_check" type="checkbox" ${ports_data[3].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_3_external" value="${ports_data[3].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_3_internal" value="${ports_data[3].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_3_protocol">
-                                            <option value="${ports_data[3].protocol}" selected hidden>${ports_data[3].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_4_check" type="checkbox" ${ports_data[4].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_4_external" value="${ports_data[4].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_4_internal" value="${ports_data[4].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_4_protocol">
-                                            <option value="${ports_data[4].protocol}" selected hidden>${ports_data[4].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_5_check" type="checkbox" ${ports_data[5].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_5_external" value="${ports_data[5].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_5_internal" value="${ports_data[5].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_5_protocol">
-                                            <option value="${ports_data[5].protocol}" selected hidden>${ports_data[5].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                    
-                                    </div>
-                                  </div>
-                                </div>
-                                <div class="accordion-item">
-                                  <h2 class="accordion-header" id="heading-2">
-                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-2" aria-expanded="false">
-                                      Volumes
-                                    </button>
-                                  </h2>
-                                  <div id="collapse-2" class="accordion-collapse collapse" data-bs-parent="#details_accordion">
-                                    <div class="accordion-body pt-0">
-                    
-                    
-                                    <div class="row mb-1 align-items-end">
-                                    <div class="col-auto">
-                                      <input class="form-check-input" name="volume_0_check" type="checkbox" ${volumes_data[0].check}>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_0_bind" value="${volumes_data[0].bind}"/>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_0_container" value="${volumes_data[0].container}"/>
-                                    </div>
-                                    <div class="col-lg-2">
-                                      <select class="form-select" name="volume_0_readwrite">
-                                        <option value="${volumes_data[0].readwrite}" selected hidden>${volumes_data[0].readwrite}</option>
-                                        <option value="rw">rw</option>
-                                        <option value="ro">ro</option>
-                                      </select>
-                                    </div>
-                                  </div>
-                    
-                                  <div class="row mb-1 align-items-end">
-                                    <div class="col-auto">
-                                      <input class="form-check-input" name="volume_1_check" type="checkbox" ${volumes_data[1].check}>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_1_bind" value="${volumes_data[1].bind}"/>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_1_container" value="${volumes_data[1].container}"/>
-                                    </div>
-                                    <div class="col-lg-2">
-                                      <select class="form-select" name="volume_1_readwrite">
-                                        <option value="${volumes_data[1].readwrite}" selected hidden>${volumes_data[1].readwrite}</option>
-                                        <option value="rw">rw</option>
-                                        <option value="ro">ro</option>
-                                      </select>
-                                    </div>
-                                  </div>
-                    
-                                  <div class="row mb-1 align-items-end">
-                                    <div class="col-auto">
-                                      <input class="form-check-input" name="volume_2_check" type="checkbox" ${volumes_data[2].check}>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_2_bind" value="${volumes_data[2].bind}"/>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_2_container" value="${volumes_data[2].container}"/>
-                                    </div>
-                                    <div class="col-lg-2">
-                                      <select class="form-select" name="volume_2_readwrite">
-                                        <option value="${volumes_data[2].readwrite}" selected hidden>${volumes_data[2].readwrite}</option>
-                                        <option value="rw">rw</option>
-                                        <option value="ro">ro</option>
-                                      </select>
-                                    </div>
-                                  </div>
-                    
-                                  <div class="row mb-1 align-items-end">
-                                    <div class="col-auto">
-                                      <input class="form-check-input" name="volume_3_check" type="checkbox" ${volumes_data[3].check}>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_3_bind" value="${volumes_data[3].bind}"/>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_3_container" value="${volumes_data[3].container}"/>
-                                    </div>
-                                    <div class="col-lg-2">
-                                      <select class="form-select" name="volume_3_readwrite">
-                                        <option value="${volumes_data[3].readwrite}" selected hidden>${volumes_data[3].readwrite}</option>
-                                        <option value="rw">rw</option>
-                                        <option value="ro">ro</option>
-                                      </select>
-                                    </div>
-                                  </div>
-                    
-                                  <div class="row mb-1 align-items-end">
-                                    <div class="col-auto">
-                                      <input class="form-check-input" name="volume_4_check" type="checkbox" ${volumes_data[4].check}>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_4_bind" value="${volumes_data[4].bind}"/>
-                                    </div>
-                                    <div class="col">
-                                      <input type="text" class="form-control" name="volume_4_container" value="${volumes_data[4].container}"/>
-                                    </div>
-                                    <div class="col-lg-2">
-                                      <select class="form-select" name="volume_4_readwrite">
-                                        <option value="${volumes_data[4].readwrite}" selected hidden>${volumes_data[4].readwrite}</option>
-                                        <option value="rw">rw</option>
-                                        <option value="ro">ro</option>
-                                      </select>
-                                    </div>
-                                  </div>
-                    
-                                  <div class="row mb-1 align-items-end">
-                                  <div class="col-auto">
-                                    <input class="form-check-input" name="volume_5_check" type="checkbox" ${volumes_data[5].check}>
-                                  </div>
-                                  <div class="col">
-                                    <input type="text" class="form-control" name="volume_5_bind" value="${volumes_data[5].bind}"/>
-                                  </div>
-                                  <div class="col">
-                                    <input type="text" class="form-control" name="volume_5_container" value="${volumes_data[5].container}"/>
-                                  </div>
-                                  <div class="col-lg-2">
-                                    <select class="form-select" name="volume_5_readwrite">
-                                      <option value="${volumes_data[5].readwrite}" selected hidden>${volumes_data[5].readwrite}</option>
-                                      <option value="rw">rw</option>
-                                      <option value="ro">ro</option>
-                                    </select>
-                                  </div>
-                                </div>
-                    
-                    
-                                    </div>
-                                  </div>
-                                </div>
-                                <div class="accordion-item">
-                                  <h2 class="accordion-header" id="heading-3">
-                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-3" aria-expanded="false">
-                                      Environment Variables
-                                    </button>
-                                  </h2>
-                                  <div id="collapse-3" class="accordion-collapse collapse" data-bs-parent="#details_accordion">
-                                    <div class="accordion-body pt-0">
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_0_check" ${env_data[0].check}>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">Variable</label>
-                                          <input type="text" class="form-control" name="env_0_name" value="${env_data[0].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">Value</label>
-                                          <input type="text" class="form-control" name="env_0_default" value="${env_data[0].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_1_check" ${env_data[1].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_1_name" value="${env_data[1].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_1_default" value="${env_data[1].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_2_check" ${env_data[2].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_2_name" value="${env_data[2].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_2_default" value="${env_data[2].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_3_check" ${env_data[3].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_3_name" value="${env_data[3].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_3_default" value="${env_data[3].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_4_check" ${env_data[4].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_4_name" value="${env_data[4].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_4_default" value="${env_data[4].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_5_check" ${env_data[5].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_5_name" value="${env_data[5].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_5_default" value="${env_data[5].default}"/>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_6_check" ${env_data[6].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_6_name" value="${env_data[6].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_6_default" value="${env_data[6].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_7_check" ${env_data[7].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_7_name" value="${env_data[7].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_7_default" value="${env_data[7].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_8_check" ${env_data[8].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_8_name" value="${env_data[8].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_8_default" value="${env_data[8].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_9_check" ${env_data[9].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_9_name" value="${env_data[9].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_9_default" value="${env_data[9].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_10_check" ${env_data[10].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_10_name" value="${env_data[10].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_10_default" value="${env_data[10].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" type="checkbox" name="env_11_check" ${env_data[11].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_11_name" value="${env_data[11].name}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="env_11_default" value="${env_data[11].default}"/>
-                                        </div>
-                                      </div>
-                    
-                    
-                    
-                    
-                                    </div>
-                                  </div>
-                                </div>
-                                <div class="accordion-item">
-                                  <h2 class="accordion-header" id="heading-4">
-                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-4" aria-expanded="false">
-                                      Labels
-                                    </button>
-                                  </h2>
-                                  <div id="collapse-4" class="accordion-collapse collapse" data-bs-parent="#details_accordion">
-                                    <div class="accordion-body pt-0">
-                    
-                    
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_0_check" ${label_data[0].check}>
-                                      </div>
-                                      <div class="col">
-                                        <label class="form-label">Variable</label>
-                                        <input type="text" class="form-control" name="label_0_name" value="${label_data[0].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <label class="form-label">Value</label>
-                                        <input type="text" class="form-control" name="label_0_value" value="${label_data[0].value}"/>
-                                      </div>
-                                    </div>
-                    
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_1_check" ${label_data[1].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_1_name" value="${label_data[1].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_1_value" value="${label_data[1].value}"/>
-                                      </div>
-                                    </div>
-                    
-                                      
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_2_check" ${label_data[2].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_2_name" value="${label_data[2].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_2_value" value="${label_data[2].value}"/>
-                                      </div>
-                                    </div>
-                    
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_3_check" ${label_data[3].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_3_name" value="${label_data[3].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_3_value" value="${label_data[3].value}"/>
-                                      </div>
-                                    </div>
-                    
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_4_check" ${label_data[4].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_4_name" value="${label_data[4].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_4_value" value="${label_data[4].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_5_check" ${label_data[5].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_5_name" value="${label_data[5].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_5_value" value="${label_data[5].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_6_check" ${label_data[6].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_6_name" value="${label_data[6].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_6_value" value="${label_data[6].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_7_check" ${label_data[7].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_7_name" value="${label_data[7].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_7_value" value="${label_data[7].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_8_check" ${label_data[8].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_8_name" value="${label_data[8].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_8_value" value="${label_data[8].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_9_check" ${label_data[9].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_9_name" value="${label_data[9].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_9_value" value="${label_data[9].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_10_check" ${label_data[10].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_10_name" value="${label_data[10].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_10_value" value="${label_data[10].value}"/>
-                                      </div>
-                                    </div>
-        
-                                    <div class="row mb-1 align-items-end">
-                                      <div class="col-auto">
-                                        <input class="form-check-input" type="checkbox" name="label_11_check" ${label_data[11].check}>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_11_name" value="${label_data[11].name}"/>
-                                      </div>
-                                      <div class="col">
-                                        <input type="text" class="form-control" name="label_11_value" value="${label_data[11].value}"/>
-                                      </div>
-                                    </div>
-                    
-                    
-                                    </div>
-                                  </div>
-                                </div>
-        
-        
-                                <div class="accordion-item">
-                                  <h2 class="accordion-header" id="heading-5">
-                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-5" aria-expanded="false">
-                                      Extras
-                                    </button>
-                                  </h2>
-                                  <div id="collapse-5" class="accordion-collapse collapse" data-bs-parent="#details_accordion">
-                                    <div class="accordion-body pt-0">
-                    
-        
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_0_check" type="checkbox" ${ports_data[0].check}>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">External Port</label>
-                                          <input type="text" class="form-control" name="port_0_external" value="${ports_data[0].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">Internal Port</label>
-                                          <input type="text" class="form-control" name="port_0_internal" value="${ports_data[0].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <label class="form-label">Protocol</label>
-                                          <select class="form-select" name="port_0_protocol">
-                                            <option value="${ports_data[0].protocol}" selected hidden>${ports_data[0].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_1_check" type="checkbox" ${ports_data[1].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_1_external" value="${ports_data[1].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_1_internal" value="${ports_data[1].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_1_protocol">
-                                            <option value="${ports_data[1].protocol}" selected hidden>${ports_data[1].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_2_check" type="checkbox" ${ports_data[2].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_2_external" value="${ports_data[2].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_2_internal" value="${ports_data[2].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_2_protocol">
-                                            <option value="${ports_data[2].protocol}" selected hidden>${ports_data[2].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_3_check" type="checkbox" ${ports_data[3].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_3_external" value="${ports_data[3].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_3_internal" value="${ports_data[3].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_3_protocol">
-                                            <option value="${ports_data[3].protocol}" selected hidden>${ports_data[3].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="port_4_check" type="checkbox" ${ports_data[4].check}>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_4_external" value="${ports_data[4].external}"/>
-                                        </div>
-                                        <div class="col">
-                                          <input type="text" class="form-control" name="port_4_internal" value="${ports_data[4].internal}"/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <select class="form-select" name="port_4_protocol">
-                                            <option value="${ports_data[4].protocol}" selected hidden>${ports_data[4].protocol}</option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                                      <div class="row mb-1 align-items-end">
-                                        <div class="col-auto">
-                                          <input class="form-check-input" name="" type="checkbox" >
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">External Port</label>
-                                          <input type="text" class="form-control" name="" value=""/>
-                                        </div>
-                                        <div class="col">
-                                          <label class="form-label">Internal Port</label>
-                                          <input type="text" class="form-control" name="" value=""/>
-                                        </div>
-                                        <div class="col-lg-2">
-                                          <label class="form-label">Protocol</label>
-                                          <select class="form-select" name="">
-                                            <option value="" selected hidden></option>
-                                            <option value="tcp">tcp</option>
-                                            <option value="udp">udp</option>
-                                          </select>
-                                        </div>
-                                      </div>
-                    
-                    
-                                    </div>
-                                  </div>
-                                </div>
-        
-        
-        
-                              </div>
-                    
-                    
-                              
-                              </form>
-                            </div>
-                            <div class="modal-footer">
-                              <button type="button" class="btn me-auto" data-bs-dismiss="modal">Close</button>
-                              <input type="submit" form="${form_id}_install" class="btn btn-success" value="Install"/>
-                            </div>
-                          </div>
-                        </div>
-                      </div>
-
-
-            <div class="modal modal-blur fade" id="log_view" tabindex="-1" style="display: none;" aria-hidden="true">
-              <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
-                <div class="modal-content">
-                  <div class="modal-header">
-                    <h5 class="modal-title">Logs</h5>
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-                  </div>
-                  <div class="modal-body">
-                    
-                  <div class="card-body">
-                        <h4>Logs:</h4>
-                        <div id="logView">
-                          <pre>No logs available</pre>
-                        </div>
-                  </div>
-        
-                  </div>
-                  <div class="modal-footer">
-                    <button type="button" class="btn me-auto" data-bs-dismiss="modal">Close</button>
-                    <button type="button" class="btn btn-info" onclick="viewLogs(this)" name="refresh"> 
-                      <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
-                        Refresh
-                    </button>
-                  </div>
-                </div>
-              </div>
-            </div>
-
-            
-
-          </div>
-        </div>
-      </div>
-      
-      <%- include('footer.ejs') %>
-      
-    </div>
-  </div>
-    
-
-  <!-- Libs JS -->
-  <script src="/libs/apexcharts/dist/apexcharts.min.js" defer></script>
-  <!-- Tabler Core -->
-  <script src="/js/tabler.min.js"></script>
-  <!-- Socket.io -->
-  <script src="/socket.io/socket.io.js"></script>
-  <script>
-    const socket = io();
-  </script>
-  <script src="/js/main.js"></script>
-    
-  </body>
-</html>
-

+ 282 - 0
views/dashboard.html

@@ -0,0 +1,282 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+    <title>DweebUI - Dashboard</title>
+    <!-- CSS files -->
+    <link href="/css/tabler.min.css" rel="stylesheet"/>
+    <link href="/css/meters.css" rel="stylesheet"/>
+    <script src="/js/htmx.min.js"></script>
+    <script src="/js/htmx-sse.js"></script>
+    <style>
+      @import url('/fonts/inter.css');
+      :root {
+        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+      }
+      body {
+        font-feature-settings: "cv03", "cv04", "cv11";
+      }
+    </style>
+  </head>
+  <body >
+  
+  <div class="page">
+
+    <%- include('navbar.html') %>
+    
+    <div class="page-wrapper">
+
+      <div class="page-body">
+        <div class="container-xl">
+          <div class="row row-deck row-cards" hx-ext="sse" sse-connect="/sse_event">
+            
+            <div class="col-12">
+              <div class="row row-cards">
+                
+                <div class="col-sm-6 col-lg-3">
+                  <div class="card card-sm">
+                    <div class="card-body">
+                      <div class="row align-items-center">
+                        <div class="col-auto">
+                          <span class="bg-green text-white avatar">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cpu" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg>
+                          </span>
+                        </div>
+
+                        <!-- HTMX -->
+                        <div class="col" name="CPU" id="green" data-hx-get="/stats" data-hx-trigger="load, every 1s">
+                          <div class="font-weight-medium">
+                            <label class="cpu-text mb-1" for="cpu">CPU 0%</label>
+                          </div>
+                          <div class="cpu-bar meter animate green">
+                            <span style="width:20%"><span></span></span>
+                          </div>
+                        </div>
+
+                      </div>
+                    </div>
+                  </div>
+                </div>
+
+                <div class="col-sm-6 col-lg-3">
+                  <div class="card card-sm">
+                    <div class="card-body">
+                      <div class="row align-items-center">
+                        <div class="col-auto">
+                          <span class="bg-blue text-white avatar">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-container" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 4v.01"></path> <path d="M20 20v.01"></path> <path d="M20 16v.01"></path> <path d="M20 12v.01"></path> <path d="M20 8v.01"></path> <path d="M8 4m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path> <path d="M4 4v.01"></path> <path d="M4 20v.01"></path> <path d="M4 16v.01"></path> <path d="M4 12v.01"></path> <path d="M4 8v.01"></path> </svg>                            
+                          </span>
+                        </div>
+
+                        <!-- HTMX -->
+                        <div class="col" name="RAM" id="blue" data-hx-get="/stats" data-hx-trigger="load, every 2s">
+                          <div class="font-weight-medium">
+                            <label class="ram-text mb-1" for="ram">RAM 0%</label>
+                          </div>
+                          <div class="ram-bar meter animate blue">
+                            <span style="width:20%"><span></span></span>
+                          </div>
+                        </div>
+
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                
+                <div class="col-sm-6 col-lg-3">
+                  <div class="card card-sm">
+                    <div class="card-body">
+                      <div class="row align-items-center">
+                        <div class="col-auto">
+                          <span class="bg-purple text-white avatar">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrows-left-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 17l-18 0"></path> <path d="M6 10l-3 -3l3 -3"></path> <path d="M3 7l18 0"></path> <path d="M18 20l3 -3l-3 -3"></path> </svg>                            
+                          </span>
+                        </div>
+
+                        <!-- HTMX -->
+                        <div class="col" name="NET" id="purple">
+                          <div class="font-weight-medium">
+                            <label id="net-text" class="net-text mb-1" for="network">Down: 0MB  Up: 0MB</label>
+                          </div>
+                          <div class="ram-bar meter animate purple">
+                            <span style="width:20%"><span></span></span>
+                          </div>
+                        </div>
+
+                      </div>
+                    </div>
+                  </div>
+                </div>
+
+                <div class="col-sm-6 col-lg-3">
+                  <div class="card card-sm">
+                    <div class="card-body">
+                      <div class="row align-items-center">
+                        <div class="col-auto">
+                          <span class="bg-orange text-white avatar">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-database" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 6m-8 0a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"></path> <path d="M4 6v6a8 3 0 0 0 16 0v-6"></path> <path d="M4 12v6a8 3 0 0 0 16 0v-6"></path></svg>
+                          </span>
+                        </div>
+
+                        <!-- HTMX -->
+                        <div class="col" name="DISK" id="orange" data-hx-get="/stats" data-hx-trigger="load, every 3s">
+                          <div class="font-weight-medium">
+                            <label class="disk-text mb-1" for="disk">DISK 0%</label>
+                          </div>
+                          <div class="meter animate orange">
+                            <span style="width:20%"><span></span></span>
+                          </div>
+                        </div>
+
+                      </div>
+                    </div>
+                  </div>
+                </div>
+
+
+              </div>
+            </div>
+
+
+            
+            <!-- HTMX -->
+            <div class="col-12">
+              <div class="row row-cards" id="containerCards" data-hx-get="/containers" data-hx-trigger="load, sse:docker" data-hx-swap="innerHTML">
+
+              </div>
+            </div>
+
+            <!-- HTMX -->
+            <!-- <div class="col-12">
+              <div class="row row-cards" data-hx-get="/installs" name="jellyfin" data-hx-trigger="load delay:2s, sse:install" data-hx-swap="beforeend" hx-target="#containerCards">
+
+              </div>
+            </div> -->
+            
+            <!-- HTMX Target-->
+            <div id="modals-here" class="modal modal-blur fade" style="display: none" aria-hidden="false" tabindex="-1">
+              <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
+                <div class="modal-content">
+                </div>
+              </div>
+            </div>
+            
+
+            <div class="modal modal-blur fade" id="log_view" tabindex="-1" style="display: none;" aria-hidden="true">
+              <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <h5 class="modal-title">Logs</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                  </div>
+                  <div class="modal-body">
+                    
+                  <div class="card-body">
+                        <h4>Logs:</h4>
+                        <div id="logView">
+                          <pre>No logs available</pre>
+                        </div>
+                  </div>
+        
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn me-auto" data-bs-dismiss="modal">Close</button>
+                    <button type="button" class="btn btn-info" onclick="viewLogs(this)" name="refresh"> 
+                      <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
+                        Refresh
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            
+
+          </div>
+        </div>
+      </div>
+      
+    <%- include('footer.html') %>
+      
+    </div>
+  </div>
+    
+
+  <script src="/libs/apexcharts/dist/apexcharts.min.js"></script>
+  <script src="/js/tabler.min.js"></script>
+  <script>
+    var options = {
+        chart: {
+          type: "line",
+          height: 40.0,
+          sparkline: {
+            enabled: true
+          },
+          animations: {
+            enabled: false
+          }
+        },
+        fill: {
+          opacity: 1
+        },
+        stroke: {
+          width: [2, 1],
+          dashArray: [0, 3],
+          lineCap: "round",
+          curve: "smooth"
+        },
+        series: [{
+          name: "CPU",
+          data: []
+        }, {
+          name: "RAM",
+          data: []
+        }],
+        tooltip: {
+          enabled: false
+        },
+        grid: {
+          strokeDashArray: 4
+        },
+        xaxis: {
+          labels: {
+            padding: 0
+          },
+          tooltip: {
+            enabled: false
+          }
+        },
+        yaxis: {
+          min: 0,
+          max: 100,
+          labels: {
+            padding: 4
+          }
+        },
+        colors: [tabler.getColor("primary"), tabler.getColor("gray-600")],
+        legend: {
+          show: false
+        }
+    }
+  </script>
+  <script>
+    function selectAll(group) {
+      
+      let checkboxes = document.getElementsByName(group);
+      if (checkboxes[0].checked == true) {
+        for (var i = 0; i < checkboxes.length; i++) {
+          checkboxes[i].checked = true;
+        }
+      } else {
+        for (var i = 0; i < checkboxes.length; i++) {
+          checkboxes[i].checked = false;
+        }
+      }
+    }
+  </script>
+
+  </body>
+</html>
+

+ 2 - 2
views/footer.ejs → views/footer.html

@@ -23,8 +23,8 @@
             All rights reserved.
           </li>
           <li class="list-inline-item">
-            <a href="#" class="link-secondary" rel="noopener">
-              v0.10
+            <a href="https://github.com/lllllllillllllillll/DweebUI/releases" class="link-secondary" rel="noopener">
+              v0.40
             </a>
           </li>
         </ul>

+ 0 - 188
views/images.ejs

@@ -1,188 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-    <title>DweebUI - Images</title>
-    <link href="/css/tabler.min.css" rel="stylesheet"/>
-    <link href="/css/demo.min.css" rel="stylesheet"/>
-    <style>
-      @import url('/fonts/inter.css');
-      :root {
-        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-      }
-      body {
-        font-feature-settings: "cv03", "cv04", "cv11";
-      }
-    </style>
-  </head>
-  <body >
-    <div class="page">
-      <!-- Navbar -->
-      <%- include('navbar.ejs') %>
-      <div class="page-wrapper">
-        <!-- Page header -->
-        
-        <!-- Page body -->
-        <div class="page-body">
-          <div class="container-xl">
-            <div class="row row-deck row-cards">
-
-              <div class="col-12 mt-12">
-                <div class="card">
-                  <div class="card-header">
-                    <h3 class="card-title">Docker Images</h3>
-                      <div class="card-options btn-list">                  
-                          <a href="#" class="btn">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
-                          Refresh
-                          </a>
-                          <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
-                          New Image
-                          </a>
-                      </div>
-                  </div>
-                  
-                  <div class="modal modal-blur fade" id="add-site" tabindex="-1" style="display: none;" aria-hidden="true">
-                    <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-                      <div class="modal-content">
-                        <div class="modal-body">
-
-                          <form action="/addsite" id="addsite" method="POST">
-                            <div class="mb-3">
-                              <div class="form-label">Type</div>
-                              <select class="form-select" name="type">
-                                <option value="reverse_proxy">Reverse Proxy</option>
-                                <option value="proxy">Proxy</option>
-                                <option value="file_server">File Server</option>
-                              </select>
-                            </div>
-                            <div class="mb-3">
-                              <label class="form-label">Domain / Subdomain</label>
-                              <input type="text" class="form-control" name="domain" placeholder="media.mydomainname.com">
-                            </div>
-                            <div class="mb-4">
-                              <div class="row g-2">
-                                <div class="col-8">
-                                  <label class="form-label">Hostname / Host IP</label>
-                                  <input type="text" class="form-control" name="host" placeholder="localhost">
-                                </div>
-                                <div class="col-4">
-                                  <label class="form-label">Port</label>
-                                  <input type="text" class="form-control" name="port" placeholder="8000">
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="divide-y">
-                                <div>
-                                  <label class="row">
-                                    <span class="col" title="HTTP Strict Transport Security (HSTS) is a simple and widely supported standard to protect visitors by ensuring that their browsers always connect to a website over HTTPS.">HSTS</span>
-                                    <span class="col-auto">
-                                      <label class="form-check form-check-single form-switch">
-                                        <input class="form-check-input" type="checkbox" name="hsts" checked="" disabled="">
-                                      </label>
-                                    </span>
-                                  </label>
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="form-label">Container</div>
-                              <select class="form-select" name="container" disabled="">
-                                <option value="0" selected></option>
-                                <option value="1">Jellyfin</option>
-                              </select>
-                            </div>
-                          </form>
-                        </div>
-                        <div class="modal-footer">
-                          <button type="button" class="btn btn-link link-secondary me-auto" data-bs-dismiss="modal">Cancel</button>
-                          <input type="submit" form="addsite" class="btn btn-success" value="Add"/>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-
-
-                  <div id="table-default" class="table-responsive">
-                    <table class="table">
-                      
-                      <%- image_list %>
-
-                    </table>
-                  </div>
-
-                  <div class="card-footer d-flex align-items-center">
-
-                    <span class="dropdown">
-                      <button class="btn dropdown-toggle align-text-top" data-bs-toggle="dropdown">Actions</button>
-                      <div class="dropdown-menu dropdown-menu-end">
-                        <button class="dropdown-item" type="submit" formaction="/enablesite">
-                          Enable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/disablesite">
-                          Disable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/removesite">
-                          Delete
-                        </button>
-                      </div>
-                    </span>
-
-                  </form>
-                                        
-                    <p class="m-0 text-muted ms-auto"><%- image_count %> Images</p>
-
-                  </div>
-                </div>
-              </div>
-
-            </div>
-          </div>
-        </div>
-        
-        <%- include('footer.ejs') %>
-        
-      </div>
-    </div>
-    
-    <!-- Libs JS -->
-    <script src="/libs/list.js/dist/list.min.js" defer></script>
-    <!-- Tabler Core -->
-    <script src="/js/tabler.min.js" defer></script>
-    <script src="/js/demo.min.js" defer></script>
-
-    <script>
-      document.addEventListener("DOMContentLoaded", function() {
-      const list = new List('table-default', {
-      	sortClass: 'table-sort',
-      	listClass: 'table-tbody',
-      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
-      		{ attr: 'data-date', name: 'sort-date' },
-      		{ attr: 'data-progress', name: 'sort-progress' },
-      		'sort-quantity'
-      	]
-      });
-      })
-    </script>
-
-    <script>
-      function selectAll() {
-        let checkboxes = document.getElementsByName('select');
-        if (checkboxes[0].checked == true) {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = true;
-          }
-        } else {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = false;
-          }
-        }
-      }
-    </script>
-    
-  </body>
-</html>

+ 116 - 0
views/images.html

@@ -0,0 +1,116 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+    <title>DweebUI - Images</title>
+    <link href="/css/tabler.min.css" rel="stylesheet"/>
+    <link href="/css/demo.min.css" rel="stylesheet"/>
+    <style>
+      @import url('/fonts/inter.css');
+      :root {
+        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+      }
+      body {
+        font-feature-settings: "cv03", "cv04", "cv11";
+      }
+    </style>
+  </head>
+  <body >
+    <div class="page">
+      <!-- Navbar -->
+      <%- include('navbar.html') %>
+      <div class="page-wrapper">
+        <!-- Page header -->
+        
+        <!-- Page body -->
+        <div class="page-body">
+          <div class="container-xl">
+            <div class="row row-deck row-cards">
+
+              <div class="col-12 mt-12">
+                <div class="card">
+                  <form method="post">
+                    <div class="card-header">
+                      <h3 class="card-title">Docker Images</h3>
+                        <div class="card-options btn-list">                  
+                            <a href="#" class="btn">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
+                              Refresh
+                            </a>
+                            <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
+                              New Image
+                            </a>
+                        </div>
+                    </div>
+                    
+
+                    <div id="table-default" class="table-responsive">
+                      <table class="table">
+                        
+                        <%- image_list %>
+
+                      </table>
+                    </div>
+
+                    <div class="card-footer d-flex align-items-center">
+
+                      <button class="btn" type="submit" formaction="/removeImage">Remove</button>
+
+                      </form>
+                                          
+                      <p class="m-0 text-muted ms-auto"><%- image_count %> Images</p>
+
+                    </div>
+                  </form>
+                </div>
+              </div>
+
+            </div>
+          </div>
+        </div>
+        
+        <%- include('footer.html') %>
+        
+      </div>
+    </div>
+    
+    <!-- Libs JS -->
+    <script src="/libs/list.js/dist/list.min.js" defer></script>
+    <!-- Tabler Core -->
+    <script src="/js/tabler.min.js" defer></script>
+    <script src="/js/demo.min.js" defer></script>
+
+    <script>
+      document.addEventListener("DOMContentLoaded", function() {
+      const list = new List('table-default', {
+      	sortClass: 'table-sort',
+      	listClass: 'table-tbody',
+      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
+      		{ attr: 'data-date', name: 'sort-date' },
+      		{ attr: 'data-progress', name: 'sort-progress' },
+      		'sort-quantity'
+      	]
+      });
+      })
+    </script>
+
+    <script>
+      function selectAll() {
+        let checkboxes = document.getElementsByName('select');
+        if (checkboxes[0].checked == true) {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = true;
+          }
+        } else {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = false;
+          }
+        }
+      }
+    </script>
+    
+  </body>
+</html>

+ 18 - 7
views/login.ejs → views/login.html

@@ -9,7 +9,7 @@
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <style>
-      @import url('fonts/inter.css');
+      @import url('/fonts/inter.css');
       :root {
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
       }
@@ -22,19 +22,27 @@
     <script src="/js/demo-theme.js"></script>
     <div class="page page-center">
       <div class="container container-tight py-4">
+        <div class="text-center">
+          <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
+              <img src="/images/logo.png" alt="DweebUI" title="DweebUI" height="100px">
+          </h1>
+        </div>
         <div class="text-center mb-4">
-          <a href="." class="navbar-brand navbar-brand-autodark"><img src="/static/logo.svg" height="50" alt=""></a>
+          <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
+            <img src="/images/dweebui.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">
+          </h1>
         </div>
+
         <div class="card card-md">
           <div class="card-body">
             <h2 class="h2 text-center mb-4">Login to your account</h2>
             <form action="/login" method="POST" novalidate>
 
-                <% if(error) { %>
-                    <div class="alert alert-danger" role="alert">
-                        <%= error %>
-                    </div>
-                <% } %>
+              <% if(error) { %>
+                <div class="alert alert-danger" role="alert">
+                    <%= error %>
+                </div>
+              <% } %>
 
               <div class="mb-2">
                 <label class="form-label">Email address</label>
@@ -70,9 +78,12 @@
         </div>
       </div>
     </div>
+ 
     <!-- Libs JS -->
     <!-- Tabler Core -->
     <script src="/js/tabler.min.js" defer></script>
     <script src="/js/demo.min.js" defer></script>
+    
+
   </body>
 </html>

+ 16 - 5
views/navbar.ejs → views/navbar.html

@@ -33,10 +33,15 @@
       aria-controls="navbar-menu" aria-expanded="false" aria-label="Toggle navigation">
       <span class="navbar-toggler-icon"></span>
     </button>
-    <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
+    <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0">
       <a href="#">
-        <img src="/static/logo.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">
+        <img src="/images/logo.png" alt="DweebUI" title="DweebUI" height="40px">
       </a>
+      <a href="#">
+        <img src="/images/dweebui.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">     
+      </a>
+      
+
     </h1>
     <div class="navbar-nav flex-row order-md-last">
       <div class="nav-item d-none d-md-flex me-3">
@@ -79,11 +84,15 @@
             <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M10 5a2 2 0 1 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6" /> <path d="M9 17v1a3 3 0 0 0 6 0v-1" /> </svg>
             <!-- <span class="badge bg-red"></span> -->
           </a>
+          
+          
           <div class="dropdown-menu dropdown-menu-arrow dropdown-menu-end dropdown-menu-card">
             <div class="card">
               <div class="card-header">
                 <h3 class="card-title">Notifications</h3>
               </div>
+              
+              
               <div class="list-group list-group-flush list-group-hoverable">
                 <div class="list-group-item">
                   <div class="row align-items-center">
@@ -120,7 +129,9 @@
                     </div>
                   </div>
                 </div>
+
               </div>
+              
             </div>
           </div>
         </div>
@@ -138,11 +149,11 @@
           </div>
         </a>
         <div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
-          <!-- <a href="#" class="dropdown-item">Status</a> -->
           <a href="/account" class="dropdown-item">Account</a>
-          <!-- <a href="#" class="dropdown-item">Feedback</a> -->
-          <!-- <div class="dropdown-divider"></div> -->
+          <a href="/variables" class="dropdown-item">Variables</a>
           <a href="/settings" class="dropdown-item">Settings</a>
+          <!-- <div class="dropdown-divider"></div> -->
+
           <a href="/logout" class="dropdown-item">Logout</a>
         </div>
       </div>

+ 0 - 188
views/networks.ejs

@@ -1,188 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-    <title>DweebUI - Networks</title>
-    <link href="/css/tabler.min.css" rel="stylesheet"/>
-    <link href="/css/demo.min.css" rel="stylesheet"/>
-    <style>
-      @import url('/fonts/inter.css');
-      :root {
-        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-      }
-      body {
-        font-feature-settings: "cv03", "cv04", "cv11";
-      }
-    </style>
-  </head>
-  <body >
-    <div class="page">
-      <!-- Navbar -->
-      <%- include('navbar.ejs') %>
-      <div class="page-wrapper">
-        <!-- Page header -->
-        
-        <!-- Page body -->
-        <div class="page-body">
-          <div class="container-xl">
-            <div class="row row-deck row-cards">
-
-              <div class="col-12 mt-12">
-                <div class="card">
-                  <div class="card-header">
-                    <h3 class="card-title">Docker Networks</h3>
-                      <div class="card-options btn-list">                  
-                          <a href="#" class="btn">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
-                          Refresh
-                          </a>
-                          <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
-                          New Network
-                          </a>
-                      </div>
-                  </div>
-                  
-                  <div class="modal modal-blur fade" id="add-site" tabindex="-1" style="display: none;" aria-hidden="true">
-                    <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-                      <div class="modal-content">
-                        <div class="modal-body">
-
-                          <form action="/addsite" id="addsite" method="POST">
-                            <div class="mb-3">
-                              <div class="form-label">Type</div>
-                              <select class="form-select" name="type">
-                                <option value="reverse_proxy">Reverse Proxy</option>
-                                <option value="proxy">Proxy</option>
-                                <option value="file_server">File Server</option>
-                              </select>
-                            </div>
-                            <div class="mb-3">
-                              <label class="form-label">Domain / Subdomain</label>
-                              <input type="text" class="form-control" name="domain" placeholder="media.mydomainname.com">
-                            </div>
-                            <div class="mb-4">
-                              <div class="row g-2">
-                                <div class="col-8">
-                                  <label class="form-label">Hostname / Host IP</label>
-                                  <input type="text" class="form-control" name="host" placeholder="localhost">
-                                </div>
-                                <div class="col-4">
-                                  <label class="form-label">Port</label>
-                                  <input type="text" class="form-control" name="port" placeholder="8000">
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="divide-y">
-                                <div>
-                                  <label class="row">
-                                    <span class="col" title="HTTP Strict Transport Security (HSTS) is a simple and widely supported standard to protect visitors by ensuring that their browsers always connect to a website over HTTPS.">HSTS</span>
-                                    <span class="col-auto">
-                                      <label class="form-check form-check-single form-switch">
-                                        <input class="form-check-input" type="checkbox" name="hsts" checked="" disabled="">
-                                      </label>
-                                    </span>
-                                  </label>
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="form-label">Container</div>
-                              <select class="form-select" name="container" disabled="">
-                                <option value="0" selected></option>
-                                <option value="1">Jellyfin</option>
-                              </select>
-                            </div>
-                          </form>
-                        </div>
-                        <div class="modal-footer">
-                          <button type="button" class="btn btn-link link-secondary me-auto" data-bs-dismiss="modal">Cancel</button>
-                          <input type="submit" form="addsite" class="btn btn-success" value="Add"/>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-
-
-                  <div id="table-default" class="table-responsive">
-                    <table class="table">
-                      
-                      <%- network_list %>
-
-                    </table>
-                  </div>
-
-                  <div class="card-footer d-flex align-items-center">
-
-                    <span class="dropdown">
-                      <button class="btn dropdown-toggle align-text-top" data-bs-toggle="dropdown">Actions</button>
-                      <div class="dropdown-menu dropdown-menu-end">
-                        <button class="dropdown-item" type="submit" formaction="/enablesite">
-                          Enable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/disablesite">
-                          Disable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/removesite">
-                          Delete
-                        </button>
-                      </div>
-                    </span>
-
-                  </form>
-                                        
-                    <p class="m-0 text-muted ms-auto"><%- network_count %> Networks</p>
-
-                  </div>
-                </div>
-              </div>
-
-            </div>
-          </div>
-        </div>
-        
-        <%- include('footer.ejs') %>
-        
-      </div>
-    </div>
-    
-    <!-- Libs JS -->
-    <script src="/libs/list.js/dist/list.min.js" defer></script>
-    <!-- Tabler Core -->
-    <script src="/js/tabler.min.js" defer></script>
-    <script src="/js/demo.min.js" defer></script>
-
-    <script>
-      document.addEventListener("DOMContentLoaded", function() {
-      const list = new List('table-default', {
-      	sortClass: 'table-sort',
-      	listClass: 'table-tbody',
-      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
-      		{ attr: 'data-date', name: 'sort-date' },
-      		{ attr: 'data-progress', name: 'sort-progress' },
-      		'sort-quantity'
-      	]
-      });
-      })
-    </script>
-
-    <script>
-      function selectAll() {
-        let checkboxes = document.getElementsByName('select');
-        if (checkboxes[0].checked == true) {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = true;
-          }
-        } else {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = false;
-          }
-        }
-      }
-    </script>
-    
-  </body>
-</html>

+ 131 - 0
views/networks.html

@@ -0,0 +1,131 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+    <title>DweebUI - Networks</title>
+    <link href="/css/tabler.min.css" rel="stylesheet"/>
+    <link href="/css/demo.min.css" rel="stylesheet"/>
+    <style>
+      @import url('/fonts/inter.css');
+      :root {
+        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+      }
+      body {
+        font-feature-settings: "cv03", "cv04", "cv11";
+      }
+    </style>
+  </head>
+  <body >
+    <div class="page">
+      <!-- Navbar -->
+      <%- include('navbar.html') %>
+      <div class="page-wrapper">
+        <!-- Page header -->
+        
+        <!-- Page body -->
+        <div class="page-body">
+          <div class="container-xl">
+            <div class="row row-deck row-cards">
+
+              <div class="col-12 mt-12">
+                <div class="card">
+                  <form method="post">
+                    <div class="card-header">
+                      <h3 class="card-title">Docker Networks</h3>
+                        <div class="card-options btn-list">                  
+                            <a href="#" class="btn">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
+                            Refresh
+                            </a>
+                            <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
+                            New Network
+                            </a>
+                        </div>
+                    </div>
+                  
+                    <div id="table-default" class="table-responsive">
+                      <table class="table">
+                        
+                        <%- network_list %>
+
+                      </table>
+                    </div>
+
+                    <div class="card-footer d-flex align-items-center">
+
+
+                      <button class="btn" type="submit" formaction="/removeNetwork">Remove</button>
+
+                      <!-- <span class="dropdown">
+                        <button class="btn dropdown-toggle align-text-top" data-bs-toggle="dropdown">Actions</button>
+                        <div class="dropdown-menu dropdown-menu-end">
+                          <button class="dropdown-item" type="submit" formaction="/submitNetworks">
+                            Enable
+                          </button>
+                          <button class="dropdown-item" type="submit" formaction="/submitNetworks">
+                            Disable
+                          </button>
+                          <button class="dropdown-item" type="submit" formaction="/submitNetworks">
+                            Delete
+                          </button>
+                        </div>
+                      </span> -->
+
+                      
+                                        
+                      <p class="m-0 text-muted ms-auto"><%- network_count %> Networks</p>
+
+                    </div>
+                  </form>
+                </div>
+              </div>
+
+            </div>
+          </div>
+        </div>
+        
+        <%- include('footer.html') %>
+        
+      </div>
+    </div>
+    
+    <!-- Libs JS -->
+    <script src="/libs/list.js/dist/list.min.js" defer></script>
+    <!-- Tabler Core -->
+    <script src="/js/tabler.min.js" defer></script>
+    <script src="/js/demo.min.js" defer></script>
+
+    <script>
+      document.addEventListener("DOMContentLoaded", function() {
+      const list = new List('table-default', {
+      	sortClass: 'table-sort',
+      	listClass: 'table-tbody',
+      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
+      		{ attr: 'data-date', name: 'sort-date' },
+      		{ attr: 'data-progress', name: 'sort-progress' },
+      		'sort-quantity'
+      	]
+      });
+      })
+    </script>
+
+    <script>
+      function selectAll() {
+        let checkboxes = document.getElementsByName('select');
+        if (checkboxes[0].checked == true) {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = true;
+          }
+        } else {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = false;
+          }
+        }
+      }
+    </script>
+    
+  </body>
+</html>

+ 2 - 2
views/portal.ejs → views/portal.html

@@ -21,7 +21,7 @@
   <body >
     <div class="page">
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
       <div class="page-wrapper">
   
         <div class="page-body">
@@ -131,7 +131,7 @@
           </div>
         </div>
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
         
       </div>
     </div>

+ 10 - 12
views/register.ejs → views/register.html

@@ -9,7 +9,7 @@
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <style>
-      @import url('fonts/inter.css');
+      @import url('/fonts/inter.css');
       :root {
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
       }
@@ -26,7 +26,7 @@
       <form class="container container-tight py-4" action="/register" method="POST" novalidate>
         
         <div class="text-center mb-4">
-          <a href="#" class="navbar-brand navbar-brand-autodark d-none"><img src="/static/logo.svg" height="36" alt=""></a>
+          <a href="#" class="navbar-brand navbar-brand-autodark d-none"><img src="/images/logo.svg" height="36" alt=""></a>
         </div>
 
         <div class="card">
@@ -88,7 +88,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="rus.jpg" class="form-imagecheck-input" checked/>
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/rus.jpg" alt="Rich Uncle Skeleton" title="Rich Uncle Skeleton" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/rus.jpg" alt="Rich Uncle Skeleton" title="Rich Uncle Skeleton" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -96,7 +96,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="burns.jpg" class="form-imagecheck-input"/>
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/burns.jpg" alt="Montgomery Burns" title="Montgomery Burns" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/burns.jpg" alt="Montgomery Burns" title="Montgomery Burns" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -104,7 +104,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="frank.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/frank.jpg" alt="Frank Grimes" title= "Frank Grimes" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/frank.jpg" alt="Frank Grimes" title= "Frank Grimes" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -112,7 +112,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="moe.jpg" class="form-imagecheck-input"/>
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/moe.jpg" alt="Moe Szyslak" title="Moe Szyslak" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/moe.jpg" alt="Moe Szyslak" title="Moe Szyslak" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -120,7 +120,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="poochie.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/poochie.jpg" alt="Poochie" title="Poochie" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/poochie.jpg" alt="Poochie" title="Poochie" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -128,7 +128,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="skinner.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/skinner.jpg" alt="Seymour Skinner" title="Seymour Skinner" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/skinner.jpg" alt="Seymour Skinner" title="Seymour Skinner" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -136,7 +136,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="moleman.png" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/moleman.png" alt="Hans Moleman" title="Hans Moleman" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/moleman.png" alt="Hans Moleman" title="Hans Moleman" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -144,7 +144,7 @@
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="duffman.png" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/duffman.png" alt="Duffman" title="Duffman" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/duffman.png" alt="Duffman" title="Duffman" class="form-imagecheck-image" width="100px">
                     </span>
                   </label>
                 </div>
@@ -176,11 +176,9 @@
               <div class="d-none d-md-flex">
                 
                 <a href="?theme=dark" class="nav-link px-0 hide-theme-dark" title="Enable dark mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
-                  <!-- Download SVG icon from http://tabler-icons.io/i/moon -->
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" /></svg>
                 </a>
                 <a href="?theme=light" class="nav-link px-0 hide-theme-light" title="Enable light mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
-                  <!-- Download SVG icon from http://tabler-icons.io/i/sun -->
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" /></svg>
                 </a>
               </div>

+ 0 - 136
views/settings.ejs

@@ -1,136 +0,0 @@
-	<!doctype html>
-	<html lang="en">
-	<head>
-		<meta charset="utf-8"/>
-		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-		<title>DweebUI - Settings</title>
-		<!-- CSS files -->
-		<link href="/css/tabler.min.css" rel="stylesheet"/>
-		<link href="/css/demo.min.css" rel="stylesheet"/>
-		<style>
-			@import url('/fonts/inter.css');
-			:root {
-			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-			}
-			body {
-			  font-feature-settings: "cv03", "cv04", "cv11";
-			}
-		  </style>
-	</head>
-	<body >
-		<div class="page">
-		<!-- Navbar -->
-		<%- include('navbar.ejs') %>
-		<div class="page-wrapper">
-			<!-- Page header -->
-			<div class="page-header d-print-none">
-			<div class="container-xl">
-				<div class="row g-2 align-items-center">
-				<div class="col">
-					<h2 class="page-title">
-					Account Settings
-					</h2>
-				</div>
-				</div>
-			</div>
-			</div>
-			<!-- Page body -->
-			<div class="page-body">
-				<div class="container-xl">
-				  <div class="card">
-					<div class="row g-0">
-					  <div class="col-3 d-none d-md-block border-end">
-						<div class="card-body">
-						  <h4 class="subheader">Business settings</h4>
-						  <div class="list-group list-group-transparent">
-						  <a href="/account" class="list-group-item list-group-item-action d-flex align-items-center">Account</a>
-						  <!-- <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">My Notifications</a>
-						  <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">Connected Apps</a> -->
-						  <a href="/settings" class="list-group-item list-group-item-action d-flex align-items-center active">Settings</a>
-						  <!-- <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">Billing & Invoices</a> -->
-						  </div>
-						  <h4 class="subheader mt-4">Experience</h4>
-						  <div class="list-group list-group-transparent">
-						  <a href="#" class="list-group-item list-group-item-action">Credits</a>
-						  </div>
-						</div>
-					  </div>
-					  <div class="col d-flex flex-column">
-					  
-						<div class="card-body">
-						  <h2 class="mb-2">Settings</h2>
-						  <p class="text-muted mb-4">Configure server below</p>
-						  
-						  <div class="row align-items-center">
-							<div class="col">
-							  <a href="./QuickConnect.bat" class="btn" download="QuickConnect.bat">
-								<!-- Download SVG icon from https://tabler-icons.io/i/brand-tabler-->
-								<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-windows" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M17.8 20l-12 -1.5c-1 -.1 -1.8 -.9 -1.8 -1.9v-9.2c0 -1 .8 -1.8 1.8 -1.9l12 -1.5c1.2 -.1 2.2 .8 2.2 1.9v12.1c0 1.2 -1.1 2.1 -2.2 1.9z"></path> <path d="M12 5l0 14"></path> <path d="M4 12l16 0"></path> </svg>
-								Windows QuickConnect
-							  </a>
-							</div>
-						  </div>
-						  
-						  <div class="row mt-4">
-							<div class="col-md">
-							  <div class="form-label">Full Name</div>
-							  <input type="text" class="form-control" value="" readonly="">
-							</div>
-							<div class="col-md">
-							  <div class="form-label">First Name</div>
-							  <input type="text" class="form-control" value="" readonly="">
-							</div>
-							<div class="col-md">
-							  <div class="form-label">Last Name</div>
-							  <input type="text" class="form-control" value="" readonly="">
-							</div>
-						  </div>
-						  <h3 class="card-title mt-4">Email</h3>
-						  <p class="card-subtitle">This contact will be shown to others publicly, so choose it carefully.</p>
-						  <div>
-							<div class="row g-2">
-							  <div class="col-auto">
-								<input type="text" class="form-control w-auto" value="" readonly="">
-							  </div>
-							  <div class="col-auto">
-								<a href="#" class="btn">Change</a>
-							  </div>
-							</div>
-						  </div>
-						  <h3 class="card-title mt-4">Password</h3>
-						  <p class="card-subtitle">You can set a permanent password if you don't want to use temporary login codes.</p>
-						  <div>
-							<a href="#" class="btn">
-							  Set new password
-							</a>
-						  </div>
-						  <h3 class="card-title mt-4">Public profile</h3>
-						  <p class="card-subtitle">Making your profile public means that anyone on the Dashkit network will be able to find
-						  you.</p>
-						  <div>
-							<label class="form-check form-switch form-switch-lg">
-							  <input class="form-check-input" type="checkbox" >
-							  <span class="form-check-label form-check-label-on">You're currently visible</span>
-							  <span class="form-check-label form-check-label-off">You're
-							  currently invisible</span>
-							</label>
-						  </div>
-						</div>
-	  
-					  </div>
-					  
-					</div>
-				  </div>
-				</div>
-			  </div>
-
-			<%- include('footer.ejs') %>
-		</div>
-		</div>
-		<!-- Libs JS -->
-		<!-- Tabler Core -->
-		<script src="/js/tabler.min.js" defer></script>
-		<script src="/js/demo.min.js" defer></script>
-	</body>
-	</html>

+ 120 - 0
views/settings.html

@@ -0,0 +1,120 @@
+	<!doctype html>
+	<html lang="en">
+	<head>
+		<meta charset="utf-8"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+		<title>DweebUI - Settings</title>
+		<!-- CSS files -->
+		<link href="/css/tabler.min.css" rel="stylesheet"/>
+		<link href="/css/demo.min.css" rel="stylesheet"/>
+		<style>
+			@import url('/fonts/inter.css');
+			:root {
+			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+			}
+			body {
+			  font-feature-settings: "cv03", "cv04", "cv11";
+			}
+		  </style>
+	</head>
+	<body >
+		<div class="page">
+		<!-- Navbar -->
+		<%- include('navbar.html') %>
+		<div class="page-wrapper">
+			<!-- Page header -->
+			<div class="page-header d-print-none">
+			<div class="container-xl">
+				<div class="row g-2 align-items-center">
+				<div class="col">
+					<h2 class="page-title">
+					Settings
+					</h2>
+				</div>
+				</div>
+			</div>
+			</div>
+			<!-- Page body -->
+			<div class="page-body">
+				<div class="container-xl">
+				  <div class="card">
+					<div class="row g-0">
+						<%- include('sidebar.html') %>
+					  	<div class="col d-flex flex-column">
+					  
+							<div class="card-body">
+							<h2 class="mb-2">Settings</h2>
+							<p class="text-muted mb-4">Configure server below</p>
+							
+							<div class="row align-items-center">
+								<div class="col">
+								<!-- <a href="./QuickConnect.bat" class="btn" download="QuickConnect.bat">
+									<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-windows" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M17.8 20l-12 -1.5c-1 -.1 -1.8 -.9 -1.8 -1.9v-9.2c0 -1 .8 -1.8 1.8 -1.9l12 -1.5c1.2 -.1 2.2 .8 2.2 1.9v12.1c0 1.2 -1.1 2.1 -2.2 1.9z"></path> <path d="M12 5l0 14"></path> <path d="M4 12l16 0"></path> </svg>
+									Windows QuickConnect
+								</a> -->
+								</div>
+							</div>
+							
+							<div class="row mt-4">
+								<div class="col-md">
+								<div class="form-label">Full Name</div>
+								<input type="text" class="form-control" value="" readonly="">
+								</div>
+								<div class="col-md">
+								<div class="form-label">First Name</div>
+								<input type="text" class="form-control" value="" readonly="">
+								</div>
+								<div class="col-md">
+								<div class="form-label">Last Name</div>
+								<input type="text" class="form-control" value="" readonly="">
+								</div>
+							</div>
+							<h3 class="card-title mt-4">Email</h3>
+							<p class="card-subtitle">This contact will be shown to others publicly, so choose it carefully.</p>
+							<div>
+								<div class="row g-2">
+								<div class="col-auto">
+									<input type="text" class="form-control w-auto" value="" readonly="">
+								</div>
+								<div class="col-auto">
+									<a href="#" class="btn">Change</a>
+								</div>
+								</div>
+							</div>
+							<h3 class="card-title mt-4">Password</h3>
+							<p class="card-subtitle">You can set a permanent password if you don't want to use temporary login codes.</p>
+							<div>
+								<a href="#" class="btn">
+								Set new password
+								</a>
+							</div>
+							<h3 class="card-title mt-4">Public profile</h3>
+							<p class="card-subtitle">Making your profile public means that anyone on the Dashkit network will be able to find
+							you.</p>
+							<div>
+								<label class="form-check form-switch form-switch-lg">
+								<input class="form-check-input" type="checkbox" >
+								<span class="form-check-label form-check-label-on">You're currently visible</span>
+								<span class="form-check-label form-check-label-off">You're
+								currently invisible</span>
+								</label>
+							</div>
+							</div>
+	  
+					  	</div>
+					  
+					</div>
+				  </div>
+				</div>
+			  </div>
+
+			<%- include('footer.html') %>
+		</div>
+		</div>
+		<!-- Libs JS -->
+		<!-- Tabler Core -->
+		<script src="/js/tabler.min.js" defer></script>
+		<script src="/js/demo.min.js" defer></script>
+	</body>
+	</html>

+ 14 - 0
views/sidebar.html

@@ -0,0 +1,14 @@
+<div class="col-3 d-none d-md-block border-end">
+	<div class="card-body">
+		<h4 class="subheader">Menu</h4>
+		<div class="list-group list-group-transparent">
+			<a href="/account" class="list-group-item list-group-item-action d-flex align-items-center">Accounts</a>
+			<a href="/variables" class="list-group-item list-group-item-action d-flex align-items-center" disabled="">Variables</a>
+			<a href="/settings" class="list-group-item list-group-item-action d-flex align-items-center">Settings</a>
+		</div>
+		<h4 class="subheader mt-4">Other</h4>
+		<div class="list-group list-group-transparent">
+			<a href="/supporters" class="list-group-item list-group-item-action">Supporters</a>
+		</div>
+	</div>
+</div>

+ 85 - 0
views/supporters.html

@@ -0,0 +1,85 @@
+	<!doctype html>
+	<html lang="en">
+	<head>
+		<meta charset="utf-8"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+		<title>DweebUI - Settings</title>
+		<!-- CSS files -->
+		<link href="/css/tabler.min.css" rel="stylesheet"/>
+		<link href="/css/demo.min.css" rel="stylesheet"/>
+		<script src="/js/htmx.min.js"></script>
+		<style>
+			@import url('/fonts/inter.css');
+			:root {
+			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+			}
+			body {
+			  font-feature-settings: "cv03", "cv04", "cv11";
+			}
+		  </style>
+	</head>
+	<body >
+	<div class="page">
+		<!-- Navbar -->
+		<%- include('navbar.html') %>
+		<div class="page-wrapper">
+			<!-- Page header -->
+			<div class="page-header d-print-none">
+				<div class="container-xl">
+					<div class="row g-2 align-items-center">
+						<div class="col">
+							<h2 class="page-title">
+								Settings
+							</h2>
+						</div>
+					</div>
+				</div>
+			</div>
+			<!-- Page body -->
+			<div class="page-body">
+				<div class="container-xl">
+					<div class="card">
+						<div class="row g-0">
+							<%- include('sidebar.html') %>
+							<div class="col d-flex flex-column">
+					
+								<div class="card-body">
+									<h2 class="mb-2">Supporters</h2>
+									<p class="text-muted mb-4">[Click to Thank]</p>
+									<div class="row align-items-center">
+										<div class="col">
+
+											<span type="button" class="avatar avatar-md bg-green-lt" hx-trigger="load, click" hx-post="/thank" hx-target="#count" name="MM"  title="MM" style="margin-right: 5px;">mm</span>
+
+											<span type="button" class="avatar avatar-md bg-cyan-lt" hx-trigger="click" hx-post="/thank" hx-target="#count" name="PD" title="PD" style="margin-right: 5px;">pd</span>
+
+
+										</div>
+									</div>
+								</div>
+								<div class="card-body">
+									<p class="text-muted mb-4">Thanks counter:</p>
+									<div class="row align-items-center">
+										<div class="col">
+
+											<span class="avatar avatar-md bg-yellow-lt" id="count" style="margin-right: 5px;">0</span>
+
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+				  	</div>
+				</div>
+			</div>
+
+				<%- include('footer.html') %>
+		</div>
+	</div>
+		<!-- Libs JS -->
+		<!-- Tabler Core -->
+		<script src="/js/tabler.min.js" defer></script>
+		<script src="/js/demo.min.js" defer></script>
+	</body>
+	</html>

+ 2 - 2
views/syslogs.ejs → views/syslogs.html

@@ -21,7 +21,7 @@
     <div class="page">
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
       <div class="page-wrapper">
 
@@ -68,7 +68,7 @@
 
           </div>
         </div>
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
       </div>
     </div>
     <!-- Libs JS -->

+ 2 - 2
views/users.ejs → views/users.html

@@ -21,7 +21,7 @@
     <div class="page">
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
       <div class="page-wrapper">
 
@@ -59,7 +59,7 @@
             </div>
           </div>
         </div>
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
       </div>
     </div>
     <!-- Libs JS -->

+ 120 - 0
views/variables.html

@@ -0,0 +1,120 @@
+	<!doctype html>
+	<html lang="en">
+	<head>
+		<meta charset="utf-8"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+		<title>DweebUI - Settings</title>
+		<!-- CSS files -->
+		<link href="/css/tabler.min.css" rel="stylesheet"/>
+		<link href="/css/demo.min.css" rel="stylesheet"/>
+		<style>
+			@import url('/fonts/inter.css');
+			:root {
+			  --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+			}
+			body {
+			  font-feature-settings: "cv03", "cv04", "cv11";
+			}
+		  </style>
+	</head>
+	<body >
+		<div class="page">
+		<!-- Navbar -->
+		<%- include('navbar.html') %>
+		<div class="page-wrapper">
+			<!-- Page header -->
+			<div class="page-header d-print-none">
+			<div class="container-xl">
+				<div class="row g-2 align-items-center">
+				<div class="col">
+					<h2 class="page-title">
+					Settings
+					</h2>
+				</div>
+				</div>
+			</div>
+			</div>
+			<!-- Page body -->
+			<div class="page-body">
+				<div class="container-xl">
+				  <div class="card">
+					<div class="row g-0">
+						<%- include('sidebar.html') %>
+					  	<div class="col d-flex flex-column">
+					  
+							<div class="card-body">
+								<h2 class="mb-2">Variables</h2>
+								<p class="text-muted mb-4">Configure default variables below.</p>
+								
+								<!-- <div class="row align-items-center">
+									<div class="col">								
+									</div>
+								</div> -->
+								
+								<div class="row mt-4">
+									<div class="col-md">
+									<div class="form-label">Match 1</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Match 2</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Default</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+								</div>
+
+
+								<div class="row mt-4">
+									<div class="col-md">
+									<div class="form-label">Match 1</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Match 2</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Default</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+								</div>
+
+
+								<div class="row mt-4">
+									<div class="col-md">
+									<div class="form-label">Match 1</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Match 2</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+									<div class="col-md">
+									<div class="form-label">Default</div>
+									<input type="text" class="form-control" value="" readonly="">
+									</div>
+								</div>
+								
+
+							</div>
+	  
+					  	</div>
+					  
+					</div>
+				  </div>
+				</div>
+			  </div>
+
+			<%- include('footer.html') %>
+		</div>
+		</div>
+		<!-- Libs JS -->
+		<!-- Tabler Core -->
+		<script src="/js/tabler.min.js" defer></script>
+		<script src="/js/demo.min.js" defer></script>
+	</body>
+	</html>

+ 0 - 188
views/volumes.ejs

@@ -1,188 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
-    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
-    <title>DweebUI - Volumes</title>
-    <link href="/css/tabler.min.css" rel="stylesheet"/>
-    <link href="/css/demo.min.css" rel="stylesheet"/>
-    <style>
-      @import url('/fonts/inter.css');
-      :root {
-        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
-      }
-      body {
-        font-feature-settings: "cv03", "cv04", "cv11";
-      }
-    </style>
-  </head>
-  <body >
-    <div class="page">
-      <!-- Navbar -->
-      <%- include('navbar.ejs') %>
-      <div class="page-wrapper">
-        <!-- Page header -->
-        
-        <!-- Page body -->
-        <div class="page-body">
-          <div class="container-xl">
-            <div class="row row-deck row-cards">
-
-              <div class="col-12 mt-12">
-                <div class="card">
-                  <div class="card-header">
-                    <h3 class="card-title">Docker Volumes</h3>
-                      <div class="card-options btn-list">                  
-                          <a href="#" class="btn">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
-                          Refresh
-                          </a>
-                          <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
-                          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
-                          New Volume
-                          </a>
-                      </div>
-                  </div>
-                  
-                  <div class="modal modal-blur fade" id="add-site" tabindex="-1" style="display: none;" aria-hidden="true">
-                    <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-                      <div class="modal-content">
-                        <div class="modal-body">
-
-                          <form action="/addsite" id="addsite" method="POST">
-                            <div class="mb-3">
-                              <div class="form-label">Type</div>
-                              <select class="form-select" name="type">
-                                <option value="reverse_proxy">Reverse Proxy</option>
-                                <option value="proxy">Proxy</option>
-                                <option value="file_server">File Server</option>
-                              </select>
-                            </div>
-                            <div class="mb-3">
-                              <label class="form-label">Domain / Subdomain</label>
-                              <input type="text" class="form-control" name="domain" placeholder="media.mydomainname.com">
-                            </div>
-                            <div class="mb-4">
-                              <div class="row g-2">
-                                <div class="col-8">
-                                  <label class="form-label">Hostname / Host IP</label>
-                                  <input type="text" class="form-control" name="host" placeholder="localhost">
-                                </div>
-                                <div class="col-4">
-                                  <label class="form-label">Port</label>
-                                  <input type="text" class="form-control" name="port" placeholder="8000">
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="divide-y">
-                                <div>
-                                  <label class="row">
-                                    <span class="col" title="HTTP Strict Transport Security (HSTS) is a simple and widely supported standard to protect visitors by ensuring that their browsers always connect to a website over HTTPS.">HSTS</span>
-                                    <span class="col-auto">
-                                      <label class="form-check form-check-single form-switch">
-                                        <input class="form-check-input" type="checkbox" name="hsts" checked="" disabled="">
-                                      </label>
-                                    </span>
-                                  </label>
-                                </div>
-                              </div>
-                            </div>
-                            <div class="mb-3">
-                              <div class="form-label">Container</div>
-                              <select class="form-select" name="container" disabled="">
-                                <option value="0" selected></option>
-                                <option value="1">Jellyfin</option>
-                              </select>
-                            </div>
-                          </form>
-                        </div>
-                        <div class="modal-footer">
-                          <button type="button" class="btn btn-link link-secondary me-auto" data-bs-dismiss="modal">Cancel</button>
-                          <input type="submit" form="addsite" class="btn btn-success" value="Add"/>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-
-
-                  <div id="table-default" class="table-responsive">
-                    <table class="table">
-                      
-                      <%- volume_list %>
-
-                    </table>
-                  </div>
-
-                  <div class="card-footer d-flex align-items-center">
-
-                    <span class="dropdown">
-                      <button class="btn dropdown-toggle align-text-top" data-bs-toggle="dropdown">Actions</button>
-                      <div class="dropdown-menu dropdown-menu-end">
-                        <button class="dropdown-item" type="submit" formaction="/enablesite">
-                          Enable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/disablesite">
-                          Disable
-                        </button>
-                        <button class="dropdown-item" type="submit" formaction="/removesite">
-                          Delete
-                        </button>
-                      </div>
-                    </span>
-
-                  </form>
-                                        
-                    <p class="m-0 text-muted ms-auto"><%- volume_count %> Volumes</p>
-
-                  </div>
-                </div>
-              </div>
-
-            </div>
-          </div>
-        </div>
-        
-        <%- include('footer.ejs') %>
-        
-      </div>
-    </div>
-    
-    <!-- Libs JS -->
-    <script src="/libs/list.js/dist/list.min.js" defer></script>
-    <!-- Tabler Core -->
-    <script src="/js/tabler.min.js" defer></script>
-    <script src="/js/demo.min.js" defer></script>
-
-    <script>
-      document.addEventListener("DOMContentLoaded", function() {
-      const list = new List('table-default', {
-      	sortClass: 'table-sort',
-      	listClass: 'table-tbody',
-      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
-      		{ attr: 'data-date', name: 'sort-date' },
-      		{ attr: 'data-progress', name: 'sort-progress' },
-      		'sort-quantity'
-      	]
-      });
-      })
-    </script>
-
-    <script>
-      function selectAll() {
-        let checkboxes = document.getElementsByName('select');
-        if (checkboxes[0].checked == true) {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = true;
-          }
-        } else {
-          for (var i = 0; i < checkboxes.length; i++) {
-            checkboxes[i].checked = false;
-          }
-        }
-      }
-    </script>
-    
-  </body>
-</html>

+ 130 - 0
views/volumes.html

@@ -0,0 +1,130 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
+    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
+    <title>DweebUI - Volumes</title>
+    <link href="/css/tabler.min.css" rel="stylesheet"/>
+    <link href="/css/demo.min.css" rel="stylesheet"/>
+    <style>
+      @import url('/fonts/inter.css');
+      :root {
+        --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
+      }
+      body {
+        font-feature-settings: "cv03", "cv04", "cv11";
+      }
+    </style>
+  </head>
+  <body >
+    <div class="page">
+      <!-- Navbar -->
+      <%- include('navbar.html') %>
+      <div class="page-wrapper">
+        <!-- Page header -->
+        
+        <!-- Page body -->
+        <div class="page-body">
+          <div class="container-xl">
+            <div class="row row-deck row-cards">
+
+              <div class="col-12 mt-12">
+                <div class="card">
+                  <form method="post">
+                    <div class="card-header">
+                      <h3 class="card-title">Docker Volumes</h3>
+                        <div class="card-options btn-list">                  
+                            <a href="#" class="btn">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path> </svg>
+                              Refresh
+                            </a>
+                            <a href="#" class="btn" data-bs-toggle="modal" data-bs-target="#not_add-site">
+                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 5l0 14"></path> <path d="M5 12l14 0"></path> </svg>
+                              New Volume
+                            </a>
+                        </div>
+                    </div>
+                  
+
+                    <div id="table-default" class="table-responsive">
+                      <table class="table">
+                        
+                        <%- volume_list %>
+
+                      </table>
+                    </div>
+
+                    <div class="card-footer d-flex align-items-center">
+
+                        <button class="btn" type="submit" formaction="/removeVolume">Remove</button>
+
+                      <!-- <span class="dropdown">
+                        <button class="btn dropdown-toggle align-text-top" data-bs-toggle="dropdown">Actions</button>
+                        <div class="dropdown-menu dropdown-menu-end">
+                          <button class="dropdown-item" type="submit" formaction="/submitVolumes">
+                            Enable
+                          </button>
+                          <button class="dropdown-item" type="submit" formaction="/submitVolumes">
+                            Disable
+                          </button>
+                          <button class="dropdown-item" type="submit" formaction="/submitVolumes">
+                            Delete
+                          </button>
+                        </div>
+                      </span> -->
+
+                                                        
+                      <p class="m-0 text-muted ms-auto"><%- volume_count %> Volumes</p>
+
+                    </div>
+                  </form>
+                </div>
+              </div>
+
+            </div>
+          </div>
+        </div>
+        
+        <%- include('footer.html') %>
+        
+      </div>
+    </div>
+    
+    <!-- Libs JS -->
+    <script src="/libs/list.js/dist/list.min.js" defer></script>
+    <!-- Tabler Core -->
+    <script src="/js/tabler.min.js" defer></script>
+    <script src="/js/demo.min.js" defer></script>
+
+    <script>
+      document.addEventListener("DOMContentLoaded", function() {
+      const list = new List('table-default', {
+      	sortClass: 'table-sort',
+      	listClass: 'table-tbody',
+      	valueNames: [ 'sort-name', 'sort-type', 'sort-city', 'sort-score',
+      		{ attr: 'data-date', name: 'sort-date' },
+      		{ attr: 'data-progress', name: 'sort-progress' },
+      		'sort-quantity'
+      	]
+      });
+      })
+    </script>
+
+    <script>
+      function selectAll() {
+        let checkboxes = document.getElementsByName('select');
+        if (checkboxes[0].checked == true) {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = true;
+          }
+        } else {
+          for (var i = 0; i < checkboxes.length; i++) {
+            checkboxes[i].checked = false;
+          }
+        }
+      }
+    </script>
+    
+  </body>
+</html>

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff