Move homelab to runit
This commit is contained in:
parent
c2f2fcc38b
commit
5400171298
|
@ -1,3 +0,0 @@
|
|||
password
|
||||
samba-users.env
|
||||
secrets
|
|
@ -1,19 +1,3 @@
|
|||
# Neale's Homelab
|
||||
My homelab now uses runit-managed podman containers.
|
||||
|
||||
This is the stuff I run on my little Raspberry Pi.
|
||||
|
||||
I guess I fiddle around with it pretty frequently.
|
||||
|
||||
## Portal
|
||||
|
||||
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.
|
||||
|
||||
Other things like this exist,
|
||||
but they all require ridiculous things like relational databases or weird config files.
|
||||
|
||||
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.
|
||||
runit essentially runs `*/run` in an endless loop.
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,19 @@
|
|||
#! /bin/sh
|
||||
|
||||
me=$(basename $(pwd))
|
||||
image=jamesits/ripe-atlas:latest
|
||||
|
||||
podman pull $image
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--cpu-shares 128 \
|
||||
--cap-drop ALL \
|
||||
--cap-add CHOWN \
|
||||
--cap-add SETUID \
|
||||
--cap-add SETGID \
|
||||
--cap-add DAC_OVERRIDE \
|
||||
--cap-add NET_RAW \
|
||||
--volume /srv/sys/atlas/etc:/var/atlas-probe/etc \
|
||||
--volume /srv/sys/atlas/status:/var/atlas-probe/status \
|
||||
$image
|
|
@ -0,0 +1,3 @@
|
|||
melville.woozle.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC6fMdw0m8wvh5+9IUcNgjxk0TgjoTT1anESnsYWIcnmS8l7PzkB/WfCEZEU0QzKvKXSp37jd30iR5U9jM7zTjYrjCJZvK0dxShC9iIgR5akAThXvlH0KkHVAvhmQda6nvMWQugp359Usp4zH90/J7G//+3HEQ9zfXW+p29bwBOdyDHfjDSIz95ysy8ivwMFKuPgAsNK4FDfBsGVHCM5xu5bn0dSaHeqGn7bneY00zwQqQot6rOw1Pao2tEY04Vu4KC1VKVWKU38dNBEFTOQBxYhiVMsxY3CIwDbEQLA5gjRnYog2KvsDP9XK4W2k1Yc9PvwoinqzcgmDS7isQ8HAoZiY06CChg3hHYXRre+TcO/UrjhzrWbxdCo7XFKBiC74ZU/+t2IkBmmHEnaTpmaLazxZD75wUH9rrWwgPVFEWNp41nj0VfQfR032MwJ2iNbvkCW/2kUWe++r56TlYQjxL8FVICVBbt6pZprMbEkQm3/iPR6ibfNNa620Kz3QRUsxM=
|
||||
melville.woozle.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBE1D4QQxjnNgmI7wteSAnAqArKQ6UTnTKmTitnU40ISizAK1j8OqXxEv0hbssNrTVyqlmLUoIeRuHd5bHGC8dEE=
|
||||
melville.woozle.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEQKIumG2Wl3papG4lcIB9+ZEe76uMbfpP+XSlUBjR0E
|
|
@ -0,0 +1,86 @@
|
|||
# Example configuration file, it's safe to copy this as the default config file without any modification.
|
||||
|
||||
# You don't have to copy this file to your instance,
|
||||
# just run `./act_runner generate-config > config.yaml` to generate a config file.
|
||||
|
||||
log:
|
||||
# The level of logging, can be trace, debug, info, warn, error, fatal
|
||||
level: info
|
||||
|
||||
runner:
|
||||
# Where to store the registration result.
|
||||
file: .runner
|
||||
# Execute how many tasks concurrently at the same time.
|
||||
capacity: 1
|
||||
# Extra environment variables to run jobs.
|
||||
envs:
|
||||
A_TEST_ENV_NAME_1: a_test_env_value_1
|
||||
A_TEST_ENV_NAME_2: a_test_env_value_2
|
||||
# Extra environment variables to run jobs from a file.
|
||||
# It will be ignored if it's empty or the file doesn't exist.
|
||||
env_file: .env
|
||||
# The timeout for a job to be finished.
|
||||
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
|
||||
# So the job could be stopped by the Gitea instance if it's timeout is shorter than this.
|
||||
timeout: 30m
|
||||
# Whether skip verifying the TLS certificate of the Gitea instance.
|
||||
insecure: false
|
||||
# The timeout for fetching the job from the Gitea instance.
|
||||
fetch_timeout: 5s
|
||||
# The interval for fetching the job from the Gitea instance.
|
||||
fetch_interval: 28s
|
||||
# The labels of a runner are used to determine which jobs the runner can run, and how to run them.
|
||||
labels: ["aarch64", "big-builder", "hugo", "python3", "go"]
|
||||
|
||||
cache:
|
||||
# Enable cache server to use actions/cache.
|
||||
enabled: true
|
||||
# The directory to store the cache data.
|
||||
# If it's empty, the cache data will be stored in $HOME/.cache/actcache.
|
||||
dir: ""
|
||||
# The host of the cache server.
|
||||
# It's not for the address to listen, but the address to connect from job containers.
|
||||
# So 0.0.0.0 is a bad choice, leave it empty to detect automatically.
|
||||
host: ""
|
||||
# The port of the cache server.
|
||||
# 0 means to use a random available port.
|
||||
port: 0
|
||||
# The external cache server URL. Valid only when enable is true.
|
||||
# If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself.
|
||||
# The URL should generally end with "/".
|
||||
external_server: ""
|
||||
|
||||
container:
|
||||
# Specifies the network to which the container will connect.
|
||||
# Could be host, bridge or the name of a custom network.
|
||||
# If it's empty, act_runner will create a network automatically.
|
||||
network: ""
|
||||
# Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
|
||||
privileged: false
|
||||
# And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
|
||||
options:
|
||||
# The parent directory of a job's working directory.
|
||||
# If it's empty, /workspace will be used.
|
||||
workdir_parent:
|
||||
# Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob
|
||||
# You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.
|
||||
# For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:
|
||||
# valid_volumes:
|
||||
# - data
|
||||
# - /src/*.json
|
||||
# If you want to allow any volume, please use the following configuration:
|
||||
# valid_volumes:
|
||||
# - '**'
|
||||
valid_volumes: []
|
||||
# overrides the docker client host with the specified one.
|
||||
# If it's empty, act_runner will find an available docker host automatically.
|
||||
# If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
|
||||
# If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work.
|
||||
docker_host: ""
|
||||
# Pull docker image(s) even if already present
|
||||
force_pull: false
|
||||
|
||||
host:
|
||||
# The parent directory of a job's working directory.
|
||||
# If it's empty, $HOME/.cache/act/ will be used.
|
||||
workdir_parent:
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--volume ./app/config.yaml:/app/config.yaml:ro \
|
||||
--volume ./app/.ssh:/app/.ssh:ro \
|
||||
--volume /srv/secrets/builder-betty/.runner:/app/.runner:ro \
|
||||
git.woozle.org/neale/big-builder:1.0 -c config.yaml daemon
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
email neale@woozle.org
|
||||
email neale@woozle.org
|
||||
#debug
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,6 @@ drive.woozle.org {
|
|||
#reverse_proxy filebrowser:80
|
||||
}
|
||||
|
||||
media.woozle.org {
|
||||
reverse_proxy jellyfin:8096
|
||||
}
|
||||
|
||||
# XXX: have this use caddy auth
|
||||
ancestry.woozle.org {
|
||||
reverse_proxy geneweb:2317
|
||||
|
@ -77,7 +73,7 @@ deergrove.woozle.org {
|
|||
}
|
||||
|
||||
handle_path /sucker/* {
|
||||
reverse_proxy host.lan:5801
|
||||
reverse_proxy sucker:8080
|
||||
}
|
||||
|
||||
handle_path /netdata/* {
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,13 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--publish 80:80 \
|
||||
--publish 443:443 \
|
||||
--volume /srv:/srv:ro \
|
||||
--volume /srv/sys/caddy:/data/caddy \
|
||||
--volume ./Caddyfile:/etc/caddy/Caddyfile:ro \
|
||||
--add-host host.lan:192.168.86.2 \
|
||||
caddy:2-alpine
|
|
@ -4,5 +4,5 @@
|
|||
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 xfer.woozle.org
|
||||
fallthrough
|
||||
}
|
||||
forward . 8.8.8.8
|
||||
forward . 1.1.1.1 1.0.0.1 8.8.8.8
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,8 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network host \
|
||||
--mount type=bind,src=$(pwd)/Corefile,dst=/home/nonroot/Corefile,readonly=true \
|
||||
coredns/coredns
|
|
@ -1,4 +0,0 @@
|
|||
address: "0.0.0.0"
|
||||
port: "8000"
|
||||
dir: "/data"
|
||||
prefix: "/"
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--dns 1.1.1.1 \
|
||||
--volume /srv/sys/ddns-updater:/updater/data \
|
||||
qmcgaw/ddns-updater
|
|
@ -1,12 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
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
|
|
@ -1,406 +0,0 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
coredns:
|
||||
image: coredns/coredns
|
||||
networks:
|
||||
- hostnet
|
||||
command:
|
||||
- -conf
|
||||
- /Corefile
|
||||
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
|
||||
extra_hosts:
|
||||
- host.docker.internal:host-gateway
|
||||
- host.lan:192.168.86.2
|
||||
|
||||
simpleauth:
|
||||
image: git.woozle.org/neale/simpleauth
|
||||
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
|
||||
networks:
|
||||
- hostnet
|
||||
environment:
|
||||
TZ: US/Mountain
|
||||
VERSION: public
|
||||
volumes:
|
||||
- type: bind
|
||||
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/storage/fam/Photos
|
||||
target: /photos/fam
|
||||
read_only: true
|
||||
- type: bind
|
||||
source: /srv/storage/ginnie/Photos
|
||||
target: /photos/ginnie
|
||||
read_only: true
|
||||
|
||||
transmission:
|
||||
image: lscr.io/linuxserver/transmission:latest
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/sys/transmission
|
||||
target: /config
|
||||
- type: bind
|
||||
source: /srv/incoming
|
||||
target: /srv/incoming
|
||||
environment:
|
||||
PEERPORT: "51413"
|
||||
ports:
|
||||
- 51413:51413
|
||||
- 51413:51413/udp
|
||||
|
||||
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
|
||||
|
||||
gitea:
|
||||
image: codeberg.org/forgejo/forgejo:1.20.5-0-rootless
|
||||
configs:
|
||||
- source: gitea-robots.txt
|
||||
target: /var/lib/gitea/custom/robots.txt
|
||||
- source: timezone
|
||||
target: /etc/timezone
|
||||
- source: localtime
|
||||
target: /etc/localtime
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/sys/gitea/app.ini
|
||||
target: /etc/gitea/app.ini
|
||||
- type: bind
|
||||
source: /srv/sys/gitea
|
||||
target: /data
|
||||
|
||||
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
|
||||
|
||||
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/
|
||||
target: /usr/local/share/geneweb/share/data
|
||||
|
||||
samba:
|
||||
image: dperson/samba
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv
|
||||
target: /srv
|
||||
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
|
||||
|
||||
webfs:
|
||||
image: sigoden/dufs
|
||||
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
|
||||
user: "911:911"
|
||||
|
||||
picoshare:
|
||||
image: git.woozle.org/neale/picoshare
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/sys/picoshare
|
||||
target: /data
|
||||
environment:
|
||||
PS_SHARED_SECRET_FILE: /run/secrets/picoshare
|
||||
secrets:
|
||||
- picoshare
|
||||
|
||||
ddns:
|
||||
image: qmcgaw/ddns-updater
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/sys/ddns-updater
|
||||
target: /updater/data
|
||||
|
||||
filebrowser:
|
||||
image: filebrowser/filebrowser
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/sys/filebrowser/database
|
||||
target: /database
|
||||
- type: bind
|
||||
source: /srv/storage
|
||||
target: /srv/storage
|
||||
- type: bind
|
||||
source: /srv/media
|
||||
target: /srv/media
|
||||
- type: bind
|
||||
source: /srv/incoming
|
||||
target: /srv/incoming
|
||||
user: "911:911"
|
||||
configs:
|
||||
- source: filebrowser.json
|
||||
target: /.filebrowser.json
|
||||
|
||||
|
||||
configs:
|
||||
dave.yaml:
|
||||
file: dave.yaml
|
||||
name: dave.yaml-v3
|
||||
Corefile:
|
||||
file: Corefile
|
||||
name: Corefile-v11
|
||||
Caddyfile:
|
||||
file: Caddyfile
|
||||
name: Caddyfile-v153
|
||||
portal.json:
|
||||
file: portal.json
|
||||
name: portal.json-v7
|
||||
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
|
||||
filebrowser.json:
|
||||
file: filebrowser.json
|
||||
name: filebrowser.json-v2
|
||||
localtime:
|
||||
file: localtime
|
||||
name: localtime-v1
|
||||
timezone:
|
||||
file: timezone
|
||||
name: timezone-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
|
||||
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
|
||||
picoshare:
|
||||
file: secrets/picoshare
|
||||
name: picoshare-v1
|
||||
|
||||
networks:
|
||||
hostnet:
|
||||
external: true
|
||||
name: host
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"port": 80,
|
||||
"baseURL": "/",
|
||||
"address": "",
|
||||
"log": "stdout",
|
||||
"database": "/database/filebrowser.db",
|
||||
"root": "/srv"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,8 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/geneweb:/usr/local/share/geneweb/share/data \
|
||||
ravermeister/geneweb
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,12 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume ./gitea-robots.txt:/var/lib/gitea/custom/robots.txt:ro \
|
||||
--volume ./timezone:/etc/timezone:ro \
|
||||
--volume /etc/localtime:/etc/localtime:ro \
|
||||
--volume /srv/sys/gitea/app.ini:/etc/gitea/app.ini \
|
||||
--volume /srv/sys/gitea:/data \
|
||||
codeberg.org/forgejo/forgejo:1.20.5-0-rootless
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/lidarr:/config \
|
||||
--volume /srv/media/music:/srv/media/music \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/lidarr
|
Binary file not shown.
|
@ -1,5 +0,0 @@
|
|||
[global]
|
||||
update every = 10
|
||||
|
||||
[plugin:proc:/proc/net/dev]
|
||||
disable by default interfaces matching = lo veth* docker*
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/nzbget:/config \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/nzbget
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,20 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
image=git.woozle.org/neale/toolbox
|
||||
|
||||
podman pull $image
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--hostname oscar \
|
||||
--security-opt label=disable \
|
||||
--privileged \
|
||||
--network host \
|
||||
--pid host \
|
||||
--volume /srv/sys/oscar:/mnt/toolbox \
|
||||
--volume /srv:/srv \
|
||||
--volume /media:/media \
|
||||
--volume /home:/home \
|
||||
--volume /dev:/dev \
|
||||
--volume /:/host \
|
||||
$image
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--env PS_SHARED_SECRET_FILE=/run/secrets/picoshare \
|
||||
--volume /srv/sys/picoshare:/data \
|
||||
--volume /srv/secrets/picoshare:/run/secrets/picoshare \
|
||||
git.woozle.org/neale/picoshare
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,12 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/pigallery2/config:/app/data/config \
|
||||
--volume /srv/sys/pigallery2/db:/app/data/db \
|
||||
--volume /srv/sys/pigallery2/cache:/app/data/cache \
|
||||
--volume /srv/storage/fam/Photos:/photos/fam:ro \
|
||||
--volume /srv/storage/ginnie/Photos:/photos/ginnie:ro \
|
||||
bpatrik/pigallery2:latest
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,11 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network host \
|
||||
--tz US/Mountain \
|
||||
--env VERSION=public \
|
||||
--mount type=bind,src=/srv/sys/plex,dst=/config \
|
||||
--mount type=bind,src=/srv/media,dst=/srv/media,readonly=true \
|
||||
lscr.io/linuxserver/plex:latest
|
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,76 @@
|
|||
[
|
||||
{
|
||||
"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/icon.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"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume ./portal.json:/web/portal.json:ro \
|
||||
--volume ./deergrove.png:/web/portal.png:ro \
|
||||
git.woozle.org/neale/portal
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,8 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/prowlarr:/config \
|
||||
lscr.io/linuxserver/prowlarr
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/radarr:/config \
|
||||
--volume /srv/media/movies:/srv/media/movies \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/radarr
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,11 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/readarr:/config \
|
||||
--volume /srv/media/books:/srv/media/books \
|
||||
--volume /srv/media/audiobooks:/srv/media/audiobooks \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/readarr:develop
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,17 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--publish 139:139 \
|
||||
--publish 445:445 \
|
||||
--env NMBD=enable \
|
||||
--env RECYCLE=disable \
|
||||
--env USERID=911 \
|
||||
--env GROUPID=911 \
|
||||
--env SHARE1='drive;/srv;yes;no;no' \
|
||||
--env SHARE2='media;/srv/media;yes;yes;yes' \
|
||||
--env-file /srv/secrets/samba-users.env \
|
||||
--volume /srv:/srv \
|
||||
dperson/samba
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/secrets/passwd:/run/secrets/passwd:ro \
|
||||
--volume /srv/secrets/simpleauth.key:/run/secrets/simpleauth.key:ro \
|
||||
git.woozle.org/neale/simpleauth
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--volume /srv/sys/sonarr:/config \
|
||||
--volume /srv/media/tv:/srv/media/tv \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/sonarr
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,15 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name=$me \
|
||||
--device-cgroup-rule 'b 11:* rmw' \
|
||||
--network homelab \
|
||||
-v /dev:/hdev \
|
||||
-v /srv/ext/incoming/sucker:/incoming/sucker \
|
||||
--cpu-shares 256 \
|
||||
--user 911:19 \
|
||||
git.woozle.org/neale/media-sucker \
|
||||
-incoming /incoming/sucker \
|
||||
-drive /hdev/sr0 /hdev/sr1 /hdev/sr2
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#! /bin/sh
|
||||
|
||||
service=/host/etc/service
|
||||
if ! [ -d $service ]; then
|
||||
echo "=== $service is not a directory" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rsync -vaxp --delete \
|
||||
--exclude=supervise/ \
|
||||
$service/* .
|
|
@ -0,0 +1,14 @@
|
|||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
. /etc/lbu/lbu.conf
|
||||
|
||||
hostname=$(hostname)
|
||||
mkdir -p /srv/backups/$hostname/
|
||||
install -p -m 0600 /media/$LBU_MEDIA/$hostname.*.tar.gz /srv/backups/$hostname/
|
||||
for path in /srv/backups/$hostname/$hostname.*.tar.gz; do
|
||||
filename=$(basename $path)
|
||||
[ -f /media/$LBU_MEDIA/$filename ] || rm $path
|
||||
done
|
||||
sleep 4h
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,12 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--publish 51413:51413 \
|
||||
--publish 51413:51413/udp \
|
||||
--env PEERPORT=51413 \
|
||||
--volume /srv/sys/transmission:/config \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
lscr.io/linuxserver/transmission:latest
|
|
@ -1,58 +0,0 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
syncthing:
|
||||
image: syncthing/syncthing
|
||||
environment:
|
||||
PUID: 911
|
||||
CGID: 911
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/syncthing
|
||||
target: /var/syncthing
|
||||
- type: bind
|
||||
source: /srv/ext
|
||||
target: /srv/ext
|
||||
ports:
|
||||
- published: 22000
|
||||
target: 22000
|
||||
protocol: tcp
|
||||
- published: 22000
|
||||
target: 22000
|
||||
protocol: udp
|
||||
- published: 21027
|
||||
target: 21027
|
||||
protocol: udp
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.syncthing.rule: "PathPrefix(`/syncthing`)"
|
||||
traefik.http.routers.syncthing.middlewares: syncthing-striparoo
|
||||
traefik.http.middlewares.syncthing-striparoo.stripprefix.prefixes: "/syncthing"
|
||||
traefik.http.services.syncthing.loadbalancer.server.port: "8384"
|
||||
|
||||
## Drop this in to get a netdata container.
|
||||
## It uses a lot of RAM and causes my machine to swap.
|
||||
## Granted, it's a lot more lightweight than nextcloud.
|
||||
## But I can live without netdata.
|
||||
netdata:
|
||||
image: netdata/netdata
|
||||
hostname: $HOSTNAME
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /
|
||||
target: /host
|
||||
read_only: true
|
||||
configs:
|
||||
- source: netdata.conf
|
||||
target: /etc/netdata/netdata.conf
|
||||
deploy:
|
||||
labels:
|
||||
traefik.http.routers.netdata.rule: "PathPrefix(`/netdata`)"
|
||||
traefik.http.middlewares.netdata-striparoo.stripprefix.prefixes: "/netdata"
|
||||
traefik.http.routers.netdata.middlewares: netdata-striparoo
|
||||
traefik.http.services.netdata.loadbalancer.server.port: "19999"
|
||||
|
||||
configs:
|
||||
netdata.conf:
|
||||
file: netdata.conf
|
||||
name: netdata.conf-v4
|
|
@ -1,10 +0,0 @@
|
|||
[server]
|
||||
domain = {{.Node.Hostname}}
|
||||
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
|
||||
serve_from_sub_path = true
|
||||
|
||||
[auth.anonymous]
|
||||
enabled = false
|
||||
org_name = Main Org.
|
||||
org_role = Viewer
|
||||
|
|
@ -1,363 +0,0 @@
|
|||
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
|
||||
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: 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:
|
||||
- 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
|
||||
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
traefik:
|
||||
image: traefik
|
||||
environment:
|
||||
TRAEFIK_API: "true"
|
||||
TRAEFIK_API_INSECURE: "true"
|
||||
TRAEFIK_ENTRYPOINTS_WEB_ADDRESS: :80
|
||||
TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO: websecure
|
||||
TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME: https
|
||||
TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS: :443
|
||||
TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_CERTRESOLVER: letsencrypt
|
||||
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCYRPT_ACME_EMAIL: neale@woozle.org
|
||||
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_STORAGE: /acme.json
|
||||
XXX_TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE_ENTRYPOINT: web
|
||||
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_TLSCHALLENGE: "true"
|
||||
TRAEFIK_PROVIDERS_DOCKER_SWARMMODE: "true"
|
||||
TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT: "false"
|
||||
ports:
|
||||
- target: 443
|
||||
published: 443
|
||||
mode: host
|
||||
- target: 80
|
||||
published: 80
|
||||
mode: host
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /var/run/docker.sock
|
||||
target: /var/run/docker.sock
|
||||
read_only: true
|
||||
- type: bind
|
||||
source: /srv/ext/sys/traefik/acme.json
|
||||
target: /acme.json
|
||||
deploy:
|
||||
labels:
|
||||
# XXX: This HSTS stuff doesn't seem to be working
|
||||
traefik.enable: "true"
|
||||
traefik.frontend.headers.STSSeconds: "31536000"
|
||||
traefik.frontend.headers.STSPreload: "true"
|
||||
traefik.http.routers.dashboard.rule: "Host(`$FQDN`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||
traefik.http.routers.dashboard.tls.certresolver: letsencrypt
|
||||
traefik.http.routers.dashboard.middlewares: forward-auth
|
||||
traefik.http.routers.dashboard.service: api@internal
|
||||
traefik.http.middlewares.forward-auth.forwardauth.address: http://simpleauth:8080/
|
||||
traefik.http.services.traefik.loadbalancer.server.port: "1"
|
||||
|
||||
simpleauth:
|
||||
image: ghcr.io/nealey/simpleauth
|
||||
secrets:
|
||||
- password
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.simpleauth.rule: "Host(`$FQDN`) && Path(`/`)"
|
||||
traefik.http.services.simpleauth.loadbalancer.server.port: "8080"
|
||||
|
||||
plex:
|
||||
image: ghcr.io/linuxserver/plex
|
||||
networks:
|
||||
- hostnet
|
||||
environment:
|
||||
TZ: US/Mountain
|
||||
VERSION: public
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/plex
|
||||
target: /config
|
||||
- type: bind
|
||||
source: /srv
|
||||
target: /srv
|
||||
read_only: true
|
||||
bind:
|
||||
propagation: rslave
|
||||
|
||||
transmission:
|
||||
image: ghcr.io/linuxserver/transmission
|
||||
networks:
|
||||
- hostnet
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/transmission
|
||||
target: /config
|
||||
- type: bind
|
||||
source: /srv/ext/incoming
|
||||
target: /srv/ext/incoming
|
||||
deploy:
|
||||
labels:
|
||||
# This isn't going to work, because transmission binds to the host network.
|
||||
traefik.http.routers.transmission.rule: "PathPrefix(`/transmission`)"
|
||||
traefik.http.services.transmission.loadbalancer.server.port: "9091"
|
||||
|
||||
ocis:
|
||||
image: owncloud/ocis:2.0.0-beta.5
|
||||
environment:
|
||||
OCIS_URL: https://drive.woozle.org/
|
||||
PROXY_TLS: "false"
|
||||
# ports:
|
||||
# - published: 9200
|
||||
# target: 9200
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/ocis/config
|
||||
target: /etc/ocis
|
||||
- type: bind
|
||||
source: /srv/ext/sys/ocis/data
|
||||
target: /var/lib/ocis
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.ocis.rule: "Host(`drive.woozle.org`)"
|
||||
traefik.http.routers.ocis.tls: "true"
|
||||
traefik.http.routers.ocis.tls.certresolver: letsencrypt
|
||||
traefik.http.services.ocis.loadbalancer.server.port: "9200"
|
||||
|
||||
gitea:
|
||||
image: gitea/gitea:1
|
||||
environment:
|
||||
USER_UID: 1000
|
||||
USER_GID: 1000
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/gitea
|
||||
target: /data
|
||||
- type: bind
|
||||
source: /etc/timezone
|
||||
target: /etc/timezone
|
||||
read_only: true
|
||||
- type: bind
|
||||
source: /etc/localtime
|
||||
target: /etc/localtime
|
||||
read_only: true
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.gitea.rule: "Host(`git.woozle.org`)"
|
||||
traefik.http.routers.gitea.middlewares: gitea-striparoo
|
||||
traefik.http.middlewares.gitea-striparoo.stripprefix.prefixes: "/gitea"
|
||||
traefik.http.services.gitea.loadbalancer.server.port: "3000"
|
||||
|
||||
atlas:
|
||||
image: ctassisf/ripe-atlas-alpine:arm64v8
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/atlas/etc
|
||||
target: /var/atlas-probe/etc
|
||||
- type: bind
|
||||
source: /srv/ext/sys/atlas/status
|
||||
target: /var/atlas-probe/status
|
||||
networks:
|
||||
- hostnet
|
||||
|
||||
## Drop this in to get a netdata container.
|
||||
## It uses a lot of RAM and causes my machine to swap.
|
||||
## Granted, it's a lot more lightweight than nextcloud.
|
||||
## But I can live without netdata.
|
||||
# netdata:
|
||||
# image: netdata/netdata
|
||||
# hostname: $HOSTNAME
|
||||
# volumes:
|
||||
# - type: bind
|
||||
# source: /
|
||||
# target: /host
|
||||
# read_only: true
|
||||
# configs:
|
||||
# - source: netdata.conf
|
||||
# target: /etc/netdata/netdata.conf
|
||||
# deploy:
|
||||
# labels:
|
||||
# traefik.http.routers.netdata.rule: "PathPrefix(`/netdata`)"
|
||||
# traefik.http.middlewares.netdata-striparoo.stripprefix.prefixes: "/netdata"
|
||||
# traefik.http.routers.netdata.middlewares: netdata-striparoo
|
||||
# traefik.http.services.netdata.loadbalancer.server.port: "19999"
|
||||
|
||||
geneweb:
|
||||
image: ravermeister/geneweb
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/sys/geneweb/etc
|
||||
target: /usr/local/share/geneweb/etc
|
||||
- type: bind
|
||||
source: /srv/ext/sys/geneweb/share/data
|
||||
target: /usr/local/share/geneweb/share/data
|
||||
- type: bind
|
||||
source: /srv/ext/sys/geneweb/log
|
||||
target: /usr/local/share/geneweb/log
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.gwsetup.rule: "PathPrefix(`/gwsetup`)"
|
||||
traefik.http.middlewares.gwsetup-striparoo.stripprefix.prefixes: "/gwsetup"
|
||||
traefik.http.routers.gwsetup.middlewares: gwsetup-striparoo,forward-auth
|
||||
traefik.http.routers.gwsetup.service: gwsetup
|
||||
traefik.http.services.gwsetup.loadbalancer.server.port: "2316"
|
||||
|
||||
traefik.http.routers.geneweb.rule: "Host(`ancestry.woozle.org`)"
|
||||
traefik.http.routers.geneweb.service: geneweb
|
||||
traefik.http.services.geneweb.loadbalancer.server.port: "2317"
|
||||
|
||||
samba:
|
||||
image: dperson/samba
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext
|
||||
target: /srv/ext
|
||||
bind:
|
||||
propagation: rslave
|
||||
environment:
|
||||
NMBD: enable
|
||||
RECYCLE: disable
|
||||
USERID: 911
|
||||
GROUPID: 911
|
||||
# name;path;browse;readonly;guest
|
||||
SHARE1: storage;/srv/ext/storage;yes;no;no
|
||||
SHARE2: media;/srv/ext/media;yes;no;no
|
||||
SHARE3: software;/srv/ext/software;yes;no;no
|
||||
SHARE4: backups;/srv/ext/backups;yes;no;no
|
||||
SHARE4: incoming;/srv/ext/incoming;yes;no;no
|
||||
env_file:
|
||||
- samba-users.env
|
||||
ports:
|
||||
- published: 139
|
||||
target: 139
|
||||
- published: 445
|
||||
target: 445
|
||||
|
||||
public:
|
||||
image: caddy
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/ext/storage/public
|
||||
target: /usr/share/caddy/public
|
||||
read_only: true
|
||||
deploy:
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.public.rule: "PathPrefix(`/public`)"
|
||||
traefik.http.services.public.loadbalancer.server.port: "80"
|
||||
|
||||
configs:
|
||||
netdata.conf:
|
||||
file: netdata.conf
|
||||
name: netdata.conf-v4
|
||||
|
||||
secrets:
|
||||
password:
|
||||
file: password
|
||||
name: password-v1
|
||||
|
||||
networks:
|
||||
hostnet:
|
||||
external: true
|
||||
name: host
|
|
@ -1,3 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
tar czf /mnt/ardenne/backups/sweetums.tar.gz /host/etc/systemd
|
|
@ -1,9 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
# We do this on the host's cron now
|
||||
exit 0
|
||||
|
||||
for mnt in /mnt/ardenne; do
|
||||
chroot /host /usr/bin/btrfs scrub start -B $mnt
|
||||
done
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
domain=woozle.org
|
||||
host=sweetums
|
||||
key=6SRjTBJG7v2bHyG9gqX913w1
|
||||
|
||||
log() {
|
||||
echo "=== $*"
|
||||
}
|
||||
|
||||
json() {
|
||||
ip4=$1
|
||||
ip6=$2
|
||||
printf '{"items": ['
|
||||
if [ -n "$ip6" ]; then
|
||||
printf '{"rrset_type":"AAAA","rrset_values":["%s"],"rrset_ttl":300},' "$ip6"
|
||||
fi
|
||||
printf '{"rrset_type":"A","rrset_values":["%s"],"rrset_ttl":300}' "$ip4"
|
||||
printf ']}'
|
||||
}
|
||||
|
||||
push() {
|
||||
host=$1
|
||||
domain=$2
|
||||
authh="Authorization: Apikey $key"
|
||||
log "$host.$domain"
|
||||
curl -s -H "$authh" -T - https://api.gandi.net/v5/livedns/domains/$domain/records/$host
|
||||
echo
|
||||
}
|
||||
|
||||
me4=$(curl -s -4 https://api64.ipify.org/)
|
||||
me6=$(curl -s -6 https://api64.ipify.org/)
|
||||
|
||||
log "IPv4: $me4"
|
||||
log "IPv6: $me6"
|
||||
|
||||
json $me4 $me6 | push sweetums woozle.org
|
||||
json $me4 | push drive woozle.org
|
|
@ -1,11 +0,0 @@
|
|||
#! /bin/sh -e
|
||||
|
||||
for user in neale amy ginnie; do
|
||||
dir=/mnt/ext/backups/$user
|
||||
rclone \
|
||||
--config=$dir/rclone.conf \
|
||||
--bwlimit=2M \
|
||||
--transfers=1 \
|
||||
sync gdrive: $dir/gdrive
|
||||
chown -R $(stat -c %u:%g $dir) $dir
|
||||
done
|
|
@ -1,3 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
date > /mnt/ext/srv/periodic/last-run
|
|
@ -1,4 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
[ $(hostname) = sweetums ] || exit 0
|
||||
wget -O - https://drive.woozle.org/cron.php >/dev/null
|
|
@ -1,7 +0,0 @@
|
|||
global:
|
||||
|
||||
scrape_configs:
|
||||
- job_name: node_exporter
|
||||
static_configs:
|
||||
- targets:
|
||||
- node-exporter:9100
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman stop $me
|
|
@ -0,0 +1,11 @@
|
|||
#! /bin/sh
|
||||
me=$(basename $(pwd))
|
||||
podman rm $me
|
||||
podman run \
|
||||
--name $me \
|
||||
--network homelab \
|
||||
--user 911:911 \
|
||||
--volume /srv/storage:/srv/storage \
|
||||
--volume /srv/incoming:/srv/incoming \
|
||||
--volume /srv/media:/srv/media \
|
||||
sigoden/dufs -A /srv
|
|
@ -1,78 +0,0 @@
|
|||
body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-family: Helvetica, sans-serif;
|
||||
background-color: #222;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav {
|
||||
overflow-x: auto;
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
display: flex;
|
||||
}
|
||||
nav img {
|
||||
max-height: 2em;
|
||||
}
|
||||
nav a {
|
||||
padding: 0.5em 1.5em;
|
||||
color: #aaa;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
nav a[data-no-menu] {
|
||||
display: none;
|
||||
}
|
||||
nav a:hover {
|
||||
background: #555;
|
||||
}
|
||||
nav a.active {
|
||||
background: #8b8;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#app {
|
||||
flex-grow: 1;
|
||||
}
|
||||
iframe {
|
||||
display: block;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icons {
|
||||
margin: auto 2em;
|
||||
}
|
||||
.icons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
max-width: 40em;
|
||||
margin: auto;
|
||||
gap: 20px;
|
||||
}
|
||||
.icons a {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.icons a img {
|
||||
max-width: 66%;
|
||||
max-height: 66%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
image-rendering: pixelated;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<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="stylesheet" href="index.css">
|
||||
<script src="index.mjs" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
</nav>
|
||||
<section id="app">
|
||||
<iframe></iframe>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
|
@ -1,109 +0,0 @@
|
|||
let frames = {}
|
||||
function activate(event, element) {
|
||||
event.preventDefault()
|
||||
|
||||
let parent = element.parentElement
|
||||
for (let e of parent.getElementsByClassName("active")) {
|
||||
e.classList.remove("active")
|
||||
}
|
||||
element.classList.add("active")
|
||||
let href = element.href
|
||||
let app = document.querySelector("#app")
|
||||
|
||||
let frame = frames[href]
|
||||
if (frame) {
|
||||
frame.dispatchEvent(new Event("load"))
|
||||
} else {
|
||||
frame = app.appendChild(document.createElement("iframe"))
|
||||
frame.addEventListener("load", e => frameLoaded(frame))
|
||||
frame.src = href
|
||||
frames[href] = frame
|
||||
}
|
||||
|
||||
for (let fhref in frames) {
|
||||
let f = frames[fhref]
|
||||
if (fhref == href) {
|
||||
f.classList.remove("hidden")
|
||||
} else {
|
||||
f.classList.add("hidden")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function frameLoaded(frame) {
|
||||
let doc = frame.contentDocument
|
||||
if (doc.title.length > 0) {
|
||||
document.title = doc.title
|
||||
}
|
||||
let icon = document.querySelector("link[rel~='icon']")
|
||||
let dicon = doc.querySelector("link[rel~='icon']")
|
||||
if (dicon) {
|
||||
icon.href = dicon.href
|
||||
} else {
|
||||
icon.href = defaultIcon
|
||||
}
|
||||
}
|
||||
|
||||
let defaultIcon = null
|
||||
async function init() {
|
||||
let doc = document.querySelector("iframe").contentDocument
|
||||
|
||||
defaultIcon = document.querySelector("link[rel~='icon']").href
|
||||
|
||||
for (let l of document.head.querySelectorAll("style")) {
|
||||
doc.head.appendChild(l.cloneNode(true))
|
||||
}
|
||||
for (let l of document.head.querySelectorAll("link[rel='stylesheet']")) {
|
||||
doc.head.appendChild(l.cloneNode())
|
||||
}
|
||||
for (let f of document.querySelectorAll("#app iframe")) {
|
||||
frames[f.src] = f
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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 = app.icon
|
||||
icon.alt = app.title
|
||||
icon.title = app.title
|
||||
icon.style.objectFit = "cover"
|
||||
} else {
|
||||
let text = dlink.appendChild(doc.createElement("div"))
|
||||
text.textContent = app.title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init)
|
||||
} else {
|
||||
init()
|
||||
}
|
Loading…
Reference in New Issue