From 78ab78f6aee78824a7a822bc1310f7db5f9ee2bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:44:11 +0000 Subject: [PATCH 01/27] Update odoo Docker tag to v20 --- apps/odoo/latest/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/odoo/latest/docker-compose.yml b/apps/odoo/latest/docker-compose.yml index 4cc5e54ac..14b6d99da 100644 --- a/apps/odoo/latest/docker-compose.yml +++ b/apps/odoo/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: odoo: - image: "odoo:19.0" + image: "odoo:20.0" container_name: ${CONTAINER_NAME} user: root restart: always From 37370a1bbc47eb0373e116faf1f892d1e0d33a8b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:41:00 +0000 Subject: [PATCH 02/27] Update registry.rocket.chat/rocketchat/rocket.chat Docker tag to v8.9.0 --- apps/rocketchat/8.7.0/docker-compose.yml | 2 +- apps/rocketchat/8.8.0/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/rocketchat/8.7.0/docker-compose.yml b/apps/rocketchat/8.7.0/docker-compose.yml index 482ef269a..524304a33 100644 --- a/apps/rocketchat/8.7.0/docker-compose.yml +++ b/apps/rocketchat/8.7.0/docker-compose.yml @@ -1,6 +1,6 @@ services: rocketchat: - image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1" + image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0" container_name: ${CONTAINER_NAME} restart: always depends_on: diff --git a/apps/rocketchat/8.8.0/docker-compose.yml b/apps/rocketchat/8.8.0/docker-compose.yml index 482ef269a..524304a33 100644 --- a/apps/rocketchat/8.8.0/docker-compose.yml +++ b/apps/rocketchat/8.8.0/docker-compose.yml @@ -1,6 +1,6 @@ services: rocketchat: - image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1" + image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0" container_name: ${CONTAINER_NAME} restart: always depends_on: From 7c2213e14f4e128c00dec0b0a528d5bfbc9ea143 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:29:54 +0000 Subject: [PATCH 03/27] Update seakee/cpa-manager-plus Docker tag to v1.14.5 --- apps/cpa-manager-plus/1.14.4/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cpa-manager-plus/1.14.4/docker-compose.yml b/apps/cpa-manager-plus/1.14.4/docker-compose.yml index 1f853eac0..384741415 100644 --- a/apps/cpa-manager-plus/1.14.4/docker-compose.yml +++ b/apps/cpa-manager-plus/1.14.4/docker-compose.yml @@ -1,6 +1,6 @@ services: cpa-manager-plus: - image: "seakee/cpa-manager-plus:v1.14.4" + image: "seakee/cpa-manager-plus:v1.14.5" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:18317" From 924158a00117194c4d1d1e8daba76bc6471575df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:39:28 +0000 Subject: [PATCH 04/27] Update finab/bark-server Docker tag to v2.3.8 --- apps/bark/2.3.7/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bark/2.3.7/docker-compose.yml b/apps/bark/2.3.7/docker-compose.yml index 156f030ce..62f5f4af5 100644 --- a/apps/bark/2.3.7/docker-compose.yml +++ b/apps/bark/2.3.7/docker-compose.yml @@ -1,6 +1,6 @@ services: bark: - image: "finab/bark-server:v2.3.7" + image: "finab/bark-server:v2.3.8" container_name: ${CONTAINER_NAME} restart: always networks: From c05e5eac334af98aeb812064ec1a2f3ea8593b82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:39:53 +0000 Subject: [PATCH 05/27] Update linuxserver/calibre Docker tag to v9.16.0 --- apps/calibre-linuxserver/9.15.0/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/calibre-linuxserver/9.15.0/docker-compose.yml b/apps/calibre-linuxserver/9.15.0/docker-compose.yml index 6a7635f83..771f200c6 100644 --- a/apps/calibre-linuxserver/9.15.0/docker-compose.yml +++ b/apps/calibre-linuxserver/9.15.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: calibre: - image: "linuxserver/calibre:9.15.0" + image: "linuxserver/calibre:9.16.0" container_name: ${CONTAINER_NAME} restart: always networks: From 3aaee433f699c6a3658a7e2589fb9a9abd7f9aaa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:38:27 +0000 Subject: [PATCH 06/27] Update docker.io/superseriousbusiness/gotosocial Docker tag to v0.22.2 --- apps/gotosocial/0.22.1/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gotosocial/0.22.1/docker-compose.yml b/apps/gotosocial/0.22.1/docker-compose.yml index af79aea9a..ff3713b58 100644 --- a/apps/gotosocial/0.22.1/docker-compose.yml +++ b/apps/gotosocial/0.22.1/docker-compose.yml @@ -1,6 +1,6 @@ services: gotosocial: - image: "docker.io/superseriousbusiness/gotosocial:0.22.1" + image: "docker.io/superseriousbusiness/gotosocial:0.22.2" container_name: ${CONTAINER_NAME} user: "1000:1000" restart: always From 5372e120c5a3e1837f81be24e19b4d32873bd6c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:38:49 +0000 Subject: [PATCH 07/27] Update linuxserver/adguardhome-sync Docker tag to v0.9.4 --- apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml b/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml index 101d5fdc0..8721f8f28 100644 --- a/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml +++ b/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: adguardhome-sync: - image: "linuxserver/adguardhome-sync:0.9.3" + image: "linuxserver/adguardhome-sync:0.9.4" container_name: ${CONTAINER_NAME} restart: always networks: From 196e3e13a9a47fe00601846dd678e0ae32f6bb13 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:38:26 +0000 Subject: [PATCH 08/27] Update linuxserver/darktable Docker tag to v25.6.2 --- apps/darktable-linuxserver/25.6.1/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/darktable-linuxserver/25.6.1/docker-compose.yml b/apps/darktable-linuxserver/25.6.1/docker-compose.yml index 13305b3fe..a6ee6c3a1 100644 --- a/apps/darktable-linuxserver/25.6.1/docker-compose.yml +++ b/apps/darktable-linuxserver/25.6.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: darktable: - image: "linuxserver/darktable:25.6.1" + image: "linuxserver/darktable:25.6.2" container_name: ${CONTAINER_NAME} restart: always networks: From a1497d4d644bb06ff496590776295133db823e7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:38:34 +0000 Subject: [PATCH 09/27] Update linuxserver/flexget Docker tag to v3.22.2 --- apps/flexget-linuxserver/3.22.1/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/flexget-linuxserver/3.22.1/docker-compose.yml b/apps/flexget-linuxserver/3.22.1/docker-compose.yml index f26b6ba8f..04bb764e8 100644 --- a/apps/flexget-linuxserver/3.22.1/docker-compose.yml +++ b/apps/flexget-linuxserver/3.22.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: flexget: - image: "linuxserver/flexget:3.22.1" + image: "linuxserver/flexget:3.22.2" container_name: ${CONTAINER_NAME} restart: always networks: From eb22bfa0cb33c886c4e759ac136fe78d72cafcf0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:38:41 +0000 Subject: [PATCH 10/27] Update linuxserver/nzbget Docker tag to v26.3.20261009 --- apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml b/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml index d7b543f28..c62444d04 100644 --- a/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml +++ b/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: nzbget: - image: "linuxserver/nzbget:26.3.20261002" + image: "linuxserver/nzbget:26.3.20261009" container_name: ${CONTAINER_NAME} restart: always networks: From 858615bc2d28b5bcbb2a76f1b2602140f43b4e4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:38:48 +0000 Subject: [PATCH 11/27] Update linuxserver/zen Docker tag to v1.23.2 --- apps/zen-linuxserver/1.23.1/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/zen-linuxserver/1.23.1/docker-compose.yml b/apps/zen-linuxserver/1.23.1/docker-compose.yml index ee37ec59e..e3e45f352 100644 --- a/apps/zen-linuxserver/1.23.1/docker-compose.yml +++ b/apps/zen-linuxserver/1.23.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: zen: - image: "linuxserver/zen:1.23.1" + image: "linuxserver/zen:1.23.2" container_name: ${CONTAINER_NAME} restart: always networks: From 7e7a22f1ef9e27c1675df73acae078c3181ec0fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 04:47:52 +0000 Subject: [PATCH 12/27] Update wiserain/flexget Docker tag to v3.22.2 --- apps/flexget/3.21.3/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/flexget/3.21.3/docker-compose.yml b/apps/flexget/3.21.3/docker-compose.yml index 80f14968c..4068ef331 100644 --- a/apps/flexget/3.21.3/docker-compose.yml +++ b/apps/flexget/3.21.3/docker-compose.yml @@ -14,7 +14,7 @@ services: - PGID=1000 - TZ=${TIME_ZONE} - FG_WEBUI_PASSWD=${WEB_PASSWORD} - image: wiserain/flexget:3.21.3 + image: wiserain/flexget:3.22.2 labels: createdBy: "Apps" From 771bd590379906f770ccf7ac089fd0b32fe58a1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 04:48:00 +0000 Subject: [PATCH 13/27] Update kuingsmile/piclist Docker tag to v3 --- apps/piclist/2.4.2/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/piclist/2.4.2/docker-compose.yml b/apps/piclist/2.4.2/docker-compose.yml index ac4f41c9f..e81d2f937 100644 --- a/apps/piclist/2.4.2/docker-compose.yml +++ b/apps/piclist/2.4.2/docker-compose.yml @@ -1,6 +1,6 @@ services: piclist: - image: "kuingsmile/piclist:v2.4.2" + image: "kuingsmile/piclist:v3.0.0" container_name: ${CONTAINER_NAME} restart: always networks: From 3c98b9b848207fc2c844e40c57167e02b2ecbed2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:51:06 +0000 Subject: [PATCH 14/27] Update linuxserver/grav Docker tag to v2.2.6 --- apps/grav-linuxserver/2.2.5/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/grav-linuxserver/2.2.5/docker-compose.yml b/apps/grav-linuxserver/2.2.5/docker-compose.yml index 6d5d023fd..4a8d4f1d5 100644 --- a/apps/grav-linuxserver/2.2.5/docker-compose.yml +++ b/apps/grav-linuxserver/2.2.5/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: grav: - image: "linuxserver/grav:2.2.5" + image: "linuxserver/grav:2.2.6" container_name: ${CONTAINER_NAME} restart: always networks: From ca15ddecd68c334da6ba5f7351af6b053734dcb6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:51:13 +0000 Subject: [PATCH 15/27] Update mtphotos/mt-photos Docker tag to v1.57.2 --- apps/mt-photos/1.57.1/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mt-photos/1.57.1/docker-compose.yml b/apps/mt-photos/1.57.1/docker-compose.yml index 25c1311d8..6cdf99be0 100644 --- a/apps/mt-photos/1.57.1/docker-compose.yml +++ b/apps/mt-photos/1.57.1/docker-compose.yml @@ -1,6 +1,6 @@ services: mt-photos: - image: "mtphotos/mt-photos:1.57.1" + image: "mtphotos/mt-photos:1.57.2" container_name: ${CONTAINER_NAME} restart: always networks: From daec97e4b14559c2d5855c0a418518b2cce1d783 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:08:49 +0000 Subject: [PATCH 16/27] Update linuxserver/ungoogled-chromium Docker tag to v155 --- .../154.0.8037/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml b/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml index 6c170428c..cb0772584 100644 --- a/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml +++ b/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: ungoogled-chromium: - image: "linuxserver/ungoogled-chromium:154.0.8037" + image: "linuxserver/ungoogled-chromium:155.0.8059" container_name: ${CONTAINER_NAME} restart: always networks: From 6e6e92b2ec7cf1ec13acb21791bd55a30849c004 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:09:01 +0000 Subject: [PATCH 17/27] Update phitux/dailytxt Docker tag to v2.6.4 --- apps/dailytxt/2.6.3/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dailytxt/2.6.3/docker-compose.yml b/apps/dailytxt/2.6.3/docker-compose.yml index cbf14dd6e..33b65735b 100644 --- a/apps/dailytxt/2.6.3/docker-compose.yml +++ b/apps/dailytxt/2.6.3/docker-compose.yml @@ -1,6 +1,6 @@ services: dailytxt: - image: "phitux/dailytxt:2.6.3" + image: "phitux/dailytxt:2.6.4" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: From 827ca3806731db4bf31bd7eb013d80c5a15a70fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:35:56 +0000 Subject: [PATCH 18/27] Update ghcr.io/dagucloud/dagu Docker tag to v2.19.0 --- apps/dagu/2.18.2/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dagu/2.18.2/docker-compose.yml b/apps/dagu/2.18.2/docker-compose.yml index a513ff9b5..2c03e1c9b 100644 --- a/apps/dagu/2.18.2/docker-compose.yml +++ b/apps/dagu/2.18.2/docker-compose.yml @@ -1,6 +1,6 @@ services: dagu: - image: "ghcr.io/dagucloud/dagu:2.18.2" + image: "ghcr.io/dagucloud/dagu:2.19.0" container_name: ${CONTAINER_NAME} restart: always networks: From de16e6363cda53bf62a9e5bb5eddd55667219a69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:54:50 +0000 Subject: [PATCH 19/27] Update linuxserver/vlc Docker tag to v3.0.24 --- apps/vlc-linuxserver/3.0.23/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vlc-linuxserver/3.0.23/docker-compose.yml b/apps/vlc-linuxserver/3.0.23/docker-compose.yml index ffde7f888..e4dd60f90 100644 --- a/apps/vlc-linuxserver/3.0.23/docker-compose.yml +++ b/apps/vlc-linuxserver/3.0.23/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: vlc: - image: "linuxserver/vlc:3.0.23" + image: "linuxserver/vlc:3.0.24" container_name: ${CONTAINER_NAME} restart: always networks: From ce2fcc99b16075c7625a72b352d5cbc4a649f05a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:43:34 +0000 Subject: [PATCH 20/27] Update linuxserver/qdirstat Docker tag to v2.0.20261012 --- apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml b/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml index 650f2fd06..80ba89afa 100644 --- a/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml +++ b/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: qdirstat: - image: "linuxserver/qdirstat:2.0.20260928" + image: "linuxserver/qdirstat:2.0.20261012" container_name: ${CONTAINER_NAME} restart: always networks: From 21f31cdb5bca36027f31b26761400eab5439778d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 03:35:55 +0000 Subject: [PATCH 21/27] Update guovern/iptv-api Docker tag to v3.1.0 --- apps/iptv-api/3.0.0/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/iptv-api/3.0.0/docker-compose.yml b/apps/iptv-api/3.0.0/docker-compose.yml index e59674dee..d06d56442 100644 --- a/apps/iptv-api/3.0.0/docker-compose.yml +++ b/apps/iptv-api/3.0.0/docker-compose.yml @@ -1,6 +1,6 @@ services: iptv-api: - image: "guovern/iptv-api:3.0.0" + image: "guovern/iptv-api:3.1.0" container_name: ${CONTAINER_NAME} restart: always tty: true From d862bf2dc5908dc6466efccdd9c0f6c3383d0400 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2026 08:02:57 +0000 Subject: [PATCH 22/27] Update linuxserver/healthchecks Docker tag to v4.4.20261012 --- apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml b/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml index 0caad2886..6b2d8945e 100644 --- a/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml +++ b/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: healthchecks: - image: "linuxserver/healthchecks:4.4.20261005" + image: "linuxserver/healthchecks:4.4.20261012" container_name: ${CONTAINER_NAME} restart: always networks: From 44a82f331bffb7dfc46d7158db01f1406ed3306e Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:15:54 +0800 Subject: [PATCH 23/27] fix(bookstack): preserve 26.05 package for 26.09 update --- .../26.09.20260924/.env.sample | 13 ++ .../26.09.20260924/data.yml | 204 ++++++++++++++++++ .../26.09.20260924/data/.gitkeep | 0 .../26.09.20260924/docker-compose.yml | 44 ++++ .../26.09.20260924/scripts/init.sh | 138 ++++++++++++ .../26.09.20260924/scripts/uninstall.sh | 2 + .../26.09.20260924/scripts/upgrade.sh | 152 +++++++++++++ 7 files changed, 553 insertions(+) create mode 100644 apps/bookstack-linuxserver/26.09.20260924/.env.sample create mode 100644 apps/bookstack-linuxserver/26.09.20260924/data.yml create mode 100644 apps/bookstack-linuxserver/26.09.20260924/data/.gitkeep create mode 100644 apps/bookstack-linuxserver/26.09.20260924/docker-compose.yml create mode 100755 apps/bookstack-linuxserver/26.09.20260924/scripts/init.sh create mode 100755 apps/bookstack-linuxserver/26.09.20260924/scripts/uninstall.sh create mode 100755 apps/bookstack-linuxserver/26.09.20260924/scripts/upgrade.sh diff --git a/apps/bookstack-linuxserver/26.09.20260924/.env.sample b/apps/bookstack-linuxserver/26.09.20260924/.env.sample new file mode 100644 index 000000000..1c3f1ae34 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/.env.sample @@ -0,0 +1,13 @@ +CONTAINER_NAME= +PANEL_APP_PORT_HTTP=6875 +CONFIG_PATH=./data/config +APP_URL=http://bookstack.example.com +APP_KEY=base64:replace-with-generated-app-key +DB_HOST=bookstack-db +DB_PORT=3306 +DB_USERNAME=bookstack +DB_DATABASE=bookstack +DB_PASSWORD=replace-with-db-password +QUEUE_CONNECTION= +DB_DATA_PATH=./data/db +TIME_ZONE=Asia/Shanghai diff --git a/apps/bookstack-linuxserver/26.09.20260924/data.yml b/apps/bookstack-linuxserver/26.09.20260924/data.yml new file mode 100644 index 000000000..3c9be7783 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/data.yml @@ -0,0 +1,204 @@ +additionalProperties: + formFields: + - default: 6875 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: '' + edit: true + envKey: APP_URL + labelEn: Application URL + labelZh: 应用访问 URL + label: + en: Application URL + zh: 应用访问 URL + zh-Hant: 應用程式存取 URL + ja: アプリケーション URL + ko: 애플리케이션 URL + ru: URL приложения + ms: URL aplikasi + pt-br: URL do aplicativo + required: true + type: text + - default: '' + edit: true + envKey: APP_KEY + labelEn: Application key + labelZh: 应用密钥 + label: + en: Application key + zh: 应用密钥 + zh-Hant: 應用程式密鑰 + ja: アプリケーションキー + ko: 애플리케이션 키 + ru: Ключ приложения + ms: Kunci aplikasi + pt-br: Chave do aplicativo + required: true + random: true + type: password + rule: paramComplexity + - default: bookstack-db + edit: true + envKey: DB_HOST + labelEn: Database host + labelZh: 数据库主机 + label: + en: Database host + zh: 数据库主机 + zh-Hant: 資料庫主機 + ja: データベースホスト + ko: 데이터베이스 호스트 + ru: Хост базы данных + ms: Hos pangkalan data + pt-br: Host do banco + required: true + type: text + - default: 3306 + edit: true + envKey: DB_PORT + labelEn: Database port + labelZh: 数据库端口 + label: + en: Database port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco + required: true + type: number + - default: bookstack + edit: true + envKey: DB_USERNAME + labelEn: Database username + labelZh: 数据库用户名 + label: + en: Database username + zh: 数据库用户名 + zh-Hant: 資料庫使用者名稱 + ja: データベースユーザー名 + ko: 데이터베이스 사용자 이름 + ru: Имя пользователя базы + ms: Nama pengguna pangkalan data + pt-br: Usuario do banco + required: true + type: text + - default: bookstack + edit: true + envKey: DB_DATABASE + labelEn: Database name + labelZh: 数据库名称 + label: + en: Database name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco + required: true + type: text + - default: '' + edit: true + envKey: DB_PASSWORD + labelEn: Database password + labelZh: 数据库密码 + label: + en: Database password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: QUEUE_CONNECTION + labelEn: Async queue connection + labelZh: 异步队列连接 + label: + en: Async queue connection + zh: 异步队列连接 + zh-Hant: 非同步佇列連線 + ja: 非同期キュー接続 + ko: 비동기 큐 연결 + ru: Подключение асинхронной очереди + ms: Sambungan baris gilir tak segerak + pt-br: Conexao de fila assincrona + required: false + type: select + values: + - label: disabled + value: '' + - label: database + value: database + - default: ./data/db + edit: true + envKey: DB_DATA_PATH + labelEn: Database data path + labelZh: 数据库数据目录 + label: + en: Database data path + zh: 数据库数据目录 + zh-Hant: 資料庫資料目錄 + ja: データベースデータディレクトリ + ko: 데이터베이스 데이터 디렉터리 + ru: Каталог данных базы + ms: Laluan data pangkalan data + pt-br: Caminho de dados do banco + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/bookstack-linuxserver/26.09.20260924/data/.gitkeep b/apps/bookstack-linuxserver/26.09.20260924/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/bookstack-linuxserver/26.09.20260924/docker-compose.yml b/apps/bookstack-linuxserver/26.09.20260924/docker-compose.yml new file mode 100644 index 000000000..04cd10b57 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/docker-compose.yml @@ -0,0 +1,44 @@ +networks: + 1panel-network: + external: true +services: + bookstack: + image: "linuxserver/bookstack:26.09.20260924" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + - APP_URL=${APP_URL} + - APP_KEY=${APP_KEY} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - DB_DATABASE=${DB_DATABASE} + - QUEUE_CONNECTION=${QUEUE_CONNECTION:-} + depends_on: + - bookstack-db + labels: + createdBy: "Apps" + bookstack-db: + image: "mariadb:11.4" + restart: always + networks: + - 1panel-network + volumes: + - "${DB_DATA_PATH}:/var/lib/mysql" + environment: + - MARIADB_ROOT_PASSWORD=${DB_PASSWORD} + - MARIADB_DATABASE=${DB_DATABASE} + - MARIADB_USER=${DB_USERNAME} + - MARIADB_PASSWORD=${DB_PASSWORD} + labels: + createdBy: "Apps" diff --git a/apps/bookstack-linuxserver/26.09.20260924/scripts/init.sh b/apps/bookstack-linuxserver/26.09.20260924/scripts/init.sh new file mode 100755 index 000000000..b80a36e37 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/scripts/init.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +LEGACY_BOOKSTACK_APP_KEY="base64:j1wQ2YbRrQ3j0tG5h6v8cYpV1gW9mN4uQ2xZ7aE3sT8=" +LEGACY_BOOKSTACK_DB_PASSWORD="bookstack-change-me" + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +generate_laravel_app_key() { + if command -v openssl >/dev/null 2>&1; then + printf 'base64:%s\n' "$(openssl rand -base64 32 | tr -d '\r\n')" + return + fi + + if command -v base64 >/dev/null 2>&1; then + printf 'base64:%s\n' "$(head -c 32 /dev/urandom | base64 | tr -d '\r\n')" + return + fi + + generate_alnum_secret +} + +laravel_app_key_is_valid() { + local value="$1" + [[ "$value" =~ ^base64:[A-Za-z0-9+/=]{43,}$ ]] && return 0 + [[ "$value" =~ ^[A-Za-z0-9]{32,}$ ]] && return 0 + return 1 +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +APP_KEY_CACHE="${APP_KEY_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_app_key}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_db_password}" + +if [[ -f "$ENV_FILE" ]]; then + app_key="$(read_env_value APP_KEY || true)" + cached_app_key="$(read_cache_value "$APP_KEY_CACHE" || true)" + if laravel_app_key_is_valid "$app_key" && [[ "$app_key" != "$LEGACY_BOOKSTACK_APP_KEY" ]]; then + write_cache_value "$APP_KEY_CACHE" "$app_key" + elif laravel_app_key_is_valid "$cached_app_key"; then + set_env_value "APP_KEY" "$cached_app_key" + else + app_key="$(generate_laravel_app_key)" + set_env_value "APP_KEY" "$app_key" + write_cache_value "$APP_KEY_CACHE" "$app_key" + fi + + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_BOOKSTACK_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi +fi + +paths=( + "${CONFIG_PATH:-./data/config}" + "${DB_DATA_PATH:-./data/db}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/bookstack-linuxserver/26.09.20260924/scripts/uninstall.sh b/apps/bookstack-linuxserver/26.09.20260924/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/bookstack-linuxserver/26.09.20260924/scripts/upgrade.sh b/apps/bookstack-linuxserver/26.09.20260924/scripts/upgrade.sh new file mode 100755 index 000000000..8d29e5f48 --- /dev/null +++ b/apps/bookstack-linuxserver/26.09.20260924/scripts/upgrade.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +LEGACY_BOOKSTACK_APP_KEY="base64:j1wQ2YbRrQ3j0tG5h6v8cYpV1gW9mN4uQ2xZ7aE3sT8=" +LEGACY_BOOKSTACK_DB_PASSWORD="bookstack-change-me" + +ensure_env_default() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key migration" + return + fi + + if grep -qE "^${key}=" "$ENV_FILE"; then + echo "$key already exists" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +generate_laravel_app_key() { + if command -v openssl >/dev/null 2>&1; then + printf 'base64:%s\n' "$(openssl rand -base64 32 | tr -d '\r\n')" + return + fi + + if command -v base64 >/dev/null 2>&1; then + printf 'base64:%s\n' "$(head -c 32 /dev/urandom | base64 | tr -d '\r\n')" + return + fi + + generate_alnum_secret +} + +laravel_app_key_is_valid() { + local value="$1" + [[ "$value" =~ ^base64:[A-Za-z0-9+/=]{43,}$ ]] && return 0 + [[ "$value" =~ ^[A-Za-z0-9]{32,}$ ]] && return 0 + return 1 +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +APP_KEY_CACHE="${APP_KEY_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_app_key}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_db_password}" + +if [[ -f "$ENV_FILE" ]]; then + app_key="$(read_env_value APP_KEY || true)" + cached_app_key="$(read_cache_value "$APP_KEY_CACHE" || true)" + if laravel_app_key_is_valid "$app_key" && [[ "$app_key" != "$LEGACY_BOOKSTACK_APP_KEY" ]]; then + write_cache_value "$APP_KEY_CACHE" "$app_key" + elif laravel_app_key_is_valid "$cached_app_key"; then + set_env_value "APP_KEY" "$cached_app_key" + else + app_key="$(generate_laravel_app_key)" + set_env_value "APP_KEY" "$app_key" + write_cache_value "$APP_KEY_CACHE" "$app_key" + fi + + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_BOOKSTACK_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi + + ensure_env_default "DB_HOST" "bookstack-db" + ensure_env_default "DB_PORT" "3306" + ensure_env_default "DB_USERNAME" "bookstack" + ensure_env_default "DB_DATABASE" "bookstack" + ensure_env_default "QUEUE_CONNECTION" "" +else + echo "$ENV_FILE not found; skipped LinuxServer environment migration" +fi From e09671caee257bb5f10f6845415ff0e425a766cb Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:18:58 +0800 Subject: [PATCH 24/27] Add primary service mappings for queued apps (#7686) --- .github/renovate-primary-services.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/renovate-primary-services.json b/.github/renovate-primary-services.json index 6257b6a4a..933915885 100644 --- a/.github/renovate-primary-services.json +++ b/.github/renovate-primary-services.json @@ -93,5 +93,14 @@ ], "zipline": [ "zipline" + ], + "bookstack-linuxserver": [ + "bookstack" + ], + "mastodon-linuxserver": [ + "mastodon" + ], + "odoo": [ + "odoo" ] } From 67717f1ed621d1958ca5a92e19fb3d950bf4323c Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Sat, 17 Oct 2026 07:48:50 +0800 Subject: [PATCH 25/27] fix(hermes-agent): preserve 2026.9.21 upgrade source --- apps/hermes-agent/2026.9.24/.env.sample | 97 ++ apps/hermes-agent/2026.9.24/data.yml | 1215 +++++++++++++++++ apps/hermes-agent/2026.9.24/data/.gitkeep | 0 .../hermes-agent/2026.9.24/docker-compose.yml | 92 ++ apps/hermes-agent/2026.9.24/scripts/init.sh | 76 ++ .../2026.9.24/scripts/uninstall.sh | 2 + .../hermes-agent/2026.9.24/scripts/upgrade.sh | 76 ++ 7 files changed, 1558 insertions(+) create mode 100755 apps/hermes-agent/2026.9.24/.env.sample create mode 100755 apps/hermes-agent/2026.9.24/data.yml create mode 100755 apps/hermes-agent/2026.9.24/data/.gitkeep create mode 100755 apps/hermes-agent/2026.9.24/docker-compose.yml create mode 100755 apps/hermes-agent/2026.9.24/scripts/init.sh create mode 100755 apps/hermes-agent/2026.9.24/scripts/uninstall.sh create mode 100755 apps/hermes-agent/2026.9.24/scripts/upgrade.sh diff --git a/apps/hermes-agent/2026.9.24/.env.sample b/apps/hermes-agent/2026.9.24/.env.sample new file mode 100755 index 000000000..eee8ffdd4 --- /dev/null +++ b/apps/hermes-agent/2026.9.24/.env.sample @@ -0,0 +1,97 @@ +# Core runtime +CONTAINER_NAME=hermes-agent +TZ=Asia/Shanghai +APP_DATA_DIR=./data +HERMES_UID=10000 +HERMES_GID=10000 +PANEL_APP_PORT_API=8642 +GATEWAY_STATIC_IP=172.18.0.240 +API_SERVER_KEY=change_me_with_a_random_api_key +API_SERVER_CORS_ORIGINS=* + +# LLM providers +OPENAI_API_KEY= +OPENROUTER_API_KEY= +ANTHROPIC_TOKEN= +ANTHROPIC_BASE_URL= +GOOGLE_API_KEY= +GLM_API_KEY= +GLM_BASE_URL=https://api.z.ai/api/paas/v4 +KIMI_API_KEY= +KIMI_BASE_URL=https://api.kimi.com/coding/v1 +MINIMAX_API_KEY= +MINIMAX_BASE_URL=https://api.minimax.io/v1 +GROQ_API_KEY= +MISTRAL_API_KEY= +COHERE_API_KEY= +DEEPSEEK_API_KEY= +TOGETHER_API_KEY= +PERPLEXITY_API_KEY= +FIREWORKS_API_KEY= +XAI_API_KEY= +HF_TOKEN= + +# Search / crawling / telemetry +EXA_API_KEY= +PARALLEL_API_KEY= +FIRECRAWL_API_KEY= +FIRECRAWL_API_URL= +HELICONE_API_KEY= + +# Gateway access control +GATEWAY_ALLOWED_USERS= + +# Telegram +TELEGRAM_BOT_TOKEN= +TELEGRAM_ALLOWED_USERS= +TELEGRAM_HOME_CHANNEL= +TELEGRAM_HOME_CHANNEL_NAME= + +# Discord +DISCORD_HOME_CHANNEL= +DISCORD_HOME_CHANNEL_NAME= +DISCORD_REQUIRE_MENTION=false +DISCORD_FREE_RESPONSE_CHANNELS= +DISCORD_AUTO_THREAD=false + +# Slack +SLACK_HOME_CHANNEL= +SLACK_HOME_CHANNEL_NAME= +SLACK_ALLOWED_USERS= + +# WhatsApp +WHATSAPP_ENABLED=false +WHATSAPP_MODE=personal +WHATSAPP_ALLOWED_USERS= + +# Signal +SIGNAL_HTTP_URL= +SIGNAL_ACCOUNT= +SIGNAL_ALLOWED_USERS= +SIGNAL_GROUP_ALLOWED_USERS= +SIGNAL_HOME_CHANNEL= +SIGNAL_HOME_CHANNEL_NAME= +SIGNAL_IGNORE_STORIES=false + +# Email +EMAIL_ADDRESS= +EMAIL_PASSWORD= +EMAIL_IMAP_HOST= +EMAIL_SMTP_HOST= +EMAIL_HOME_ADDRESS= +EMAIL_HOME_ADDRESS_NAME= + +# GitHub +GH_TOKEN= +GITHUB_APP_ID= +GITHUB_APP_PRIVATE_KEY_PATH= +GITHUB_APP_INSTALLATION_ID= + +# Home Assistant +HASS_URL= +HASS_TOKEN= + +# Modal / Daytona +MODAL_TOKEN_ID= +MODAL_TOKEN_SECRET= +DAYTONA_API_KEY= diff --git a/apps/hermes-agent/2026.9.24/data.yml b/apps/hermes-agent/2026.9.24/data.yml new file mode 100755 index 000000000..60bc5a92b --- /dev/null +++ b/apps/hermes-agent/2026.9.24/data.yml @@ -0,0 +1,1215 @@ +additionalProperties: + formFields: + - default: "安装后容器会以 gateway run 常驻运行,并启用 8642 端口的 OpenAI-compatible API 与健康检查。API_SERVER_KEY 用于保护除公开健康检查外的 API,请保持随机且不要泄露;旧版本升级时脚本只会为缺失或空白值生成随机 Key,不覆盖已有配置。至少需要配置 1 个模型提供商 Key;若要直接接入 Telegram,再补 TELEGRAM_BOT_TOKEN。容器使用 HERMES_UID / HERMES_GID(默认 10000:10000)访问数据目录。" + disabled: true + envKey: TIPS + labelEn: Tips + labelZh: 提示 + label: + en: Tips + zh: 提示 + zh-Hant: 提示 + ja: ヒント + ko: 팁 + ru: Подсказка + ms: Petua + pt-br: Dicas + edit: true + required: true + type: text + - default: Asia/Shanghai + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + edit: true + required: true + type: text + - default: ./data + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretório de dados + edit: true + required: true + type: text + - default: 10000 + envKey: HERMES_UID + labelEn: Hermes Runtime UID + labelZh: Hermes 运行用户 UID + label: + en: Hermes Runtime UID + zh: Hermes 运行用户 UID + zh-Hant: Hermes 執行使用者 UID + ja: Hermes 実行ユーザー UID + ko: Hermes 실행 사용자 UID + ru: UID пользователя Hermes + ms: UID pengguna runtime Hermes + pt-br: UID do usuário de execução do Hermes + edit: true + required: true + type: number + - default: 10000 + envKey: HERMES_GID + labelEn: Hermes Runtime GID + labelZh: Hermes 运行用户 GID + label: + en: Hermes Runtime GID + zh: Hermes 运行用户 GID + zh-Hant: Hermes 執行使用者 GID + ja: Hermes 実行ユーザー GID + ko: Hermes 실행 사용자 GID + ru: GID пользователя Hermes + ms: GID pengguna runtime Hermes + pt-br: GID do usuário de execução do Hermes + edit: true + required: true + type: number + - default: 8642 + envKey: PANEL_APP_PORT_API + labelEn: Gateway API Port + labelZh: 网关 API 端口 + label: + en: Gateway API Port + zh: 网关 API 端口 + zh-Hant: 閘道 API 連接埠 + ja: Gateway API ポート + ko: 게이트웨이 API 포트 + ru: Порт API шлюза + ms: Port API gerbang + pt-br: Porta da API do gateway + edit: true + required: true + rule: paramPort + type: number + - default: "" + envKey: API_SERVER_KEY + labelEn: Gateway API Key + labelZh: 网关 API 密钥 + label: + en: Gateway API Key + zh: 网关 API 密钥 + zh-Hant: 閘道 API 金鑰 + ja: Gateway API キー + ko: 게이트웨이 API 키 + ru: API-ключ шлюза + ms: Kunci API gateway + pt-br: Chave da API do gateway + edit: true + random: true + required: true + rule: paramComplexity + type: password + - default: "*" + envKey: API_SERVER_CORS_ORIGINS + labelEn: Gateway CORS Origins + labelZh: 网关 CORS 来源 + label: + en: Gateway CORS Origins + zh: 网关 CORS 来源 + zh-Hant: 閘道 CORS 來源 + ja: Gateway CORS オリジン + ko: 게이트웨이 CORS 출처 + ru: Источники CORS шлюза + ms: Asal CORS gateway + pt-br: Origens CORS do gateway + edit: true + required: true + type: text + - default: 172.18.0.240 + envKey: GATEWAY_STATIC_IP + labelEn: Gateway Static IP + labelZh: 网关静态 IP + label: + en: Gateway Static IP + zh: 网关静态 IP + zh-Hant: 閘道靜態 IP + ja: Gateway 静的 IP + ko: 게이트웨이 고정 IP + ru: Статический IP шлюза + ms: IP statik gateway + pt-br: IP estático do gateway + edit: true + required: true + type: text + - default: "" + envKey: OPENAI_API_KEY + labelEn: OpenAI / OpenAI-compatible API Key + labelZh: OpenAI / OpenAI-compatible API 密钥 + label: + en: OpenAI / OpenAI-compatible API Key + zh: OpenAI / OpenAI-compatible API 密钥 + zh-Hant: OpenAI / OpenAI-compatible API 金鑰 + ja: OpenAI / OpenAI-compatible API キー + ko: OpenAI / OpenAI-compatible API 키 + ru: API-ключ OpenAI / OpenAI-compatible + ms: Kunci API OpenAI / OpenAI-compatible + pt-br: Chave de API OpenAI / OpenAI-compatible + edit: true + required: false + type: password + - default: "" + envKey: OPENROUTER_API_KEY + labelEn: OpenRouter API Key + labelZh: OpenRouter API 密钥 + label: + en: OpenRouter API Key + zh: OpenRouter API 密钥 + zh-Hant: OpenRouter API 金鑰 + ja: OpenRouter API キー + ko: OpenRouter API 키 + ru: API-ключ OpenRouter + ms: Kunci API OpenRouter + pt-br: Chave de API OpenRouter + edit: true + required: false + type: password + - default: "" + envKey: ANTHROPIC_TOKEN + labelEn: Anthropic API Token + labelZh: Anthropic API 密钥 + label: + en: Anthropic API Token + zh: Anthropic API 密钥 + zh-Hant: Anthropic API 金鑰 + ja: Anthropic API トークン + ko: Anthropic API 토큰 + ru: Токен API Anthropic + ms: Token API Anthropic + pt-br: Token da API Anthropic + edit: true + required: false + type: password + - default: "" + envKey: ANTHROPIC_BASE_URL + labelEn: Anthropic Base URL + labelZh: Anthropic 基础地址 + label: + en: Anthropic Base URL + zh: Anthropic 基础地址 + zh-Hant: Anthropic 基礎位址 + ja: Anthropic ベース URL + ko: Anthropic 기본 URL + ru: Базовый URL Anthropic + ms: URL Asas Anthropic + pt-br: URL base da Anthropic + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: GOOGLE_API_KEY + labelEn: Google API Key + labelZh: Google API 密钥 + label: + en: Google API Key + zh: Google API 密钥 + zh-Hant: Google API 金鑰 + ja: Google API キー + ko: Google API 키 + ru: API-ключ Google + ms: Kunci API Google + pt-br: Chave de API Google + edit: true + required: false + type: password + - default: "" + envKey: DEEPSEEK_API_KEY + labelEn: DeepSeek API Key + labelZh: DeepSeek API 密钥 + label: + en: DeepSeek API Key + zh: DeepSeek API 密钥 + zh-Hant: DeepSeek API 金鑰 + ja: DeepSeek API キー + ko: DeepSeek API 키 + ru: API-ключ DeepSeek + ms: Kunci API DeepSeek + pt-br: Chave de API DeepSeek + edit: true + required: false + type: password + - default: "" + envKey: MISTRAL_API_KEY + labelEn: Mistral API Key + labelZh: Mistral API 密钥 + label: + en: Mistral API Key + zh: Mistral API 密钥 + zh-Hant: Mistral API 金鑰 + ja: Mistral API キー + ko: Mistral API 키 + ru: API-ключ Mistral + ms: Kunci API Mistral + pt-br: Chave de API Mistral + edit: true + required: false + type: password + - default: "" + envKey: GROQ_API_KEY + labelEn: Groq API Key + labelZh: Groq API 密钥 + label: + en: Groq API Key + zh: Groq API 密钥 + zh-Hant: Groq API 金鑰 + ja: Groq API キー + ko: Groq API 키 + ru: API-ключ Groq + ms: Kunci API Groq + pt-br: Chave de API Groq + edit: true + required: false + type: password + - default: "" + envKey: TOGETHER_API_KEY + labelEn: Together API Key + labelZh: Together API 密钥 + label: + en: Together API Key + zh: Together API 密钥 + zh-Hant: Together API 金鑰 + ja: Together API キー + ko: Together API 키 + ru: API-ключ Together + ms: Kunci API Together + pt-br: Chave de API Together + edit: true + required: false + type: password + - default: "" + envKey: PERPLEXITY_API_KEY + labelEn: Perplexity API Key + labelZh: Perplexity API 密钥 + label: + en: Perplexity API Key + zh: Perplexity API 密钥 + zh-Hant: Perplexity API 金鑰 + ja: Perplexity API キー + ko: Perplexity API 키 + ru: API-ключ Perplexity + ms: Kunci API Perplexity + pt-br: Chave de API Perplexity + edit: true + required: false + type: password + - default: "" + envKey: COHERE_API_KEY + labelEn: Cohere API Key + labelZh: Cohere API 密钥 + label: + en: Cohere API Key + zh: Cohere API 密钥 + zh-Hant: Cohere API 金鑰 + ja: Cohere API キー + ko: Cohere API 키 + ru: API-ключ Cohere + ms: Kunci API Cohere + pt-br: Chave de API Cohere + edit: true + required: false + type: password + - default: "" + envKey: FIREWORKS_API_KEY + labelEn: Fireworks API Key + labelZh: Fireworks API 密钥 + label: + en: Fireworks API Key + zh: Fireworks API 密钥 + zh-Hant: Fireworks API 金鑰 + ja: Fireworks API キー + ko: Fireworks API 키 + ru: API-ключ Fireworks + ms: Kunci API Fireworks + pt-br: Chave de API Fireworks + edit: true + required: false + type: password + - default: "" + envKey: XAI_API_KEY + labelEn: xAI API Key + labelZh: xAI API 密钥 + label: + en: xAI API Key + zh: xAI API 密钥 + zh-Hant: xAI API 金鑰 + ja: xAI API キー + ko: xAI API 키 + ru: API-ключ xAI + ms: Kunci API xAI + pt-br: Chave de API xAI + edit: true + required: false + type: password + - default: "" + envKey: GLM_API_KEY + labelEn: GLM API Key + labelZh: GLM API 密钥 + label: + en: GLM API Key + zh: GLM API 密钥 + zh-Hant: GLM API 金鑰 + ja: GLM API キー + ko: GLM API 키 + ru: API-ключ GLM + ms: Kunci API GLM + pt-br: Chave de API GLM + edit: true + required: false + type: password + - default: https://api.z.ai/api/paas/v4 + envKey: GLM_BASE_URL + labelEn: GLM Base URL + labelZh: GLM 基础地址 + label: + en: GLM Base URL + zh: GLM 基础地址 + zh-Hant: GLM 基礎位址 + ja: GLM ベース URL + ko: GLM 기본 URL + ru: Базовый URL GLM + ms: URL Asas GLM + pt-br: URL base do GLM + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: KIMI_API_KEY + labelEn: Kimi API Key + labelZh: Kimi API 密钥 + label: + en: Kimi API Key + zh: Kimi API 密钥 + zh-Hant: Kimi API 金鑰 + ja: Kimi API キー + ko: Kimi API 키 + ru: API-ключ Kimi + ms: Kunci API Kimi + pt-br: Chave de API Kimi + edit: true + required: false + type: password + - default: https://api.kimi.com/coding/v1 + envKey: KIMI_BASE_URL + labelEn: Kimi Base URL + labelZh: Kimi 基础地址 + label: + en: Kimi Base URL + zh: Kimi 基础地址 + zh-Hant: Kimi 基礎位址 + ja: Kimi ベース URL + ko: Kimi 기본 URL + ru: Базовый URL Kimi + ms: URL Asas Kimi + pt-br: URL base do Kimi + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: MINIMAX_API_KEY + labelEn: MiniMax API Key + labelZh: MiniMax API 密钥 + label: + en: MiniMax API Key + zh: MiniMax API 密钥 + zh-Hant: MiniMax API 金鑰 + ja: MiniMax API キー + ko: MiniMax API 키 + ru: API-ключ MiniMax + ms: Kunci API MiniMax + pt-br: Chave de API MiniMax + edit: true + required: false + type: password + - default: https://api.minimax.io/v1 + envKey: MINIMAX_BASE_URL + labelEn: MiniMax Base URL + labelZh: MiniMax 基础地址 + label: + en: MiniMax Base URL + zh: MiniMax 基础地址 + zh-Hant: MiniMax 基礎位址 + ja: MiniMax ベース URL + ko: MiniMax 기본 URL + ru: Базовый URL MiniMax + ms: URL Asas MiniMax + pt-br: URL base do MiniMax + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: HF_TOKEN + labelEn: Hugging Face Token + labelZh: Hugging Face Token + label: + en: Hugging Face Token + zh: Hugging Face Token + zh-Hant: Hugging Face Token + ja: Hugging Face トークン + ko: Hugging Face 토큰 + ru: Токен Hugging Face + ms: Token Hugging Face + pt-br: Token do Hugging Face + edit: true + required: false + type: password + - default: "" + envKey: TELEGRAM_BOT_TOKEN + labelEn: Telegram Bot Token + labelZh: Telegram 机器人 Token + label: + en: Telegram Bot Token + zh: Telegram 机器人 Token + zh-Hant: Telegram 機器人 Token + ja: Telegram ボットトークン + ko: Telegram 봇 토큰 + ru: Токен Telegram-бота + ms: Token Bot Telegram + pt-br: Token do bot do Telegram + edit: true + required: false + type: password + - default: "" + envKey: TELEGRAM_ALLOWED_USERS + labelEn: Telegram Allowed Users + labelZh: Telegram 允许用户 ID + label: + en: Telegram Allowed Users + zh: Telegram 允许用户 ID + zh-Hant: Telegram 允許使用者 ID + ja: Telegram 許可ユーザー ID + ko: Telegram 허용 사용자 ID + ru: Разрешённые ID пользователей Telegram + ms: ID pengguna Telegram yang dibenarkan + pt-br: IDs de usuários permitidos do Telegram + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: TELEGRAM_HOME_CHANNEL + labelEn: Telegram Home Channel + labelZh: Telegram 默认回传会话 + label: + en: Telegram Home Channel + zh: Telegram 默认回传会话 + zh-Hant: Telegram 預設回傳會話 + ja: Telegram 既定返信先チャネル + ko: Telegram 기본 회신 채널 + ru: Домашний канал Telegram + ms: Saluran rumah Telegram + pt-br: Canal principal do Telegram + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: TELEGRAM_HOME_CHANNEL_NAME + labelEn: Telegram Home Channel Name + labelZh: Telegram 默认回传会话名 + label: + en: Telegram Home Channel Name + zh: Telegram 默认回传会话名 + zh-Hant: Telegram 預設回傳會話名稱 + ja: Telegram 既定返信先名 + ko: Telegram 기본 회신 채널명 + ru: Имя домашнего канала Telegram + ms: Nama saluran rumah Telegram + pt-br: Nome do canal principal do Telegram + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: DISCORD_HOME_CHANNEL + labelEn: Discord Home Channel + labelZh: Discord 默认回传频道 + label: + en: Discord Home Channel + zh: Discord 默认回传频道 + zh-Hant: Discord 預設回傳頻道 + ja: Discord 既定返信先チャンネル + ko: Discord 기본 회신 채널 + ru: Домашний канал Discord + ms: Saluran rumah Discord + pt-br: Canal principal do Discord + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: DISCORD_HOME_CHANNEL_NAME + labelEn: Discord Home Channel Name + labelZh: Discord 默认回传频道名 + label: + en: Discord Home Channel Name + zh: Discord 默认回传频道名 + zh-Hant: Discord 預設回傳頻道名稱 + ja: Discord 既定返信先名 + ko: Discord 기본 회신 채널명 + ru: Имя домашнего канала Discord + ms: Nama saluran rumah Discord + pt-br: Nome do canal principal do Discord + edit: true + required: false + rule: paramCommon + type: text + - default: "false" + envKey: DISCORD_REQUIRE_MENTION + labelEn: Discord Require Mention + labelZh: Discord 仅提及时响应 + label: + en: Discord Require Mention + zh: Discord 仅提及时响应 + zh-Hant: Discord 僅被提及時回應 + ja: Discord メンション時のみ応答 + ko: Discord 멘션 시에만 응답 + ru: Discord отвечает только при упоминании + ms: Discord hanya membalas apabila disebut + pt-br: Discord responde apenas quando mencionado + edit: true + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: "" + envKey: DISCORD_FREE_RESPONSE_CHANNELS + labelEn: Discord Free Response Channels + labelZh: Discord 自由回复频道 + label: + en: Discord Free Response Channels + zh: Discord 自由回复频道 + zh-Hant: Discord 自由回覆頻道 + ja: Discord 自由応答チャンネル + ko: Discord 자유 응답 채널 + ru: Каналы Discord со свободным ответом + ms: Saluran balasan bebas Discord + pt-br: Canais de resposta livre do Discord + edit: true + required: false + rule: paramCommon + type: text + - default: "false" + envKey: DISCORD_AUTO_THREAD + labelEn: Discord Auto Thread + labelZh: Discord 自动线程 + label: + en: Discord Auto Thread + zh: Discord 自动线程 + zh-Hant: Discord 自動討論串 + ja: Discord 自動スレッド + ko: Discord 자동 스레드 + ru: Авто-треды Discord + ms: Thread automatik Discord + pt-br: Thread automático do Discord + edit: true + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: "" + envKey: SLACK_HOME_CHANNEL + labelEn: Slack Home Channel + labelZh: Slack 默认回传频道 + label: + en: Slack Home Channel + zh: Slack 默认回传频道 + zh-Hant: Slack 預設回傳頻道 + ja: Slack 既定返信先チャンネル + ko: Slack 기본 회신 채널 + ru: Домашний канал Slack + ms: Saluran rumah Slack + pt-br: Canal principal do Slack + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SLACK_HOME_CHANNEL_NAME + labelEn: Slack Home Channel Name + labelZh: Slack 默认回传频道名 + label: + en: Slack Home Channel Name + zh: Slack 默认回传频道名 + zh-Hant: Slack 預設回傳頻道名稱 + ja: Slack 既定返信先名 + ko: Slack 기본 회신 채널명 + ru: Имя домашнего канала Slack + ms: Nama saluran rumah Slack + pt-br: Nome do canal principal do Slack + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SLACK_ALLOWED_USERS + labelEn: Slack Allowed Users + labelZh: Slack 允许用户 + label: + en: Slack Allowed Users + zh: Slack 允许用户 + zh-Hant: Slack 允許使用者 + ja: Slack 許可ユーザー + ko: Slack 허용 사용자 + ru: Разрешённые пользователи Slack + ms: Pengguna Slack yang dibenarkan + pt-br: Usuários permitidos do Slack + edit: true + required: false + rule: paramCommon + type: text + - default: "false" + envKey: WHATSAPP_ENABLED + labelEn: WhatsApp Enabled + labelZh: 启用 WhatsApp + label: + en: WhatsApp Enabled + zh: 启用 WhatsApp + zh-Hant: 啟用 WhatsApp + ja: WhatsApp を有効化 + ko: WhatsApp 활성화 + ru: Включить WhatsApp + ms: Dayakan WhatsApp + pt-br: Ativar WhatsApp + edit: true + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: personal + envKey: WHATSAPP_MODE + labelEn: WhatsApp Mode + labelZh: WhatsApp 模式 + label: + en: WhatsApp Mode + zh: WhatsApp 模式 + zh-Hant: WhatsApp 模式 + ja: WhatsApp モード + ko: WhatsApp 모드 + ru: Режим WhatsApp + ms: Mod WhatsApp + pt-br: Modo do WhatsApp + edit: true + required: false + type: select + values: + - label: personal + value: personal + - label: business + value: business + - default: "" + envKey: WHATSAPP_ALLOWED_USERS + labelEn: WhatsApp Allowed Users + labelZh: WhatsApp 允许用户 + label: + en: WhatsApp Allowed Users + zh: WhatsApp 允许用户 + zh-Hant: WhatsApp 允許使用者 + ja: WhatsApp 許可ユーザー + ko: WhatsApp 허용 사용자 + ru: Разрешённые пользователи WhatsApp + ms: Pengguna WhatsApp yang dibenarkan + pt-br: Usuários permitidos do WhatsApp + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SIGNAL_HTTP_URL + labelEn: Signal HTTP URL + labelZh: Signal HTTP 地址 + label: + en: Signal HTTP URL + zh: Signal HTTP 地址 + zh-Hant: Signal HTTP 位址 + ja: Signal HTTP アドレス + ko: Signal HTTP 주소 + ru: HTTP URL Signal + ms: URL HTTP Signal + pt-br: URL HTTP do Signal + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: SIGNAL_ACCOUNT + labelEn: Signal Account + labelZh: Signal 账号 + label: + en: Signal Account + zh: Signal 账号 + zh-Hant: Signal 帳號 + ja: Signal アカウント + ko: Signal 계정 + ru: Аккаунт Signal + ms: Akaun Signal + pt-br: Conta do Signal + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SIGNAL_ALLOWED_USERS + labelEn: Signal Allowed Users + labelZh: Signal 允许用户 + label: + en: Signal Allowed Users + zh: Signal 允许用户 + zh-Hant: Signal 允許使用者 + ja: Signal 許可ユーザー + ko: Signal 허용 사용자 + ru: Разрешённые пользователи Signal + ms: Pengguna Signal yang dibenarkan + pt-br: Usuários permitidos do Signal + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SIGNAL_GROUP_ALLOWED_USERS + labelEn: Signal Group Allowed Users + labelZh: Signal 群组允许用户 + label: + en: Signal Group Allowed Users + zh: Signal 群组允许用户 + zh-Hant: Signal 群組允許使用者 + ja: Signal グループ許可ユーザー + ko: Signal 그룹 허용 사용자 + ru: Разрешённые пользователи групп Signal + ms: Pengguna kumpulan Signal yang dibenarkan + pt-br: Usuários permitidos de grupos do Signal + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SIGNAL_HOME_CHANNEL + labelEn: Signal Home Channel + labelZh: Signal 默认回传会话 + label: + en: Signal Home Channel + zh: Signal 默认回传会话 + zh-Hant: Signal 預設回傳會話 + ja: Signal 既定返信先 + ko: Signal 기본 회신 채널 + ru: Домашний канал Signal + ms: Saluran rumah Signal + pt-br: Canal principal do Signal + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: SIGNAL_HOME_CHANNEL_NAME + labelEn: Signal Home Channel Name + labelZh: Signal 默认回传会话名 + label: + en: Signal Home Channel Name + zh: Signal 默认回传会话名 + zh-Hant: Signal 預設回傳會話名稱 + ja: Signal 既定返信先名 + ko: Signal 기본 회신 채널명 + ru: Имя домашнего канала Signal + ms: Nama saluran rumah Signal + pt-br: Nome do canal principal do Signal + edit: true + required: false + rule: paramCommon + type: text + - default: "false" + envKey: SIGNAL_IGNORE_STORIES + labelEn: Signal Ignore Stories + labelZh: Signal 忽略动态 + label: + en: Signal Ignore Stories + zh: Signal 忽略动态 + zh-Hant: Signal 忽略動態 + ja: Signal ストーリーを無視 + ko: Signal 스토리 무시 + ru: Игнорировать stories в Signal + ms: Abaikan stories Signal + pt-br: Ignorar stories do Signal + edit: true + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: "" + envKey: EMAIL_ADDRESS + labelEn: Email Address + labelZh: 邮箱地址 + label: + en: Email Address + zh: 邮箱地址 + zh-Hant: 郵箱地址 + ja: メールアドレス + ko: 이메일 주소 + ru: Адрес электронной почты + ms: Alamat e-mel + pt-br: Endereço de e-mail + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: EMAIL_PASSWORD + labelEn: Email Password + labelZh: 邮箱密码 + label: + en: Email Password + zh: 邮箱密码 + zh-Hant: 郵箱密碼 + ja: メールパスワード + ko: 이메일 비밀번호 + ru: Пароль почты + ms: Kata laluan e-mel + pt-br: Senha do e-mail + edit: true + required: false + type: password + - default: "" + envKey: EMAIL_IMAP_HOST + labelEn: Email IMAP Host + labelZh: 邮箱 IMAP 主机 + label: + en: Email IMAP Host + zh: 邮箱 IMAP 主机 + zh-Hant: 郵箱 IMAP 主機 + ja: メール IMAP ホスト + ko: 이메일 IMAP 호스트 + ru: IMAP-хост почты + ms: Hos IMAP e-mel + pt-br: Host IMAP do e-mail + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: EMAIL_SMTP_HOST + labelEn: Email SMTP Host + labelZh: 邮箱 SMTP 主机 + label: + en: Email SMTP Host + zh: 邮箱 SMTP 主机 + zh-Hant: 郵箱 SMTP 主機 + ja: メール SMTP ホスト + ko: 이메일 SMTP 호스트 + ru: SMTP-хост почты + ms: Hos SMTP e-mel + pt-br: Host SMTP do e-mail + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: EMAIL_HOME_ADDRESS + labelEn: Email Home Address + labelZh: 默认收件地址 + label: + en: Email Home Address + zh: 默认收件地址 + zh-Hant: 預設收件地址 + ja: 既定受信アドレス + ko: 기본 수신 주소 + ru: Домашний адрес почты + ms: Alamat rumah e-mel + pt-br: Endereço principal de e-mail + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: EMAIL_HOME_ADDRESS_NAME + labelEn: Email Home Address Name + labelZh: 默认收件名称 + label: + en: Email Home Address Name + zh: 默认收件名称 + zh-Hant: 預設收件名稱 + ja: 既定受信名 + ko: 기본 수신 이름 + ru: Имя домашнего адреса почты + ms: Nama alamat rumah e-mel + pt-br: Nome do endereço principal de e-mail + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: GH_TOKEN + labelEn: GitHub Token + labelZh: GitHub Token + label: + en: GitHub Token + zh: GitHub Token + zh-Hant: GitHub Token + ja: GitHub トークン + ko: GitHub 토큰 + ru: Токен GitHub + ms: Token GitHub + pt-br: Token do GitHub + edit: true + required: false + type: password + - default: "" + envKey: GITHUB_APP_ID + labelEn: GitHub App ID + labelZh: GitHub App ID + label: + en: GitHub App ID + zh: GitHub App ID + zh-Hant: GitHub App ID + ja: GitHub App 識別子 + ko: GitHub 앱 ID + ru: ID GitHub App + ms: ID GitHub App + pt-br: ID do GitHub App + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: GITHUB_APP_PRIVATE_KEY_PATH + labelEn: GitHub App Private Key Path + labelZh: GitHub App 私钥路径 + label: + en: GitHub App Private Key Path + zh: GitHub App 私钥路径 + zh-Hant: GitHub App 私鑰路徑 + ja: GitHub App 秘密鍵パス + ko: GitHub App 비공개 키 경로 + ru: Путь к приватному ключу GitHub App + ms: Laluan kunci peribadi GitHub App + pt-br: Caminho da chave privada do GitHub App + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: GITHUB_APP_INSTALLATION_ID + labelEn: GitHub App Installation ID + labelZh: GitHub App Installation ID + label: + en: GitHub App Installation ID + zh: GitHub App Installation ID + zh-Hant: GitHub App Installation ID + ja: GitHub App インストール ID + ko: GitHub App 설치 ID + ru: ID установки GitHub App + ms: ID pemasangan GitHub App + pt-br: ID de instalação do GitHub App + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: EXA_API_KEY + labelEn: Exa API Key + labelZh: Exa API 密钥 + label: + en: Exa API Key + zh: Exa API 密钥 + zh-Hant: Exa API 金鑰 + ja: Exa API キー + ko: Exa API 키 + ru: API-ключ Exa + ms: Kunci API Exa + pt-br: Chave de API Exa + edit: true + required: false + type: password + - default: "" + envKey: PARALLEL_API_KEY + labelEn: Parallel API Key + labelZh: Parallel API 密钥 + label: + en: Parallel API Key + zh: Parallel API 密钥 + zh-Hant: Parallel API 金鑰 + ja: Parallel API キー + ko: Parallel API 키 + ru: API-ключ Parallel + ms: Kunci API Parallel + pt-br: Chave de API Parallel + edit: true + required: false + type: password + - default: "" + envKey: FIRECRAWL_API_KEY + labelEn: Firecrawl API Key + labelZh: Firecrawl API 密钥 + label: + en: Firecrawl API Key + zh: Firecrawl API 密钥 + zh-Hant: Firecrawl API 金鑰 + ja: Firecrawl API キー + ko: Firecrawl API 키 + ru: API-ключ Firecrawl + ms: Kunci API Firecrawl + pt-br: Chave de API Firecrawl + edit: true + required: false + type: password + - default: "" + envKey: FIRECRAWL_API_URL + labelEn: Firecrawl API URL + labelZh: Firecrawl API 地址 + label: + en: Firecrawl API URL + zh: Firecrawl API 地址 + zh-Hant: Firecrawl API 位址 + ja: Firecrawl API アドレス + ko: Firecrawl API 주소 + ru: URL API Firecrawl + ms: URL API Firecrawl + pt-br: URL da API Firecrawl + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: HELICONE_API_KEY + labelEn: Helicone API Key + labelZh: Helicone API 密钥 + label: + en: Helicone API Key + zh: Helicone API 密钥 + zh-Hant: Helicone API 金鑰 + ja: Helicone API キー + ko: Helicone API 키 + ru: API-ключ Helicone + ms: Kunci API Helicone + pt-br: Chave de API Helicone + edit: true + required: false + type: password + - default: "" + envKey: HASS_URL + labelEn: Home Assistant URL + labelZh: Home Assistant 地址 + label: + en: Home Assistant URL + zh: Home Assistant 地址 + zh-Hant: Home Assistant 位址 + ja: Home Assistant アドレス + ko: Home Assistant 주소 + ru: URL Home Assistant + ms: URL Home Assistant + pt-br: URL do Home Assistant + edit: true + required: false + rule: paramExtUrl + type: text + - default: "" + envKey: HASS_TOKEN + labelEn: Home Assistant Token + labelZh: Home Assistant Token + label: + en: Home Assistant Token + zh: Home Assistant Token + zh-Hant: Home Assistant Token + ja: Home Assistant トークン + ko: Home Assistant 토큰 + ru: Токен Home Assistant + ms: Token Home Assistant + pt-br: Token do Home Assistant + edit: true + required: false + type: password + - default: "" + envKey: GATEWAY_ALLOWED_USERS + labelEn: Gateway Allowed Users + labelZh: 网关允许用户 + label: + en: Gateway Allowed Users + zh: 网关允许用户 + zh-Hant: 網關允許使用者 + ja: ゲートウェイ許可ユーザー + ko: 게이트웨이 허용 사용자 + ru: Разрешённые пользователи шлюза + ms: Pengguna gerbang yang dibenarkan + pt-br: Usuários permitidos do gateway + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: MODAL_TOKEN_ID + labelEn: Modal Token ID + labelZh: Modal Token ID + label: + en: Modal Token ID + zh: Modal Token ID + zh-Hant: Modal Token ID + ja: Modal トークン ID + ko: Modal 토큰 ID + ru: Token ID Modal + ms: ID token Modal + pt-br: ID do token Modal + edit: true + required: false + rule: paramCommon + type: text + - default: "" + envKey: MODAL_TOKEN_SECRET + labelEn: Modal Token Secret + labelZh: Modal Token Secret + label: + en: Modal Token Secret + zh: Modal Token Secret + zh-Hant: Modal Token Secret + ja: Modal トークンシークレット + ko: Modal 토큰 시크릿 + ru: Секрет токена Modal + ms: Rahsia token Modal + pt-br: Segredo do token Modal + edit: true + required: false + type: password + - default: "" + envKey: DAYTONA_API_KEY + labelEn: Daytona API Key + labelZh: Daytona API 密钥 + label: + en: Daytona API Key + zh: Daytona API 密钥 + zh-Hant: Daytona API 金鑰 + ja: Daytona API キー + ko: Daytona API 키 + ru: API-ключ Daytona + ms: Kunci API Daytona + pt-br: Chave de API Daytona + edit: true + required: false + type: password diff --git a/apps/hermes-agent/2026.9.24/data/.gitkeep b/apps/hermes-agent/2026.9.24/data/.gitkeep new file mode 100755 index 000000000..e69de29bb diff --git a/apps/hermes-agent/2026.9.24/docker-compose.yml b/apps/hermes-agent/2026.9.24/docker-compose.yml new file mode 100755 index 000000000..57d52730d --- /dev/null +++ b/apps/hermes-agent/2026.9.24/docker-compose.yml @@ -0,0 +1,92 @@ +services: + hermes-agent: + image: "nousresearch/hermes-agent:v2026.9.24" + container_name: ${CONTAINER_NAME} + command: + - gateway + - run + environment: + - TZ=${TZ} + - HERMES_UID=${HERMES_UID:-10000} + - HERMES_GID=${HERMES_GID:-10000} + - API_SERVER_ENABLED=true + - API_SERVER_HOST=0.0.0.0 + - API_SERVER_KEY=${API_SERVER_KEY} + - API_SERVER_CORS_ORIGINS=${API_SERVER_CORS_ORIGINS:-*} + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-} + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} + - GLM_API_KEY=${GLM_API_KEY:-} + - GLM_BASE_URL=${GLM_BASE_URL:-} + - KIMI_API_KEY=${KIMI_API_KEY:-} + - KIMI_BASE_URL=${KIMI_BASE_URL:-} + - MINIMAX_API_KEY=${MINIMAX_API_KEY:-} + - MINIMAX_BASE_URL=${MINIMAX_BASE_URL:-} + - ANTHROPIC_TOKEN=${ANTHROPIC_TOKEN:-} + - GROQ_API_KEY=${GROQ_API_KEY:-} + - MISTRAL_API_KEY=${MISTRAL_API_KEY:-} + - COHERE_API_KEY=${COHERE_API_KEY:-} + - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-} + - TOGETHER_API_KEY=${TOGETHER_API_KEY:-} + - PERPLEXITY_API_KEY=${PERPLEXITY_API_KEY:-} + - FIREWORKS_API_KEY=${FIREWORKS_API_KEY:-} + - XAI_API_KEY=${XAI_API_KEY:-} + - HF_TOKEN=${HF_TOKEN:-} + - EXA_API_KEY=${EXA_API_KEY:-} + - PARALLEL_API_KEY=${PARALLEL_API_KEY:-} + - FIRECRAWL_API_KEY=${FIRECRAWL_API_KEY:-} + - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-} + - TELEGRAM_ALLOWED_USERS=${TELEGRAM_ALLOWED_USERS:-} + - TELEGRAM_HOME_CHANNEL=${TELEGRAM_HOME_CHANNEL:-} + - ANTHROPIC_BASE_URL=${ANTHROPIC_BASE_URL:-} + - TELEGRAM_HOME_CHANNEL_NAME=${TELEGRAM_HOME_CHANNEL_NAME:-} + - DISCORD_HOME_CHANNEL=${DISCORD_HOME_CHANNEL:-} + - DISCORD_HOME_CHANNEL_NAME=${DISCORD_HOME_CHANNEL_NAME:-} + - DISCORD_REQUIRE_MENTION=${DISCORD_REQUIRE_MENTION:-} + - DISCORD_FREE_RESPONSE_CHANNELS=${DISCORD_FREE_RESPONSE_CHANNELS:-} + - DISCORD_AUTO_THREAD=${DISCORD_AUTO_THREAD:-} + - SLACK_HOME_CHANNEL=${SLACK_HOME_CHANNEL:-} + - SLACK_HOME_CHANNEL_NAME=${SLACK_HOME_CHANNEL_NAME:-} + - SLACK_ALLOWED_USERS=${SLACK_ALLOWED_USERS:-} + - WHATSAPP_ENABLED=${WHATSAPP_ENABLED:-} + - WHATSAPP_MODE=${WHATSAPP_MODE:-} + - WHATSAPP_ALLOWED_USERS=${WHATSAPP_ALLOWED_USERS:-} + - SIGNAL_HTTP_URL=${SIGNAL_HTTP_URL:-} + - SIGNAL_ACCOUNT=${SIGNAL_ACCOUNT:-} + - SIGNAL_ALLOWED_USERS=${SIGNAL_ALLOWED_USERS:-} + - SIGNAL_GROUP_ALLOWED_USERS=${SIGNAL_GROUP_ALLOWED_USERS:-} + - SIGNAL_HOME_CHANNEL=${SIGNAL_HOME_CHANNEL:-} + - SIGNAL_HOME_CHANNEL_NAME=${SIGNAL_HOME_CHANNEL_NAME:-} + - SIGNAL_IGNORE_STORIES=${SIGNAL_IGNORE_STORIES:-} + - HASS_URL=${HASS_URL:-} + - HASS_TOKEN=${HASS_TOKEN:-} + - HELICONE_API_KEY=${HELICONE_API_KEY:-} + - FIRECRAWL_API_URL=${FIRECRAWL_API_URL:-} + - GH_TOKEN=${GH_TOKEN:-} + - GITHUB_APP_ID=${GITHUB_APP_ID:-} + - GITHUB_APP_PRIVATE_KEY_PATH=${GITHUB_APP_PRIVATE_KEY_PATH:-} + - GITHUB_APP_INSTALLATION_ID=${GITHUB_APP_INSTALLATION_ID:-} + - EMAIL_ADDRESS=${EMAIL_ADDRESS:-} + - EMAIL_PASSWORD=${EMAIL_PASSWORD:-} + - EMAIL_IMAP_HOST=${EMAIL_IMAP_HOST:-} + - EMAIL_SMTP_HOST=${EMAIL_SMTP_HOST:-} + - EMAIL_HOME_ADDRESS=${EMAIL_HOME_ADDRESS:-} + - EMAIL_HOME_ADDRESS_NAME=${EMAIL_HOME_ADDRESS_NAME:-} + - GATEWAY_ALLOWED_USERS=${GATEWAY_ALLOWED_USERS:-} + - MODAL_TOKEN_ID=${MODAL_TOKEN_ID:-} + - MODAL_TOKEN_SECRET=${MODAL_TOKEN_SECRET:-} + - DAYTONA_API_KEY=${DAYTONA_API_KEY:-} + networks: + 1panel-network: + ipv4_address: ${GATEWAY_STATIC_IP} + ports: + - "${PANEL_APP_PORT_API}:8642" + volumes: + - "${APP_DATA_DIR}:/opt/data" + restart: unless-stopped + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/hermes-agent/2026.9.24/scripts/init.sh b/apps/hermes-agent/2026.9.24/scripts/init.sh new file mode 100755 index 000000000..7ddc6041b --- /dev/null +++ b/apps/hermes-agent/2026.9.24/scripts/init.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +ENV_FILE="$ROOT_DIR/.env" +APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}" +DEFAULT_UID="10000" +DEFAULT_GID="10000" +TARGET_UID="${HERMES_UID:-$DEFAULT_UID}" +TARGET_GID="${HERMES_GID:-$DEFAULT_GID}" + +if [[ "$APP_DATA_DIR_RAW" = /* ]]; then + APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW" +else + APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}" +fi + +mkdir -p "$APP_DATA_DIR_ABS" + +upsert_env_if_missing() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + return 0 + fi + + if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + fi +} + +generate_random_value() { + local length="$1" + local value="" + + while [[ ${#value} -lt $length ]]; do + value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + fi +} + +if [[ -f "$ENV_FILE" ]]; then + EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + [[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID" + [[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID" +fi + +upsert_env_if_missing "HERMES_UID" "$TARGET_UID" +upsert_env_if_missing "HERMES_GID" "$TARGET_GID" +ensure_env_value "API_SERVER_KEY" "$(generate_random_value 48)" +ensure_env_value "API_SERVER_CORS_ORIGINS" "*" + +CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') +CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') + +if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then + chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true +fi diff --git a/apps/hermes-agent/2026.9.24/scripts/uninstall.sh b/apps/hermes-agent/2026.9.24/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/hermes-agent/2026.9.24/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/hermes-agent/2026.9.24/scripts/upgrade.sh b/apps/hermes-agent/2026.9.24/scripts/upgrade.sh new file mode 100755 index 000000000..7ddc6041b --- /dev/null +++ b/apps/hermes-agent/2026.9.24/scripts/upgrade.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +ENV_FILE="$ROOT_DIR/.env" +APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}" +DEFAULT_UID="10000" +DEFAULT_GID="10000" +TARGET_UID="${HERMES_UID:-$DEFAULT_UID}" +TARGET_GID="${HERMES_GID:-$DEFAULT_GID}" + +if [[ "$APP_DATA_DIR_RAW" = /* ]]; then + APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW" +else + APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}" +fi + +mkdir -p "$APP_DATA_DIR_ABS" + +upsert_env_if_missing() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + return 0 + fi + + if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + fi +} + +generate_random_value() { + local length="$1" + local value="" + + while [[ ${#value} -lt $length ]]; do + value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + fi +} + +if [[ -f "$ENV_FILE" ]]; then + EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + [[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID" + [[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID" +fi + +upsert_env_if_missing "HERMES_UID" "$TARGET_UID" +upsert_env_if_missing "HERMES_GID" "$TARGET_GID" +ensure_env_value "API_SERVER_KEY" "$(generate_random_value 48)" +ensure_env_value "API_SERVER_CORS_ORIGINS" "*" + +CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') +CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') + +if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then + chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true +fi From 0086e28ed878df9defd8ff0a98752955b1ac2967 Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Sat, 17 Oct 2026 07:48:50 +0800 Subject: [PATCH 26/27] fix(hermes-dashboard): preserve 2026.9.21 upgrade source --- apps/hermes-dashboard/2026.9.24/.env.sample | 10 ++ apps/hermes-dashboard/2026.9.24/data.yml | 169 ++++++++++++++++++ .../2026.9.24/docker-compose.yml | 30 ++++ .../2026.9.24/scripts/init.sh | 77 ++++++++ .../2026.9.24/scripts/uninstall.sh | 2 + .../2026.9.24/scripts/upgrade.sh | 77 ++++++++ 6 files changed, 365 insertions(+) create mode 100755 apps/hermes-dashboard/2026.9.24/.env.sample create mode 100755 apps/hermes-dashboard/2026.9.24/data.yml create mode 100755 apps/hermes-dashboard/2026.9.24/docker-compose.yml create mode 100755 apps/hermes-dashboard/2026.9.24/scripts/init.sh create mode 100755 apps/hermes-dashboard/2026.9.24/scripts/uninstall.sh create mode 100755 apps/hermes-dashboard/2026.9.24/scripts/upgrade.sh diff --git a/apps/hermes-dashboard/2026.9.24/.env.sample b/apps/hermes-dashboard/2026.9.24/.env.sample new file mode 100755 index 000000000..403e8cea0 --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/.env.sample @@ -0,0 +1,10 @@ +CONTAINER_NAME=hermes-dashboard +TZ=Asia/Shanghai +APP_DATA_DIR=/opt/1panel/apps/local/hermes-agent/hermes-agent/data +HERMES_UID=10000 +HERMES_GID=10000 +PANEL_APP_PORT_HTTP=9119 +GATEWAY_HEALTH_URL=http://172.18.0.240:8642 +DASHBOARD_USERNAME=admin +DASHBOARD_PASSWORD=change_me_with_a_strong_password +DASHBOARD_SESSION_SECRET=change_me_with_a_random_session_secret diff --git a/apps/hermes-dashboard/2026.9.24/data.yml b/apps/hermes-dashboard/2026.9.24/data.yml new file mode 100755 index 000000000..9c2f60a5a --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/data.yml @@ -0,0 +1,169 @@ +additionalProperties: + formFields: + - default: "Hermes Dashboard 是附属应用。请先部署 Hermes gateway,并把 APP_DATA_DIR 指向 Hermes Agent 的同一份数据目录,再把 GATEWAY_HEALTH_URL 填成它的完整地址。公开绑定现在强制启用认证,`--insecure` 已不能绕过认证;请设置 Dashboard 用户名、强密码和会话签名密钥。旧版本升级时,脚本只会为缺失或空白的认证项生成随机值,不会覆盖已有配置;升级完成后请在应用参数中设置自己知道的密码。" + disabled: true + envKey: TIPS + labelEn: Tips + labelZh: 提示 + label: + en: Tips + zh: 提示 + zh-Hant: 提示 + ja: ヒント + ko: 팁 + ru: Подсказка + ms: Petua + pt-br: Dicas + edit: true + required: true + type: text + - default: Asia/Shanghai + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + edit: true + required: true + type: text + - default: /opt/1panel/apps/local/hermes-agent/hermes-agent/data + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretório de dados + edit: true + required: true + type: text + - default: 10000 + envKey: HERMES_UID + labelEn: Hermes Runtime UID + labelZh: Hermes 运行用户 UID + label: + en: Hermes Runtime UID + zh: Hermes 运行用户 UID + zh-Hant: Hermes 執行使用者 UID + ja: Hermes 実行ユーザー UID + ko: Hermes 실행 사용자 UID + ru: UID пользователя Hermes + ms: UID pengguna runtime Hermes + pt-br: UID do usuário de execução do Hermes + edit: true + required: true + type: number + - default: 10000 + envKey: HERMES_GID + labelEn: Hermes Runtime GID + labelZh: Hermes 运行用户 GID + label: + en: Hermes Runtime GID + zh: Hermes 运行用户 GID + zh-Hant: Hermes 執行使用者 GID + ja: Hermes 実行ユーザー GID + ko: Hermes 실행 사용자 GID + ru: GID пользователя Hermes + ms: GID pengguna runtime Hermes + pt-br: GID do usuário de execução do Hermes + edit: true + required: true + type: number + - default: 9119 + envKey: PANEL_APP_PORT_HTTP + labelEn: Dashboard Port + labelZh: Dashboard 端口 + label: + en: Dashboard Port + zh: Dashboard 端口 + zh-Hant: Dashboard 連接埠 + ja: Dashboard ポート + ko: 대시보드 포트 + ru: Порт dashboard + ms: Port dashboard + pt-br: Porta do dashboard + edit: true + required: true + rule: paramPort + type: number + - default: http://172.18.0.240:8642 + envKey: GATEWAY_HEALTH_URL + labelEn: Gateway Health URL + labelZh: 网关健康检查 URL + label: + en: Gateway Health URL + zh: 网关健康检查 URL + zh-Hant: 閘道健康檢查 URL + ja: Gateway ヘルス URL + ko: 게이트웨이 헬스 URL + ru: URL проверки шлюза + ms: URL kesihatan gateway + pt-br: URL de verificação do gateway + edit: true + required: true + rule: paramExtUrl + type: text + - default: admin + envKey: DASHBOARD_USERNAME + labelEn: Dashboard Username + labelZh: Dashboard 用户名 + label: + en: Dashboard Username + zh: Dashboard 用户名 + zh-Hant: Dashboard 使用者名稱 + ja: Dashboard ユーザー名 + ko: Dashboard 사용자 이름 + ru: Имя пользователя dashboard + ms: Nama pengguna dashboard + pt-br: Usuário do dashboard + edit: true + required: true + type: text + - default: "" + envKey: DASHBOARD_PASSWORD + labelEn: Dashboard Password + labelZh: Dashboard 密码 + label: + en: Dashboard Password + zh: Dashboard 密码 + zh-Hant: Dashboard 密碼 + ja: Dashboard パスワード + ko: Dashboard 비밀번호 + ru: Пароль dashboard + ms: Kata laluan dashboard + pt-br: Senha do dashboard + edit: true + random: true + required: true + rule: paramComplexity + type: password + - default: "" + envKey: DASHBOARD_SESSION_SECRET + labelEn: Dashboard Session Secret + labelZh: Dashboard 会话签名密钥 + label: + en: Dashboard Session Secret + zh: Dashboard 会话签名密钥 + zh-Hant: Dashboard 工作階段簽章密鑰 + ja: Dashboard セッション署名シークレット + ko: Dashboard 세션 서명 비밀값 + ru: Секрет подписи сессии dashboard + ms: Rahsia tandatangan sesi dashboard + pt-br: Segredo de assinatura da sessão do dashboard + edit: true + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/hermes-dashboard/2026.9.24/docker-compose.yml b/apps/hermes-dashboard/2026.9.24/docker-compose.yml new file mode 100755 index 000000000..285822422 --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/docker-compose.yml @@ -0,0 +1,30 @@ +services: + hermes-dashboard: + image: "nousresearch/hermes-agent:v2026.9.24" + container_name: ${CONTAINER_NAME} + command: + - dashboard + - --host + - 0.0.0.0 + - --no-open + environment: + - TZ=${TZ} + - HERMES_UID=${HERMES_UID:-10000} + - HERMES_GID=${HERMES_GID:-10000} + - GATEWAY_HEALTH_URL=${GATEWAY_HEALTH_URL} + - HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${DASHBOARD_USERNAME} + - HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${DASHBOARD_PASSWORD} + - HERMES_DASHBOARD_BASIC_AUTH_SECRET=${DASHBOARD_SESSION_SECRET} + ports: + - "${PANEL_APP_PORT_HTTP}:9119" + volumes: + - "${APP_DATA_DIR}:/opt/data" + restart: unless-stopped + networks: + - 1panel-network + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/hermes-dashboard/2026.9.24/scripts/init.sh b/apps/hermes-dashboard/2026.9.24/scripts/init.sh new file mode 100755 index 000000000..c46d420b4 --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/scripts/init.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +ENV_FILE="$ROOT_DIR/.env" +APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}" +DEFAULT_UID="10000" +DEFAULT_GID="10000" +TARGET_UID="${HERMES_UID:-$DEFAULT_UID}" +TARGET_GID="${HERMES_GID:-$DEFAULT_GID}" + +if [[ "$APP_DATA_DIR_RAW" = /* ]]; then + APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW" +else + APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}" +fi + +mkdir -p "$APP_DATA_DIR_ABS" + +upsert_env_if_missing() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + return 0 + fi + + if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + fi +} + +generate_random_value() { + local length="$1" + local value="" + + while [[ ${#value} -lt $length ]]; do + value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + fi +} + +if [[ -f "$ENV_FILE" ]]; then + EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + [[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID" + [[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID" +fi + +upsert_env_if_missing "HERMES_UID" "$TARGET_UID" +upsert_env_if_missing "HERMES_GID" "$TARGET_GID" +ensure_env_value "DASHBOARD_USERNAME" "admin" +ensure_env_value "DASHBOARD_PASSWORD" "$(generate_random_value 32)" +ensure_env_value "DASHBOARD_SESSION_SECRET" "$(generate_random_value 48)" + +CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') +CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') + +if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then + chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true +fi diff --git a/apps/hermes-dashboard/2026.9.24/scripts/uninstall.sh b/apps/hermes-dashboard/2026.9.24/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/hermes-dashboard/2026.9.24/scripts/upgrade.sh b/apps/hermes-dashboard/2026.9.24/scripts/upgrade.sh new file mode 100755 index 000000000..c46d420b4 --- /dev/null +++ b/apps/hermes-dashboard/2026.9.24/scripts/upgrade.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +ENV_FILE="$ROOT_DIR/.env" +APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}" +DEFAULT_UID="10000" +DEFAULT_GID="10000" +TARGET_UID="${HERMES_UID:-$DEFAULT_UID}" +TARGET_GID="${HERMES_GID:-$DEFAULT_GID}" + +if [[ "$APP_DATA_DIR_RAW" = /* ]]; then + APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW" +else + APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}" +fi + +mkdir -p "$APP_DATA_DIR_ABS" + +upsert_env_if_missing() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + return 0 + fi + + if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + fi +} + +generate_random_value() { + local length="$1" + local value="" + + while [[ ${#value} -lt $length ]]; do + value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null; then + printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" + elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + fi +} + +if [[ -f "$ENV_FILE" ]]; then + EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true) + [[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID" + [[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID" +fi + +upsert_env_if_missing "HERMES_UID" "$TARGET_UID" +upsert_env_if_missing "HERMES_GID" "$TARGET_GID" +ensure_env_value "DASHBOARD_USERNAME" "admin" +ensure_env_value "DASHBOARD_PASSWORD" "$(generate_random_value 32)" +ensure_env_value "DASHBOARD_SESSION_SECRET" "$(generate_random_value 48)" + +CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') +CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '') + +if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then + chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true +fi From 6bf6df6bdd0825bf69000986bd33ea1c3c141174 Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Sat, 17 Oct 2026 10:23:22 +0800 Subject: [PATCH 27/27] Add Mastodon v4.7.3 package directory --- apps/mastodon-linuxserver/4.7.3/.env.sample | 31 ++ apps/mastodon-linuxserver/4.7.3/data.yml | 505 ++++++++++++++++++ apps/mastodon-linuxserver/4.7.3/data/.gitkeep | 0 .../4.7.3/docker-compose.yml | 72 +++ .../4.7.3/scripts/init.sh | 236 ++++++++ .../4.7.3/scripts/uninstall.sh | 2 + .../4.7.3/scripts/upgrade.sh | 251 +++++++++ 7 files changed, 1097 insertions(+) create mode 100644 apps/mastodon-linuxserver/4.7.3/.env.sample create mode 100644 apps/mastodon-linuxserver/4.7.3/data.yml create mode 100644 apps/mastodon-linuxserver/4.7.3/data/.gitkeep create mode 100644 apps/mastodon-linuxserver/4.7.3/docker-compose.yml create mode 100755 apps/mastodon-linuxserver/4.7.3/scripts/init.sh create mode 100755 apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh create mode 100755 apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh diff --git a/apps/mastodon-linuxserver/4.7.3/.env.sample b/apps/mastodon-linuxserver/4.7.3/.env.sample new file mode 100644 index 000000000..1ad21ac97 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/.env.sample @@ -0,0 +1,31 @@ +CONTAINER_NAME= +PANEL_APP_PORT_HTTP=80 +PANEL_APP_PORT_HTTPS=443 +CONFIG_PATH=./data/config +LOCAL_DOMAIN=example.com +WEB_DOMAIN= +REDIS_HOST=mastodon-redis +REDIS_PORT=6379 +DB_HOST=mastodon-db +DB_PORT=5432 +DB_USER=mastodon +DB_NAME=mastodon +DB_PASSWORD=replace-with-db-password +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=replace-with-generated-active-record-primary-key +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=replace-with-generated-active-record-deterministic-key +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=replace-with-generated-active-record-salt +SECRET_KEY_BASE=replace-with-generated-secret-key-base +OTP_SECRET=replace-with-generated-otp-secret +VAPID_PRIVATE_KEY=replace-with-generated-vapid-private-key +VAPID_PUBLIC_KEY=replace-with-generated-vapid-public-key +SMTP_SERVER=mail.example.com +SMTP_PORT=25 +SMTP_LOGIN= +SMTP_PASSWORD= +SMTP_FROM_ADDRESS=notifications@example.com +MASTODON_PROMETHEUS_EXPORTER_ENABLED=false +SIDEKIQ_THREADS=5 +DB_POOL=5 +DB_DATA_PATH=./data/db +REDIS_DATA_PATH=./data/redis +TIME_ZONE=Asia/Shanghai diff --git a/apps/mastodon-linuxserver/4.7.3/data.yml b/apps/mastodon-linuxserver/4.7.3/data.yml new file mode 100644 index 000000000..a533c1819 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/data.yml @@ -0,0 +1,505 @@ +additionalProperties: + formFields: + - default: 80 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: 443 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: HTTPS-порт + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: '' + edit: true + envKey: LOCAL_DOMAIN + labelEn: Local domain + labelZh: 实例域名 + label: + en: Local domain + zh: 实例域名 + zh-Hant: 實例網域 + ja: ローカルドメイン + ko: 로컬 도메인 + ru: Локальный домен + ms: Domain setempat + pt-br: Dominio local + required: true + type: text + - default: '' + edit: true + envKey: WEB_DOMAIN + labelEn: Web domain + labelZh: Web 域名 + label: + en: Web domain + zh: Web 域名 + zh-Hant: Web 網域 + ja: Web ドメイン + ko: Web 도메인 + ru: Web-домен + ms: Domain web + pt-br: Dominio web + required: false + type: text + - default: "false" + edit: true + envKey: MASTODON_PROMETHEUS_EXPORTER_ENABLED + labelEn: Prometheus exporter + labelZh: Prometheus 导出器 + label: + en: Prometheus exporter + zh: Prometheus 导出器 + zh-Hant: Prometheus 匯出器 + ja: Prometheus エクスポーター + ko: Prometheus exporter + ru: Экспортер Prometheus + ms: Pengeksport Prometheus + pt-br: Exportador Prometheus + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: mastodon-db + edit: true + envKey: DB_HOST + labelEn: Database host + labelZh: 数据库主机 + label: + en: Database host + zh: 数据库主机 + zh-Hant: 資料庫主機 + ja: データベースホスト + ko: 데이터베이스 호스트 + ru: Хост базы данных + ms: Hos pangkalan data + pt-br: Host do banco + required: true + type: text + - default: 5432 + edit: true + envKey: DB_PORT + labelEn: Database port + labelZh: 数据库端口 + label: + en: Database port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco + required: true + type: number + - default: mastodon + edit: true + envKey: DB_USER + labelEn: Database username + labelZh: 数据库用户名 + label: + en: Database username + zh: 数据库用户名 + zh-Hant: 資料庫使用者名稱 + ja: データベースユーザー名 + ko: 데이터베이스 사용자 이름 + ru: Имя пользователя базы + ms: Nama pengguna pangkalan data + pt-br: Usuario do banco + required: true + type: text + - default: mastodon + edit: true + envKey: DB_NAME + labelEn: Database name + labelZh: 数据库名称 + label: + en: Database name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco + required: true + type: text + - default: mastodon-redis + edit: true + envKey: REDIS_HOST + labelEn: Redis host + labelZh: Redis 主机 + label: + en: Redis host + zh: Redis 主机 + zh-Hant: Redis 主機 + ja: Redis ホスト + ko: Redis 호스트 + ru: Хост Redis + ms: Hos Redis + pt-br: Host do Redis + required: true + type: text + - default: 6379 + edit: true + envKey: REDIS_PORT + labelEn: Redis port + labelZh: Redis 端口 + label: + en: Redis port + zh: Redis 端口 + zh-Hant: Redis 連接埠 + ja: Redis ポート + ko: Redis 포트 + ru: Порт Redis + ms: Port Redis + pt-br: Porta do Redis + required: true + type: number + - default: '' + edit: true + envKey: DB_PASSWORD + labelEn: Database password + labelZh: 数据库密码 + label: + en: Database password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: SECRET_KEY_BASE + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: OTP_SECRET + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: VAPID_PRIVATE_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: VAPID_PUBLIC_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: SMTP_SERVER + labelEn: SMTP server + labelZh: SMTP 服务器 + label: + en: SMTP server + zh: SMTP 服务器 + zh-Hant: SMTP 伺服器 + ja: SMTP サーバー + ko: SMTP 서버 + ru: SMTP-сервер + ms: Pelayan SMTP + pt-br: Servidor SMTP + required: true + type: text + - default: 25 + edit: true + envKey: SMTP_PORT + labelEn: SMTP port + labelZh: SMTP 端口 + label: + en: SMTP port + zh: SMTP 端口 + zh-Hant: SMTP 連接埠 + ja: SMTP ポート + ko: SMTP 포트 + ru: Порт SMTP + ms: Port SMTP + pt-br: Porta SMTP + required: true + type: number + - default: '' + edit: true + envKey: SMTP_LOGIN + labelEn: SMTP login + labelZh: SMTP 登录名 + label: + en: SMTP login + zh: SMTP 登录名 + zh-Hant: SMTP 登入名 + ja: SMTP ログイン + ko: SMTP 로그인 + ru: Логин SMTP + ms: Log masuk SMTP + pt-br: Login SMTP + required: false + type: text + - default: '' + edit: true + envKey: SMTP_PASSWORD + labelEn: SMTP password + labelZh: SMTP 密码 + label: + en: SMTP password + zh: SMTP 密码 + zh-Hant: SMTP 密碼 + ja: SMTP パスワード + ko: SMTP 비밀번호 + ru: Пароль SMTP + ms: Kata laluan SMTP + pt-br: Senha SMTP + required: false + type: password + - default: '' + edit: true + envKey: SMTP_FROM_ADDRESS + labelEn: SMTP from address + labelZh: SMTP 发件地址 + label: + en: SMTP from address + zh: SMTP 发件地址 + zh-Hant: SMTP 寄件地址 + ja: SMTP 送信元アドレス + ko: SMTP 발신 주소 + ru: SMTP адрес отправителя + ms: Alamat pengirim SMTP + pt-br: Endereco remetente SMTP + required: true + type: text + - default: 5 + edit: true + envKey: SIDEKIQ_THREADS + labelEn: Sidekiq threads + labelZh: Sidekiq 线程数 + label: + en: Sidekiq threads + zh: Sidekiq 线程数 + zh-Hant: Sidekiq 執行緒數 + ja: Sidekiq スレッド数 + ko: Sidekiq 스레드 수 + ru: Потоки Sidekiq + ms: Benang Sidekiq + pt-br: Threads Sidekiq + required: true + type: number + - default: 5 + edit: true + envKey: DB_POOL + labelEn: Database pool size + labelZh: 数据库连接池大小 + label: + en: Database pool size + zh: 数据库连接池大小 + zh-Hant: 資料庫連線池大小 + ja: データベースプールサイズ + ko: 데이터베이스 풀 크기 + ru: Размер пула базы данных + ms: Saiz kolam pangkalan data + pt-br: Tamanho do pool do banco + required: true + type: number + - default: ./data/db + edit: true + envKey: DB_DATA_PATH + labelEn: Database data path + labelZh: 数据库数据目录 + label: + en: Database data path + zh: 数据库数据目录 + zh-Hant: 資料庫資料目錄 + ja: データベースデータディレクトリ + ko: 데이터베이스 데이터 디렉터リ + ru: Каталог данных базы + ms: Laluan data pangkalan data + pt-br: Caminho de dados do banco + required: true + type: text + - default: ./data/redis + edit: true + envKey: REDIS_DATA_PATH + labelEn: Redis data path + labelZh: Redis 数据目录 + label: + en: Redis data path + zh: Redis 数据目录 + zh-Hant: Redis 資料目錄 + ja: Redis データディレクトリ + ko: Redis 데이터 디렉터리 + ru: Каталог данных Redis + ms: Laluan data Redis + pt-br: Caminho de dados Redis + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/mastodon-linuxserver/4.7.3/data/.gitkeep b/apps/mastodon-linuxserver/4.7.3/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/mastodon-linuxserver/4.7.3/docker-compose.yml b/apps/mastodon-linuxserver/4.7.3/docker-compose.yml new file mode 100644 index 000000000..22b6e6ef1 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/docker-compose.yml @@ -0,0 +1,72 @@ +networks: + 1panel-network: + external: true +services: + mastodon: + image: "linuxserver/mastodon:4.7.3" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + - "${PANEL_APP_PORT_HTTPS}:443" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + - LOCAL_DOMAIN=${LOCAL_DOMAIN} + - REDIS_HOST=${REDIS_HOST} + - REDIS_PORT=${REDIS_PORT} + - DB_HOST=${DB_HOST} + - DB_USER=${DB_USER} + - DB_NAME=${DB_NAME} + - DB_PASS=${DB_PASSWORD} + - DB_PORT=${DB_PORT} + - ES_ENABLED=false + - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} + - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} + - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} + - SECRET_KEY_BASE=${SECRET_KEY_BASE} + - OTP_SECRET=${OTP_SECRET} + - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} + - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} + - SMTP_SERVER=${SMTP_SERVER} + - SMTP_PORT=${SMTP_PORT} + - SMTP_LOGIN=${SMTP_LOGIN:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} + - S3_ENABLED=false + - WEB_DOMAIN=${WEB_DOMAIN:-} + - MASTODON_PROMETHEUS_EXPORTER_ENABLED=${MASTODON_PROMETHEUS_EXPORTER_ENABLED} + - SIDEKIQ_THREADS=${SIDEKIQ_THREADS} + - DB_POOL=${DB_POOL} + depends_on: + - mastodon-db + - mastodon-redis + labels: + createdBy: "Apps" + mastodon-db: + image: "postgres:16-alpine" + restart: always + networks: + - 1panel-network + volumes: + - "${DB_DATA_PATH}:/var/lib/postgresql/data" + environment: + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + labels: + createdBy: "Apps" + mastodon-redis: + image: "redis:7-alpine" + restart: always + networks: + - 1panel-network + volumes: + - "${REDIS_DATA_PATH}:/data" + labels: + createdBy: "Apps" diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/init.sh b/apps/mastodon-linuxserver/4.7.3/scripts/init.sh new file mode 100755 index 000000000..bee255c50 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/init.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4" +LEGACY_DB_PASSWORD="mastodon-change-me" +LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo" +LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0" +LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1" +LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0" +LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac" +LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E=" +LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8=" + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +run_mastodon_generator() { + docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1" +} + +extract_generator_value() { + local output="$1" + local key="$2" + printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1 +} + +is_active_record_secret_valid() { + local value="$1" + [[ "$value" =~ ^[A-Za-z0-9]{32}$ ]] +} + +is_hex_secret_valid() { + local value="$1" + [[ "$value" =~ ^[a-f0-9]{128}$ ]] +} + +is_vapid_key_valid() { + local value="$1" + [[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]] +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}" +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}" +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}" +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}" +SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}" +OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}" +VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}" +VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}" + +if [[ -f "$ENV_FILE" ]]; then + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi + + active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)" + active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)" + active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)" + cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)" + cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)" + cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)" + if is_active_record_secret_valid "$active_primary" \ + && is_active_record_secret_valid "$active_deterministic" \ + && is_active_record_secret_valid "$active_salt" \ + && [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + && [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + && [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + elif is_active_record_secret_valid "$cached_active_primary" \ + && is_active_record_secret_valid "$cached_active_deterministic" \ + && is_active_record_secret_valid "$cached_active_salt"; then + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt" + elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + || [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + || [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \ + || ! is_active_record_secret_valid "$active_primary" \ + || ! is_active_record_secret_valid "$active_deterministic" \ + || ! is_active_record_secret_valid "$active_salt"; then + active_record_output="$(run_mastodon_generator generate-active-record)" + active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")" + active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")" + active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")" + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + fi + + secret_key_base="$(read_env_value SECRET_KEY_BASE || true)" + if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then + cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)" + if is_hex_secret_valid "$cached_secret_key_base"; then + set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base" + else + secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "SECRET_KEY_BASE" "$secret_key_base" + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + else + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + + otp_secret="$(read_env_value OTP_SECRET || true)" + if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then + cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)" + if is_hex_secret_valid "$cached_otp_secret"; then + set_env_value "OTP_SECRET" "$cached_otp_secret" + else + otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "OTP_SECRET" "$otp_secret" + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + else + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + + vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)" + vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)" + cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)" + cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)" + if is_vapid_key_valid "$vapid_private_key" \ + && is_vapid_key_valid "$vapid_public_key" \ + && [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + && [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key" + elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then + set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key" + set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key" + elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + || [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \ + || ! is_vapid_key_valid "$vapid_private_key" \ + || ! is_vapid_key_valid "$vapid_public_key"; then + vapid_output="$(run_mastodon_generator generate-vapid)" + set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + fi +fi + +paths=( + "${CONFIG_PATH:-./data/config}" + "${DB_DATA_PATH:-./data/db}" + "${REDIS_DATA_PATH:-./data/redis}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh b/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh b/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh new file mode 100755 index 000000000..8e4e6de98 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh @@ -0,0 +1,251 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4" +LEGACY_DB_PASSWORD="mastodon-change-me" +LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo" +LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0" +LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1" +LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0" +LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac" +LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E=" +LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8=" + +ensure_env_default() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key migration" + return + fi + + if grep -qE "^${key}=" "$ENV_FILE"; then + echo "$key already exists" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +run_mastodon_generator() { + docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1" +} + +extract_generator_value() { + local output="$1" + local key="$2" + printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1 +} + +is_active_record_secret_valid() { + local value="$1" + [[ "$value" =~ ^[A-Za-z0-9]{32}$ ]] +} + +is_hex_secret_valid() { + local value="$1" + [[ "$value" =~ ^[a-f0-9]{128}$ ]] +} + +is_vapid_key_valid() { + local value="$1" + [[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]] +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}" +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}" +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}" +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}" +SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}" +OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}" +VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}" +VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}" + +if [[ -f "$ENV_FILE" ]]; then + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi + + active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)" + active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)" + active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)" + cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)" + cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)" + cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)" + if is_active_record_secret_valid "$active_primary" \ + && is_active_record_secret_valid "$active_deterministic" \ + && is_active_record_secret_valid "$active_salt" \ + && [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + && [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + && [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + elif is_active_record_secret_valid "$cached_active_primary" \ + && is_active_record_secret_valid "$cached_active_deterministic" \ + && is_active_record_secret_valid "$cached_active_salt"; then + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt" + elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + || [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + || [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \ + || ! is_active_record_secret_valid "$active_primary" \ + || ! is_active_record_secret_valid "$active_deterministic" \ + || ! is_active_record_secret_valid "$active_salt"; then + active_record_output="$(run_mastodon_generator generate-active-record)" + active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")" + active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")" + active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")" + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + fi + + secret_key_base="$(read_env_value SECRET_KEY_BASE || true)" + if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then + cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)" + if is_hex_secret_valid "$cached_secret_key_base"; then + set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base" + else + secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "SECRET_KEY_BASE" "$secret_key_base" + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + else + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + + otp_secret="$(read_env_value OTP_SECRET || true)" + if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then + cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)" + if is_hex_secret_valid "$cached_otp_secret"; then + set_env_value "OTP_SECRET" "$cached_otp_secret" + else + otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "OTP_SECRET" "$otp_secret" + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + else + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + + vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)" + vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)" + cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)" + cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)" + if is_vapid_key_valid "$vapid_private_key" \ + && is_vapid_key_valid "$vapid_public_key" \ + && [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + && [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key" + elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then + set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key" + set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key" + elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + || [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \ + || ! is_vapid_key_valid "$vapid_private_key" \ + || ! is_vapid_key_valid "$vapid_public_key"; then + vapid_output="$(run_mastodon_generator generate-vapid)" + set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + fi + + ensure_env_default "REDIS_HOST" "mastodon-redis" + ensure_env_default "REDIS_PORT" "6379" + ensure_env_default "DB_HOST" "mastodon-db" + ensure_env_default "DB_PORT" "5432" + ensure_env_default "DB_USER" "mastodon" + ensure_env_default "DB_NAME" "mastodon" + ensure_env_default "MASTODON_PROMETHEUS_EXPORTER_ENABLED" "false" +else + echo "$ENV_FILE not found; skipped LinuxServer environment migration" +fi