diff --git a/.gitignore b/.gitignore index 21b3964..a3e6436 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,16 @@ # allow git to enter directories !*/ venv/ +core/crowdsec/config +database +data +db +docs +examples +searx +tests +utils + # keep essential project files !.gitignore !.gitattributes diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-environment-setup-in-conftest.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-environment-setup-in-conftest.py new file mode 100644 index 0000000..17ffce6 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-environment-setup-in-conftest.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 8af5ca464225c888f5438a0fd226937e2ccabca4 Gitea 1757451832 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-geocode.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-geocode.py new file mode 100644 index 0000000..cd73075 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-geocode.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 bb76e891d59a88beeb4f5b233cbecfd94a8f0cae Gitea 1756461833 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-route_metrics.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-route_metrics.py new file mode 100644 index 0000000..2621d06 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-route_metrics.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 f5998826f6b9ed820f5a97a67ce11402e72fd2f3 Gitea 1756461833 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-tracking-simulator.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-tracking-simulator.py new file mode 100644 index 0000000..bf22e80 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/add-logging-to-tracking-simulator.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 791c61ee8129113a02d67ffa171ed48843cdf025 Gitea 1756815232 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/extend-sqlite-tuning-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/extend-sqlite-tuning-in-database.py new file mode 100644 index 0000000..b5ed21e --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/extend-sqlite-tuning-in-database.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 e76063242b47225e6614f17749b1bf2ba0b70ac9 Gitea 1757407432 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/fix-route-handling-in-routing.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/fix-route-handling-in-routing.py new file mode 100644 index 0000000..6a1e1fb --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/fix-route-handling-in-routing.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1c93f2ab9ceef7601c5db93de0ddb057aef5b4b1 Gitea 1756433632 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/handle-api-response-errors-in-routing.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/handle-api-response-errors-in-routing.py new file mode 100644 index 0000000..fa40dd5 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/handle-api-response-errors-in-routing.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 0404d47f0ba21269865f307f3fd53e746ff155a4 Gitea 1756436033 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/refactor-database-path-handling-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/refactor-database-path-handling-in-database.py new file mode 100644 index 0000000..812244d --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/refactor-database-path-handling-in-database.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 80111cd7579abc6319f5d357da060db8186babaf Gitea 1758786607 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-fcm-message-construction-in-notifications.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-fcm-message-construction-in-notifications.py new file mode 100644 index 0000000..eac11ef --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-fcm-message-construction-in-notifications.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 0fea5ebd8be8d93f95630bdc5cc9ecc0b0bbac43 Gitea 1756949032 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-role-check-in-ws.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-role-check-in-ws.py new file mode 100644 index 0000000..35b4da7 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-role-check-in-ws.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 7b87a0e2a6c03e5344da2fe6a391c1f1fb269b5c Gitea 1756851832 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-user-seed-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-user-seed-in-database.py new file mode 100644 index 0000000..5623d8e --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/logs/refs/heads/codex/update-user-seed-in-database.py @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 bde15e4b736be753b1272a3277f528d7eb75d371 Gitea 1756508033 +0000 fetch --prune --tags origin: storing head diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-environment-setup-in-conftest.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-environment-setup-in-conftest.py new file mode 100644 index 0000000..5b90c94 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-environment-setup-in-conftest.py @@ -0,0 +1 @@ +8af5ca464225c888f5438a0fd226937e2ccabca4 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-geocode.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-geocode.py new file mode 100644 index 0000000..7266f8f --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-geocode.py @@ -0,0 +1 @@ +bb76e891d59a88beeb4f5b233cbecfd94a8f0cae diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-route_metrics.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-route_metrics.py new file mode 100644 index 0000000..6df0506 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-route_metrics.py @@ -0,0 +1 @@ +f5998826f6b9ed820f5a97a67ce11402e72fd2f3 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-tracking-simulator.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-tracking-simulator.py new file mode 100644 index 0000000..a2076f0 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/add-logging-to-tracking-simulator.py @@ -0,0 +1 @@ +791c61ee8129113a02d67ffa171ed48843cdf025 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/extend-sqlite-tuning-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/extend-sqlite-tuning-in-database.py new file mode 100644 index 0000000..cd8c913 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/extend-sqlite-tuning-in-database.py @@ -0,0 +1 @@ +e76063242b47225e6614f17749b1bf2ba0b70ac9 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/fix-route-handling-in-routing.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/fix-route-handling-in-routing.py new file mode 100644 index 0000000..ef73ce7 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/fix-route-handling-in-routing.py @@ -0,0 +1 @@ +1c93f2ab9ceef7601c5db93de0ddb057aef5b4b1 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/handle-api-response-errors-in-routing.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/handle-api-response-errors-in-routing.py new file mode 100644 index 0000000..0186adc --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/handle-api-response-errors-in-routing.py @@ -0,0 +1 @@ +0404d47f0ba21269865f307f3fd53e746ff155a4 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/refactor-database-path-handling-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/refactor-database-path-handling-in-database.py new file mode 100644 index 0000000..4b75700 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/refactor-database-path-handling-in-database.py @@ -0,0 +1 @@ +80111cd7579abc6319f5d357da060db8186babaf diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-fcm-message-construction-in-notifications.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-fcm-message-construction-in-notifications.py new file mode 100644 index 0000000..7fdace2 --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-fcm-message-construction-in-notifications.py @@ -0,0 +1 @@ +0fea5ebd8be8d93f95630bdc5cc9ecc0b0bbac43 diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-role-check-in-ws.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-role-check-in-ws.py new file mode 100644 index 0000000..dd3247b --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-role-check-in-ws.py @@ -0,0 +1 @@ +7b87a0e2a6c03e5344da2fe6a391c1f1fb269b5c diff --git a/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-user-seed-in-database.py b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-user-seed-in-database.py new file mode 100644 index 0000000..081ed4c --- /dev/null +++ b/apps/gitea/data/git/repositories/beatzaplenty/limo-booking-app.git/refs/heads/codex/update-user-seed-in-database.py @@ -0,0 +1 @@ +bde15e4b736be753b1272a3277f528d7eb75d371 diff --git a/gitea/docker-compose.yml b/apps/gitea/docker-compose.yml similarity index 60% rename from gitea/docker-compose.yml rename to apps/gitea/docker-compose.yml index 919c42e..55f6bc3 100644 --- a/gitea/docker-compose.yml +++ b/apps/gitea/docker-compose.yml @@ -1,5 +1,7 @@ services: gitea: + profiles: ["apps","all","gitea"] + container_name: gitea image: gitea/gitea:latest # change to 1-rootless once find out how to move data. restart: always environment: @@ -8,37 +10,30 @@ services: - GITEA__database__DB_TYPE=sqlite3 - GITEA__server__ROOT_URL=https://gitea.lan.ddnsgeek.com/ volumes: - - ./data:/data + - ${PROJECT_ROOT}/apps/gitea/data:/data networks: - - traefik_reverse_proxy + - traefik labels: - "traefik.enable=true" -<<<<<<< HEAD -======= - # - "traefik.docker.network=proxy" ->>>>>>> d534f12f7412d036eb157e1463952b9e2b826b2d - "traefik.http.routers.gitea.rule=Host(`gitea.lan.ddnsgeek.com`)" - "traefik.http.routers.gitea.entrypoints=websecure" - "traefik.http.routers.gitea.tls=true" - "traefik.http.routers.gitea.tls.certresolver=myresolver" - "traefik.http.services.gitea.loadbalancer.server.port=3000" - "io.portainer.accesscontrol.public" -<<<<<<< HEAD - healthcheck: - test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/v1/version >/dev/null"] - interval: 15s - timeout: 5s - retries: 8 - start_period: 45s -======= - - "traefik.http.routers.gitea.middlewares=error-pages-middleware" + - "traefik.docker.network=core_traefik" + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/healthz >/dev/null"] + interval: 30s + timeout: 5s + retries: 6 + start_period: 120s #volumes: # gitea_data: ->>>>>>> d534f12f7412d036eb157e1463952b9e2b826b2d -networks: - traefik_reverse_proxy: - external: true +#networks: +# traefik_reverse_proxy: +# external: true diff --git a/apps/gramps/docker-compose.yml b/apps/gramps/docker-compose.yml new file mode 100644 index 0000000..80f01af --- /dev/null +++ b/apps/gramps/docker-compose.yml @@ -0,0 +1,71 @@ +services: + gramps-db: + profiles: ["apps","all","gramps"] + image: postgres:13 + container_name: gramps-db + restart: always + environment: + POSTGRES_USER: gramps + POSTGRES_PASSWORD: grampspassword + POSTGRES_DB: gramps + volumes: + - ${PROJECT_ROOT}/apps/gramps/db:/var/lib/postgresql + networks: + - gramps + healthcheck: + test: ["CMD-SHELL", "pg_isready -h db -p 5432 -U gramps -d gramps"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 30s + + + grampsweb: + profiles: ["apps","all","gramps"] + image: ghcr.io/gramps-project/grampsweb:latest + container_name: gramps-web + depends_on: + - gramps-db + restart: always +# ports: +# - "5000:5000" # access via http://localhost:5000 + environment: + DB_URI: postgresql://gramps:grampspassword@db:5432/gramps + GRAMPSWEB_LOGLEVEL: INFO + # default admin user created on first run: + INITIAL_ADMIN: admin + INITIAL_ADMIN_PASSWORD: admin + # optional: storage paths inside container + GRAMPSWEB_MEDIAPATH: /app/media + GRAMPSWEB_TREE: "main" + volumes: + - ${PROJECT_ROOT}/apps/gramps/data/users:/app/users + - ${PROJECT_ROOT}/apps/gramps/data/media:/app/media + - ${PROJECT_ROOT}/apps/gramps/data/cache:/app/cache + labels: + - "traefik.http.routers.gramps.rule=Host(`familytree.lan.ddnsgeek.com`)" + - "traefik.enable=true" + - "traefik.http.routers.gramps.entrypoints=websecure" + - "traefik.http.routers.gramps.tls.certresolver=myresolver" + - "io.portainer.accesscontrol.public" + - "traefik.http.services.gramps.loadbalancer.server.port=5000" + - "traefik.docker.network=core_traefik" + networks: + - traefik + - gramps + healthcheck: + test: + - CMD-SHELL + - wget -qO- http://127.0.0.1:5000/ >/dev/null + interval: 30s + timeout: 5s + retries: 6 + start_period: 60s + + + +networks: +# traefik_reverse_proxy: +# external: true + gramps: +# driver: bridge diff --git a/nextcloud/Dockerfile b/apps/nextcloud/Dockerfile similarity index 55% rename from nextcloud/Dockerfile rename to apps/nextcloud/Dockerfile index a766a9c..d53ae29 100644 --- a/nextcloud/Dockerfile +++ b/apps/nextcloud/Dockerfile @@ -1,4 +1,4 @@ -FROM nextcloud:latest +FROM nextcloud:production #RUN groupadd -r doods && useradd -m -s /bin/bash -d /opt/doods -g doods doods #RUN chsh -s /usr/sbin/nologin root @@ -9,4 +9,11 @@ FROM nextcloud:latest #ENV HOME /opt/doods -USER www-data +#USER root + +RUN apt-get update && \ + apt-get install -y ffmpeg && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +#USER www-data diff --git a/apps/nextcloud/docker-compose.yml b/apps/nextcloud/docker-compose.yml new file mode 100644 index 0000000..cd5b94f --- /dev/null +++ b/apps/nextcloud/docker-compose.yml @@ -0,0 +1,130 @@ +services: + nextcloud-webapp: +# image: nextcloud:production + profiles: ["apps","all","nextcloud"] + build: + context: ${PROJECT_ROOT}/apps/nextcloud + container_name: nextcloud-webapp + restart: always + hostname: nextcloud.lan.ddnsgeek.com + volumes: + - ${PROJECT_ROOT}/apps/nextcloud/data:/var/www/html/data:rw + - ${PROJECT_ROOT}/apps/nextcloud/config:/var/www/html/config:rw + - type: tmpfs + target: /tmp:exec + depends_on: + - nextcloud-db + - nextcloud-redis + environment: + - MYSQL_PASSWORD=R1m@dmin + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_HOST=nextcloud_db:3306 + - NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.lan.ddnsgeek.com + - OVERWRITEPROTOCOL=https + - OVERWRITECLIURL=https://nextcloud.lan.ddnsgeek.com + + - SMTP_HOST=smtp.gmail.com + - SMTP_SECURE=tls + - SMTP_PORT=587 + - SMTP_AUTHTYPE=login + - MAIL_FROM_ADDRESS=beatz174 + - MAIL_DOMAIN=gmail.com + - SMTP_NAME=beatz174@gmail.com + - SMTP_PASSWORD=kqdw fvml wlag ldgv + + - REDIS_HOST=redis + - REDIS_HOST_PORT=6379 + - REDIS_HOST_PASSWORD=TzBF8wcJNmVd9p2CTmBejPS9dpye6kWQeH3DmrQS9TPfTRriSHFN5VqH4CgzcuVZYWH2GBb7QU5GuEpNDGYdKjM6hjmLyjSgCFMiPms3Hv9n + networks: + - traefik + - nextcloud + labels: + - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.lan.ddnsgeek.com`)" + - "traefik.enable=true" + - "traefik.http.routers.nextcloud.entrypoints=websecure" + - "traefik.http.routers.nextcloud.tls.certresolver=myresolver" + - "io.portainer.accesscontrol.public" + - "traefik.http.routers.nextcloud.middlewares=nextcloud-dav, nextcloud-webfinger" + - "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav" + - "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/" + - "traefik.http.middlewares.nextcloud-nodeinfo.replacepathregex.regex=^/.well-known/nodeinfo" + - "traefik.http.middlewares.nextcloud-nodeinfo.replacepathregex.replacement=/nextcloud/index.php/.well-known/nodeinfo/" + - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.permanent=true" + - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.regex=https://(.*)/.well-known/webfinger" + - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.replacement=https://$${1}/nextcloud/index.php/.well-known/webfinger" + - "traefik.docker.network=core_traefik" + + healthcheck: + test: + - CMD-SHELL + - >- + php -r '$$f=@fsockopen("127.0.0.1",80,$$e,$$s,2); if(!$$f) exit(1); + fwrite($$f,"GET /status.php HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n"); + $$o=""; while(!feof($$f)){$$o.=fgets($$f,1024);} fclose($$f); + if(strpos($$o,"\"installed\":true")===false) exit(1);' + interval: 30s + timeout: 5s + retries: 6 + start_period: 180s + + + + + nextcloud-db: + image: mariadb:11.4 + restart: always + profiles: ["apps","all","nextcloud"] + container_name: nextcloud-db + hostname: nextcloud_db + command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW + volumes: + - ${PROJECT_ROOT}/apps/nextcloud/database:/var/lib/mysql:rw + environment: + - MYSQL_ROOT_PASSWORD=R1m@dmin + - MYSQL_PASSWORD=R1m@dmin + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MARIADB_AUTO_UPGRADE=1 + - NEXTCLOUD_ADMIN_USER=admin + - NEXTCLOUD_ADMIN_PASSWORD=R1m@dmin + networks: + - nextcloud + labels: + - "io.portainer.accesscontrol.public" + healthcheck: + test: ["CMD-SHELL", "mariadb-admin ping -u nextcloud --password=R1m@dmin --silent"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 60s + + + nextcloud-redis: + image: "redis" + profiles: ["apps","all","nextcloud"] + command: ["redis-server", "--requirepass", "TzBF8wcJNmVd9p2CTmBejPS9dpye6kWQeH3DmrQS9TPfTRriSHFN5VqH4CgzcuVZYWH2GBb7QU5GuEpNDGYdKjM6hjmLyjSgCFMiPms3Hv9n", "--appendonly", "yes", "--save", "60", "1000"] + hostname: redis + container_name: nextcloud-redis + environment: + - REDIS_HOST_PASSWORD=TzBF8wcJNmVd9p2CTmBejPS9dpye6kWQeH3DmrQS9TPfTRriSHFN5VqH4CgzcuVZYWH2GBb7QU5GuEpNDGYdKjM6hjmLyjSgCFMiPms3Hv9n + volumes: + - ${PROJECT_ROOT}/apps/nextcloud/data/redis:/data:rw + restart: always + networks: + - nextcloud + labels: + - "io.portainer.accesscontrol.public" + healthcheck: + test: ["CMD-SHELL", "redis-cli -a TzBF8wcJNmVd9p2CTmBejPS9dpye6kWQeH3DmrQS9TPfTRriSHFN5VqH4CgzcuVZYWH2GBb7QU5GuEpNDGYdKjM6hjmLyjSgCFMiPms3Hv9n PING | grep -q PONG"] + interval: 10s + timeout: 5s + retries: 6 + start_period: 10s + + +networks: +# traefik_reverse_proxy: +# external: true + nextcloud: +# driver: bridge diff --git a/passbolt/Dockerfile b/apps/passbolt/Dockerfile similarity index 100% rename from passbolt/Dockerfile rename to apps/passbolt/Dockerfile diff --git a/passbolt/docker-compose.yml b/apps/passbolt/docker-compose.yml similarity index 56% rename from passbolt/docker-compose.yml rename to apps/passbolt/docker-compose.yml index af0a5c6..f7c0e7a 100644 --- a/passbolt/docker-compose.yml +++ b/apps/passbolt/docker-compose.yml @@ -1,73 +1,79 @@ services: - db: + passbolt-db: + profiles: ["apps","all","passbolt"] + container_name: passbolt-db image: mariadb:12 - restart: unless-stopped + restart: always environment: MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "passbolt" MYSQL_USER: "passbolt" MYSQL_PASSWORD: "P4ssb0lt" volumes: - - ./data/database:/var/lib/mysql + - ${PROJECT_ROOT}/apps/passbolt/data/database:/var/lib/mysql networks: - - internal + - passbolt healthcheck: test: ["CMD-SHELL", "mariadb-admin ping -h 127.0.0.1 -u\"$$MARIADB_USER\" -p\"$$MARIADB_PASSWORD\" --silent"] interval: 10s timeout: 5s retries: 12 start_period: 60s + labels: + - "io.portainer.accesscontrol.public" - webapp: + passbolt-webapp: image: passbolt/passbolt:latest-ce + profiles: ["apps","all","passbolt"] + container_name: passbolt-webapp #Alternatively you can use rootless: - restart: unless-stopped + restart: always depends_on: - - db + - passbolt-db environment: APP_FULL_BASE_URL: https://passbolt.lan.ddnsgeek.com - DATASOURCES_DEFAULT_HOST: "db" + DATASOURCES_DEFAULT_HOST: "passbolt-db" DATASOURCES_DEFAULT_USERNAME: "passbolt" DATASOURCES_DEFAULT_PASSWORD: "P4ssb0lt" DATASOURCES_DEFAULT_DATABASE: "passbolt" + PASSBOLT_GPG_SERVER_KEY_FINGERPRINT: "CBBB2B8F3E9FACA114537ACB8965B750F7363586" volumes: - - ./data/gpg:/etc/passbolt/gpg - - ./data/jwt:/etc/passbolt/jwt + - ${PROJECT_ROOT}/apps/passbolt/data/gpg:/etc/passbolt/gpg + - ${PROJECT_ROOT}/apps/passbolt/data/jwt:/etc/passbolt/jwt command: [ "/usr/bin/wait-for.sh", "-t", "0", - "db:3306", + "passbolt-db:3306", "--", "/docker-entrypoint.sh", ] networks: - - traefik_reverse_proxy - - internal + - traefik + - passbolt labels: - "traefik.http.routers.passbolt.rule=Host(`passbolt.lan.ddnsgeek.com`)" - "traefik.enable=true" - "traefik.http.routers.passbolt.entrypoints=websecure" - "traefik.http.routers.passbolt.tls.certresolver=myresolver" - "io.portainer.accesscontrol.public" - - "traefik.http.routers.passbolt.middlewares=error-pages-middleware" - - "traefik.docker.network=traefik_reverse_proxy" + - "traefik.docker.network=core_traefik" -# healthcheck: -# test: > -# CMD-SHELL + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost/healthcheck/status | grep -qx OK"] # su -s /bin/sh -c "/usr/share/php/passbolt/bin/cake passbolt healthcheck" www-data # | grep -q "No error found" -# interval: 30s -# timeout: 10s -# retries: 6 -# start_period: 120s + interval: 30s + timeout: 10s + retries: 6 + start_period: 120s networks: - traefik_reverse_proxy: - external: true - internal: - driver: bridge +# traefik_reverse_proxy: +# external: true +# internal: +# driver: bridge + passbolt: diff --git a/searxng/Dockerfile b/apps/searxng/Dockerfile similarity index 100% rename from searxng/Dockerfile rename to apps/searxng/Dockerfile diff --git a/searxng/docker-compose.yml b/apps/searxng/docker-compose.yml similarity index 62% rename from searxng/docker-compose.yml rename to apps/searxng/docker-compose.yml index 479b60f..34a2e5e 100644 --- a/searxng/docker-compose.yml +++ b/apps/searxng/docker-compose.yml @@ -1,6 +1,8 @@ services: - webapp: + searxng-webapp: image: searxng/searxng + profiles: ["apps","all","searxng"] + container_name: searxng-webapp restart: always read_only: true tmpfs: @@ -9,30 +11,22 @@ services: - /run hostname: searxng.lan.ddnsgeek.com networks: - - traefik_reverse_proxy - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 + - traefik labels: - "traefik.http.routers.searxng.rule=Host(`searxng.lan.ddnsgeek.com`)" - "traefik.enable=true" - "traefik.http.routers.searxng.entrypoints=websecure" - "traefik.http.routers.searxng.tls.certresolver=myresolver" - "io.portainer.accesscontrol.public" - - "traefik.http.routers.searxng.middlewares=error-pages-middleware" +# - "traefik.http.routers.searxng.middlewares=crowdsec@file,secHeaders@file,error-pages-middleware" + - "traefik.http.services.searxng.loadbalancer.server.port=8080" healthcheck: -# test: > -# CMD-SHELL -# python3 -c "import urllib.request,sys; -# u=urllib.request.urlopen('http://127.0.0.1:8080/', timeout=2); -# sys.exit(0 if u.status==200 else 1)" test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; r=urllib.request.urlopen('http://127.0.0.1:8080/', timeout=3); sys.exit(0 if 200<=r.status<400 else 1)\""] interval: 20s timeout: 5s retries: 8 start_period: 30s -networks: - traefik_reverse_proxy: - external: true +#networks: +# traefik_reverse_proxy: +# external: true diff --git a/searxng/dockerfiles/docker-entrypoint.sh b/apps/searxng/dockerfiles/docker-entrypoint.sh similarity index 100% rename from searxng/dockerfiles/docker-entrypoint.sh rename to apps/searxng/dockerfiles/docker-entrypoint.sh diff --git a/searxng/docs/conf.py b/apps/searxng/docs/conf.py similarity index 100% rename from searxng/docs/conf.py rename to apps/searxng/docs/conf.py diff --git a/searxng/docs/user/.gitignore b/apps/searxng/docs/user/.gitignore similarity index 100% rename from searxng/docs/user/.gitignore rename to apps/searxng/docs/user/.gitignore diff --git a/searxng/examples/basic_engine.py b/apps/searxng/examples/basic_engine.py similarity index 100% rename from searxng/examples/basic_engine.py rename to apps/searxng/examples/basic_engine.py diff --git a/searxng/searx/__init__.py b/apps/searxng/searx/__init__.py similarity index 100% rename from searxng/searx/__init__.py rename to apps/searxng/searx/__init__.py diff --git a/searxng/searx/answerers/__init__.py b/apps/searxng/searx/answerers/__init__.py similarity index 100% rename from searxng/searx/answerers/__init__.py rename to apps/searxng/searx/answerers/__init__.py diff --git a/searxng/searx/answerers/random/answerer.py b/apps/searxng/searx/answerers/random/answerer.py similarity index 100% rename from searxng/searx/answerers/random/answerer.py rename to apps/searxng/searx/answerers/random/answerer.py diff --git a/searxng/searx/answerers/statistics/answerer.py b/apps/searxng/searx/answerers/statistics/answerer.py similarity index 100% rename from searxng/searx/answerers/statistics/answerer.py rename to apps/searxng/searx/answerers/statistics/answerer.py diff --git a/searxng/searx/autocomplete.py b/apps/searxng/searx/autocomplete.py similarity index 100% rename from searxng/searx/autocomplete.py rename to apps/searxng/searx/autocomplete.py diff --git a/searxng/searx/babel_extract.py b/apps/searxng/searx/babel_extract.py similarity index 100% rename from searxng/searx/babel_extract.py rename to apps/searxng/searx/babel_extract.py diff --git a/searxng/searx/botdetection/__init__.py b/apps/searxng/searx/botdetection/__init__.py similarity index 100% rename from searxng/searx/botdetection/__init__.py rename to apps/searxng/searx/botdetection/__init__.py diff --git a/searxng/searx/botdetection/_helpers.py b/apps/searxng/searx/botdetection/_helpers.py similarity index 100% rename from searxng/searx/botdetection/_helpers.py rename to apps/searxng/searx/botdetection/_helpers.py diff --git a/searxng/searx/botdetection/http_accept.py b/apps/searxng/searx/botdetection/http_accept.py similarity index 100% rename from searxng/searx/botdetection/http_accept.py rename to apps/searxng/searx/botdetection/http_accept.py diff --git a/searxng/searx/botdetection/http_accept_encoding.py b/apps/searxng/searx/botdetection/http_accept_encoding.py similarity index 100% rename from searxng/searx/botdetection/http_accept_encoding.py rename to apps/searxng/searx/botdetection/http_accept_encoding.py diff --git a/searxng/searx/botdetection/http_accept_language.py b/apps/searxng/searx/botdetection/http_accept_language.py similarity index 100% rename from searxng/searx/botdetection/http_accept_language.py rename to apps/searxng/searx/botdetection/http_accept_language.py diff --git a/searxng/searx/botdetection/http_connection.py b/apps/searxng/searx/botdetection/http_connection.py similarity index 100% rename from searxng/searx/botdetection/http_connection.py rename to apps/searxng/searx/botdetection/http_connection.py diff --git a/searxng/searx/botdetection/http_user_agent.py b/apps/searxng/searx/botdetection/http_user_agent.py similarity index 100% rename from searxng/searx/botdetection/http_user_agent.py rename to apps/searxng/searx/botdetection/http_user_agent.py diff --git a/searxng/searx/botdetection/ip_limit.py b/apps/searxng/searx/botdetection/ip_limit.py similarity index 100% rename from searxng/searx/botdetection/ip_limit.py rename to apps/searxng/searx/botdetection/ip_limit.py diff --git a/searxng/searx/botdetection/ip_lists.py b/apps/searxng/searx/botdetection/ip_lists.py similarity index 100% rename from searxng/searx/botdetection/ip_lists.py rename to apps/searxng/searx/botdetection/ip_lists.py diff --git a/searxng/searx/botdetection/limiter.py b/apps/searxng/searx/botdetection/limiter.py similarity index 100% rename from searxng/searx/botdetection/limiter.py rename to apps/searxng/searx/botdetection/limiter.py diff --git a/searxng/searx/botdetection/link_token.py b/apps/searxng/searx/botdetection/link_token.py similarity index 100% rename from searxng/searx/botdetection/link_token.py rename to apps/searxng/searx/botdetection/link_token.py diff --git a/searxng/searx/compat.py b/apps/searxng/searx/compat.py similarity index 100% rename from searxng/searx/compat.py rename to apps/searxng/searx/compat.py diff --git a/searxng/searx/data/__init__.py b/apps/searxng/searx/data/__init__.py similarity index 100% rename from searxng/searx/data/__init__.py rename to apps/searxng/searx/data/__init__.py diff --git a/searxng/searx/enginelib/__init__.py b/apps/searxng/searx/enginelib/__init__.py similarity index 100% rename from searxng/searx/enginelib/__init__.py rename to apps/searxng/searx/enginelib/__init__.py diff --git a/searxng/searx/enginelib/traits.py b/apps/searxng/searx/enginelib/traits.py similarity index 100% rename from searxng/searx/enginelib/traits.py rename to apps/searxng/searx/enginelib/traits.py diff --git a/searxng/searx/engines/1337x.py b/apps/searxng/searx/engines/1337x.py similarity index 100% rename from searxng/searx/engines/1337x.py rename to apps/searxng/searx/engines/1337x.py diff --git a/searxng/searx/engines/9gag.py b/apps/searxng/searx/engines/9gag.py similarity index 100% rename from searxng/searx/engines/9gag.py rename to apps/searxng/searx/engines/9gag.py diff --git a/searxng/searx/engines/__init__.py b/apps/searxng/searx/engines/__init__.py similarity index 100% rename from searxng/searx/engines/__init__.py rename to apps/searxng/searx/engines/__init__.py diff --git a/searxng/searx/engines/ahmia.py b/apps/searxng/searx/engines/ahmia.py similarity index 100% rename from searxng/searx/engines/ahmia.py rename to apps/searxng/searx/engines/ahmia.py diff --git a/searxng/searx/engines/annas_archive.py b/apps/searxng/searx/engines/annas_archive.py similarity index 100% rename from searxng/searx/engines/annas_archive.py rename to apps/searxng/searx/engines/annas_archive.py diff --git a/searxng/searx/engines/apkmirror.py b/apps/searxng/searx/engines/apkmirror.py similarity index 100% rename from searxng/searx/engines/apkmirror.py rename to apps/searxng/searx/engines/apkmirror.py diff --git a/searxng/searx/engines/apple_app_store.py b/apps/searxng/searx/engines/apple_app_store.py similarity index 100% rename from searxng/searx/engines/apple_app_store.py rename to apps/searxng/searx/engines/apple_app_store.py diff --git a/searxng/searx/engines/apple_maps.py b/apps/searxng/searx/engines/apple_maps.py similarity index 100% rename from searxng/searx/engines/apple_maps.py rename to apps/searxng/searx/engines/apple_maps.py diff --git a/searxng/searx/engines/archlinux.py b/apps/searxng/searx/engines/archlinux.py similarity index 100% rename from searxng/searx/engines/archlinux.py rename to apps/searxng/searx/engines/archlinux.py diff --git a/searxng/searx/engines/artic.py b/apps/searxng/searx/engines/artic.py similarity index 100% rename from searxng/searx/engines/artic.py rename to apps/searxng/searx/engines/artic.py diff --git a/searxng/searx/engines/arxiv.py b/apps/searxng/searx/engines/arxiv.py similarity index 100% rename from searxng/searx/engines/arxiv.py rename to apps/searxng/searx/engines/arxiv.py diff --git a/searxng/searx/engines/bandcamp.py b/apps/searxng/searx/engines/bandcamp.py similarity index 100% rename from searxng/searx/engines/bandcamp.py rename to apps/searxng/searx/engines/bandcamp.py diff --git a/searxng/searx/engines/base.py b/apps/searxng/searx/engines/base.py similarity index 100% rename from searxng/searx/engines/base.py rename to apps/searxng/searx/engines/base.py diff --git a/searxng/searx/engines/bing.py b/apps/searxng/searx/engines/bing.py similarity index 100% rename from searxng/searx/engines/bing.py rename to apps/searxng/searx/engines/bing.py diff --git a/searxng/searx/engines/bing_images.py b/apps/searxng/searx/engines/bing_images.py similarity index 100% rename from searxng/searx/engines/bing_images.py rename to apps/searxng/searx/engines/bing_images.py diff --git a/searxng/searx/engines/bing_news.py b/apps/searxng/searx/engines/bing_news.py similarity index 100% rename from searxng/searx/engines/bing_news.py rename to apps/searxng/searx/engines/bing_news.py diff --git a/searxng/searx/engines/bing_videos.py b/apps/searxng/searx/engines/bing_videos.py similarity index 100% rename from searxng/searx/engines/bing_videos.py rename to apps/searxng/searx/engines/bing_videos.py diff --git a/searxng/searx/engines/brave.py b/apps/searxng/searx/engines/brave.py similarity index 100% rename from searxng/searx/engines/brave.py rename to apps/searxng/searx/engines/brave.py diff --git a/searxng/searx/engines/bt4g.py b/apps/searxng/searx/engines/bt4g.py similarity index 100% rename from searxng/searx/engines/bt4g.py rename to apps/searxng/searx/engines/bt4g.py diff --git a/searxng/searx/engines/btdigg.py b/apps/searxng/searx/engines/btdigg.py similarity index 100% rename from searxng/searx/engines/btdigg.py rename to apps/searxng/searx/engines/btdigg.py diff --git a/searxng/searx/engines/command.py b/apps/searxng/searx/engines/command.py similarity index 100% rename from searxng/searx/engines/command.py rename to apps/searxng/searx/engines/command.py diff --git a/searxng/searx/engines/core.py b/apps/searxng/searx/engines/core.py similarity index 100% rename from searxng/searx/engines/core.py rename to apps/searxng/searx/engines/core.py diff --git a/searxng/searx/engines/crossref.py b/apps/searxng/searx/engines/crossref.py similarity index 100% rename from searxng/searx/engines/crossref.py rename to apps/searxng/searx/engines/crossref.py diff --git a/searxng/searx/engines/currency_convert.py b/apps/searxng/searx/engines/currency_convert.py similarity index 100% rename from searxng/searx/engines/currency_convert.py rename to apps/searxng/searx/engines/currency_convert.py diff --git a/searxng/searx/engines/dailymotion.py b/apps/searxng/searx/engines/dailymotion.py similarity index 100% rename from searxng/searx/engines/dailymotion.py rename to apps/searxng/searx/engines/dailymotion.py diff --git a/searxng/searx/engines/deepl.py b/apps/searxng/searx/engines/deepl.py similarity index 100% rename from searxng/searx/engines/deepl.py rename to apps/searxng/searx/engines/deepl.py diff --git a/searxng/searx/engines/deezer.py b/apps/searxng/searx/engines/deezer.py similarity index 100% rename from searxng/searx/engines/deezer.py rename to apps/searxng/searx/engines/deezer.py diff --git a/searxng/searx/engines/demo_offline.py b/apps/searxng/searx/engines/demo_offline.py similarity index 100% rename from searxng/searx/engines/demo_offline.py rename to apps/searxng/searx/engines/demo_offline.py diff --git a/searxng/searx/engines/demo_online.py b/apps/searxng/searx/engines/demo_online.py similarity index 100% rename from searxng/searx/engines/demo_online.py rename to apps/searxng/searx/engines/demo_online.py diff --git a/searxng/searx/engines/deviantart.py b/apps/searxng/searx/engines/deviantart.py similarity index 100% rename from searxng/searx/engines/deviantart.py rename to apps/searxng/searx/engines/deviantart.py diff --git a/searxng/searx/engines/dictzone.py b/apps/searxng/searx/engines/dictzone.py similarity index 100% rename from searxng/searx/engines/dictzone.py rename to apps/searxng/searx/engines/dictzone.py diff --git a/searxng/searx/engines/digbt.py b/apps/searxng/searx/engines/digbt.py similarity index 100% rename from searxng/searx/engines/digbt.py rename to apps/searxng/searx/engines/digbt.py diff --git a/searxng/searx/engines/docker_hub.py b/apps/searxng/searx/engines/docker_hub.py similarity index 100% rename from searxng/searx/engines/docker_hub.py rename to apps/searxng/searx/engines/docker_hub.py diff --git a/searxng/searx/engines/doku.py b/apps/searxng/searx/engines/doku.py similarity index 100% rename from searxng/searx/engines/doku.py rename to apps/searxng/searx/engines/doku.py diff --git a/searxng/searx/engines/duckduckgo.py b/apps/searxng/searx/engines/duckduckgo.py similarity index 100% rename from searxng/searx/engines/duckduckgo.py rename to apps/searxng/searx/engines/duckduckgo.py diff --git a/searxng/searx/engines/duckduckgo_definitions.py b/apps/searxng/searx/engines/duckduckgo_definitions.py similarity index 100% rename from searxng/searx/engines/duckduckgo_definitions.py rename to apps/searxng/searx/engines/duckduckgo_definitions.py diff --git a/searxng/searx/engines/duckduckgo_images.py b/apps/searxng/searx/engines/duckduckgo_images.py similarity index 100% rename from searxng/searx/engines/duckduckgo_images.py rename to apps/searxng/searx/engines/duckduckgo_images.py diff --git a/searxng/searx/engines/duckduckgo_weather.py b/apps/searxng/searx/engines/duckduckgo_weather.py similarity index 100% rename from searxng/searx/engines/duckduckgo_weather.py rename to apps/searxng/searx/engines/duckduckgo_weather.py diff --git a/searxng/searx/engines/duden.py b/apps/searxng/searx/engines/duden.py similarity index 100% rename from searxng/searx/engines/duden.py rename to apps/searxng/searx/engines/duden.py diff --git a/searxng/searx/engines/dummy-offline.py b/apps/searxng/searx/engines/dummy-offline.py similarity index 100% rename from searxng/searx/engines/dummy-offline.py rename to apps/searxng/searx/engines/dummy-offline.py diff --git a/searxng/searx/engines/dummy.py b/apps/searxng/searx/engines/dummy.py similarity index 100% rename from searxng/searx/engines/dummy.py rename to apps/searxng/searx/engines/dummy.py diff --git a/searxng/searx/engines/ebay.py b/apps/searxng/searx/engines/ebay.py similarity index 100% rename from searxng/searx/engines/ebay.py rename to apps/searxng/searx/engines/ebay.py diff --git a/searxng/searx/engines/elasticsearch.py b/apps/searxng/searx/engines/elasticsearch.py similarity index 100% rename from searxng/searx/engines/elasticsearch.py rename to apps/searxng/searx/engines/elasticsearch.py diff --git a/searxng/searx/engines/emojipedia.py b/apps/searxng/searx/engines/emojipedia.py similarity index 100% rename from searxng/searx/engines/emojipedia.py rename to apps/searxng/searx/engines/emojipedia.py diff --git a/searxng/searx/engines/fdroid.py b/apps/searxng/searx/engines/fdroid.py similarity index 100% rename from searxng/searx/engines/fdroid.py rename to apps/searxng/searx/engines/fdroid.py diff --git a/searxng/searx/engines/flickr.py b/apps/searxng/searx/engines/flickr.py similarity index 100% rename from searxng/searx/engines/flickr.py rename to apps/searxng/searx/engines/flickr.py diff --git a/searxng/searx/engines/flickr_noapi.py b/apps/searxng/searx/engines/flickr_noapi.py similarity index 100% rename from searxng/searx/engines/flickr_noapi.py rename to apps/searxng/searx/engines/flickr_noapi.py diff --git a/searxng/searx/engines/framalibre.py b/apps/searxng/searx/engines/framalibre.py similarity index 100% rename from searxng/searx/engines/framalibre.py rename to apps/searxng/searx/engines/framalibre.py diff --git a/searxng/searx/engines/freesound.py b/apps/searxng/searx/engines/freesound.py similarity index 100% rename from searxng/searx/engines/freesound.py rename to apps/searxng/searx/engines/freesound.py diff --git a/searxng/searx/engines/frinkiac.py b/apps/searxng/searx/engines/frinkiac.py similarity index 100% rename from searxng/searx/engines/frinkiac.py rename to apps/searxng/searx/engines/frinkiac.py diff --git a/searxng/searx/engines/genius.py b/apps/searxng/searx/engines/genius.py similarity index 100% rename from searxng/searx/engines/genius.py rename to apps/searxng/searx/engines/genius.py diff --git a/searxng/searx/engines/gentoo.py b/apps/searxng/searx/engines/gentoo.py similarity index 100% rename from searxng/searx/engines/gentoo.py rename to apps/searxng/searx/engines/gentoo.py diff --git a/searxng/searx/engines/github.py b/apps/searxng/searx/engines/github.py similarity index 100% rename from searxng/searx/engines/github.py rename to apps/searxng/searx/engines/github.py diff --git a/searxng/searx/engines/google.py b/apps/searxng/searx/engines/google.py similarity index 100% rename from searxng/searx/engines/google.py rename to apps/searxng/searx/engines/google.py diff --git a/searxng/searx/engines/google_images.py b/apps/searxng/searx/engines/google_images.py similarity index 100% rename from searxng/searx/engines/google_images.py rename to apps/searxng/searx/engines/google_images.py diff --git a/searxng/searx/engines/google_news.py b/apps/searxng/searx/engines/google_news.py similarity index 100% rename from searxng/searx/engines/google_news.py rename to apps/searxng/searx/engines/google_news.py diff --git a/searxng/searx/engines/google_play.py b/apps/searxng/searx/engines/google_play.py similarity index 100% rename from searxng/searx/engines/google_play.py rename to apps/searxng/searx/engines/google_play.py diff --git a/searxng/searx/engines/google_scholar.py b/apps/searxng/searx/engines/google_scholar.py similarity index 100% rename from searxng/searx/engines/google_scholar.py rename to apps/searxng/searx/engines/google_scholar.py diff --git a/searxng/searx/engines/google_videos.py b/apps/searxng/searx/engines/google_videos.py similarity index 100% rename from searxng/searx/engines/google_videos.py rename to apps/searxng/searx/engines/google_videos.py diff --git a/searxng/searx/engines/imdb.py b/apps/searxng/searx/engines/imdb.py similarity index 100% rename from searxng/searx/engines/imdb.py rename to apps/searxng/searx/engines/imdb.py diff --git a/searxng/searx/engines/ina.py b/apps/searxng/searx/engines/ina.py similarity index 100% rename from searxng/searx/engines/ina.py rename to apps/searxng/searx/engines/ina.py diff --git a/searxng/searx/engines/invidious.py b/apps/searxng/searx/engines/invidious.py similarity index 100% rename from searxng/searx/engines/invidious.py rename to apps/searxng/searx/engines/invidious.py diff --git a/searxng/searx/engines/jisho.py b/apps/searxng/searx/engines/jisho.py similarity index 100% rename from searxng/searx/engines/jisho.py rename to apps/searxng/searx/engines/jisho.py diff --git a/searxng/searx/engines/json_engine.py b/apps/searxng/searx/engines/json_engine.py similarity index 100% rename from searxng/searx/engines/json_engine.py rename to apps/searxng/searx/engines/json_engine.py diff --git a/searxng/searx/engines/kickass.py b/apps/searxng/searx/engines/kickass.py similarity index 100% rename from searxng/searx/engines/kickass.py rename to apps/searxng/searx/engines/kickass.py diff --git a/searxng/searx/engines/lemmy.py b/apps/searxng/searx/engines/lemmy.py similarity index 100% rename from searxng/searx/engines/lemmy.py rename to apps/searxng/searx/engines/lemmy.py diff --git a/searxng/searx/engines/lingva.py b/apps/searxng/searx/engines/lingva.py similarity index 100% rename from searxng/searx/engines/lingva.py rename to apps/searxng/searx/engines/lingva.py diff --git a/searxng/searx/engines/loc.py b/apps/searxng/searx/engines/loc.py similarity index 100% rename from searxng/searx/engines/loc.py rename to apps/searxng/searx/engines/loc.py diff --git a/searxng/searx/engines/mediathekviewweb.py b/apps/searxng/searx/engines/mediathekviewweb.py similarity index 100% rename from searxng/searx/engines/mediathekviewweb.py rename to apps/searxng/searx/engines/mediathekviewweb.py diff --git a/searxng/searx/engines/mediawiki.py b/apps/searxng/searx/engines/mediawiki.py similarity index 100% rename from searxng/searx/engines/mediawiki.py rename to apps/searxng/searx/engines/mediawiki.py diff --git a/searxng/searx/engines/meilisearch.py b/apps/searxng/searx/engines/meilisearch.py similarity index 100% rename from searxng/searx/engines/meilisearch.py rename to apps/searxng/searx/engines/meilisearch.py diff --git a/searxng/searx/engines/metacpan.py b/apps/searxng/searx/engines/metacpan.py similarity index 100% rename from searxng/searx/engines/metacpan.py rename to apps/searxng/searx/engines/metacpan.py diff --git a/searxng/searx/engines/mixcloud.py b/apps/searxng/searx/engines/mixcloud.py similarity index 100% rename from searxng/searx/engines/mixcloud.py rename to apps/searxng/searx/engines/mixcloud.py diff --git a/searxng/searx/engines/mongodb.py b/apps/searxng/searx/engines/mongodb.py similarity index 100% rename from searxng/searx/engines/mongodb.py rename to apps/searxng/searx/engines/mongodb.py diff --git a/searxng/searx/engines/mysql_server.py b/apps/searxng/searx/engines/mysql_server.py similarity index 100% rename from searxng/searx/engines/mysql_server.py rename to apps/searxng/searx/engines/mysql_server.py diff --git a/searxng/searx/engines/nyaa.py b/apps/searxng/searx/engines/nyaa.py similarity index 100% rename from searxng/searx/engines/nyaa.py rename to apps/searxng/searx/engines/nyaa.py diff --git a/searxng/searx/engines/opensemantic.py b/apps/searxng/searx/engines/opensemantic.py similarity index 100% rename from searxng/searx/engines/opensemantic.py rename to apps/searxng/searx/engines/opensemantic.py diff --git a/searxng/searx/engines/openstreetmap.py b/apps/searxng/searx/engines/openstreetmap.py similarity index 100% rename from searxng/searx/engines/openstreetmap.py rename to apps/searxng/searx/engines/openstreetmap.py diff --git a/searxng/searx/engines/openverse.py b/apps/searxng/searx/engines/openverse.py similarity index 100% rename from searxng/searx/engines/openverse.py rename to apps/searxng/searx/engines/openverse.py diff --git a/searxng/searx/engines/pdbe.py b/apps/searxng/searx/engines/pdbe.py similarity index 100% rename from searxng/searx/engines/pdbe.py rename to apps/searxng/searx/engines/pdbe.py diff --git a/searxng/searx/engines/peertube.py b/apps/searxng/searx/engines/peertube.py similarity index 100% rename from searxng/searx/engines/peertube.py rename to apps/searxng/searx/engines/peertube.py diff --git a/searxng/searx/engines/photon.py b/apps/searxng/searx/engines/photon.py similarity index 100% rename from searxng/searx/engines/photon.py rename to apps/searxng/searx/engines/photon.py diff --git a/searxng/searx/engines/piped.py b/apps/searxng/searx/engines/piped.py similarity index 100% rename from searxng/searx/engines/piped.py rename to apps/searxng/searx/engines/piped.py diff --git a/searxng/searx/engines/piratebay.py b/apps/searxng/searx/engines/piratebay.py similarity index 100% rename from searxng/searx/engines/piratebay.py rename to apps/searxng/searx/engines/piratebay.py diff --git a/searxng/searx/engines/postgresql.py b/apps/searxng/searx/engines/postgresql.py similarity index 100% rename from searxng/searx/engines/postgresql.py rename to apps/searxng/searx/engines/postgresql.py diff --git a/searxng/searx/engines/pubmed.py b/apps/searxng/searx/engines/pubmed.py similarity index 100% rename from searxng/searx/engines/pubmed.py rename to apps/searxng/searx/engines/pubmed.py diff --git a/searxng/searx/engines/qwant.py b/apps/searxng/searx/engines/qwant.py similarity index 100% rename from searxng/searx/engines/qwant.py rename to apps/searxng/searx/engines/qwant.py diff --git a/searxng/searx/engines/recoll.py b/apps/searxng/searx/engines/recoll.py similarity index 100% rename from searxng/searx/engines/recoll.py rename to apps/searxng/searx/engines/recoll.py diff --git a/searxng/searx/engines/reddit.py b/apps/searxng/searx/engines/reddit.py similarity index 100% rename from searxng/searx/engines/reddit.py rename to apps/searxng/searx/engines/reddit.py diff --git a/searxng/searx/engines/redis_server.py b/apps/searxng/searx/engines/redis_server.py similarity index 100% rename from searxng/searx/engines/redis_server.py rename to apps/searxng/searx/engines/redis_server.py diff --git a/searxng/searx/engines/rumble.py b/apps/searxng/searx/engines/rumble.py similarity index 100% rename from searxng/searx/engines/rumble.py rename to apps/searxng/searx/engines/rumble.py diff --git a/searxng/searx/engines/scanr_structures.py b/apps/searxng/searx/engines/scanr_structures.py similarity index 100% rename from searxng/searx/engines/scanr_structures.py rename to apps/searxng/searx/engines/scanr_structures.py diff --git a/searxng/searx/engines/searchcode_code.py b/apps/searxng/searx/engines/searchcode_code.py similarity index 100% rename from searxng/searx/engines/searchcode_code.py rename to apps/searxng/searx/engines/searchcode_code.py diff --git a/searxng/searx/engines/searx_engine.py b/apps/searxng/searx/engines/searx_engine.py similarity index 100% rename from searxng/searx/engines/searx_engine.py rename to apps/searxng/searx/engines/searx_engine.py diff --git a/searxng/searx/engines/semantic_scholar.py b/apps/searxng/searx/engines/semantic_scholar.py similarity index 100% rename from searxng/searx/engines/semantic_scholar.py rename to apps/searxng/searx/engines/semantic_scholar.py diff --git a/searxng/searx/engines/sepiasearch.py b/apps/searxng/searx/engines/sepiasearch.py similarity index 100% rename from searxng/searx/engines/sepiasearch.py rename to apps/searxng/searx/engines/sepiasearch.py diff --git a/searxng/searx/engines/seznam.py b/apps/searxng/searx/engines/seznam.py similarity index 100% rename from searxng/searx/engines/seznam.py rename to apps/searxng/searx/engines/seznam.py diff --git a/searxng/searx/engines/sjp.py b/apps/searxng/searx/engines/sjp.py similarity index 100% rename from searxng/searx/engines/sjp.py rename to apps/searxng/searx/engines/sjp.py diff --git a/searxng/searx/engines/solidtorrents.py b/apps/searxng/searx/engines/solidtorrents.py similarity index 100% rename from searxng/searx/engines/solidtorrents.py rename to apps/searxng/searx/engines/solidtorrents.py diff --git a/searxng/searx/engines/solr.py b/apps/searxng/searx/engines/solr.py similarity index 100% rename from searxng/searx/engines/solr.py rename to apps/searxng/searx/engines/solr.py diff --git a/searxng/searx/engines/soundcloud.py b/apps/searxng/searx/engines/soundcloud.py similarity index 100% rename from searxng/searx/engines/soundcloud.py rename to apps/searxng/searx/engines/soundcloud.py diff --git a/searxng/searx/engines/spotify.py b/apps/searxng/searx/engines/spotify.py similarity index 100% rename from searxng/searx/engines/spotify.py rename to apps/searxng/searx/engines/spotify.py diff --git a/searxng/searx/engines/springer.py b/apps/searxng/searx/engines/springer.py similarity index 100% rename from searxng/searx/engines/springer.py rename to apps/searxng/searx/engines/springer.py diff --git a/searxng/searx/engines/sqlite.py b/apps/searxng/searx/engines/sqlite.py similarity index 100% rename from searxng/searx/engines/sqlite.py rename to apps/searxng/searx/engines/sqlite.py diff --git a/searxng/searx/engines/stackexchange.py b/apps/searxng/searx/engines/stackexchange.py similarity index 100% rename from searxng/searx/engines/stackexchange.py rename to apps/searxng/searx/engines/stackexchange.py diff --git a/searxng/searx/engines/startpage.py b/apps/searxng/searx/engines/startpage.py similarity index 100% rename from searxng/searx/engines/startpage.py rename to apps/searxng/searx/engines/startpage.py diff --git a/searxng/searx/engines/tagesschau.py b/apps/searxng/searx/engines/tagesschau.py similarity index 100% rename from searxng/searx/engines/tagesschau.py rename to apps/searxng/searx/engines/tagesschau.py diff --git a/searxng/searx/engines/tineye.py b/apps/searxng/searx/engines/tineye.py similarity index 100% rename from searxng/searx/engines/tineye.py rename to apps/searxng/searx/engines/tineye.py diff --git a/searxng/searx/engines/tokyotoshokan.py b/apps/searxng/searx/engines/tokyotoshokan.py similarity index 100% rename from searxng/searx/engines/tokyotoshokan.py rename to apps/searxng/searx/engines/tokyotoshokan.py diff --git a/searxng/searx/engines/torznab.py b/apps/searxng/searx/engines/torznab.py similarity index 100% rename from searxng/searx/engines/torznab.py rename to apps/searxng/searx/engines/torznab.py diff --git a/searxng/searx/engines/translated.py b/apps/searxng/searx/engines/translated.py similarity index 100% rename from searxng/searx/engines/translated.py rename to apps/searxng/searx/engines/translated.py diff --git a/searxng/searx/engines/twitter.py b/apps/searxng/searx/engines/twitter.py similarity index 100% rename from searxng/searx/engines/twitter.py rename to apps/searxng/searx/engines/twitter.py diff --git a/searxng/searx/engines/unsplash.py b/apps/searxng/searx/engines/unsplash.py similarity index 100% rename from searxng/searx/engines/unsplash.py rename to apps/searxng/searx/engines/unsplash.py diff --git a/searxng/searx/engines/vimeo.py b/apps/searxng/searx/engines/vimeo.py similarity index 100% rename from searxng/searx/engines/vimeo.py rename to apps/searxng/searx/engines/vimeo.py diff --git a/searxng/searx/engines/wikidata.py b/apps/searxng/searx/engines/wikidata.py similarity index 100% rename from searxng/searx/engines/wikidata.py rename to apps/searxng/searx/engines/wikidata.py diff --git a/searxng/searx/engines/wikipedia.py b/apps/searxng/searx/engines/wikipedia.py similarity index 100% rename from searxng/searx/engines/wikipedia.py rename to apps/searxng/searx/engines/wikipedia.py diff --git a/searxng/searx/engines/wolframalpha_api.py b/apps/searxng/searx/engines/wolframalpha_api.py similarity index 100% rename from searxng/searx/engines/wolframalpha_api.py rename to apps/searxng/searx/engines/wolframalpha_api.py diff --git a/searxng/searx/engines/wolframalpha_noapi.py b/apps/searxng/searx/engines/wolframalpha_noapi.py similarity index 100% rename from searxng/searx/engines/wolframalpha_noapi.py rename to apps/searxng/searx/engines/wolframalpha_noapi.py diff --git a/searxng/searx/engines/wordnik.py b/apps/searxng/searx/engines/wordnik.py similarity index 100% rename from searxng/searx/engines/wordnik.py rename to apps/searxng/searx/engines/wordnik.py diff --git a/searxng/searx/engines/wttr.py b/apps/searxng/searx/engines/wttr.py similarity index 100% rename from searxng/searx/engines/wttr.py rename to apps/searxng/searx/engines/wttr.py diff --git a/searxng/searx/engines/www1x.py b/apps/searxng/searx/engines/www1x.py similarity index 100% rename from searxng/searx/engines/www1x.py rename to apps/searxng/searx/engines/www1x.py diff --git a/searxng/searx/engines/xpath.py b/apps/searxng/searx/engines/xpath.py similarity index 100% rename from searxng/searx/engines/xpath.py rename to apps/searxng/searx/engines/xpath.py diff --git a/searxng/searx/engines/yacy.py b/apps/searxng/searx/engines/yacy.py similarity index 100% rename from searxng/searx/engines/yacy.py rename to apps/searxng/searx/engines/yacy.py diff --git a/searxng/searx/engines/yahoo.py b/apps/searxng/searx/engines/yahoo.py similarity index 100% rename from searxng/searx/engines/yahoo.py rename to apps/searxng/searx/engines/yahoo.py diff --git a/searxng/searx/engines/yahoo_news.py b/apps/searxng/searx/engines/yahoo_news.py similarity index 100% rename from searxng/searx/engines/yahoo_news.py rename to apps/searxng/searx/engines/yahoo_news.py diff --git a/searxng/searx/engines/youtube_api.py b/apps/searxng/searx/engines/youtube_api.py similarity index 100% rename from searxng/searx/engines/youtube_api.py rename to apps/searxng/searx/engines/youtube_api.py diff --git a/searxng/searx/engines/youtube_noapi.py b/apps/searxng/searx/engines/youtube_noapi.py similarity index 100% rename from searxng/searx/engines/youtube_noapi.py rename to apps/searxng/searx/engines/youtube_noapi.py diff --git a/searxng/searx/engines/zlibrary.py b/apps/searxng/searx/engines/zlibrary.py similarity index 100% rename from searxng/searx/engines/zlibrary.py rename to apps/searxng/searx/engines/zlibrary.py diff --git a/searxng/searx/exceptions.py b/apps/searxng/searx/exceptions.py similarity index 100% rename from searxng/searx/exceptions.py rename to apps/searxng/searx/exceptions.py diff --git a/searxng/searx/external_bang.py b/apps/searxng/searx/external_bang.py similarity index 100% rename from searxng/searx/external_bang.py rename to apps/searxng/searx/external_bang.py diff --git a/searxng/searx/external_urls.py b/apps/searxng/searx/external_urls.py similarity index 100% rename from searxng/searx/external_urls.py rename to apps/searxng/searx/external_urls.py diff --git a/searxng/searx/flaskfix.py b/apps/searxng/searx/flaskfix.py similarity index 100% rename from searxng/searx/flaskfix.py rename to apps/searxng/searx/flaskfix.py diff --git a/searxng/searx/infopage/__init__.py b/apps/searxng/searx/infopage/__init__.py similarity index 100% rename from searxng/searx/infopage/__init__.py rename to apps/searxng/searx/infopage/__init__.py diff --git a/searxng/searx/locales.py b/apps/searxng/searx/locales.py similarity index 100% rename from searxng/searx/locales.py rename to apps/searxng/searx/locales.py diff --git a/searxng/searx/metrics/__init__.py b/apps/searxng/searx/metrics/__init__.py similarity index 100% rename from searxng/searx/metrics/__init__.py rename to apps/searxng/searx/metrics/__init__.py diff --git a/searxng/searx/metrics/error_recorder.py b/apps/searxng/searx/metrics/error_recorder.py similarity index 100% rename from searxng/searx/metrics/error_recorder.py rename to apps/searxng/searx/metrics/error_recorder.py diff --git a/searxng/searx/metrics/models.py b/apps/searxng/searx/metrics/models.py similarity index 100% rename from searxng/searx/metrics/models.py rename to apps/searxng/searx/metrics/models.py diff --git a/searxng/searx/network/__init__.py b/apps/searxng/searx/network/__init__.py similarity index 100% rename from searxng/searx/network/__init__.py rename to apps/searxng/searx/network/__init__.py diff --git a/searxng/searx/network/client.py b/apps/searxng/searx/network/client.py similarity index 100% rename from searxng/searx/network/client.py rename to apps/searxng/searx/network/client.py diff --git a/searxng/searx/network/network.py b/apps/searxng/searx/network/network.py similarity index 100% rename from searxng/searx/network/network.py rename to apps/searxng/searx/network/network.py diff --git a/searxng/searx/network/raise_for_httperror.py b/apps/searxng/searx/network/raise_for_httperror.py similarity index 100% rename from searxng/searx/network/raise_for_httperror.py rename to apps/searxng/searx/network/raise_for_httperror.py diff --git a/searxng/searx/plugins/__init__.py b/apps/searxng/searx/plugins/__init__.py similarity index 100% rename from searxng/searx/plugins/__init__.py rename to apps/searxng/searx/plugins/__init__.py diff --git a/searxng/searx/plugins/ahmia_filter.py b/apps/searxng/searx/plugins/ahmia_filter.py similarity index 100% rename from searxng/searx/plugins/ahmia_filter.py rename to apps/searxng/searx/plugins/ahmia_filter.py diff --git a/searxng/searx/plugins/hash_plugin.py b/apps/searxng/searx/plugins/hash_plugin.py similarity index 100% rename from searxng/searx/plugins/hash_plugin.py rename to apps/searxng/searx/plugins/hash_plugin.py diff --git a/searxng/searx/plugins/hostname_replace.py b/apps/searxng/searx/plugins/hostname_replace.py similarity index 100% rename from searxng/searx/plugins/hostname_replace.py rename to apps/searxng/searx/plugins/hostname_replace.py diff --git a/searxng/searx/plugins/limiter.py b/apps/searxng/searx/plugins/limiter.py similarity index 100% rename from searxng/searx/plugins/limiter.py rename to apps/searxng/searx/plugins/limiter.py diff --git a/searxng/searx/plugins/oa_doi_rewrite.py b/apps/searxng/searx/plugins/oa_doi_rewrite.py similarity index 100% rename from searxng/searx/plugins/oa_doi_rewrite.py rename to apps/searxng/searx/plugins/oa_doi_rewrite.py diff --git a/searxng/searx/plugins/search_on_category_select.py b/apps/searxng/searx/plugins/search_on_category_select.py similarity index 100% rename from searxng/searx/plugins/search_on_category_select.py rename to apps/searxng/searx/plugins/search_on_category_select.py diff --git a/searxng/searx/plugins/self_info.py b/apps/searxng/searx/plugins/self_info.py similarity index 100% rename from searxng/searx/plugins/self_info.py rename to apps/searxng/searx/plugins/self_info.py diff --git a/searxng/searx/plugins/tor_check.py b/apps/searxng/searx/plugins/tor_check.py similarity index 100% rename from searxng/searx/plugins/tor_check.py rename to apps/searxng/searx/plugins/tor_check.py diff --git a/searxng/searx/plugins/tracker_url_remover.py b/apps/searxng/searx/plugins/tracker_url_remover.py similarity index 100% rename from searxng/searx/plugins/tracker_url_remover.py rename to apps/searxng/searx/plugins/tracker_url_remover.py diff --git a/searxng/searx/plugins/vim_hotkeys.py b/apps/searxng/searx/plugins/vim_hotkeys.py similarity index 100% rename from searxng/searx/plugins/vim_hotkeys.py rename to apps/searxng/searx/plugins/vim_hotkeys.py diff --git a/searxng/searx/preferences.py b/apps/searxng/searx/preferences.py similarity index 100% rename from searxng/searx/preferences.py rename to apps/searxng/searx/preferences.py diff --git a/searxng/searx/query.py b/apps/searxng/searx/query.py similarity index 100% rename from searxng/searx/query.py rename to apps/searxng/searx/query.py diff --git a/searxng/searx/redisdb.py b/apps/searxng/searx/redisdb.py similarity index 100% rename from searxng/searx/redisdb.py rename to apps/searxng/searx/redisdb.py diff --git a/searxng/searx/redislib.py b/apps/searxng/searx/redislib.py similarity index 100% rename from searxng/searx/redislib.py rename to apps/searxng/searx/redislib.py diff --git a/searxng/searx/results.py b/apps/searxng/searx/results.py similarity index 100% rename from searxng/searx/results.py rename to apps/searxng/searx/results.py diff --git a/searxng/searx/search/__init__.py b/apps/searxng/searx/search/__init__.py similarity index 100% rename from searxng/searx/search/__init__.py rename to apps/searxng/searx/search/__init__.py diff --git a/searxng/searx/search/checker/__init__.py b/apps/searxng/searx/search/checker/__init__.py similarity index 100% rename from searxng/searx/search/checker/__init__.py rename to apps/searxng/searx/search/checker/__init__.py diff --git a/searxng/searx/search/checker/__main__.py b/apps/searxng/searx/search/checker/__main__.py similarity index 100% rename from searxng/searx/search/checker/__main__.py rename to apps/searxng/searx/search/checker/__main__.py diff --git a/searxng/searx/search/checker/background.py b/apps/searxng/searx/search/checker/background.py similarity index 100% rename from searxng/searx/search/checker/background.py rename to apps/searxng/searx/search/checker/background.py diff --git a/searxng/searx/search/checker/impl.py b/apps/searxng/searx/search/checker/impl.py similarity index 100% rename from searxng/searx/search/checker/impl.py rename to apps/searxng/searx/search/checker/impl.py diff --git a/searxng/searx/search/checker/scheduler.py b/apps/searxng/searx/search/checker/scheduler.py similarity index 100% rename from searxng/searx/search/checker/scheduler.py rename to apps/searxng/searx/search/checker/scheduler.py diff --git a/searxng/searx/search/models.py b/apps/searxng/searx/search/models.py similarity index 100% rename from searxng/searx/search/models.py rename to apps/searxng/searx/search/models.py diff --git a/searxng/searx/search/processors/__init__.py b/apps/searxng/searx/search/processors/__init__.py similarity index 100% rename from searxng/searx/search/processors/__init__.py rename to apps/searxng/searx/search/processors/__init__.py diff --git a/searxng/searx/search/processors/abstract.py b/apps/searxng/searx/search/processors/abstract.py similarity index 100% rename from searxng/searx/search/processors/abstract.py rename to apps/searxng/searx/search/processors/abstract.py diff --git a/searxng/searx/search/processors/offline.py b/apps/searxng/searx/search/processors/offline.py similarity index 100% rename from searxng/searx/search/processors/offline.py rename to apps/searxng/searx/search/processors/offline.py diff --git a/searxng/searx/search/processors/online.py b/apps/searxng/searx/search/processors/online.py similarity index 100% rename from searxng/searx/search/processors/online.py rename to apps/searxng/searx/search/processors/online.py diff --git a/searxng/searx/search/processors/online_currency.py b/apps/searxng/searx/search/processors/online_currency.py similarity index 100% rename from searxng/searx/search/processors/online_currency.py rename to apps/searxng/searx/search/processors/online_currency.py diff --git a/searxng/searx/search/processors/online_dictionary.py b/apps/searxng/searx/search/processors/online_dictionary.py similarity index 100% rename from searxng/searx/search/processors/online_dictionary.py rename to apps/searxng/searx/search/processors/online_dictionary.py diff --git a/searxng/searx/search/processors/online_url_search.py b/apps/searxng/searx/search/processors/online_url_search.py similarity index 100% rename from searxng/searx/search/processors/online_url_search.py rename to apps/searxng/searx/search/processors/online_url_search.py diff --git a/searxng/searx/settings.yml b/apps/searxng/searx/settings.yml similarity index 100% rename from searxng/searx/settings.yml rename to apps/searxng/searx/settings.yml diff --git a/searxng/searx/settings_defaults.py b/apps/searxng/searx/settings_defaults.py similarity index 100% rename from searxng/searx/settings_defaults.py rename to apps/searxng/searx/settings_defaults.py diff --git a/searxng/searx/settings_loader.py b/apps/searxng/searx/settings_loader.py similarity index 100% rename from searxng/searx/settings_loader.py rename to apps/searxng/searx/settings_loader.py diff --git a/searxng/searx/static/plugins/external_plugins/.gitignore b/apps/searxng/searx/static/plugins/external_plugins/.gitignore similarity index 100% rename from searxng/searx/static/plugins/external_plugins/.gitignore rename to apps/searxng/searx/static/plugins/external_plugins/.gitignore diff --git a/searxng/searx/static/themes/simple/.gitattributes b/apps/searxng/searx/static/themes/simple/.gitattributes similarity index 100% rename from searxng/searx/static/themes/simple/.gitattributes rename to apps/searxng/searx/static/themes/simple/.gitattributes diff --git a/searxng/searx/static/themes/simple/.gitignore b/apps/searxng/searx/static/themes/simple/.gitignore similarity index 100% rename from searxng/searx/static/themes/simple/.gitignore rename to apps/searxng/searx/static/themes/simple/.gitignore diff --git a/searxng/searx/sxng_locales.py b/apps/searxng/searx/sxng_locales.py similarity index 100% rename from searxng/searx/sxng_locales.py rename to apps/searxng/searx/sxng_locales.py diff --git a/searxng/searx/tools/__init__.py b/apps/searxng/searx/tools/__init__.py similarity index 100% rename from searxng/searx/tools/__init__.py rename to apps/searxng/searx/tools/__init__.py diff --git a/searxng/searx/tools/config.py b/apps/searxng/searx/tools/config.py similarity index 100% rename from searxng/searx/tools/config.py rename to apps/searxng/searx/tools/config.py diff --git a/searxng/searx/unixthreadname.py b/apps/searxng/searx/unixthreadname.py similarity index 100% rename from searxng/searx/unixthreadname.py rename to apps/searxng/searx/unixthreadname.py diff --git a/searxng/searx/utils.py b/apps/searxng/searx/utils.py similarity index 100% rename from searxng/searx/utils.py rename to apps/searxng/searx/utils.py diff --git a/searxng/searx/version.py b/apps/searxng/searx/version.py similarity index 100% rename from searxng/searx/version.py rename to apps/searxng/searx/version.py diff --git a/searxng/searx/webadapter.py b/apps/searxng/searx/webadapter.py similarity index 100% rename from searxng/searx/webadapter.py rename to apps/searxng/searx/webadapter.py diff --git a/searxng/searx/webapp.py b/apps/searxng/searx/webapp.py similarity index 100% rename from searxng/searx/webapp.py rename to apps/searxng/searx/webapp.py diff --git a/searxng/searx/webutils.py b/apps/searxng/searx/webutils.py similarity index 100% rename from searxng/searx/webutils.py rename to apps/searxng/searx/webutils.py diff --git a/searxng/searxng_extra/__init__.py b/apps/searxng/searxng_extra/__init__.py similarity index 100% rename from searxng/searxng_extra/__init__.py rename to apps/searxng/searxng_extra/__init__.py diff --git a/searxng/searxng_extra/standalone_searx.py b/apps/searxng/searxng_extra/standalone_searx.py similarity index 100% rename from searxng/searxng_extra/standalone_searx.py rename to apps/searxng/searxng_extra/standalone_searx.py diff --git a/searxng/searxng_extra/update/__init__.py b/apps/searxng/searxng_extra/update/__init__.py similarity index 100% rename from searxng/searxng_extra/update/__init__.py rename to apps/searxng/searxng_extra/update/__init__.py diff --git a/searxng/searxng_extra/update/update_ahmia_blacklist.py b/apps/searxng/searxng_extra/update/update_ahmia_blacklist.py similarity index 100% rename from searxng/searxng_extra/update/update_ahmia_blacklist.py rename to apps/searxng/searxng_extra/update/update_ahmia_blacklist.py diff --git a/searxng/searxng_extra/update/update_currencies.py b/apps/searxng/searxng_extra/update/update_currencies.py similarity index 100% rename from searxng/searxng_extra/update/update_currencies.py rename to apps/searxng/searxng_extra/update/update_currencies.py diff --git a/searxng/searxng_extra/update/update_engine_descriptions.py b/apps/searxng/searxng_extra/update/update_engine_descriptions.py similarity index 100% rename from searxng/searxng_extra/update/update_engine_descriptions.py rename to apps/searxng/searxng_extra/update/update_engine_descriptions.py diff --git a/searxng/searxng_extra/update/update_engine_traits.py b/apps/searxng/searxng_extra/update/update_engine_traits.py similarity index 100% rename from searxng/searxng_extra/update/update_engine_traits.py rename to apps/searxng/searxng_extra/update/update_engine_traits.py diff --git a/searxng/searxng_extra/update/update_external_bangs.py b/apps/searxng/searxng_extra/update/update_external_bangs.py similarity index 100% rename from searxng/searxng_extra/update/update_external_bangs.py rename to apps/searxng/searxng_extra/update/update_external_bangs.py diff --git a/searxng/searxng_extra/update/update_firefox_version.py b/apps/searxng/searxng_extra/update/update_firefox_version.py similarity index 100% rename from searxng/searxng_extra/update/update_firefox_version.py rename to apps/searxng/searxng_extra/update/update_firefox_version.py diff --git a/searxng/searxng_extra/update/update_osm_keys_tags.py b/apps/searxng/searxng_extra/update/update_osm_keys_tags.py similarity index 100% rename from searxng/searxng_extra/update/update_osm_keys_tags.py rename to apps/searxng/searxng_extra/update/update_osm_keys_tags.py diff --git a/searxng/searxng_extra/update/update_pygments.py b/apps/searxng/searxng_extra/update/update_pygments.py similarity index 100% rename from searxng/searxng_extra/update/update_pygments.py rename to apps/searxng/searxng_extra/update/update_pygments.py diff --git a/searxng/searxng_extra/update/update_wikidata_units.py b/apps/searxng/searxng_extra/update/update_wikidata_units.py similarity index 100% rename from searxng/searxng_extra/update/update_wikidata_units.py rename to apps/searxng/searxng_extra/update/update_wikidata_units.py diff --git a/searxng/setup.py b/apps/searxng/setup.py similarity index 100% rename from searxng/setup.py rename to apps/searxng/setup.py diff --git a/searxng/tests/__init__.py b/apps/searxng/tests/__init__.py similarity index 100% rename from searxng/tests/__init__.py rename to apps/searxng/tests/__init__.py diff --git a/searxng/tests/robot/__init__.py b/apps/searxng/tests/robot/__init__.py similarity index 100% rename from searxng/tests/robot/__init__.py rename to apps/searxng/tests/robot/__init__.py diff --git a/searxng/tests/robot/__main__.py b/apps/searxng/tests/robot/__main__.py similarity index 100% rename from searxng/tests/robot/__main__.py rename to apps/searxng/tests/robot/__main__.py diff --git a/searxng/tests/robot/settings_robot.yml b/apps/searxng/tests/robot/settings_robot.yml similarity index 100% rename from searxng/tests/robot/settings_robot.yml rename to apps/searxng/tests/robot/settings_robot.yml diff --git a/searxng/tests/robot/test_webapp.py b/apps/searxng/tests/robot/test_webapp.py similarity index 100% rename from searxng/tests/robot/test_webapp.py rename to apps/searxng/tests/robot/test_webapp.py diff --git a/searxng/tests/unit/__init__.py b/apps/searxng/tests/unit/__init__.py similarity index 100% rename from searxng/tests/unit/__init__.py rename to apps/searxng/tests/unit/__init__.py diff --git a/searxng/tests/unit/engines/test_command.py b/apps/searxng/tests/unit/engines/test_command.py similarity index 100% rename from searxng/tests/unit/engines/test_command.py rename to apps/searxng/tests/unit/engines/test_command.py diff --git a/searxng/tests/unit/engines/test_xpath.py b/apps/searxng/tests/unit/engines/test_xpath.py similarity index 100% rename from searxng/tests/unit/engines/test_xpath.py rename to apps/searxng/tests/unit/engines/test_xpath.py diff --git a/searxng/tests/unit/network/__init__.py b/apps/searxng/tests/unit/network/__init__.py similarity index 100% rename from searxng/tests/unit/network/__init__.py rename to apps/searxng/tests/unit/network/__init__.py diff --git a/searxng/tests/unit/network/test_network.py b/apps/searxng/tests/unit/network/test_network.py similarity index 100% rename from searxng/tests/unit/network/test_network.py rename to apps/searxng/tests/unit/network/test_network.py diff --git a/searxng/tests/unit/settings/empty_settings.yml b/apps/searxng/tests/unit/settings/empty_settings.yml similarity index 100% rename from searxng/tests/unit/settings/empty_settings.yml rename to apps/searxng/tests/unit/settings/empty_settings.yml diff --git a/searxng/tests/unit/settings/syntaxerror_settings.yml b/apps/searxng/tests/unit/settings/syntaxerror_settings.yml similarity index 100% rename from searxng/tests/unit/settings/syntaxerror_settings.yml rename to apps/searxng/tests/unit/settings/syntaxerror_settings.yml diff --git a/searxng/tests/unit/settings/test_settings.yml b/apps/searxng/tests/unit/settings/test_settings.yml similarity index 100% rename from searxng/tests/unit/settings/test_settings.yml rename to apps/searxng/tests/unit/settings/test_settings.yml diff --git a/searxng/tests/unit/settings/user_settings.yml b/apps/searxng/tests/unit/settings/user_settings.yml similarity index 100% rename from searxng/tests/unit/settings/user_settings.yml rename to apps/searxng/tests/unit/settings/user_settings.yml diff --git a/searxng/tests/unit/settings/user_settings_keep_only.yml b/apps/searxng/tests/unit/settings/user_settings_keep_only.yml similarity index 100% rename from searxng/tests/unit/settings/user_settings_keep_only.yml rename to apps/searxng/tests/unit/settings/user_settings_keep_only.yml diff --git a/searxng/tests/unit/settings/user_settings_remove.yml b/apps/searxng/tests/unit/settings/user_settings_remove.yml similarity index 100% rename from searxng/tests/unit/settings/user_settings_remove.yml rename to apps/searxng/tests/unit/settings/user_settings_remove.yml diff --git a/searxng/tests/unit/settings/user_settings_remove2.yml b/apps/searxng/tests/unit/settings/user_settings_remove2.yml similarity index 100% rename from searxng/tests/unit/settings/user_settings_remove2.yml rename to apps/searxng/tests/unit/settings/user_settings_remove2.yml diff --git a/searxng/tests/unit/settings/user_settings_simple.yml b/apps/searxng/tests/unit/settings/user_settings_simple.yml similarity index 100% rename from searxng/tests/unit/settings/user_settings_simple.yml rename to apps/searxng/tests/unit/settings/user_settings_simple.yml diff --git a/searxng/tests/unit/test_answerers.py b/apps/searxng/tests/unit/test_answerers.py similarity index 100% rename from searxng/tests/unit/test_answerers.py rename to apps/searxng/tests/unit/test_answerers.py diff --git a/searxng/tests/unit/test_engines_init.py b/apps/searxng/tests/unit/test_engines_init.py similarity index 100% rename from searxng/tests/unit/test_engines_init.py rename to apps/searxng/tests/unit/test_engines_init.py diff --git a/searxng/tests/unit/test_exceptions.py b/apps/searxng/tests/unit/test_exceptions.py similarity index 100% rename from searxng/tests/unit/test_exceptions.py rename to apps/searxng/tests/unit/test_exceptions.py diff --git a/searxng/tests/unit/test_external_bangs.py b/apps/searxng/tests/unit/test_external_bangs.py similarity index 100% rename from searxng/tests/unit/test_external_bangs.py rename to apps/searxng/tests/unit/test_external_bangs.py diff --git a/searxng/tests/unit/test_locales.py b/apps/searxng/tests/unit/test_locales.py similarity index 100% rename from searxng/tests/unit/test_locales.py rename to apps/searxng/tests/unit/test_locales.py diff --git a/searxng/tests/unit/test_plugins.py b/apps/searxng/tests/unit/test_plugins.py similarity index 100% rename from searxng/tests/unit/test_plugins.py rename to apps/searxng/tests/unit/test_plugins.py diff --git a/searxng/tests/unit/test_preferences.py b/apps/searxng/tests/unit/test_preferences.py similarity index 100% rename from searxng/tests/unit/test_preferences.py rename to apps/searxng/tests/unit/test_preferences.py diff --git a/searxng/tests/unit/test_query.py b/apps/searxng/tests/unit/test_query.py similarity index 100% rename from searxng/tests/unit/test_query.py rename to apps/searxng/tests/unit/test_query.py diff --git a/searxng/tests/unit/test_results.py b/apps/searxng/tests/unit/test_results.py similarity index 100% rename from searxng/tests/unit/test_results.py rename to apps/searxng/tests/unit/test_results.py diff --git a/searxng/tests/unit/test_search.py b/apps/searxng/tests/unit/test_search.py similarity index 100% rename from searxng/tests/unit/test_search.py rename to apps/searxng/tests/unit/test_search.py diff --git a/searxng/tests/unit/test_settings_loader.py b/apps/searxng/tests/unit/test_settings_loader.py similarity index 100% rename from searxng/tests/unit/test_settings_loader.py rename to apps/searxng/tests/unit/test_settings_loader.py diff --git a/searxng/tests/unit/test_utils.py b/apps/searxng/tests/unit/test_utils.py similarity index 100% rename from searxng/tests/unit/test_utils.py rename to apps/searxng/tests/unit/test_utils.py diff --git a/searxng/tests/unit/test_webadapter.py b/apps/searxng/tests/unit/test_webadapter.py similarity index 100% rename from searxng/tests/unit/test_webadapter.py rename to apps/searxng/tests/unit/test_webadapter.py diff --git a/searxng/tests/unit/test_webapp.py b/apps/searxng/tests/unit/test_webapp.py similarity index 100% rename from searxng/tests/unit/test_webapp.py rename to apps/searxng/tests/unit/test_webapp.py diff --git a/searxng/tests/unit/test_webutils.py b/apps/searxng/tests/unit/test_webutils.py similarity index 100% rename from searxng/tests/unit/test_webutils.py rename to apps/searxng/tests/unit/test_webutils.py diff --git a/searxng/utils/build_env.py b/apps/searxng/utils/build_env.py similarity index 100% rename from searxng/utils/build_env.py rename to apps/searxng/utils/build_env.py diff --git a/searxng/utils/filtron.sh b/apps/searxng/utils/filtron.sh similarity index 100% rename from searxng/utils/filtron.sh rename to apps/searxng/utils/filtron.sh diff --git a/searxng/utils/lib.sh b/apps/searxng/utils/lib.sh similarity index 100% rename from searxng/utils/lib.sh rename to apps/searxng/utils/lib.sh diff --git a/searxng/utils/lib_go.sh b/apps/searxng/utils/lib_go.sh similarity index 100% rename from searxng/utils/lib_go.sh rename to apps/searxng/utils/lib_go.sh diff --git a/searxng/utils/lib_nvm.sh b/apps/searxng/utils/lib_nvm.sh similarity index 100% rename from searxng/utils/lib_nvm.sh rename to apps/searxng/utils/lib_nvm.sh diff --git a/searxng/utils/lib_redis.sh b/apps/searxng/utils/lib_redis.sh similarity index 100% rename from searxng/utils/lib_redis.sh rename to apps/searxng/utils/lib_redis.sh diff --git a/searxng/utils/lib_sxng_data.sh b/apps/searxng/utils/lib_sxng_data.sh similarity index 100% rename from searxng/utils/lib_sxng_data.sh rename to apps/searxng/utils/lib_sxng_data.sh diff --git a/searxng/utils/lib_sxng_node.sh b/apps/searxng/utils/lib_sxng_node.sh similarity index 100% rename from searxng/utils/lib_sxng_node.sh rename to apps/searxng/utils/lib_sxng_node.sh diff --git a/searxng/utils/lib_sxng_static.sh b/apps/searxng/utils/lib_sxng_static.sh similarity index 100% rename from searxng/utils/lib_sxng_static.sh rename to apps/searxng/utils/lib_sxng_static.sh diff --git a/searxng/utils/lib_sxng_test.sh b/apps/searxng/utils/lib_sxng_test.sh similarity index 100% rename from searxng/utils/lib_sxng_test.sh rename to apps/searxng/utils/lib_sxng_test.sh diff --git a/searxng/utils/lib_sxng_themes.sh b/apps/searxng/utils/lib_sxng_themes.sh similarity index 100% rename from searxng/utils/lib_sxng_themes.sh rename to apps/searxng/utils/lib_sxng_themes.sh diff --git a/searxng/utils/lib_sxng_weblate.sh b/apps/searxng/utils/lib_sxng_weblate.sh similarity index 100% rename from searxng/utils/lib_sxng_weblate.sh rename to apps/searxng/utils/lib_sxng_weblate.sh diff --git a/searxng/utils/lxc.sh b/apps/searxng/utils/lxc.sh similarity index 100% rename from searxng/utils/lxc.sh rename to apps/searxng/utils/lxc.sh diff --git a/searxng/utils/morty.sh b/apps/searxng/utils/morty.sh similarity index 100% rename from searxng/utils/morty.sh rename to apps/searxng/utils/morty.sh diff --git a/searxng/utils/searx.sh b/apps/searxng/utils/searx.sh similarity index 100% rename from searxng/utils/searx.sh rename to apps/searxng/utils/searx.sh diff --git a/searxng/utils/searxng.sh b/apps/searxng/utils/searxng.sh similarity index 100% rename from searxng/utils/searxng.sh rename to apps/searxng/utils/searxng.sh diff --git a/searxng/utils/searxng_check.py b/apps/searxng/utils/searxng_check.py similarity index 100% rename from searxng/utils/searxng_check.py rename to apps/searxng/utils/searxng_check.py diff --git a/searxng/utils/templates/etc/searxng/settings.yml b/apps/searxng/utils/templates/etc/searxng/settings.yml similarity index 100% rename from searxng/utils/templates/etc/searxng/settings.yml rename to apps/searxng/utils/templates/etc/searxng/settings.yml diff --git a/apps/shift-recorder b/apps/shift-recorder new file mode 160000 index 0000000..144ca87 --- /dev/null +++ b/apps/shift-recorder @@ -0,0 +1 @@ +Subproject commit 144ca876c0948bae7cabb402a23bac031169d2d7 diff --git a/apps/stockfill b/apps/stockfill new file mode 160000 index 0000000..8a2b6a1 --- /dev/null +++ b/apps/stockfill @@ -0,0 +1 @@ +Subproject commit 8a2b6a183b133a1ac8619c0ff4b705101264ce3f diff --git a/core/authelia/configuration.yml b/core/authelia/configuration.yml new file mode 100644 index 0000000..b27674e --- /dev/null +++ b/core/authelia/configuration.yml @@ -0,0 +1,54 @@ +server.address: tcp://0.0.0.0:9091 + +log: + level: info + +identity_validation.reset_password.jwt_secret: T72Xcxa4d7xpQRypFDZpunlZt0IjqspojmBlxBr69gnkRjzR144YgjZsgFYZK0gS + +session: + secret: BYksO7YUAJ8gXx9Endgpe46RgB10nkeKpD1qcQPt0GuYGQm2pS2zjJtNOrCEqpav + cookies: + - domain: lan.ddnsgeek.com + authelia_url: https://auth.lan.ddnsgeek.com + +storage: + encryption_key: N7mkWziClgDhLgZDRkRwU6jEHmGF6ciOt53pzoFcZ0meEV1AZCC5bWZd24jeu19y + local: + path: /config/data/db.sqlite3 + +authentication_backend: + file: + path: /config/users_database.yml + +access_control: + default_policy: deny + rules: +# - domain: "*.lan.ddnsgeek.com" +# policy: two_factor + - domain: alertmanager.lan.ddnsgeek.com + resources: + - "^/api/.*" + policy: bypass + + - domain: influxdb.lan.ddnsgeek.com + resources: + - "^/health" + policy: bypass + + - domain: prometheus.lan.ddnsgeek.com + resources: + - "^/-/healthy" + policy: bypass + + - domain: traefik.lan.ddnsgeek.com + resources: + - "^/metrics" + policy: bypass + + + - domain: "*.lan.ddnsgeek.com" + policy: two_factor + +notifier: + filesystem: + filename: /config/data/notification.txt diff --git a/core/authelia/users_database.yml b/core/authelia/users_database.yml new file mode 100644 index 0000000..1edca83 --- /dev/null +++ b/core/authelia/users_database.yml @@ -0,0 +1,7 @@ +users: + admin: + displayname: "Admin" + password: "$argon2id$v=19$m=65536,t=3,p=4$WWdEjxd4avk4SA6T1OMDmQ$ml7znY+R1STq4d1n+RQhIiRL2Hu6RDVPR5uC+zbjPe4" + email: wayne.bennett@live.com + groups: + - admins diff --git a/core/crowdsec/Dockerfile b/core/crowdsec/Dockerfile new file mode 100644 index 0000000..959b597 --- /dev/null +++ b/core/crowdsec/Dockerfile @@ -0,0 +1,6 @@ +FROM crowdsecurity/crowdsec:latest + +COPY config/ /etc/crowdsec +# Install required components at build time +RUN cscli hub update && \ + cscli collections install crowdsecurity/traefik --force diff --git a/core/crowdsec/data/detect.yaml b/core/crowdsec/data/detect.yaml new file mode 120000 index 0000000..7d61fba --- /dev/null +++ b/core/crowdsec/data/detect.yaml @@ -0,0 +1 @@ +/staging/var/lib/crowdsec/data/detect.yaml \ No newline at end of file diff --git a/core/docker-compose.yml b/core/docker-compose.yml new file mode 100644 index 0000000..63eaa40 --- /dev/null +++ b/core/docker-compose.yml @@ -0,0 +1,123 @@ +services: + traefik: + profiles: ["core","all","traefik"] + image: traefik:3 + container_name: traefik + restart: always + read_only: true + hostname: traefik.lan.ddnsgeek.com + depends_on: + - error-pages + - authelia + - crowdsec + + ports: + - "80:80" + - "443:443" + + build: + context: ${PROJECT_ROOT}/core + + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${PROJECT_ROOT}/core/traefik/data/letsencrypt:/letsencrypt + - ${PROJECT_ROOT}/core/traefik/data/logs:/logs + - ${PROJECT_ROOT}/core/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro + - ${PROJECT_ROOT}/core/traefik/traefik.yml:/etc/traefik/traefik.yml:ro + - ${PROJECT_ROOT}/core/traefik/data/plugins:/plugins-storage + + healthcheck: + test: traefik healthcheck --ping + + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.rule=Host(`traefik.lan.ddnsgeek.com`)" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.entrypoints=websecure" + - "traefik.http.routers.traefik.tls.certresolver=myresolver" + - "traefik.http.routers.traefik.middlewares=authelia" + - "io.portainer.accesscontrol.public" + - "traefik.docker.network=core_traefik" + - "traefik.http.routers.traefik.observability.tracing=true" + + networks: +# - reverse_proxy +# - prometheus_edge + - traefik + crowdsec: +# image: crowdsecurity/crowdsec:latest + profiles: ["core","all","traefik"] + build: ${PROJECT_ROOT}/core/crowdsec + container_name: crowdsec + restart: always + environment: + - COLLECTIONS=crowdsecurity/traefik + volumes: + - ${PROJECT_ROOT}/core/crowdsec/logs:/logs:ro + - ${PROJECT_ROOT}/core/crowdsec/data:/var/lib/crowdsec/data + - ${PROJECT_ROOT}/core/crowdsec/config:/etc/crowdsec + networks: +# - reverse_proxy + - traefik + healthcheck: + test: ["CMD-SHELL", "cscli metrics || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + + error-pages: + profiles: ["core","all","traefik"] + image: tarampampam/error-pages:3 + restart: always + container_name: error-pages + read_only: true + environment: + TEMPLATE_NAME: app-down + networks: +# - reverse_proxy + - traefik + hostname: error-pages + labels: + - "traefik.enable=true" + # use as "fallback" for any NON-registered services (with priority below normal) + - "traefik.http.routers.error-pages-router.rule=HostRegexp(`{host:.+}`)" + # should say that all of your services work on https + - "traefik.http.routers.error-pages-router.entrypoints=web" + - "traefik.http.routers.error-pages-router.middlewares=error-pages-middleware" + # "errors" middleware settings + - "traefik.http.middlewares.error-pages-middleware.errors.status=400-599" + - "traefik.http.middlewares.error-pages-middleware.errors.service=error-pages-service" + - "traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html" + # define service properties + - "traefik.http.services.error-pages-service.loadbalancer.server.port=8080" + - "io.portainer.accesscontrol.public" + + authelia: + profiles: ["core","all","traefik"] + image: authelia/authelia + restart: always + build: + context: ${PROJECT_ROOT}/core/authelia + volumes: + - ${PROJECT_ROOT}/core/authelia:/config + networks: +# - reverse_proxy + - traefik + container_name: authelia + labels: + - traefik.enable=true + - traefik.http.routers.authelia.rule=Host(`auth.lan.ddnsgeek.com`) + - traefik.http.routers.authelia.entrypoints=websecure + - traefik.http.routers.authelia.tls=true + - traefik.http.routers.authelia.tls.certresolver=myresolver + - io.portainer.accesscontrol.public + - traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.lan.ddnsgeek.com/ + - traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true + - traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups + - traefik.http.middlewares.authelia.forwardauth.maxResponseBodySize=2097152 +#networks: +# reverse_proxy: +# driver: bridge +# prometheus_edge: +# external: true diff --git a/core/test/Dockerfile b/core/test/Dockerfile new file mode 100644 index 0000000..9927bef --- /dev/null +++ b/core/test/Dockerfile @@ -0,0 +1,17 @@ +FROM debian:latest + +#RUN groupadd -g 1000 appuser || true && \ +# useradd -m -u 1000 -g 1000 -s /bin/bash appuser +#RUN groupadd -g 999 docker || true && usermod -aG docker appuser +WORKDIR /app +COPY exporter.py . + +RUN mkdir -p /data + +RUN apt update && apt install python3 pip -y + +RUN pip install --no-cache-dir docker prometheus_client requests pyyaml --break-system-packages + +#USER appuser + +CMD ["bash"] diff --git a/core/test/docker-compose.yml b/core/test/docker-compose.yml new file mode 100644 index 0000000..23b7bfc --- /dev/null +++ b/core/test/docker-compose.yml @@ -0,0 +1,60 @@ +services: + update-test: + image: nginx:1.27.4 + container_name: update-test + profiles: ["test"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] # returns 0 if Nginx is up + interval: 5s + timeout: 2s + retries: 3 + start_period: 2s + + docker-update-exporter-test: + profiles: ["test"] + build: + context: ${PROJECT_ROOT}/core/test + container_name: docker-update-exporter-test + stdin_open: true + tty: true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${PROJECT_ROOT}/monitoring/docker-exporter/data:/data:rw +# - ${PROJECT_ROOT}/services-up.sh:/app/services-up.sh:ro + + + + - ${PROJECT_ROOT}:/compose + - ${PROJECT_ROOT}/default-environment.env:/compose/default-environment.env:ro + - ${PROJECT_ROOT}/default-network.yml:/compose/default-network.yml:ro + - ${PROJECT_ROOT}/core/docker-compose.yml:/compose/core/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/prometheus/docker-compose.yml:/compose/monitoring/prometheus/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/gotify/docker-compose.yml:/compose/monitoring/gotify/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/grafana/docker-compose.yml:/compose/monitoring/grafana/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/portainer/docker-compose.yml:/compose/monitoring/portainer/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/uptime-kuma/docker-compose.yml:/compose/monitoring/uptime-kuma/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/gitea/docker-compose.yml:/compose/apps/gitea/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/gramps/docker-compose.yml:/compose/apps/gramps/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/nextcloud/docker-compose.yml:/compose/apps/nextcloud/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/passbolt/docker-compose.yml:/compose/apps/passbolt/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/searxng/docker-compose.yml:/compose/apps/searxng/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/shift-recorder/docker-compose.yml:/compose/apps/shift-recorder/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/stockfill/docker-compose.yml:/compose/apps/stockfill/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/node-red/docker-compose.yml:/compose/monitoring/node-red/docker-compose.yml:ro + - ${PROJECT_ROOT}/core/test/docker-compose.yml:/compose/core/test/docker-compose.yml:ro + + + + +# ports: +# - "9105:9105" + restart: unless-stopped + networks: +# - edge + - monitor +# healthcheck: +# test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:9105/metrics')"] +# interval: 30s +# timeout: 5s +# retries: 3 +# start_period: 10s diff --git a/core/test/exporter.py b/core/test/exporter.py new file mode 100644 index 0000000..571f41c --- /dev/null +++ b/core/test/exporter.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +import os +import re +import time +import json +import docker +import requests +import yaml +from prometheus_client import Gauge, start_http_server + +# --- Config --- +EXPORTER_PORT = 9105 +CHECK_INTERVAL = 60 +CACHE_TTL = 6 * 3600 +SERVICES_UP_SCRIPT = "/compose/services-up.sh" +CACHE_FILE = "/data/remote_digest_cache.json" + +client = docker.from_env() + +# --- Metrics --- +CONTAINER_UPDATE = Gauge( + "docker_container_update_available", + "1 if container image is out of date (compose drift or registry), 0 otherwise", + ["container", "compose_image", "running_image", "com_docker_compose_project"] +) + +LAST_CHECK = Gauge( + "docker_image_update_last_check_timestamp", + "Last time the update check ran (unix timestamp)" +) + +# --- Persistent Cache --- + +def load_cache(): + if not os.path.exists(CACHE_FILE): + return {} + + try: + with open(CACHE_FILE, "r") as f: + return json.load(f) + except Exception as e: + print(f"[cache] Failed to load cache: {e}") + return {} + +def save_cache(): + try: + os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True) + with open(CACHE_FILE, "w") as f: + json.dump(REMOTE_DIGEST_CACHE, f) + except Exception as e: + print(f"[cache] Failed to save cache: {e}") + +REMOTE_DIGEST_CACHE = load_cache() + +# --- Helpers --- + +def get_project_prefix_from_script(script_path): + project_prefix = "core-" # fallback + if not os.path.exists(script_path): + return project_prefix + + with open(script_path, "r") as f: + for line in f: + line = line.strip() + m = re.match(r'PROJECT\s*=\s*["\']?([^"\']+)["\']?', line) + if m: + project_prefix = m.group(1) + "-" + break + + return project_prefix + +def get_local_digest(image_name): + try: + img = client.images.get(image_name) + digests = img.attrs.get("RepoDigests", []) + if digests: + return digests[0].split("@")[1] + except Exception as e: + print(f"[local_digest] Error for {image_name}: {e}") + + return None + +def get_remote_digest(image_name): + now = time.time() + original = image_name + + # Use cached value if still valid + if original in REMOTE_DIGEST_CACHE: + digest, ts = REMOTE_DIGEST_CACHE[original] + if now - ts < CACHE_TTL: + return digest + + try: + if "/" not in image_name: + registry = "docker.io" + repo = "library/" + image_name + else: + parts = image_name.split("/") + if "." in parts[0] or ":" in parts[0]: + registry = parts[0] + repo = "/".join(parts[1:]) + else: + registry = "docker.io" + repo = image_name + + if ":" in repo: + repo, tag = repo.rsplit(":", 1) + else: + tag = "latest" + + token = None + manifest_url = None + + if registry in ["docker.io", "registry-1.docker.io"]: + token_res = requests.get( + "https://auth.docker.io/token", + params={ + "service": "registry.docker.io", + "scope": f"repository:{repo}:pull" + }, + timeout=10 + ) + token = token_res.json().get("token") + manifest_url = f"https://registry-1.docker.io/v2/{repo}/manifests/{tag}" + + elif registry == "ghcr.io": + token_res = requests.get( + "https://ghcr.io/token", + params={"scope": f"repository:{repo}:pull"}, + timeout=10 + ) + token = token_res.json().get("token") + manifest_url = f"https://ghcr.io/v2/{repo}/manifests/{tag}" + + else: + return None + + res = requests.get( + manifest_url, + headers={ + "Authorization": f"Bearer {token}" if token else "", + "Accept": "application/vnd.docker.distribution.manifest.v2+json" + }, + timeout=10 + ) + + if res.status_code == 200: + digest = res.headers.get("Docker-Content-Digest") + + # Save to persistent cache + REMOTE_DIGEST_CACHE[original] = (digest, now) + save_cache() + + return digest + + except Exception as e: + print(f"[remote_digest] Error for {image_name}: {e}") + + return None + +def get_compose_files_from_script(script_path): + files = [] + + if not os.path.exists(script_path): + print(f"[compose_files] Script not found: {script_path}") + return files + + base_dir = os.path.dirname(script_path) + + with open(script_path, "r") as f: + content = f.read() + + match = re.search(r'FILES\s*=\s*\((.*?)\)', content, re.DOTALL) + + if match: + lines = match.group(1).splitlines() + for line in lines: + line = line.strip() + + if line.startswith("-f"): + rel_path = line[2:].strip() + + if rel_path: + full_path = os.path.join(base_dir, rel_path) + full_path = os.path.normpath(full_path) + + print(f"[compose_files] {rel_path} -> {full_path}") + + files.append(full_path) + + return files + +def parse_compose_files(compose_files): + """Return mapping service_name -> (expected image, is_built)""" + service_to_image = {} + + for f in compose_files: + if not os.path.exists(f): + continue + + try: + with open(f, "r") as stream: + data = yaml.safe_load(stream) + services = data.get("services", {}) + + for service_name, service_def in services.items(): + image = service_def.get("image") + is_built = False + + if not image: + build_ctx = service_def.get("build") + + if isinstance(build_ctx, dict): + context_path = build_ctx.get("context", ".") + dockerfile_path = os.path.join( + context_path, + build_ctx.get("dockerfile", "Dockerfile") + ) + elif isinstance(build_ctx, str): + context_path = build_ctx + dockerfile_path = os.path.join(context_path, "Dockerfile") + else: + dockerfile_path = None + + if dockerfile_path and os.path.exists(dockerfile_path): + try: + with open(dockerfile_path, "r") as df: + for line in df: + line = line.strip() + if line.upper().startswith("LABEL") and "image=" in line: + m = re.search( + r'image=["\']?([^"\']+)["\']?', + line + ) + if m: + image = m.group(1) + break + except Exception as e: + print(f"[dockerfile] Error reading {dockerfile_path}: {e}") + + if not image: + image = f"{service_name}:latest" + + is_built = True + + service_to_image[service_name] = (image, is_built) + + except Exception as e: + print(f"[compose_parse] Failed {f}: {e}") + + return service_to_image + +def check_containers(): + CONTAINER_UPDATE.clear() + + PROJECT_PREFIX = get_project_prefix_from_script(SERVICES_UP_SCRIPT) + compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT) + service_to_image = parse_compose_files(compose_files) + + for container in client.containers.list(): + project_label = container.labels.get("com.docker.compose.project") + + if not project_label: + continue # skip non-compose containers + + service_label = container.labels.get("com.docker.compose.service") + running_image = container.attrs["Config"]["Image"] + compose_image = None + is_built = False + + if service_label and service_label in service_to_image: + compose_image, is_built = service_to_image[service_label] + + if is_built: + compose_image_name, _, _ = compose_image.partition(":") + compose_image = f"{PROJECT_PREFIX}{compose_image_name}" + + update_flag = 0 + + if is_built: + if running_image != compose_image: + update_flag = 1 + else: + local_digest = get_local_digest(running_image) + remote_digest = get_remote_digest(service_to_image[service_label][0]) + + if local_digest and remote_digest and local_digest != remote_digest: + update_flag = 1 + else: + if running_image != compose_image: + update_flag = 1 + else: + local_digest = get_local_digest(running_image) + remote_digest = get_remote_digest(running_image) + + if local_digest and remote_digest and local_digest != remote_digest: + update_flag = 1 + + CONTAINER_UPDATE.labels( + container=container.name, + compose_image=compose_image if compose_image else "unknown", + running_image=running_image, + com_docker_compose_project=project_label + ).set(update_flag) + + print( + f"{container.name} | " + f"running={running_image} | " + f"compose={compose_image} | " + f"update={update_flag}" + ) + + LAST_CHECK.set(time.time()) + +if __name__ == "__main__": + start_http_server(EXPORTER_PORT) + print(f"Docker update exporter running on :{EXPORTER_PORT}") + + while True: + try: + check_containers() + except Exception as e: + print(f"[main] Error: {e}") + + time.sleep(CHECK_INTERVAL) diff --git a/core/traefik/data/dynamic.yaml b/core/traefik/data/dynamic.yaml new file mode 100644 index 0000000..c71be22 --- /dev/null +++ b/core/traefik/data/dynamic.yaml @@ -0,0 +1,22 @@ +http: + middlewares: + crowdsec: + plugin: + crowdsec-bouncer: + crowdsecMode: live + crowdsecLapiKey: HeneLa2mazFVzl5+DQRKOdchBuJxKdjrHsHBE/03Acs + crowdsecLapiHost: crowdsec:8080 + crowdsecLapiScheme: http + + secHeaders: + headers: + browserXssFilter: true + contentTypeNosniff: true + frameDeny: true +# sslRedirect: true + #HSTS Configuration + stsIncludeSubdomains: true + stsPreload: true + stsSeconds: 15552000 + forceSTSHeader: true + customFrameOptionsValue: "SAMEORIGIN" diff --git a/traefik/data/plugins.yaml b/core/traefik/data/plugins.yaml similarity index 100% rename from traefik/data/plugins.yaml rename to core/traefik/data/plugins.yaml diff --git a/core/traefik/dynamic.yml b/core/traefik/dynamic.yml new file mode 100644 index 0000000..47f950e --- /dev/null +++ b/core/traefik/dynamic.yml @@ -0,0 +1,33 @@ +http: + middlewares: + crowdsec: + plugin: + crowdsec-bouncer: + crowdsecMode: live + crowdsecLapiKey: HeneLa2mazFVzl5+DQRKOdchBuJxKdjrHsHBE/03Acs + crowdsecLapiHost: crowdsec:8080 + crowdsecLapiScheme: http + + secHeaders: + headers: + browserXssFilter: true + contentTypeNosniff: true + frameDeny: true +# sslRedirect: true + #HSTS Configuration + stsIncludeSubdomains: true + stsPreload: true + stsSeconds: 15552000 + forceSTSHeader: true + customFrameOptionsValue: "SAMEORIGIN" +# tracing-middleware: +# tracing: +# serviceName: traefik +# sampleRate: 1.0 + default-chain: + chain: + middlewares: + - secHeaders@file + - crowdsec@file +# - tracing-middleware@file + - error-pages-middleware@docker diff --git a/core/traefik/traefik.yml b/core/traefik/traefik.yml new file mode 100644 index 0000000..d333401 --- /dev/null +++ b/core/traefik/traefik.yml @@ -0,0 +1,80 @@ +log: + level: INFO + +accessLog: + filePath: /logs/access.log + format: json + +api: + dashboard: true + insecure: false + +ping: {} + +providers: + docker: + exposedByDefault: false + + file: + filename: /etc/traefik/dynamic.yml + watch: true + +entryPoints: + web: + address: ":80" + forwardedHeaders: + insecure: true + http: + redirections: + entryPoint: + to: websecure + scheme: https + + websecure: + address: ":443" + forwardedHeaders: + insecure: true + http: + middlewares: + - default-chain@file +# observability: +# tracing: +# enabled: true +# metrics: +# address: ":9100" + +certificatesResolvers: + myresolver: + acme: + email: wayne.bennett@live.com + storage: /letsencrypt/acme.json + httpChallenge: + entryPoint: web + +experimental: + plugins: + crowdsec-bouncer: + moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin + version: v1.4.2 +metrics: + prometheus: +# entryPoint: metrics:9100 # optional, default is "metrics" + buckets: + - 0.1 + - 0.3 + - 1.2 + - 5.0 + addEntryPointsLabels: true # add labels for each entrypoint + addServicesLabels: true # add labels for each service +#tracing: +# serviceName: traefik +# sampleRate: 1.0 + +# otlp: +# grpc: +# endpoint: tempo:4317 +# insecure: true +# enabled: true +# http: +# enabled: true +# endpoint: http://tempo:4318/v1/traces diff --git a/default-network.yml b/default-network.yml new file mode 100644 index 0000000..31e199d --- /dev/null +++ b/default-network.yml @@ -0,0 +1,5 @@ +networks: + traefik: + driver: bridge + monitor: + diff --git a/monitoring/docker-exporter/Dockerfile b/monitoring/docker-exporter/Dockerfile new file mode 100644 index 0000000..7d741b7 --- /dev/null +++ b/monitoring/docker-exporter/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-slim + +#RUN groupadd -g 1000 appuser || true && \ +# useradd -m -u 1000 -g 1000 -s /bin/bash appuser +#RUN groupadd -g 999 docker || true && usermod -aG docker appuser +WORKDIR /app +COPY exporter.py . + +RUN mkdir -p /data + +RUN pip install --no-cache-dir docker prometheus_client requests pyyaml + +#USER appuser + +CMD ["python", "-u", "exporter.py"] diff --git a/monitoring/docker-exporter/exporter.py b/monitoring/docker-exporter/exporter.py new file mode 100644 index 0000000..f44787f --- /dev/null +++ b/monitoring/docker-exporter/exporter.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +import os +import re +import time +import json +import logging +import docker +import requests +import yaml +from prometheus_client import Gauge, start_http_server + +# --- Logging --- +LOG_LEVEL = os.getenv("LOG_LEVEL", "DEBUG").upper() +logging.basicConfig( + level=getattr(logging, LOG_LEVEL, logging.DEBUG), + format="%(asctime)s [%(levelname)s] %(message)s" +) +logger = logging.getLogger("docker-update-exporter") + +# --- Config --- +EXPORTER_PORT = 9105 +CHECK_INTERVAL = 60 +CACHE_TTL = 6 * 3600 +SERVICES_UP_SCRIPT = "/compose/services-up.sh" +CACHE_FILE = "/data/remote_digest_cache.json" + +client = docker.from_env() + +# --- Metrics --- +CONTAINER_UPDATE = Gauge( + "docker_container_update_available", + "1 if container image is out of date (compose drift or registry), 0 otherwise", + ["container", "compose_image", "running_image", "com_docker_compose_project"] +) + +LAST_CHECK = Gauge( + "docker_image_update_last_check_timestamp", + "Last time the update check ran (unix timestamp)" +) + +# --- Persistent Cache --- +def load_cache(): + if not os.path.exists(CACHE_FILE): + logger.info(f"Cache file does not exist yet: {CACHE_FILE}") + return {} + try: + with open(CACHE_FILE, "r") as f: + cache = json.load(f) + logger.info(f"Loaded {len(cache)} cached remote digests") + return cache + except Exception as e: + logger.error(f"Failed to load cache: {e}") + return {} + +def save_cache(): + try: + os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True) + with open(CACHE_FILE, "w") as f: + json.dump(REMOTE_DIGEST_CACHE, f) + logger.debug(f"Saved {len(REMOTE_DIGEST_CACHE)} remote digests to cache") + except Exception as e: + logger.error(f"Failed to save cache: {e}") + +REMOTE_DIGEST_CACHE = load_cache() + +# --- Helpers --- +def get_project_prefix_from_script(script_path): + prefix = "core-" + if not os.path.exists(script_path): + return prefix + try: + with open(script_path) as f: + for line in f: + m = re.match(r'PROJECT\s*=\s*["\']?([^"\']+)', line) + if m: + return m.group(1) + "-" + except Exception as e: + logger.warning(f"Failed reading project prefix: {e}") + return prefix + +def get_local_digest(image_name): + try: + img = client.images.get(image_name) + digests = img.attrs.get("RepoDigests", []) + if digests: + digest = digests[0].split("@")[1] + logger.debug(f"Local digest for {image_name}: {digest}") + return digest + logger.debug(f"No local digest found for {image_name}") + except Exception: + pass + return None + +def get_remote_digest(image_name): + now = time.time() + if image_name in REMOTE_DIGEST_CACHE: + digest, ts = REMOTE_DIGEST_CACHE[image_name] + if now - ts < CACHE_TTL: + return digest + + try: + if "/" not in image_name: + registry = "docker.io" + repo = "library/" + image_name + else: + parts = image_name.split("/") + if "." in parts[0] or ":" in parts[0]: + registry = parts[0] + repo = "/".join(parts[1:]) + else: + registry = "docker.io" + repo = image_name + + if ":" in repo: + repo, tag = repo.rsplit(":", 1) + else: + tag = "latest" + + if registry in ["docker.io", "registry-1.docker.io"]: + token_res = requests.get( + "https://auth.docker.io/token", + params={"service": "registry.docker.io", "scope": f"repository:{repo}:pull"}, + timeout=10 + ) + token = token_res.json().get("token") + manifest_url = f"https://registry-1.docker.io/v2/{repo}/manifests/{tag}" + elif registry == "ghcr.io": + token_res = requests.get( + "https://ghcr.io/token", + params={"service": "ghcr.io", "scope": f"repository:{repo}:pull"}, + timeout=10 + ) + token = token_res.json().get("token") + manifest_url = f"https://ghcr.io/v2/{repo}/manifests/{tag}" + else: + logger.warning(f"Unsupported registry {registry} for {image_name}") + return None + + if not token: + return None + + res = requests.get( + manifest_url, + headers={"Authorization": f"Bearer {token}", "Accept": "application/vnd.docker.distribution.manifest.v2+json"}, + timeout=10 + ) + if res.status_code == 200: + digest = res.headers.get("Docker-Content-Digest") + REMOTE_DIGEST_CACHE[image_name] = (digest, now) + save_cache() + return digest + except Exception as e: + logger.debug(f"Error fetching remote digest for {image_name}: {e}") + + return None + +# --- Dockerfile Image Extraction --- +def parse_dockerfile_for_image(dockerfile_path): + if not os.path.exists(dockerfile_path): + return None + + image_name = None + + try: + with open(dockerfile_path) as df: + for line in df: + line = line.strip() + # Prefer LABEL with image if present + if "LABEL" in line and "image=" in line: + match = re.search(r'image=["\']?([^"\']+)["\']?', line) + if match: + image_name = match.group(1) + logger.debug(f"Found LABEL image={image_name} in {dockerfile_path}") + return image_name + + # If no LABEL, use the FROM line as fallback + df.seek(0) + for line in df: + line = line.strip() + if line.upper().startswith("FROM "): + parts = line.split() + if len(parts) >= 2: + base_image = parts[1] + logger.debug(f"Found base FROM {base_image} in {dockerfile_path}") + return base_image + except Exception as e: + logger.debug(f"Error reading Dockerfile {dockerfile_path}: {e}") + + return image_name + +# --- Compose parsing --- +def get_compose_files_from_script(script_path): + files = [] + if not os.path.exists(script_path): + return files + base_dir = os.path.dirname(script_path) + try: + with open(script_path) as f: + content = f.read() + match = re.search(r'FILES\s*=\s*\((.*?)\)', content, re.DOTALL) + if match: + for line in match.group(1).splitlines(): + line = line.strip() + if line.startswith("-f"): + path = line[2:].strip() + if path: + full = os.path.normpath(os.path.join(base_dir, path)) + files.append(full) + except Exception as e: + logger.warning(f"Failed parsing services-up.sh: {e}") + return files + +def parse_compose_services(compose_files): + svc_map = {} + for f in compose_files: + try: + with open(f) as stream: + data = yaml.safe_load(stream) or {} + for svc_name, svc_def in data.get("services", {}).items(): + image = svc_def.get("image") + is_built = False + if not image and "build" in svc_def: + is_built = True + build_ctx = svc_def["build"] + dockerfile_path = None + + if isinstance(build_ctx, dict): + context = build_ctx.get("context", ".") + dockerfile_path = os.path.join(context, build_ctx.get("dockerfile", "Dockerfile")) + elif isinstance(build_ctx, str): + dockerfile_path = os.path.join(build_ctx, "Dockerfile") + + image = parse_dockerfile_for_image(dockerfile_path) + + if not image: + logger.info(f"Defaulting build image for {svc_name} to {svc_name}:latest") + image = f"{svc_name}:latest" + + svc_map[svc_name] = (image, is_built) + except Exception as e: + logger.warning(f"Failed parsing {f}: {e}") + + logger.debug(f"Service image mapping: {svc_map}") + return svc_map + +# --- Main check --- +def check_containers(): + CONTAINER_UPDATE.clear() + + prefix = get_project_prefix_from_script(SERVICES_UP_SCRIPT) + compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT) + svc_map = parse_compose_services(compose_files) + + containers = client.containers.list() + for container in containers: + proj = container.labels.get("com.docker.compose.project") + if not proj: + continue + + svc = container.labels.get("com.docker.compose.service") + running = container.attrs["Config"]["Image"] + + compose_image = None + is_built = False + if svc in svc_map: + compose_image, is_built = svc_map[svc] + if is_built: + name, _, _ = compose_image.partition(":") + compose_image = f"{prefix}{name}" + + update_flag = 0 + + local_digest = get_local_digest(running) + remote_digest = get_remote_digest(compose_image if is_built else running) + + if local_digest and remote_digest and local_digest != remote_digest: + update_flag = 1 + + CONTAINER_UPDATE.labels( + container=container.name, + compose_image=compose_image or "unknown", + running_image=running, + com_docker_compose_project=proj + ).set(update_flag) + +# --- Runner --- +if __name__ == "__main__": + start_http_server(EXPORTER_PORT) + while True: + try: + check_containers() + except Exception as e: + logger.exception(f"update check failed: {e}") + time.sleep(CHECK_INTERVAL) diff --git a/monitoring/gotify/docker-compose.yml b/monitoring/gotify/docker-compose.yml new file mode 100644 index 0000000..bad0989 --- /dev/null +++ b/monitoring/gotify/docker-compose.yml @@ -0,0 +1,32 @@ +services: + gotify: + profiles: ["monitoring","all","gotify"] + image: gotify/server:latest + container_name: gotify + restart: always + + volumes: + - ${PROJECT_ROOT}/monitoring/gotify/data:/app/data + + environment: + - TZ=${TZ} + - GOTIFY_DEFAULTUSER_NAME=admin + - GOTIFY_DEFAULTUSER_PASS=R1m@dmin + - GOTIFY_REGISTRATION=false + + networks: +# - traefik_reverse_proxy + - traefik + + labels: + - "traefik.enable=true" + - "traefik.docker.network=core_traefik" + - "io.portainer.accesscontrol.public" + - "traefik.http.routers.gotify.rule=Host(`gotify.lan.ddnsgeek.com`)" + - "traefik.http.routers.gotify.entrypoints=websecure" + - "traefik.http.routers.gotify.tls.certresolver=myresolver" + - "traefik.http.services.gotify.loadbalancer.server.port=80" + +#networks: +# traefik_reverse_proxy: +# external: true diff --git a/monitoring/gotify/docker-health-to-gotify.sh b/monitoring/gotify/docker-health-to-gotify.sh new file mode 100755 index 0000000..fa52b9d --- /dev/null +++ b/monitoring/gotify/docker-health-to-gotify.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +#: "${GOTIFY_URL:?Set GOTIFY_URL (e.g. https://gotify.lan.ddnsgeek.com)}" +#: "${GOTIFY_TOKEN:?Set GOTIFY_TOKEN (AAM..CtNmUGoNIV)}" + +GOTIFY_URL="https://gotify.lan.ddnsgeek.com" +GOTIFY_TOKEN="ADuOnDBG7C27hcf" + +STATE_DIR="./docker-health-alert" +STATE_FILE="${STATE_DIR}/last_unhealthy.txt" +mkdir -p "$STATE_DIR" + +# Collect unhealthy running containers (ignore those with no healthcheck) +unhealthy="$( + docker ps -q | while read -r id; do + status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}no-healthcheck{{end}}' "$id")" + name="$(docker inspect --format '{{.Name}}' "$id" | sed 's|^/||')" + if [[ "$status" == "unhealthy" ]]; then + echo "$name" + fi + done | sort +)" + +# Only alert on change +last="$(cat "$STATE_FILE" 2>/dev/null || true)" +if [[ "$unhealthy" != "$last" ]]; then + if [[ -n "$unhealthy" ]]; then + msg="Unhealthy containers: $(echo "$unhealthy" | paste -sd ', ' -)" + title="Docker: UNHEALTHY" + priority=8 + else + msg="All containers healthy again." + title="Docker: RECOVERED" + priority=4 + fi + + curl -fsS -X POST "${GOTIFY_URL%/}/message" \ + -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \ + -F "title=${title}" \ + -F "message=${msg}" \ + -F "priority=${priority}" >/dev/null + + printf "%s" "$unhealthy" > "$STATE_FILE" +fi diff --git a/monitoring/grafana/docker-compose.yml b/monitoring/grafana/docker-compose.yml new file mode 100644 index 0000000..1806f02 --- /dev/null +++ b/monitoring/grafana/docker-compose.yml @@ -0,0 +1,49 @@ +services: + grafana: + profiles: ["monitoring","all","grafana"] + image: grafana/grafana:latest + container_name: grafana + restart: unless-stopped + environment: + - GF_SERVER_ROOT_URL=https://grafana.lan.ddnsgeek.com/ + volumes: + - ${PROJECT_ROOT}/monitoring/grafana/data:/var/lib/grafana + networks: +# - traefik_reverse_proxy +# - prometheus_edge + - traefik + - monitor + labels: + - "traefik.http.routers.grafana.rule=Host(`grafana.lan.ddnsgeek.com`)" + - "traefik.enable=true" + - "traefik.http.routers.grafana.entrypoints=websecure" + - "traefik.http.routers.grafana.tls.certresolver=myresolver" + - "io.portainer.accesscontrol.public" + - "traefik.http.services.grafana.loadbalancer.server.port=3000" + - "traefik.docker.network=core_traefik" + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +# tempo: +# image: grafana/tempo:latest +# container_name: tempo +# command: +# - "-config.file=/etc/tempo/config.yaml" +# volumes: +# - ./tempo/config.yaml:/etc/tempo/config.yaml +# - ./tempo/data:/var/lib/tempo +# ports: +# - "4317:4317" # OTLP gRPC endpoint for Traefik +# - "3200:3200" # optional: HTTP endpoint +# networks: +# - prometheus_edge + +#networks: +# traefik_reverse_proxy: +# external: true +# prometheus_edge: +# external: true diff --git a/monitoring/node-red/Dockerfile b/monitoring/node-red/Dockerfile new file mode 100644 index 0000000..6bf9008 --- /dev/null +++ b/monitoring/node-red/Dockerfile @@ -0,0 +1,6 @@ +FROM nodered/node-red:latest + +USER root +RUN apk add --no-cache docker-cli docker-cli-compose +RUN addgroup -g 131 -S docker && addgroup node-red docker +USER node-red diff --git a/monitoring/node-red/data/test-container.sh b/monitoring/node-red/data/test-container.sh new file mode 100755 index 0000000..22d65bb --- /dev/null +++ b/monitoring/node-red/data/test-container.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# test-container.sh +# Usage: ./test-container.sh container_name + +container="$1" +test_name="testing-${container}" +compose_script="/compose/services-up.sh" + +# Run container in detached mode +$compose_script --profile all run -d --name "$test_name" --build "$container" >/dev/null 2>&1 + +# Poll health status +timeout=60 # seconds +interval=2 # seconds +elapsed=0 +result=1 # default to failure + +while [ $elapsed -lt $timeout ]; do + status=$(docker inspect --format='{{.State.Health.Status}}' "$test_name" 2>/dev/null) + if [ "$status" == "healthy" ]; then +# echo "healthy" + result=0 # success + break + elif [ "$status" == "unhealthy" ]; then +# echo "unhealthy" + result=1 # failure + break + fi + sleep $interval + elapsed=$((elapsed + interval)) +done + +# Timeout case +if [ $elapsed -ge $timeout ]; then +# echo "timeout" + result=1 +fi + +# Cleanup +docker rm "$test_name" --force >/dev/null 2>&1 + +#echo "Exiting with $result" >&2 + +echo $result +exit $result diff --git a/monitoring/node-red/docker-compose.yml b/monitoring/node-red/docker-compose.yml new file mode 100644 index 0000000..5f6343d --- /dev/null +++ b/monitoring/node-red/docker-compose.yml @@ -0,0 +1,50 @@ +services: + node-red: +# image: nodered/node-red:latest + build: + context: ${PROJECT_ROOT}/monitoring/node-red + container_name: node-red + profiles: ["monitoring","all"] + restart: unless-stopped + privileged: true +# ports: +# - "1880:1880" + volumes: + - ${PROJECT_ROOT}/monitoring/node-red/data:/data + - /var/run/docker.sock:/var/run/docker.sock:rw + - ${PROJECT_ROOT}:/compose + - ${PROJECT_ROOT}/default-environment.env:/usr/src/node-red/default-environment.env:ro + - ${PROJECT_ROOT}/default-network.yml:/usr/src/node-red/default-network.yml:ro + - ${PROJECT_ROOT}/core/docker-compose.yml:/usr/src/node-red/core/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/prometheus/docker-compose.yml:/usr/src/node-red/monitoring/prometheus/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/gotify/docker-compose.yml:/usr/src/node-red/monitoring/gotify/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/grafana/docker-compose.yml:/usr/src/node-red/monitoring/grafana/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/portainer/docker-compose.yml:/usr/src/node-red/monitoring/portainer/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/uptime-kuma/docker-compose.yml:/usr/src/node-red/monitoring/uptime-kuma/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/gitea/docker-compose.yml:/usr/src/node-red/apps/gitea/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/gramps/docker-compose.yml:/usr/src/node-red/apps/gramps/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/nextcloud/docker-compose.yml:/usr/src/node-red/apps/nextcloud/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/passbolt/docker-compose.yml:/usr/src/node-red/apps/passbolt/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/searxng/docker-compose.yml:/usr/src/node-red/apps/searxng/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/shift-recorder/docker-compose.yml:/usr/src/node-red/apps/shift-recorder/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/stockfill/docker-compose.yml:/usr/src/node-red/apps/stockfill/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/node-red/docker-compose.yml:/usr/src/node-red/monitoring/node-red/docker-compose.yml:ro + - ${PROJECT_ROOT}/core/test/docker-compose.yml:/usr/src/node-red/core/test/docker-compose.yml:ro + +# - /run/current-system/sw/bin/docker:/usr/bin/docker:ro +# depends_on: +# - mosquitto +# - influxdb + networks: + - monitor + - traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.node-red.rule=Host(`node-red.lan.ddnsgeek.com`)" +# - "traefik.http.routers.node-red.service=api@internal" + - "traefik.http.routers.node-red.entrypoints=websecure" + - "traefik.http.routers.node-red.tls.certresolver=myresolver" + - "traefik.http.routers.node-red.middlewares=authelia" + - "io.portainer.accesscontrol.public" + - "traefik.docker.network=core_traefik" + - "traefik.http.services.node-red.loadbalancer.server.port=1880" diff --git a/monitoring/portainer/docker-compose.yml b/monitoring/portainer/docker-compose.yml new file mode 100644 index 0000000..57badde --- /dev/null +++ b/monitoring/portainer/docker-compose.yml @@ -0,0 +1,40 @@ +services: + portainer: + profiles: ["monitoring","all","portainer"] + image: portainer/portainer-ce:latest + container_name: portainer + restart: unless-stopped + command: -H unix:///var/run/docker.sock + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${PROJECT_ROOT}/monitoring/portainer/data:/data + networks: +# - traefik_reverse_proxy + - traefik + labels: + - traefik.enable=true + + # Router + - traefik.http.routers.portainer.rule=Host(`portainer.lan.ddnsgeek.com`) + - traefik.http.routers.portainer.entrypoints=websecure + - traefik.http.routers.portainer.tls=true + - traefik.http.routers.portainer.tls.certresolver=myresolver + - io.portainer.accesscontrol.public + # Service -> Portainer listens on 9000 inside the container + - traefik.http.services.portainer.loadbalancer.server.port=9000 + + environment: + - GODEBUG=netdns=cgo +# healthcheck: +# test: ["CMD", "wget", "--spider", "-q", "https://portainer.lan.ddnsgeek.com/api/status"] +# interval: 30s +# timeout: 10s +# retries: 3 +# start_period: 30s + + +#networks: +# traefik_reverse_proxy: +# external: true +# internal: +# driver: bridge diff --git a/monitoring/prometheus/docker-compose.yml b/monitoring/prometheus/docker-compose.yml new file mode 100644 index 0000000..dbcfc88 --- /dev/null +++ b/monitoring/prometheus/docker-compose.yml @@ -0,0 +1,230 @@ +#version: "3.8" + +services: + prometheus: + profiles: ["monitoring","all","prometheus"] + image: prom/prometheus:latest + container_name: prometheus + depends_on: +# - alertmanager + - telegraf + - influxdb + - node-exporter + - docker-update-exporter + - pihole-exporter + command: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + - "--storage.tsdb.retention.time=15d" +# build: +# context: ${PROJECT_ROOT}/monitoring/prometheus + volumes: + - ${PROJECT_ROOT}/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ${PROJECT_ROOT}/monitoring/prometheus/data:/prometheus + - ${PROJECT_ROOT}/monitoring/prometheus/rules:/etc/prometheus/rules:ro + + restart: unless-stopped + labels: + - "traefik.http.routers.prometheus.rule=Host(`prometheus.lan.ddnsgeek.com`)" + - "traefik.enable=true" + - "traefik.http.routers.prometheus.entrypoints=websecure" + - "traefik.http.routers.prometheus.tls.certresolver=myresolver" + - "io.portainer.accesscontrol.public" + - "traefik.http.services.prometheus.loadbalancer.server.port=9090" + - "traefik.http.routers.prometheus.middlewares=authelia" + - "traefik.docker.network=core_traefik" + networks: +# - edge +# - traefik_reverse_proxy + - traefik + - monitor + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +# alertmanager: +# image: prom/alertmanager:latest +# container_name: alertmanager +# command: +# - "--config.file=/etc/alertmanager/alertmanager.yml" +# volumes: +# - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro +# restart: unless-stopped +# networks: +# - edge +# - traefik_reverse_proxy +# healthcheck: +# test: ["CMD", "wget", "--spider", "-q", "http://localhost:9093/-/healthy"] +# interval: 30s +# timeout: 10s +# retries: 3 +# start_period: 20s +# labels: +# - "traefik.http.routers.alertmanager.rule=Host(`alertmanager.lan.ddnsgeek.com`)" +# - "traefik.enable=true" +# - "traefik.http.routers.alertmanager.entrypoints=websecure" +# - "traefik.http.routers.alertmanager.tls.certresolver=myresolver" +# - "io.portainer.accesscontrol.public" +# - "traefik.http.services.alertmanager.loadbalancer.server.port=9093" +# - "traefik.http.routers.alertmanager.middlewares=authelia" +# - "traefik.docker.network=traefik_reverse_proxy" + + node-exporter: + profiles: ["monitoring","all","prometheus-exporters"] + image: prom/node-exporter:latest + container_name: node-exporter + pid: host + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - "--path.procfs=/host/proc" + - "--path.sysfs=/host/sys" + - "--path.rootfs=/rootfs" + restart: unless-stopped + networks: +# - edge + - monitor + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9100/metrics"] + interval: 30s + timeout: 10s + retries: 3 + + + influxdb: + profiles: ["monitoring","all","prometheus"] + image: influxdb:2.7 + container_name: influxdb + restart: unless-stopped + volumes: + - ${PROJECT_ROOT}/monitoring/influxdb:/var/lib/influxdb2 + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: adminpassword + DOCKER_INFLUXDB_INIT_ORG: pbs + DOCKER_INFLUXDB_INIT_BUCKET: telemetry + networks: +# - edge +# - traefik_reverse_proxy + - traefik + - monitor + labels: + - "traefik.http.routers.influxdb.rule=Host(`influxdb.lan.ddnsgeek.com`)" + - "traefik.enable=true" + - "traefik.http.routers.influxdb.entrypoints=websecure" + - "traefik.http.routers.influxdb.tls.certresolver=myresolver" + - "io.portainer.accesscontrol.public" + - "traefik.http.services.influxdb.loadbalancer.server.port=8086" + - "traefik.http.routers.influxdb.middlewares=authelia" + - "traefik.docker.network=core_traefik" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8086/health || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + telegraf: + profiles: ["monitoring","all","prometheus"] + image: telegraf:latest + group_add: + - "131" + privileged: true + container_name: telegraf + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${PROJECT_ROOT}/monitoring/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro + networks: +# - edge + - monitor + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9273/metrics || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + + docker-update-exporter: + profiles: ["monitoring","all","prometheus-exporters"] + build: + context: ${PROJECT_ROOT}/monitoring/docker-exporter + container_name: docker-update-exporter +# volumes: +# - /var/run/docker.sock:/var/run/docker.sock +# - ${PROJECT_ROOT}/monitoring/docker-exporter/data:/data:rw +# - ${PROJECT_ROOT}/services-up.sh:/app/services-up.sh:ro + environment: + LOG_LEVEL: DEBUG + + volumes: + - ~/.docker/config.json:/root/.docker/config.json:ro + - /var/run/docker.sock:/var/run/docker.sock + - ${PROJECT_ROOT}/monitoring/docker-exporter/data:/data:rw + - ${PROJECT_ROOT}:/compose + - ${PROJECT_ROOT}/default-environment.env:/compose/default-environment.env:ro + - ${PROJECT_ROOT}/default-network.yml:/compose/default-network.yml:ro + - ${PROJECT_ROOT}/core/docker-compose.yml:/compose/core/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/prometheus/docker-compose.yml:/compose/monitoring/prometheus/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/gotify/docker-compose.yml:/compose/monitoring/gotify/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/grafana/docker-compose.yml:/compose/monitoring/grafana/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/portainer/docker-compose.yml:/compose/monitoring/portainer/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/uptime-kuma/docker-compose.yml:/compose/monitoring/uptime-kuma/docker-compose.yml:> + - ${PROJECT_ROOT}/apps/gitea/docker-compose.yml:/compose/apps/gitea/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/gramps/docker-compose.yml:/compose/apps/gramps/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/nextcloud/docker-compose.yml:/compose/apps/nextcloud/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/passbolt/docker-compose.yml:/compose/apps/passbolt/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/searxng/docker-compose.yml:/compose/apps/searxng/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/shift-recorder/docker-compose.yml:/compose/apps/shift-recorder/docker-compose.yml:ro + - ${PROJECT_ROOT}/apps/stockfill/docker-compose.yml:/compose/apps/stockfill/docker-compose.yml:ro + - ${PROJECT_ROOT}/monitoring/node-red/docker-compose.yml:/compose/monitoring/node-red/docker-compose.yml:ro + - ${PROJECT_ROOT}/core/test/docker-compose.yml:/compose/core/test/docker-compose.yml:ro + + +# ports: +# - "9105:9105" + restart: unless-stopped + networks: +# - edge + - monitor + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:9105/metrics')"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + pihole-exporter: + profiles: ["monitoring","all","prometheus-exporters"] + image: ekofr/pihole-exporter:latest + container_name: pihole-exporter + environment: + PIHOLE_HOSTNAME: pihole.sweet.home + PIHOLE_PASSWORD: "" + PORT: 9617 + ports: + - "9617:9617" + restart: unless-stopped + networks: +# - edge + - monitor + + + +#networks: +# internal: +# internal: true +# edge: +# internal: false + +# traefik_reverse_proxy: +# external: true + + diff --git a/monitoring/prometheus/prometheus.yml b/monitoring/prometheus/prometheus.yml new file mode 100644 index 0000000..283d254 --- /dev/null +++ b/monitoring/prometheus/prometheus.yml @@ -0,0 +1,160 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +#alerting: +# alertmanagers: +# - static_configs: +# - targets: +# - alertmanager:9093 + +scrape_configs: + + # Prometheus itself + - job_name: "prometheus" + static_configs: + - targets: ["prometheus:9090"] + labels: + role: prometheus + # ========================= + # Node Exporters (ALL hosts) + # ========================= + - job_name: "node" + static_configs: + - targets: + - node-exporter:9100 + labels: + role: docker + + - targets: + - raspberrypi.tail13f623.ts.net:9100 + labels: + role: raspberrypi + + - targets: + - pve.sweet.home:9100 + labels: + role: proxmox + + - targets: + - pbs.sweet.home:9100 + labels: + role: backup + + - targets: + - pihole:9100 + labels: + role: pihole + + - targets: + - server:9100 + labels: + role: server + + - targets: + - nix-cache:9100 + labels: + role: cache + + # ========================= + # Telegraf (Docker metrics) + # ========================= + - job_name: "telegraf" + static_configs: + - targets: + - telegraf:9273 + labels: + role: docker + + # ========================= + # Traefik (all instances) + # ========================= + - job_name: "traefik" + static_configs: + - targets: + - traefik.lan.ddnsgeek.com:8080 + labels: + role: docker + + - targets: + - raspberrypi.tail13f623.ts.net:8080 + labels: + role: raspberrypi + + metric_relabel_configs: + - source_labels: [service] + regex: '(.+)@.+' + target_label: service + replacement: '$1' + + # ========================= + # Uptime Kuma (separate due to auth) + # ========================= + - job_name: "kuma" + metrics_path: /metrics + scrape_interval: 30s + + basic_auth: + username: wayne.bennett@live.com + password: '4vjCco?[%{=+,t`):C' + + static_configs: + - targets: + - monitor-kuma:3001 + labels: + role: docker + + - targets: + - kuma.lan.ddnsgeek.com + labels: + role: raspberrypi + + # ========================= + # Proxmox Storage Exporters + # ========================= + - job_name: "proxmox-storage" + metrics_path: /metrics + static_configs: + - targets: + - pve.sweet.home:9101 + labels: + role: proxmox + storage: lvm + + - targets: + - pbs.sweet.home:9102 + labels: + role: backup + storage: datastore + + # ========================= + # Docker Updates Exporter + # ========================= + + + - job_name: "container-updates" + static_configs: + - targets: + - docker-update-exporter:9105 + labels: + role: docker + - targets: + - raspberrypi.tail13f623.ts.net:9105 + labels: + role: raspberrypi + + # ========================= + # pihole Exporter + # ========================= + + + - job_name: "pihole" + static_configs: + - targets: + - pihole-exporter:9617 + labels: + role: pihole + + +#rule_files: +# - /etc/prometheus/rules/*.yml diff --git a/monitoring/prometheus/rules/alerts.yml b/monitoring/prometheus/rules/alerts.yml new file mode 100644 index 0000000..9cc51eb --- /dev/null +++ b/monitoring/prometheus/rules/alerts.yml @@ -0,0 +1,28 @@ +groups: + - name: system + rules: + - alert: HostHighCPU +# expr: rate(node_cpu_seconds_total{mode!="idle"}[2m]) > 0.9 + expr: 100 * (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[2m]))) > 90 + for: 2m + labels: + severity: warning + annotations: + summary: "High CPU usage on host" + + - alert: ContainerRestarting +# expr: increase(container_start_time_seconds[10m]) > 3 + expr: increase(container_restart_count[10m]) > 3 + for: 1m + labels: + severity: warning + annotations: + summary: "Container restarting frequently" + + - alert: AlwaysFiring + expr: vector(1) + for: 10s + labels: + severity: critical + annotations: + summary: "This alert should always fire" diff --git a/monitoring/uptime-kuma/docker-compose.yml b/monitoring/uptime-kuma/docker-compose.yml new file mode 100644 index 0000000..cc76317 --- /dev/null +++ b/monitoring/uptime-kuma/docker-compose.yml @@ -0,0 +1,31 @@ +services: + monitor-kuma: + profiles: ["monitoring","all","uptime-kuma"] + image: louislam/uptime-kuma:2.1.1 + container_name: monitor-kuma + restart: always + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${PROJECT_ROOT}/monitoring/uptime-kuma/data:/app/data +# ports: +# - 8888:3001 + labels: + - traefik.enable=true + + # Router + - traefik.http.routers.monitor.rule=Host(`monitor-kuma.lan.ddnsgeek.com`) + - traefik.http.routers.monitor.entrypoints=websecure + - traefik.http.routers.monitor.tls=true + - traefik.http.routers.monitor.tls.certresolver=myresolver + - io.portainer.accesscontrol.public + - traefik.docker.network=core_traefik + # Service -> container port + - traefik.http.services.monitor.loadbalancer.server.port=3001 + networks: +# - traefik_reverse_proxy + - traefik + - monitor + +#networks: +# traefik_reverse_proxy: +# external: true diff --git a/nextcloud/docker-compose.yml b/nextcloud/docker-compose.yml deleted file mode 100644 index c6961c9..0000000 --- a/nextcloud/docker-compose.yml +++ /dev/null @@ -1,146 +0,0 @@ -services: - webapp: - image: nextcloud:production - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 - restart: always - hostname: nextcloud.lan.ddnsgeek.com - volumes: - - ./data:/var/www/html/data:rw - - ./config:/var/www/html/config:rw - depends_on: - - database - - redis - environment: - - MYSQL_PASSWORD=R1m@dmin - - MYSQL_DATABASE=nextcloud - - MYSQL_USER=nextcloud - - MYSQL_HOST=nextcloud_db:3306 - - NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.lan.ddnsgeek.com - - OVERWRITEPROTOCOL=https - - OVERWRITECLIURL=https://nextcloud.lan.ddnsgeek.com - - - SMTP_HOST=smtp-mail.outlook.com - - SMTP_SECURE=tls - - SMTP_PORT=587 - - SMTP_AUTHTYPE=login - - MAIL_FROM_ADDRESS=wayne.bennett@live.com - - MAIL_DOMAIN=live.com - - SMTP_NAME=wayne.bennett - - SMTP_PASSWORD=uscdbrjunqmkgglf - - - REDIS_HOST=redis - - REDIS_HOST_PORT=6379 - - REDIS_HOST_PASSWORD=${NEXTCLOUD_REDIS_PASSWORD} - networks: - - traefik_reverse_proxy - - internal - labels: - - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.lan.ddnsgeek.com`)" - - "traefik.enable=true" - - "traefik.http.routers.nextcloud.entrypoints=websecure" - - "traefik.http.routers.nextcloud.tls.certresolver=myresolver" - - "io.portainer.accesscontrol.public" - - "traefik.http.routers.nextcloud.middlewares=error-pages-middleware, nextcloud-dav, secHeaders@file, nextcloud-webfinger" - - "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav" - - "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/" - - "traefik.http.middlewares.nextcloud-nodeinfo.replacepathregex.regex=^/.well-known/nodeinfo" - - "traefik.http.middlewares.nextcloud-nodeinfo.replacepathregex.replacement=/nextcloud/index.php/.well-known/nodeinfo/" - - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.permanent=true" - - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.regex=https://(.*)/.well-known/webfinger" - - "traefik.http.middlewares.nextcloud-webfinger.redirectregex.replacement=https://$${1}/nextcloud/index.php/.well-known/webfinger" - - "traefik.docker.network=traefik_reverse_proxy" -# - "traefik.http.middlewares.nextcloudHeader.headers.stsSeconds=15552000" -# - "traefik.http.middlewares.nextcloudHeader.headers.stsIncludeSubdomains=true" -# - "traefik.http.middlewares.nextcloudHeader.headers.stsPreload=true" -# - "traefik.http.middlewares.nextcloudHeader.headers.forceSTSHeader=true" - -# - "traefik.http.routers.nextcloud.middlewares=error-pages-middleware, secHeaders@file, nextcloud_redirectregex, nextcloud-webfinger" -# - "traefik.http.middlewares.nextcloud_redirectregex.redirectregex.permanent=true" -# - "traefik.http.middlewares.nextcloud_redirectregex.redirectregex.regex='https://(.*)/.well-known/(?:card|cal)dav'" -# - "traefik.http.middlewares.nextcloud_redirectregex.redirectregex.replacement='https://$${1}/remote.php/dav'" - -# healthcheck: -# test: > -# CMD-SHELL -# php -r '$f=fsockopen("127.0.0.1",80,$e,$s,2); if(!$f) exit(1); -# fwrite($f,"GET /status.php HTTP/1.0\r\nHost: localhost\r\n\r\n"); -# $o=""; while(!feof($f)){$o.=fgets($f,128);} fclose($f); -# if(strpos($o,"\"installed\":true")===false) exit(1);' -# test: "curl -fsS http://127.0.0.1/status.php | grep -q '\"installed\":true'" -# test: > -# CMD-SHELL -# 'c=$(curl -fsS -o /dev/null -w "%{http_code}" http://127.0.0.1/status.php) \ -# && [ "$c" -ge 200 ] && [ "$c" -lt 400 ] \ -# && curl -fsS http://127.0.0.1/status.php | grep -q "\"installed\":true"' -# interval: 15s -# timeout: 5s -# retries: 10 -# start_period: 120s - - - - - database: - image: mariadb:12 - restart: always - hostname: nextcloud_db - command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 - volumes: - - ./database:/var/lib/mysql:rw - environment: - - MYSQL_ROOT_PASSWORD=R1m@dmin - - MYSQL_PASSWORD=R1m@dmin - - MYSQL_DATABASE=nextcloud - - MYSQL_USER=nextcloud - - MARIADB_AUTO_UPGRADE=1 - - NEXTCLOUD_ADMIN_USER=admin - - NEXTCLOUD_ADMIN_PASSWORD=R1m@dmin - networks: - - internal -# healthcheck: -# test: "/usr/bin/mysql --user=nextcloud --password=R1m@dmin --execute \"SHOW DATABASES;\"" - labels: - - "io.portainer.accesscontrol.public" - healthcheck: - test: ["CMD-SHELL", "mariadb-admin ping -h 127.0.0.1 -u\"$$MARIADB_USER\" -p\"$$MARIADB_PASSWORD\" --silent"] - interval: 10s - timeout: 5s - retries: 12 - start_period: 60s - - - redis: - image: "redis" - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 - command: ["redis-server", "--requirepass", "${NEXTCLOUD_REDIS_PASSWORD}", "--appendonly", "yes", "--save", "60", "1000"] - hostname: redis - volumes: - - ./data/redis:/data:rw - restart: unless-stopped - networks: - - internal - labels: - - "io.portainer.accesscontrol.public" - # healthcheck: - # test: ["CMD-SHELL", "redis-cli -a $$NEXTCLOUD_REDIS_PASSWORD PING | grep -q PONG"] - # interval: 10s - # timeout: 5s -# retries: 6 -# start_period: 10s - - -networks: - traefik_reverse_proxy: - external: true - internal: - driver: bridge diff --git a/services-up.sh b/services-up.sh new file mode 100755 index 0000000..dd9b591 --- /dev/null +++ b/services-up.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +ENV="default-environment.env" +PROJECT="core" +FILES=( + -f default-network.yml + -f core/docker-compose.yml + -f monitoring/prometheus/docker-compose.yml + -f monitoring/gotify/docker-compose.yml + -f monitoring/grafana/docker-compose.yml + -f monitoring/portainer/docker-compose.yml + -f monitoring/uptime-kuma/docker-compose.yml + -f apps/gitea/docker-compose.yml + -f apps/gramps/docker-compose.yml + -f apps/nextcloud/docker-compose.yml + -f apps/passbolt/docker-compose.yml + -f apps/searxng/docker-compose.yml + -f apps/shift-recorder/docker-compose.yml + -f apps/stockfill/docker-compose.yml + -f monitoring/node-red/docker-compose.yml + -f core/test/docker-compose.yml +) + +docker compose -p $PROJECT --env-file $ENV "${FILES[@]}" $1 $2 $3 $4 $5 $6 $7 $8 $9 diff --git a/traefik/docker-compose.yml b/traefik/docker-compose.yml deleted file mode 100644 index 593b289..0000000 --- a/traefik/docker-compose.yml +++ /dev/null @@ -1,88 +0,0 @@ -services: - reverse-proxy: - restart: always - # The official v2 Traefik docker image - image: traefik:3 - read_only: true - hostname: traefik.lan.ddnsgeek.com - depends_on: - - error-pages - # Enables the web UI and tells Traefik to listen to docker - command: - - "--log.level=INFO" - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--entrypoints.web.address=:80" - - "--entrypoints.websecure.address=:443" - - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - - "--certificatesresolvers.myresolver.acme.httpchallenge=true" - - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" - - "--certificatesresolvers.myresolver.acme.email=wayne.bennett@live.com" - - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" - - "--ping=true" - - "--api=true" - - "--api.dashboard=true" - - "--providers.file.filename=/plugins.yaml" -# - "--ping=true" -# - "--ping.entrypoint=web" - ports: - # The HTTP port - - "192.168.2.249:80:80" - - "192.168.2.249:443:443" - volumes: - # So that Traefik can listen to the Docker events - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./data/letsencrypt:/letsencrypt:rw - - ./data/plugins.yaml:/plugins.yaml:ro - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 - healthcheck: - test: traefik healthcheck --ping - - labels: - - "traefik.enable=true" - - "traefik.http.routers.traefik.rule=Host(`traefik.lan.ddnsgeek.com`)" - - "traefik.http.routers.traefik.service=api@internal" - - "traefik.http.routers.traefik.middlewares=auth, error-pages-middleware" - - "traefik.http.middlewares.auth.basicauth.users=beatzaplenty:$$apr1$$rQ8iCgI4$$Y/u2AttE3tb1sIQwrIGU0." - - "traefik.http.routers.traefik.entrypoints=websecure" - - "traefik.http.routers.traefik.tls.certresolver=myresolver" - - "traefik.http.routers.traefik.tls=true" - - "io.portainer.accesscontrol.public" - networks: - - reverse_proxy - - error-pages: - image: tarampampam/error-pages:3 # Using the latest tag is highly discouraged. Please, use tags in X.Y.Z format - read_only: true - environment: - TEMPLATE_NAME: app-down # set the error pages template - hostname: error-pages - restart: always - labels: - - "traefik.enable=true" - # use as "fallback" for any NON-registered services (with priority below normal) - - "traefik.http.routers.error-pages-router.rule=HostRegexp(`{host:.+}`)" - # should say that all of your services work on https - - "traefik.http.routers.error-pages-router.entrypoints=web" - - "traefik.http.routers.error-pages-router.middlewares=error-pages-middleware" - # "errors" middleware settings - - "traefik.http.middlewares.error-pages-middleware.errors.status=400-599" - - "traefik.http.middlewares.error-pages-middleware.errors.service=error-pages-service" - - "traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html" - # define service properties - - "traefik.http.services.error-pages-service.loadbalancer.server.port=8080" - - "io.portainer.accesscontrol.public" - deploy: - restart_policy: - condition: on-failure - max_attempts: 5 - networks: - - reverse_proxy - -networks: - reverse_proxy: - driver: bridge diff --git a/traefik/traefik.Dockerfile b/traefik/traefik.Dockerfile deleted file mode 100644 index c32576b..0000000 --- a/traefik/traefik.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM traefik:latest - -# Create unprivileged user. -RUN adduser -h /nonexistent -s /sbin/nologin -DH -g Traefik traefik - -# Initialize directories and permissions. -RUN mkdir -p /data -RUN chown -R traefik:traefik /data - -# Run as unprivileged user. -USER traefik:traefik - diff --git a/update-containers.py b/update-containers.py index 5f40d09..d914d2c 100755 --- a/update-containers.py +++ b/update-containers.py @@ -17,7 +17,7 @@ with open(LOG_FILE, 'w'): logging.basicConfig(filename=LOG_FILE, level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%H:%M:%S') -DEFAULT_STACKS = ["traefik", "nextcloud", "passbolt", "searxng", "gitea"] +DEFAULT_STACKS = ["traefik", "nextcloud", "passbolt", "searxng", "gitea", "gotify", "grafana", "gramps", "portainer", "prometheus", "uptime-kuma"] DOCKER_PATH = Path.home() / "docker" def run_compose_command(service_dir: Path, command: list[str]): @@ -60,7 +60,7 @@ def main(stacks): logging.info(f"Updating service: {service}") try: run_compose_command(service_dir, ["pull"]) - run_compose_command(service_dir, ["up", "-d"]) + run_compose_command(service_dir, ["up", "-d", "--build"]) check_container_status(docker_client, service) except Exception as e: logging.error(f"Failed to update {service}: {e}") diff --git a/update-containers.sh b/update-containers.sh index b6f0c94..ed16807 100755 --- a/update-containers.sh +++ b/update-containers.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash #APP="app" QM=0 -STACKS="traefik nextcloud passbolt searxng" +STACKS="traefik nextcloud passbolt searxng gitea" LOG_FILE="/mnt/docker-persistent-data/docker/update-containers.log" usage() { # echo "Usage: $0 [-q quiet mode optional list compose services to recreate]" 1>&2