diff --git a/homelab/Caddyfile b/homelab/Caddyfile index 5306960..3b8bbd1 100644 --- a/homelab/Caddyfile +++ b/homelab/Caddyfile @@ -61,6 +61,12 @@ ancestry.woozle.org { reverse_proxy geneweb:2317 } +photos.woozle.org { + import restricted-access + reverse_proxy pigallery2:80 +} + + ## ## handle sends original path ## handle_path truncates path @@ -96,6 +102,9 @@ deergrove.woozle.org { handle /prowlarr/* { reverse_proxy prowlarr:9696 } + handle /unmanic/* { + reverse_proxy unmanic:8888 + } handle_path /sucker/* { reverse_proxy host.lan:5801 diff --git a/homelab/authelia.yaml b/homelab/authelia.yaml index 059ffc4..99856d2 100644 --- a/homelab/authelia.yaml +++ b/homelab/authelia.yaml @@ -39,6 +39,7 @@ access_control: - '^/nzbget/' - '^/transmission/' - '^/sucker/' + - '^/unmanic/' policy: one_factor - domain: deergrove.woozle.org @@ -82,3 +83,15 @@ access_control: - '^/storage/shared/' policy: one_factor + - domain: photos.woozle.org + resources: + - '^(/pgapi)?/gallery/(?P\w+)' + - '^(/pgapi)?/gallery/(?P\w+)' + - '^(/pgapi)?/gallery/content/' + policy: one_factor + - domain: photos.woozle.org + resources: + - '^(/pgapi)?/gallery/.' + policy: deny + - domain: photos.woozle.org + policy: one_factor diff --git a/homelab/deploy.sh b/homelab/deploy.sh index b1d7ab9..60239bc 100755 --- a/homelab/deploy.sh +++ b/homelab/deploy.sh @@ -1,11 +1,12 @@ #! /bin/sh -caddy_hash () { - echo -n "$1 " - echo "$2" | docker run --rm -i caddy caddy hash-password -} - stack=$(basename $(pwd)) -docker --context deergrove stack deploy -c docker-compose.yaml --prune $stack +extra="--resolve-image changed" +if [ "$1" = "--slow" ]; then + extra= + shift +fi + +docker --context deergrove stack deploy -c docker-compose.yaml --prune $extra "$@" $stack #docker --context deergrove compose up --detach diff --git a/homelab/docker-compose.yaml b/homelab/docker-compose.yaml index a2f2899..fb0311a 100644 --- a/homelab/docker-compose.yaml +++ b/homelab/docker-compose.yaml @@ -36,6 +36,8 @@ services: target: /www/deergrove.png - source: index.css target: /www/index.css + - source: portal.json + target: /www/portal.json - source: browser.html target: /browser.html extra_hosts: @@ -66,6 +68,7 @@ services: source: /srv/sys/authelia target: /srv/sys/authelia + # Authelia wants this redis: image: redis:alpine command: @@ -94,6 +97,23 @@ services: target: /srv read_only: true + pigallery2: + image: bpatrik/pigallery2:latest + volumes: + - type: bind + source: /srv/sys/pigallery2/config + target: /app/data/config + - type: bind + source: /srv/sys/pigallery2/db + target: /app/data/db + - type: bind + source: /srv/sys/pigallery2/cache + target: /app/data/cache + - type: bind + source: /srv/media/photos + target: /srv/media/photos + read_only: true + transmission: image: lscr.io/linuxserver/transmission volumes: @@ -109,6 +129,19 @@ services: - 51413:51413 - 51413:51413/udp + unmanic: + image: josh5/unmanic + volumes: + - type: bind + source: /srv/media + target: /srv/media + - type: bind + source: /srv/sys/unmanic/config + target: /config + - type: bind + source: /srv/sys/unmanic/cache + target: /tmp/unmanic + sonarr: image: lscr.io/linuxserver/sonarr extra_hosts: @@ -221,6 +254,8 @@ services: netdata: image: netdata/netdata hostname: "{{.Node.Hostname}}" + deploy: + replicas: 0 environment: NETDATA_DISABLE_CLOUD: "1" cap_add: @@ -245,14 +280,8 @@ services: image: ravermeister/geneweb volumes: - type: bind - source: /srv/sys/geneweb/etc - target: /usr/local/share/geneweb/etc - - type: bind - source: /srv/sys/geneweb/share/data + source: /srv/sys/geneweb/ target: /usr/local/share/geneweb/share/data - - type: bind - source: /srv/sys/geneweb/log - target: /usr/local/share/geneweb/log samba: image: dperson/samba @@ -334,16 +363,19 @@ configs: name: Corefile-v4 Caddyfile: file: Caddyfile - name: Caddyfile-v120 + name: Caddyfile-v133 index.html: file: www/index.html - name: index.html-v42 + name: index.html-v43 index.mjs: file: www/index.mjs - name: index.mjs-v8 + name: index.mjs-v12 index.css: file: www/index.css name: index.css-v2 + portal.json: + file: portal.json + name: portal.json-v3 browser.html: file: www/browser.html name: browser.html-v3 @@ -352,7 +384,7 @@ configs: name: deergrove.png-v1 authelia.yaml: file: authelia.yaml - name: authelia.yaml-v28 + name: authelia.yaml-v36 netdata.conf: file: netdata.conf name: netdata.conf-v1 diff --git a/homelab/portal.json b/homelab/portal.json new file mode 100644 index 0000000..46c1775 --- /dev/null +++ b/homelab/portal.json @@ -0,0 +1,70 @@ +[ + { + "href": "https://deergrove.woozle.org/radarr/", + "icon": "/radarr/Content/Images/logo.svg", + "title": "Movies" + }, + { + "href": "https://deergrove.woozle.org/sonarr/", + "icon": "/sonarr/Content/Images/logo.svg", + "title": "Episodes" + }, + { + "href": "https://deergrove.woozle.org/lidarr/", + "icon": "/lidarr/Content/Images/logo.svg", + "title": "Music" + }, + { + "href": "https://deergrove.woozle.org/readarr/", + "icon": "/readarr/Content/Images/logo.svg", + "title": "Books" + }, + { + "href": "https://deergrove.woozle.org/sucker/", + "icon": "/sucker/cd-dvd.svg", + "title": "Media Sucker" + }, + { + "href": "https://deergrove.woozle.org/prowlarr/", + "icon": "/prowlarr/Content/Images/logo.png", + "title": "Searcher" + }, + { + "href": "https://deergrove.woozle.org/nzbget/", + "icon": "/nzbget/img/favicon-256x256.png", + "title": "Usenet" + }, + { + "title": "BitTorrent", + "href": "https://deergrove.woozle.org/transmission/web/", + "icon": "/transmission/web/images/webclip-icon.png" + }, + { + "href": "https://deergrove.woozle.org/octoprint/", + "icon": "/octoprint/static/img/logo.png", + "title": "3D Printer" + }, + { + "href": "https://git.woozle.org/", + "icon": "https://git.woozle.org/assets/img/logo.svg", + "title": "Git", + "target": "_blank" + }, + { + "href": "https://drive.woozle.org/", + "icon": "https://drive.woozle.org/storage/public/icons/cloud-folder.png", + "title": "Storage", + "target": "_blank" + }, + { + "href": "https://ancestry.woozle.org/", + "icon": "https://ancestry.woozle.org/images/arbre_start.png", + "title": "Genealogy", + "target": "_blank" + }, + { + "href": "https://auth.woozle.org/logout/", + "title": "Logout", + "target": "_top" + } +] diff --git a/homelab/www/index.html b/homelab/www/index.html index 9e05142..cce5201 100644 --- a/homelab/www/index.html +++ b/homelab/www/index.html @@ -10,25 +10,6 @@
diff --git a/homelab/www/index.mjs b/homelab/www/index.mjs index 086c2fc..65264b2 100644 --- a/homelab/www/index.mjs +++ b/homelab/www/index.mjs @@ -1,8 +1,5 @@ let frames = {} function activate(event, element) { - if (element.target) { - return - } event.preventDefault() let parent = element.parentElement @@ -49,7 +46,7 @@ function frameLoaded(frame) { } let defaultIcon = null -function init() { +async function init() { let doc = document.querySelector("iframe").contentDocument defaultIcon = document.querySelector("link[rel~='icon']").href @@ -68,25 +65,39 @@ function init() { let icons = doc.body.appendChild(doc.createElement("section")) icons.classList.add("icons") - for (let link of document.querySelectorAll("nav a")) { - let dlink = icons.appendChild(link.cloneNode(true)) - dlink.textContent = "" + let nav = document.querySelector("nav") + let resp = await fetch("portal.json") + let obj = await resp.json() + for (let app of obj) { + let hlink = null + if (app.target != "_blank") { + hlink = nav.appendChild(document.createElement("a")) + hlink.href = app.href + hlink.textContent = app.title + if (app.target) { + hlink.target = app.target + } else { + hlink.addEventListener("click", event => activate(event, hlink)) + } + } - if (link.dataset.icon == "") { - dlink.remove() - } else if (link.dataset.icon) { + let dlink = icons.appendChild(doc.createElement("a")) + dlink.href = app.href + if (app.target) { + dlink.target = app.target + } else { + dlink.addEventListener("click", event => activate(event, hlink)) + } + if (app.icon) { let icon = dlink.appendChild(doc.createElement("img")) - icon.src = link.dataset.icon - icon.alt = link.title + icon.src = app.icon + icon.alt = app.title + icon.title = app.title icon.style.objectFit = "cover" } else { let text = dlink.appendChild(doc.createElement("div")) - text.textContent = link.textContent + text.textContent = app.title } - - // Make both of them update the selected tab - link.addEventListener("click", event => activate(event, link)) - dlink.addEventListener("click", event => activate(event, link)) } }