Compare commits

..

No commits in common. "e32e4f586f6cddcbe8759924c445e65e35d69a6a" and "b54e137b2ead0e9ab9ba3b05df2bf6c78fe013d2" have entirely different histories.

16 changed files with 227 additions and 658 deletions

View File

@ -1,13 +1,11 @@
{
email neale@woozle.org
#debug
}
(restricted-access) {
forward_auth simpleauth:8080 {
uri /
copy_headers X-Simpleauth-Username
header_down X-Simpleauth-Domain "woozle.org"
copy_headers X-Simpleauth-Token
}
}
@ -17,11 +15,19 @@ git.woozle.org {
drive.woozle.org {
import restricted-access
reverse_proxy webfs:5000
}
media.woozle.org {
reverse_proxy jellyfin:8096
# XXX: browsing says method not allowed
@nondav {
method HEAD GET
}
# route overrides built-in ordering
route {
file_server @nondav {
root /srv/
browse /browser.html
}
reverse_proxy webdav:8000
}
}
# XXX: have this use caddy auth
@ -29,69 +35,89 @@ 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
##
deergrove.woozle.org {
import restricted-access
(deergrove) {
handle_path /ddns/* {
import restricted-access
reverse_proxy ddns:8000
}
handle /transmission/* {
reverse_proxy transmission:9091
import restricted-access
reverse_proxy host.docker.internal:9091
}
handle /nzbget/* {
import restricted-access
reverse_proxy nzbget:6789
}
handle /sonarr/* {
import restricted-access
reverse_proxy sonarr:8989
}
handle /radarr/* {
import restricted-access
reverse_proxy radarr:7878
}
handle /readarr/* {
import restricted-access
reverse_proxy readarr:8787
}
handle /lidarr/* {
import restricted-access
reverse_proxy lidarr:8686
}
handle /prowlarr/* {
import restricted-access
reverse_proxy prowlarr:9696
}
handle /unmanic/* {
reverse_proxy unmanic:8888
}
handle_path /sucker/* {
reverse_proxy host.lan:5801
import restricted-access
reverse_proxy host.docker.internal:5880
}
handle_path /netdata/* {
reverse_proxy netdata:19999
}
# Octoprint serves up broken webcam URLs
uri replace /webcam/ /octoprint/webcam/
handle_path /octoprint/* {
import restricted-access
reverse_proxy {
to 192.168.86.20:80
header_up X-Script-Name "/octoprint"
}
}
handle /webcam/* {
# Octoprint doesn't properly prefix webcam URLs
import restricted-access
reverse_proxy {
to 192.168.86.20:80
}
}
handle_path /public/* {
file_server {
root /srv/storage/public
}
}
handle {
reverse_proxy portal:8080
import restricted-access
file_server {
root /www
}
}
}
deergrove.woozle.org {
import deergrove
}
sweetums.lan {
tls internal
import deergrove
}

View File

@ -1,7 +1,7 @@
. {
bind 192.168.86.2
bind lan
hosts {
192.168.86.2 sweetums.woozle.org deergrove.woozle.org drive.woozle.org git.woozle.org ancestry.woozle.org media.woozle.org photos.woozle.org auth.woozle.org
192.168.86.2 sweetums.woozle.org deergrove.woozle.org drive.woozle.org git.woozle.org ancestry.woozle.org
fallthrough
}
forward . 8.8.8.8

View File

@ -4,16 +4,14 @@ This is the stuff I run on my little Raspberry Pi.
I guess I fiddle around with it pretty frequently.
## Portal
## Routing
In the [www](www) directory is a static HTML/JavaScript portal thing I wrote.
It doesn't need any server configuration,
you just edit the HTML to manage what apps it serves.
My ISP uses Carrier-Grade NAT,
which I would have called IP Masquerading.
Other things like this exist,
but they all require ridiculous things like relational databases or weird config files.
This means I can't bind ports on a routeable IP.
So instead what I do is run this SSH connection off to my cloud server,
listens for incoming connections on port 5800,
and then have my cloud server proxy stuff to port 5800.
I should probably package this up or something,
but then I'd have to put effort into publicizing it,
and run a ticketing system or something,
so meh.
It's a gross kludge but it works well :)

View File

@ -1,9 +0,0 @@
* Single Sign-On
* [x] Replace simpleauth with somebody else's project
* [x] Set up Forgejo OIDC to Authelia (there's a guide on Authelia's site)
* [x] Persist "remember me" across reboots
* LDAP restrictions
* [x] People can only r/w their own storage
* [x] Public storage
* [x] Per-Group storage
* [x] Media-Sucker secure setup (bind to 0.0.0.0 opens to internet)

View File

@ -1,12 +1,10 @@
#! /bin/sh
caddy_hash () {
echo -n "$1 "
echo "$2" | docker run --rm -i caddy caddy hash-password
}
stack=$(basename $(pwd))
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
docker --context deergrove stack deploy -c docker-compose.yaml --prune $stack

View File

@ -28,44 +28,28 @@ services:
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
- source: index.html
target: /www/index.html
- source: index.mjs
target: /www/index.mjs
- source: index.css
target: /www/index.css
- source: browser.html
target: /browser.html
extra_hosts:
- host.docker.internal:host-gateway
- host.lan:192.168.86.2
simpleauth:
image: git.woozle.org/neale/simpleauth
command:
- -secret
- /run/secrets/simpleauth.key
secrets:
- passwd
- simpleauth.key
portal:
image: git.woozle.org/neale/portal
configs:
- source: portal.json
target: /web/portal.json
- source: deergrove.png
target: /web/portal.png
jellyfin:
image: jellyfin/jellyfin
deploy:
replicas: 0
environment:
TZ: US/Mountain
volumes:
- type: bind
source: /srv/sys/jellyfin/config
target: /config
- type: bind
source: /srv/sys/jellyfin/cache
target: /cache
- type: bind
source: /srv/media/
target: /srv/media/
read_only: true
plex:
image: lscr.io/linuxserver/plex:latest
image: ghcr.io/linuxserver/plex:1.29.2
networks:
- hostnet
environment:
@ -76,29 +60,14 @@ services:
source: /srv/sys/plex
target: /config
- type: bind
source: /srv/media/
target: /srv/media/
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
source: /srv
target: /srv
read_only: true
bind:
propagation: rslave
transmission:
image: lscr.io/linuxserver/transmission:latest
image: lscr.io/linuxserver/transmission
volumes:
- type: bind
source: /srv/sys/transmission
@ -106,11 +75,8 @@ services:
- type: bind
source: /srv/incoming
target: /srv/incoming
environment:
PEERPORT: "51413"
ports:
- 51413:51413
- 51413:51413/udp
networks:
- hostnet
sonarr:
image: lscr.io/linuxserver/sonarr
@ -172,7 +138,7 @@ services:
source: /srv/incoming
target: /srv/incoming
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
image: lscr.io/linuxserver/prowlarr:develop
extra_hosts:
- host.docker.internal:host-gateway
volumes:
@ -191,16 +157,10 @@ services:
target: /srv/incoming
gitea:
image: codeberg.org/forgejo/forgejo:1.18-rootless
secrets:
- source: gitea.ini
target: /etc/gitea/app.ini
uid: "1000"
gid: "1000"
mode: 0400
configs:
- source: gitea-robots.txt
target: /var/lib/gitea/custom/robots.txt
image: gitea/gitea:1
environment:
USER_UID: 1000
USER_GID: 1000
volumes:
- type: bind
source: /srv/sys/gitea
@ -224,37 +184,18 @@ services:
source: /srv/sys/atlas/status
target: /var/atlas-probe/status
netdata:
image: netdata/netdata
hostname: "{{.Node.Hostname}}"
deploy:
replicas: 0
environment:
NETDATA_DISABLE_CLOUD: "1"
cap_add:
- SYS_PTRACE
volumes:
- type: bind
source: /
target: /host
read_only: true
- type: bind
source: /srv/sys/netdata/lib
target: /var/lib/netdata
- type: bind
source: /srv/sys/netdata/cache
target: /var/cache/netdata
configs:
- source: netdata.conf
target: /etc/netdata/netdata.conf
geneweb:
image: ravermeister/geneweb
volumes:
- type: bind
source: /srv/sys/geneweb/
source: /srv/sys/geneweb/etc
target: /usr/local/share/geneweb/etc
- type: bind
source: /srv/sys/geneweb/share/data
target: /usr/local/share/geneweb/share/data
- type: bind
source: /srv/sys/geneweb/log
target: /usr/local/share/geneweb/log
samba:
image: dperson/samba
@ -280,21 +221,15 @@ services:
- published: 445
target: 445
webfs:
image: sigoden/dufs
webdav:
image: micromata/dave
volumes:
- type: bind
source: /srv/storage
target: /srv/storage
- type: bind
source: /srv/incoming
target: /srv/incoming
- type: bind
source: /srv/media
target: /srv/media
command:
- -A
- /srv
source: /srv
target: /data
configs:
- source: dave.yaml
target: /config/config.yaml
user: "911:911"
ddns:
@ -307,8 +242,6 @@ services:
target: /updater/data
tunnel:
deploy:
replicas: 0
image: lscr.io/linuxserver/openssh-server
user: abc
entrypoint:
@ -339,22 +272,22 @@ configs:
name: dave.yaml-v3
Corefile:
file: Corefile
name: Corefile-v7
name: Corefile-v2
Caddyfile:
file: Caddyfile
name: Caddyfile-v145
portal.json:
file: portal.json
name: portal.json-v6
deergrove.png:
file: www/deergrove.png
name: deergrove.png-v1
netdata.conf:
file: netdata.conf
name: netdata.conf-v1
gitea-robots.txt:
file: gitea-robots.txt
name: gitea-robots.txt-v1
name: Caddyfile-v80
index.html:
file: www/index.html
name: index.html-v32
index.mjs:
file: www/index.mjs
name: index.mjs-v1
index.css:
file: www/index.css
name: index.css-v1
browser.html:
file: www/browser.html
name: browser.html-v3
secrets:
passwd:
@ -369,24 +302,6 @@ secrets:
known_hosts:
file: secrets/known_hosts
name: known_hosts-v1
gitea.ini:
file: secrets/gitea.ini
name: gitea.ini-v4
jwt.secret:
file: secrets/jwt.secret
name: jwt.secret-v1
storage.secret:
file: secrets/storage.secret
name: storage.secret-v1
session.secret:
file: secrets/session.secret
name: session.secret-v1
users.yaml:
file: secrets/users.yaml
name: users.yaml-v9
authelia.oidc.yaml:
file: secrets/authelia.oidc.yaml
name: authelia.oidc.yaml-v2
networks:
hostnet:

View File

@ -1,76 +0,0 @@
[
{
"title": "Storage",
"href": "https://drive.woozle.org/",
"icon": "https://drive.woozle.org/storage/public/icons/cloud-folder.png",
"target": "_blank"
},
{
"title": "Photos",
"href": "https://photos.woozle.org/",
"icon": "https://photos.woozle.org/assets/icon_inv.png",
"target": "_blank"
},
{
"title": "Git",
"href": "https://git.woozle.org/",
"icon": "https://git.woozle.org/assets/img/logo.svg",
"target": "_blank"
},
{
"title": "Genealogy",
"href": "https://ancestry.woozle.org/",
"icon": "https://ancestry.woozle.org/images/arbre_start.png",
"target": "_blank"
},
{
"title": "Movies",
"href": "https://deergrove.woozle.org/radarr/",
"icon": "/radarr/Content/Images/logo.svg"
},
{
"title": "Episodes",
"href": "https://deergrove.woozle.org/sonarr/",
"icon": "/sonarr/Content/Images/logo.svg"
},
{
"title": "Music",
"href": "https://deergrove.woozle.org/lidarr/",
"icon": "/lidarr/Content/Images/logo.svg"
},
{
"title": "Books",
"href": "https://deergrove.woozle.org/readarr/",
"icon": "/readarr/Content/Images/logo.svg"
},
{
"title": "Media Sucker",
"href": "https://deergrove.woozle.org/sucker/",
"icon": "/sucker/cd-dvd.svg"
},
{
"title": "Searcher",
"href": "https://deergrove.woozle.org/prowlarr/",
"icon": "/prowlarr/Content/Images/logo.png"
},
{
"title": "Usenet",
"href": "https://deergrove.woozle.org/nzbget/",
"icon": "/nzbget/img/favicon-256x256.png"
},
{
"title": "BitTorrent",
"href": "https://deergrove.woozle.org/transmission/web/",
"icon": "/transmission/web/images/webclip-icon.png"
},
{
"title": "3D Printer",
"href": "https://deergrove.woozle.org/octoprint/",
"icon": "/octoprint/static/img/logo.png"
},
{
"title": "Host Stats",
"href": "/stat.html",
"app": "stat"
}
}

View File

@ -1,363 +1,47 @@
version: "3.8"
services:
coredns:
image: coredns/coredns
networks:
- hostnet
configs:
- source: Corefile
target: /Corefile
caddy:
image: caddy:2-alpine
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /srv
target: /srv
read_only: true
- type: bind
source: /srv/sys/caddy
target: /data/caddy
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
- source: index.html
target: /www/index.html
- source: index.mjs
target: /www/index.mjs
- source: deergrove.png
target: /www/deergrove.png
- source: index.css
target: /www/index.css
- source: browser.html
target: /browser.html
extra_hosts:
- host.docker.internal:host-gateway
authelia:
image: authelia/authelia
environment:
AUTHELIA_JWT_SECRET_FILE: /run/secrets/jwt.secret
AUTHELIA_SESSION_SECRET_FILE: /run/secrets/session.secret
AUTHELIA_STORAGE_ENCRYPTION_FILE: /run/secrets/storage.secret
secrets:
- jwt.secret
- session.secret
- storage.secret
- users.yaml
configs:
- source: authelia.yaml
target: /config/configuration.yml
volumes:
- type: bind
source: /srv/sys/authelia
target: /srv/sys/authelia
jellyfin:
image: jellyfin/jellyfin
image: ghcr.io/linuxserver/jellyfin:10.7.7
ports:
- target: 8096
published: 8096
- target: 7359
published: 7359
protocol: udp
- target: 1900
published: 1900
protocol: udp
environment:
TZ: US/Mountain
volumes:
- type: bind
source: /srv/sys/jellyfin/config
source: /mnt/ext/srv/jellyfin
target: /config
- type: bind
source: /srv/sys/jellyfin/cache
target: /cache
- type: bind
source: /srv/media
target: /srv/media
source: /media
target: /media
read_only: true
plex:
image: ghcr.io/linuxserver/plex:1.29.2
networks:
- hostnet
environment:
TZ: US/Mountain
VERSION: public
volumes:
- type: bind
source: /srv/sys/plex
target: /config
- type: bind
source: /srv
target: /srv
read_only: true
transmission:
image: lscr.io/linuxserver/transmission
volumes:
- type: bind
source: /srv/sys/transmission
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
networks:
- hostnet
sonarr:
image: lscr.io/linuxserver/sonarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/sonarr
target: /config
- type: bind
source: /srv/media/tv
target: /srv/media/tv
- type: bind
source: /srv/incoming
target: /srv/incoming
radarr:
image: lscr.io/linuxserver/radarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/radarr
target: /config
- type: bind
source: /srv/media/movies
target: /srv/media/movies
- type: bind
source: /srv/incoming
target: /srv/incoming
lidarr:
image: lscr.io/linuxserver/lidarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/lidarr
target: /config
- type: bind
source: /srv/media/music
target: /srv/media/music
- type: bind
source: /srv/incoming
target: /srv/incoming
readarr:
image: lscr.io/linuxserver/readarr:develop
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/readarr
target: /config
- type: bind
source: /srv/media/books
target: /srv/media/books
- type: bind
source: /srv/media/audiobooks
target: /srv/media/audiobooks
- type: bind
source: /srv/incoming
target: /srv/incoming
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/prowlarr
target: /config
nzbget:
image: lscr.io/linuxserver/nzbget
volumes:
- type: bind
source: /srv/sys/nzbget
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
forgejo:
image: codeberg.org/forgejo/forgejo:1.18-rootless
secrets:
- source: forgejo.ini
target: /etc/gitea/app.ini
uid: "1000"
gid: "1000"
mode: 0400
volumes:
- type: bind
source: /srv/sys/forgejo
target: /data
- type: bind
source: /etc/timezone
target: /etc/timezone
read_only: true
- type: bind
source: /etc/localtime
target: /etc/localtime
read_only: true
atlas:
image: ctassisf/ripe-atlas-alpine:arm64v8
volumes:
- type: bind
source: /srv/sys/atlas/etc
target: /var/atlas-probe/etc
- type: bind
source: /srv/sys/atlas/status
target: /var/atlas-probe/status
geneweb:
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
target: /usr/local/share/geneweb/share/data
- type: bind
source: /srv/sys/geneweb/log
target: /usr/local/share/geneweb/log
samba:
image: dperson/samba
volumes:
- type: bind
source: /srv
target: /srv
bind:
propagation: rslave
environment:
NMBD: enable
RECYCLE: disable
USERID: 911
GROUPID: 911
# name;path;browse;readonly;guest
SHARE1: drive;/srv;yes;no;no
SHARE2: retropie;/srv/media/games/retropie;yes;yes;yes
env_file:
- secrets/samba-users.env
ports:
- published: 139
target: 139
- published: 445
target: 445
webdav:
image: micromata/dave
volumes:
- type: bind
source: /srv
target: /data
configs:
- source: dave.yaml
target: /config/config.yaml
user: "911:911"
ddns:
image: qmcgaw/ddns-updater
dns:
- 1.1.1.1
volumes:
source: /dev/video10
target: /dev/video10
- type: bind
source: /srv/sys/ddns-updater
target: /updater/data
tunnel:
deploy:
replicas: 0
image: lscr.io/linuxserver/openssh-server
user: abc
entrypoint:
- /usr/bin/ssh
- -N
- -R 172.17.0.1:5880:caddy:80 # 172.17.0.1 = docker host IP
- -R :5822:host.docker.internal:22
- -o ServerAliveInterval=30
- core@melville.woozle.org
extra_hosts:
- host.docker.internal:host-gateway
secrets:
- source: tunnel
target: /config/.ssh/id_rsa
uid: "911"
gid: "911"
mode: 0600
- source: known_hosts
target: /config/.ssh/known_hosts
uid: "911"
gid: "911"
mode: 0600
configs:
dave.yaml:
file: dave.yaml
name: dave.yaml-v3
Corefile:
file: Corefile
name: Corefile-v3
Caddyfile:
file: Caddyfile
name: Caddyfile-v89
index.html:
file: www/index.html
name: index.html-v36
index.mjs:
file: www/index.mjs
name: index.mjs-v1
index.css:
file: www/index.css
name: index.css-v1
browser.html:
file: www/browser.html
name: browser.html-v3
deergrove.png:
file: www/deergrove.png
name: deergrove.png-v1
authelia.yaml:
file: authelia.yaml
name: authelia.yaml-v1
secrets:
passwd:
file: secrets/passwd
name: passwd-v2
simpleauth.key:
file: secrets/simpleauth.key
name: simpleauth.key-v1
tunnel:
file: secrets/tunnel
name: tunnel-v1
known_hosts:
file: secrets/known_hosts
name: known_hosts-v1
forgejo.ini:
file: secrets/forgejo.ini
name: forgejo.ini-v1
jwt.secret:
file: secrets/jwt.secret
name: jwt.secret-v1
storage.secret:
file: secrets/storage.secret
name: storage.secret-v1
session.secret:
file: secrets/session.secret
name: session.secret-v1
users.yaml:
file: secrets/users.yaml
name: users.yaml-v1
networks:
hostnet:
external: true
name: host
source: /dev/video11
target: /dev/video11
- type: bind
source: /dev/video12
target: /dev/video12
- type: bind
source: /dev/video13
target: /dev/video13
- type: bind
source: /dev/video14
target: /dev/video14
- type: bind
source: /dev/video15
target: /dev/video15
- type: bind
source: /dev/video16
target: /dev/video16

36
homelab/www/browser.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>Woozle Drive</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/dom111/webdav-js/assets/css/style-min.css">
<script>
function init() {
// This is dumb, but webdav-min.js doesn't have any checks to make sure the document is loaded.
let scr = document.head.appendChild(document.createElement("script"))
scr.src = "https://cdn.jsdelivr.net/gh/dom111/webdav-js/src/webdav-min.js"
for (let e of document.querySelectorAll(".listing")) {
console.log("Let's pray the WebDAV stuff works!", e)
e.remove()
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init)
} else {
init()
}
</script>
</head>
<body>
<ul class="listing">
{{range .Items}}
<li>
<a href="{{html .URL}}">{{html .Name}}</a>
<span class="size" data-value="{{.Size}}">{{.HumanSize}}</span>
<time>{{.HumanModTime "2006-01-02T15:04:05Z"}}</time>
</li>
{{end}}
</ul>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -27,7 +27,7 @@ nav a {
text-decoration: none;
white-space: nowrap;
}
nav a[data-no-menu] {
nav a[target] {
display: none;
}
nav a:hover {

View File

@ -4,12 +4,29 @@
<title>Deer Grove</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="deergrove.png">
<link rel="icon" href="icons/deergrove.png">
<link rel="stylesheet" href="index.css">
<script src="index.mjs" type="module"></script>
</head>
<body>
<nav>
<a href="/sonarr/" data-icon="/sonarr/Content/Images/logo.svg" title="Episode manager">Shows</a>
<a href="/radarr/" data-icon="/radarr/Content/Images/logo.svg" title="Movie manager">Movies</a>
<a href="/lidarr/" data-icon="/lidarr/Content/Images/logo.svg" title="Music manager">Music</a>
<a href="/readarr/" data-icon="/readarr/Content/Images/logo.svg" title="Book manager">Books</a>
<a href="/sucker/" data-icon="/sucker/cd-dvd.svg" title="Media Sucker">Sucker</a>
<hr>
<a href="/prowlarr/" data-icon="/prowlarr/Content/Images/logo.png" title="Indexer/Searcher">Search</a>
<a href="/nzbget/" data-icon="/nzbget/img/favicon-256x256.png" title="Usenet downloader">Usenet</a>
<a href="/transmission/web/" data-icon="/transmission/web/style/transmission/images/logo.png" title="BitTorrent downloader">BitTorrent</a>
<hr>
<a href="/octoprint/" data-icon="/octoprint/static/img/logo.png" title="3D Printer Front-End">Octoprint</a>
<a href="/wallart/" data-icon="/wallart/wallart.png" title="Wall Art uploader">Wall Art</a>
<!-- Items that launch a new tab don't appear in the top menu -->
<a href="https://git.woozle.org" target="_blank" data-icon="https://git.woozle.org/assets/img/logo.svg" title="Git repositories">Git</a>
<a href="https://drive.woozle.org/" target="_blank" data-icon="/public/icons/cloud-folder.png" titled="Shared storage">Drive</a>
<a href="https://ancestry.woozle.org/" target="_blank" data-icon="https://ancestry.woozle.org/images/favicon_gwd.png" title="Genealogy">Ancestry</a>
</nav>
<section id="app">
<iframe></iframe>

View File

@ -1,5 +1,8 @@
let frames = {}
function activate(event, element) {
if (element.target) {
return
}
event.preventDefault()
let parent = element.parentElement
@ -46,7 +49,7 @@ function frameLoaded(frame) {
}
let defaultIcon = null
async function init() {
function init() {
let doc = document.querySelector("iframe").contentDocument
defaultIcon = document.querySelector("link[rel~='icon']").href
@ -65,40 +68,22 @@ async function init() {
let icons = doc.body.appendChild(doc.createElement("section"))
icons.classList.add("icons")
let portalURL = new URL("portal.json", window.location)
let resp = await fetch(portalURL)
let obj = await resp.json()
let nav = document.querySelector("nav")
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))
}
}
for (let link of document.querySelectorAll("nav a")) {
let dlink = icons.appendChild(link.cloneNode(true))
dlink.textContent = ""
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) {
if (link.dataset.icon) {
let icon = dlink.appendChild(doc.createElement("img"))
icon.src = app.icon
icon.alt = app.title
icon.title = app.title
icon.src = link.dataset.icon
icon.style.objectFit = "cover"
} else {
let text = dlink.appendChild(doc.createElement("div"))
text.textContent = app.title
text.textContent = link.textContent
}
// Make both of them update the selected tab
link.addEventListener("click", event => activate(event, link))
dlink.addEventListener("click", event => activate(event, link))
}
}

View File

@ -17,11 +17,6 @@ deergrove.woozle.org, git.woozle.org, ancestry.woozle.org, drive.woozle.org {
reverse_proxy host.docker.internal:5880
}
passwords.woozle.org {
reverse_proxy /notifications/hub vaultwarden:3012
reverse_proxy vaultwarden:80
}
www.woozle.org, woozle.org {
root * /srv/www/woozle.org
file_server

View File

@ -36,5 +36,5 @@ services:
configs:
Caddyfile:
file: Caddyfile
name: Caddyfile-v11
name: Caddyfile-v9