From 5784e494b1f2040dc5db43aa344bcf29c78c7a94 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 20 Sep 2026 09:52:48 +0000 Subject: [PATCH 1/7] update some apps --- Notes.md | 14 +- apps/cloudbeaver/.env | 51 ++- apps/cloudbeaver/CHANGELOG.md | 7 +- apps/cloudbeaver/Notes.md | 3 - apps/cloudbeaver/README.md | 88 ++++- apps/cloudbeaver/docker-compose.yml | 21 +- apps/cloudbeaver/src/{filelist => README.md} | 0 apps/cloudbeaver/src/after_up.sh | 1 - apps/cloudbeaver/src/get_version.sh | 1 - apps/cloudbeaver/tests/cases.yml | 5 + apps/cloudbeaver/variables.json | 20 +- apps/databasus/.env | 38 +- apps/databasus/CHANGELOG.md | 7 +- apps/databasus/README.md | 90 +++++ apps/databasus/docker-compose.yml | 14 +- apps/databasus/tests/cases.yml | 5 + apps/databasus/variables.json | 20 +- apps/dolibarr/.env | 60 +++- apps/dolibarr/CHANGELOG.md | 9 +- apps/dolibarr/README.md | 93 ++++- apps/dolibarr/docker-compose.yml | 56 ++- apps/dolibarr/tests/cases.yml | 5 + apps/dolibarr/variables.json | 26 +- apps/uptimekuma/.env | 39 ++- apps/uptimekuma/CHANGELOG.md | 8 +- apps/uptimekuma/Notes.md | 1 - apps/uptimekuma/README.md | 85 ++++- apps/uptimekuma/docker-compose.yml | 20 +- apps/uptimekuma/tests/cases.yml | 5 + apps/uptimekuma/variables.json | 20 +- apps/varnish/.env | 31 +- apps/varnish/CHANGELOG.md | 10 +- apps/varnish/Notes.md | 28 +- apps/varnish/README.md | 86 ++++- apps/varnish/docker-compose.yml | 12 +- apps/varnish/src/default.vcl | 82 +++-- apps/varnish/tests/cases.yml | 4 + apps/varnish/tests/smoke.sh | 20 ++ apps/varnish/variables.json | 2 +- apps/vault/.env | 39 ++- apps/vault/CHANGELOG.md | 13 +- apps/vault/README.md | 89 ++++- apps/vault/docker-compose.yml | 21 +- apps/vault/tests/cases.yml | 5 + apps/vault/variables.json | 15 +- apps/vaultwarden/.env | 51 ++- apps/vaultwarden/CHANGELOG.md | 13 +- apps/vaultwarden/README.md | 90 ++++- apps/vaultwarden/docker-compose.yml | 42 ++- apps/vaultwarden/tests/cases.yml | 5 + apps/vaultwarden/variables.json | 9 +- apps/vespa/.env | 34 +- apps/vespa/CHANGELOG.md | 14 +- apps/vespa/Notes.md | 9 +- apps/vespa/README.md | 86 ++++- apps/vespa/docker-compose.yml | 44 +-- apps/vespa/tests/cases.yml | 2 + apps/vespa/variables.json | 11 +- apps/wazuh/.env | 89 ++--- apps/wazuh/CHANGELOG.md | 9 +- apps/wazuh/Dockerfile | 21 -- apps/wazuh/Notes.md | 111 +++++- apps/wazuh/README.md | 121 ++++++- apps/wazuh/docker-compose.yml | 176 +++++++--- apps/wazuh/src/certs.yml | 16 + apps/wazuh/src/internal_users.yml | 56 +++ apps/wazuh/src/opensearch_dashboards.yml | 16 + apps/wazuh/src/wazuh.indexer.yml | 36 ++ apps/wazuh/src/wazuh.yml | 7 + .../src/wazuh_indexer_ssl_certs/.gitkeep | 0 apps/wazuh/src/wazuh_manager.conf | 311 +++++++++++++++++ apps/wazuh/tests/cases.yml | 9 + apps/wazuh/tests/check.sh | 21 ++ apps/wazuh/variables.json | 24 +- apps/weaviate/.env | 29 +- apps/weaviate/CHANGELOG.md | 12 +- apps/weaviate/Notes.md | 1 - apps/weaviate/README.md | 89 ++++- apps/weaviate/docker-compose.yml | 60 ++-- apps/weaviate/tests/cases.yml | 8 + apps/weaviate/variables.json | 9 +- apps/webcheck/src/nginx-proxy.conf.template | 57 --- apps/webcheck/src/php_exra.ini | 8 - apps/youtrack/.env | 37 +- apps/youtrack/CHANGELOG.md | 11 +- apps/youtrack/Notes.md | 7 - apps/youtrack/README.md | 86 ++++- apps/youtrack/docker-compose.yml | 18 +- apps/youtrack/variables.json | 20 +- apps/zammad/.env | 72 ++-- apps/zammad/CHANGELOG.md | 8 +- apps/zammad/README.md | 99 +++++- apps/zammad/docker-compose.yml | 104 ++++-- apps/zammad/src/postgresql_init.sh | 41 ++- apps/zammad/tests/cases.yml | 5 + apps/zammad/variables.json | 37 +- apps/zentao/.env | 53 ++- apps/zentao/CHANGELOG.md | 8 +- apps/zentao/Notes.md | 11 - apps/zentao/README.md | 86 ++++- apps/zentao/docker-compose.yml | 23 +- apps/zentao/tests/cases.yml | 5 + apps/zentao/variables.json | 11 +- apps/zulip/.env | 60 +++- apps/zulip/CHANGELOG.md | 10 +- apps/zulip/Notes.md | 13 +- apps/zulip/README.md | 93 ++++- apps/zulip/docker-compose.yml | 45 +-- apps/zulip/src/README.md | 4 +- apps/zulip/src/create-default-realm.sh | 32 ++ apps/zulip/tests/cases.yml | 7 + apps/zulip/tests/login-page.sh | 9 + apps/zulip/variables.json | 36 +- {apps => archive/apps}/v2ray/.env | 0 {apps => archive/apps}/v2ray/CHANGELOG.md | 0 {apps => archive/apps}/v2ray/Notes.md | 0 {apps => archive/apps}/v2ray/README.md | 0 .../apps}/v2ray/docker-compose.yml | 0 {apps => archive/apps}/v2ray/src/README.md | 0 {apps => archive/apps}/v2ray/src/config.json | 0 {apps => archive/apps}/v2ray/variables.json | 0 {apps => archive/apps}/webcheck/.env | 0 {apps => archive/apps}/webcheck/CHANGELOG.md | 0 {apps => archive/apps}/webcheck/Notes.md | 0 {apps => archive/apps}/webcheck/README.md | 0 .../apps}/webcheck/docker-compose.yml | 0 {apps => archive/apps}/webcheck/src/README.md | 0 .../webcheck}/src/nginx-proxy.conf.template | 0 .../apps/webcheck}/src/php_exra.ini | 0 .../apps}/webcheck/variables.json | 0 {apps => archive/apps}/wikijs/.env | 0 {apps => archive/apps}/wikijs/CHANGELOG.md | 0 {apps => archive/apps}/wikijs/Notes.md | 0 {apps => archive/apps}/wikijs/README.md | 0 .../apps}/wikijs/docker-compose.yml | 0 {apps => archive/apps}/wikijs/src/README.md | 0 {apps => archive/apps}/wikijs/variables.json | 0 {apps => archive/apps}/windmill/.env | 0 {apps => archive/apps}/windmill/CHANGELOG.md | 0 {apps => archive/apps}/windmill/Notes.md | 0 {apps => archive/apps}/windmill/README.md | 0 .../apps}/windmill/docker-compose.yml | 0 {apps => archive/apps}/windmill/src/Caddyfile | 0 {apps => archive/apps}/windmill/src/README.md | 0 .../apps}/windmill/variables.json | 0 {apps => archive/apps}/wireguard/.env | 0 {apps => archive/apps}/wireguard/CHANGELOG.md | 0 {apps => archive/apps}/wireguard/Notes.md | 0 {apps => archive/apps}/wireguard/README.md | 0 .../apps}/wireguard/docker-compose.yml | 0 {apps => archive/apps}/wireguard/getkeys.sh | 0 .../apps}/wireguard/src/README.md | 0 .../apps}/wireguard/src/nginx_proxy.conf | 0 .../apps}/wireguard/variables.json | 0 {apps => archive/apps}/zabbix/.env | 0 {apps => archive/apps}/zabbix/CHANGELOG.md | 0 {apps => archive/apps}/zabbix/Notes.md | 0 {apps => archive/apps}/zabbix/README.md | 0 .../apps}/zabbix/docker-compose.yml | 0 {apps => archive/apps}/zabbix/src/README.md | 0 {apps => archive/apps}/zabbix/variables.json | 0 {apps => archive/apps}/zerotier/.env | 0 {apps => archive/apps}/zerotier/CHANGELOG.md | 0 {apps => archive/apps}/zerotier/Notes.md | 0 {apps => archive/apps}/zerotier/README.md | 0 .../apps}/zerotier/docker-compose.yml | 0 {apps => archive/apps}/zerotier/src/README.md | 0 .../apps}/zerotier/variables.json | 0 {apps => archive/apps}/zitadel/.env | 0 {apps => archive/apps}/zitadel/CHANGELOG.md | 0 {apps => archive/apps}/zitadel/Notes.md | 0 {apps => archive/apps}/zitadel/README.md | 0 .../apps}/zitadel/docker-compose.yml | 0 {apps => archive/apps}/zitadel/src/README.md | 0 {apps => archive/apps}/zitadel/variables.json | 0 {apps => archive/apps}/zookeeper/.env | 0 {apps => archive/apps}/zookeeper/CHANGELOG.md | 0 {apps => archive/apps}/zookeeper/Notes.md | 0 {apps => archive/apps}/zookeeper/README.md | 0 .../apps}/zookeeper/docker-compose.yml | 0 .../apps}/zookeeper/src/README.md | 0 .../apps}/zookeeper/variables.json | 0 cli/README.md | 2 +- i18n/translation.json | 8 + list-check.md | 327 ++++++++++++++++++ metadata/archive.yaml | 27 ++ metadata/catalog/vespa.json | 13 + metadata/maintenance.yaml | 1 - skills/app-update/SKILL.md | 48 +-- skills/deploy-validation/SKILL.md | 2 +- 190 files changed, 3551 insertions(+), 903 deletions(-) delete mode 100644 apps/cloudbeaver/Notes.md rename apps/cloudbeaver/src/{filelist => README.md} (100%) delete mode 100644 apps/cloudbeaver/src/after_up.sh delete mode 100644 apps/cloudbeaver/src/get_version.sh create mode 100644 apps/cloudbeaver/tests/cases.yml create mode 100644 apps/databasus/README.md create mode 100644 apps/databasus/tests/cases.yml create mode 100644 apps/dolibarr/tests/cases.yml delete mode 100644 apps/uptimekuma/Notes.md create mode 100644 apps/uptimekuma/tests/cases.yml create mode 100644 apps/varnish/tests/cases.yml create mode 100644 apps/varnish/tests/smoke.sh create mode 100644 apps/vault/tests/cases.yml create mode 100644 apps/vaultwarden/tests/cases.yml create mode 100644 apps/vespa/tests/cases.yml delete mode 100644 apps/wazuh/Dockerfile create mode 100755 apps/wazuh/src/certs.yml create mode 100644 apps/wazuh/src/internal_users.yml create mode 100644 apps/wazuh/src/opensearch_dashboards.yml create mode 100644 apps/wazuh/src/wazuh.indexer.yml create mode 100644 apps/wazuh/src/wazuh.yml create mode 100644 apps/wazuh/src/wazuh_indexer_ssl_certs/.gitkeep create mode 100644 apps/wazuh/src/wazuh_manager.conf create mode 100644 apps/wazuh/tests/cases.yml create mode 100755 apps/wazuh/tests/check.sh delete mode 100644 apps/weaviate/Notes.md create mode 100644 apps/weaviate/tests/cases.yml delete mode 100644 apps/webcheck/src/nginx-proxy.conf.template delete mode 100644 apps/webcheck/src/php_exra.ini delete mode 100644 apps/youtrack/Notes.md mode change 100644 => 100755 apps/zammad/src/postgresql_init.sh create mode 100644 apps/zammad/tests/cases.yml delete mode 100644 apps/zentao/Notes.md create mode 100644 apps/zentao/tests/cases.yml create mode 100755 apps/zulip/src/create-default-realm.sh create mode 100644 apps/zulip/tests/cases.yml create mode 100755 apps/zulip/tests/login-page.sh rename {apps => archive/apps}/v2ray/.env (100%) rename {apps => archive/apps}/v2ray/CHANGELOG.md (100%) rename {apps => archive/apps}/v2ray/Notes.md (100%) rename {apps => archive/apps}/v2ray/README.md (100%) rename {apps => archive/apps}/v2ray/docker-compose.yml (100%) rename {apps => archive/apps}/v2ray/src/README.md (100%) rename {apps => archive/apps}/v2ray/src/config.json (100%) rename {apps => archive/apps}/v2ray/variables.json (100%) rename {apps => archive/apps}/webcheck/.env (100%) rename {apps => archive/apps}/webcheck/CHANGELOG.md (100%) rename {apps => archive/apps}/webcheck/Notes.md (100%) rename {apps => archive/apps}/webcheck/README.md (100%) rename {apps => archive/apps}/webcheck/docker-compose.yml (100%) rename {apps => archive/apps}/webcheck/src/README.md (100%) rename {apps/wazuh => archive/apps/webcheck}/src/nginx-proxy.conf.template (100%) rename {apps/wazuh => archive/apps/webcheck}/src/php_exra.ini (100%) rename {apps => archive/apps}/webcheck/variables.json (100%) rename {apps => archive/apps}/wikijs/.env (100%) rename {apps => archive/apps}/wikijs/CHANGELOG.md (100%) rename {apps => archive/apps}/wikijs/Notes.md (100%) rename {apps => archive/apps}/wikijs/README.md (100%) rename {apps => archive/apps}/wikijs/docker-compose.yml (100%) rename {apps => archive/apps}/wikijs/src/README.md (100%) rename {apps => archive/apps}/wikijs/variables.json (100%) rename {apps => archive/apps}/windmill/.env (100%) rename {apps => archive/apps}/windmill/CHANGELOG.md (100%) rename {apps => archive/apps}/windmill/Notes.md (100%) rename {apps => archive/apps}/windmill/README.md (100%) rename {apps => archive/apps}/windmill/docker-compose.yml (100%) rename {apps => archive/apps}/windmill/src/Caddyfile (100%) rename {apps => archive/apps}/windmill/src/README.md (100%) rename {apps => archive/apps}/windmill/variables.json (100%) rename {apps => archive/apps}/wireguard/.env (100%) rename {apps => archive/apps}/wireguard/CHANGELOG.md (100%) rename {apps => archive/apps}/wireguard/Notes.md (100%) rename {apps => archive/apps}/wireguard/README.md (100%) rename {apps => archive/apps}/wireguard/docker-compose.yml (100%) rename {apps => archive/apps}/wireguard/getkeys.sh (100%) rename {apps => archive/apps}/wireguard/src/README.md (100%) rename {apps => archive/apps}/wireguard/src/nginx_proxy.conf (100%) rename {apps => archive/apps}/wireguard/variables.json (100%) rename {apps => archive/apps}/zabbix/.env (100%) rename {apps => archive/apps}/zabbix/CHANGELOG.md (100%) rename {apps => archive/apps}/zabbix/Notes.md (100%) rename {apps => archive/apps}/zabbix/README.md (100%) rename {apps => archive/apps}/zabbix/docker-compose.yml (100%) rename {apps => archive/apps}/zabbix/src/README.md (100%) rename {apps => archive/apps}/zabbix/variables.json (100%) rename {apps => archive/apps}/zerotier/.env (100%) rename {apps => archive/apps}/zerotier/CHANGELOG.md (100%) rename {apps => archive/apps}/zerotier/Notes.md (100%) rename {apps => archive/apps}/zerotier/README.md (100%) rename {apps => archive/apps}/zerotier/docker-compose.yml (100%) rename {apps => archive/apps}/zerotier/src/README.md (100%) rename {apps => archive/apps}/zerotier/variables.json (100%) rename {apps => archive/apps}/zitadel/.env (100%) rename {apps => archive/apps}/zitadel/CHANGELOG.md (100%) rename {apps => archive/apps}/zitadel/Notes.md (100%) rename {apps => archive/apps}/zitadel/README.md (100%) rename {apps => archive/apps}/zitadel/docker-compose.yml (100%) rename {apps => archive/apps}/zitadel/src/README.md (100%) rename {apps => archive/apps}/zitadel/variables.json (100%) rename {apps => archive/apps}/zookeeper/.env (100%) rename {apps => archive/apps}/zookeeper/CHANGELOG.md (100%) rename {apps => archive/apps}/zookeeper/Notes.md (100%) rename {apps => archive/apps}/zookeeper/README.md (100%) rename {apps => archive/apps}/zookeeper/docker-compose.yml (100%) rename {apps => archive/apps}/zookeeper/src/README.md (100%) rename {apps => archive/apps}/zookeeper/variables.json (100%) create mode 100644 list-check.md create mode 100644 metadata/catalog/vespa.json diff --git a/Notes.md b/Notes.md index 6ded29c8d..6f709198c 100644 --- a/Notes.md +++ b/Notes.md @@ -29,5 +29,17 @@ Docker Model Runner InfluxDB 开源时序数据库 OpenClaw Canvas LMS -laravel Semaphore + + +cloudreve,compreface,commafeed, coze,dashy,ejbca, frigate, falcon + +Qdrant +e2e test for: zammad, vaultwarden, wazuh, varnish +varnish not have config file +pangolin.net +RustDesk +elizaOS +Nuclear + +port define? \ No newline at end of file diff --git a/apps/cloudbeaver/.env b/apps/cloudbeaver/.env index 80105c23d..386428787 100644 --- a/apps/cloudbeaver/.env +++ b/apps/cloudbeaver/.env @@ -1,22 +1,49 @@ -W9_VERSION='25.2.2' -W9_DIST='community' W9_REPO=dbeaver/cloudbeaver +W9_DIST=community +W9_VERSION=26.2 + W9_POWER_PASSWORD='Bwmj2DDuZoM!Q08G' +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='cloudbeaver' + +W9_ID=cloudbeaver + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=8978 -W9_HTTP_PORT_SET='9090' -W9_URL='appname.example.com' +W9_HTTP_PORT_SET=9090 -# dont't user [admin] which is cloudbeaver system variable +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +# Do not use [admin], which is reserved by CloudBeaver. W9_LOGIN_USER=cbadmin -W9_LOGIN_PASSWORD=$W9_POWER_PASSWORD +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} +W9_URL=appname.example.com +W9_URL_REPLACE=true + W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### -# CloudBeaver environments: https://github.com/dbeaver/cloudbeaver/wiki/Server-configuration#automatic-server-configuration +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# CloudBeaver image environment variables +# Docs: https://github.com/dbeaver/cloudbeaver/wiki/Server-configuration#automatic-server-configuration +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: CB_SERVER_NAME="CloudBeaver Server" -CB_SERVER_URL=$W9_URL -CB_ADMIN_NAME=$W9_LOGIN_USER -CB_ADMIN_PASSWORD=$W9_POWER_PASSWORD +CB_SERVER_URL=${W9_URL} +CB_ADMIN_NAME=${W9_LOGIN_USER} +CB_ADMIN_PASSWORD=${W9_POWER_PASSWORD} + +# Not used by default; enable only when needed: +# CLOUDBEAVER_WEB_SERVER_PORT=8978 +# CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED=false +# CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS=false +# CLOUDBEAVER_DB_DRIVER=postgres-jdbc +# CLOUDBEAVER_DB_URL=jdbc:postgresql://postgres:5432/cloudbeaver diff --git a/apps/cloudbeaver/CHANGELOG.md b/apps/cloudbeaver/CHANGELOG.md index 582cf46c5..922e2ad71 100644 --- a/apps/cloudbeaver/CHANGELOG.md +++ b/apps/cloudbeaver/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-17 +- Updated `W9_VERSION` from `25.2.2` to `26.2` and aligned `variables.json.edition` with the current upstream `x.x` tag. +- Normalized `.env` and `docker-compose.yml` to current repository policy: braced `${VAR}` references, a port purpose comment, no image/docs source comments, and no obsolete compose `version` key. +- Added `W9_URL_REPLACE=true` because `CB_SERVER_URL` references `W9_URL`. +- Added `upstream.docs`, `variables.json.access`, and `env.first_startup_only`; regenerated the README. diff --git a/apps/cloudbeaver/Notes.md b/apps/cloudbeaver/Notes.md deleted file mode 100644 index 6fd49fd5f..000000000 --- a/apps/cloudbeaver/Notes.md +++ /dev/null @@ -1,3 +0,0 @@ -# CloudBeaver - -Not found any enviroments diff --git a/apps/cloudbeaver/README.md b/apps/cloudbeaver/README.md index c9a339e33..f40b98ae8 100644 --- a/apps/cloudbeaver/README.md +++ b/apps/cloudbeaver/README.md @@ -1,26 +1,86 @@ -# CloudBeaver on Docker +# CloudBeaver on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for CloudBeaver: +## Quick Start +### Deploy Verification - - community: 25.1.0, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **CloudBeaver**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. CloudBeaver creates the administrator account from `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD` on first start. +2. Sign in and create a database connection. +3. Try a core feature, such as the SQL editor. -The following are the minimal [recommended requirements](https://github.com/dbeaver/cloudbeaver/wiki): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 4 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to CloudBeaver and change the password from the user profile. +2. `W9_LOGIN_PASSWORD` seeds the administrator password on first startup; changing `.env` later does not change an existing password. +3. If you cannot sign in, reset the password with the upstream admin password recovery procedure. + -## Install +## Configuration Reference -You can install this CloudBeaver by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [CloudBeaver Docker image](https://hub.docker.com/r/dbeaver/cloudbeaver) and makes some improvements below. -If you want use CloudBeaver with **Websoft9 Business Support** free, you can [subscribe CloudBeaver](https://www.websoft9.com/apps) on Cloud platform + +The package passes the server name, the public server URL, and the initial administrator credentials through `.env` (`CB_*`). The administrator account is created on first start, and `CB_SERVER_URL` is wired to `W9_URL`. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[CloudBeaver Administrator Guide](https://support.websoft9.com/docs/cloudbeaver) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 26.2, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8978 | + + +### Data Directory + + +Data is persisted in the `cloudbeaver` volume, mounted at `/opt/cloudbeaver/workspace`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_LOGIN_PASSWORD` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [CloudBeaver Administrator Guide](https://support.websoft9.com/docs/cloudbeaver) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/dbeaver/cloudbeaver) + +- [GitHub docs](https://github.com/dbeaver/cloudbeaver/wiki/Server-configuration) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/cloudbeaver/docker-compose.yml b/apps/cloudbeaver/docker-compose.yml index 7ecb5b29f..d069e07e5 100644 --- a/apps/cloudbeaver/docker-compose.yml +++ b/apps/cloudbeaver/docker-compose.yml @@ -1,18 +1,13 @@ -# image: https://hub.docker.com/r/dbeaver/cloudbeaver -# config docs: https://github.com/dbeaver/cloudbeaver/wiki/Server-configuration#automatic-server-configuration - -version: "3.8" - services: cloudbeaver: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID - restart: unless-stopped - env_file: .env - ports: - - '$W9_HTTP_PORT_SET:8978' - volumes: - - cloudbeaver:/opt/cloudbeaver/workspace + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + restart: unless-stopped + env_file: .env + ports: + - "${W9_HTTP_PORT_SET}:8978" # Web Console + volumes: + - cloudbeaver:/opt/cloudbeaver/workspace networks: default: diff --git a/apps/cloudbeaver/src/filelist b/apps/cloudbeaver/src/README.md similarity index 100% rename from apps/cloudbeaver/src/filelist rename to apps/cloudbeaver/src/README.md diff --git a/apps/cloudbeaver/src/after_up.sh b/apps/cloudbeaver/src/after_up.sh deleted file mode 100644 index 8b1378917..000000000 --- a/apps/cloudbeaver/src/after_up.sh +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/cloudbeaver/src/get_version.sh b/apps/cloudbeaver/src/get_version.sh deleted file mode 100644 index 42c4975e5..000000000 --- a/apps/cloudbeaver/src/get_version.sh +++ /dev/null @@ -1 +0,0 @@ -sudo echo "cloudbeaver version: $(docker exec -i $1 sed -n '3p' /opt/cloudbeaver/server/readme.txt)" 1>> /data/logs/install_version.txt diff --git a/apps/cloudbeaver/tests/cases.yml b/apps/cloudbeaver/tests/cases.yml new file mode 100644 index 000000000..fe6388e32 --- /dev/null +++ b/apps/cloudbeaver/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: status-endpoint + type: web-access + path: /status + expect_status: 200 diff --git a/apps/cloudbeaver/variables.json b/apps/cloudbeaver/variables.json index 162b63976..f76cfc528 100644 --- a/apps/cloudbeaver/variables.json +++ b/apps/cloudbeaver/variables.json @@ -2,21 +2,35 @@ "name": "cloudbeaver", "trademark": "CloudBeaver", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/dbeaver/cloudbeaver", + "docs": [ + "https://github.com/dbeaver/cloudbeaver/wiki/Server-configuration" + ] + }, "edition": [ { "dist": "community", "version": [ - "25.2.2", + "26.2", "latest" ] } ], + "access": { + "web": { + "port": 8978, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "4" }, - "upstream": { - "image": "https://hub.docker.com/r/dbeaver/cloudbeaver" + "env": { + "first_startup_only": [ + "W9_LOGIN_PASSWORD" + ] } } diff --git a/apps/databasus/.env b/apps/databasus/.env index f9d777cb7..b15fdaf84 100644 --- a/apps/databasus/.env +++ b/apps/databasus/.env @@ -1,11 +1,41 @@ -W9_REPO="databasus/databasus" +W9_REPO=databasus/databasus W9_DIST=community -W9_VERSION="latest" +W9_VERSION=v3.57.1 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_HTTP_PORT_SET=9001 W9_ID=databasus + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=4005 -W9_URL=example.youdomain.com +W9_HTTP_PORT_SET=9001 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +W9_URL=appname.example.com +W9_URL_REPLACE=true + W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Databasus image environment variables +# Docs: https://databasus.com/advanced-config +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +DATABASUS_URL=http://${W9_URL} + +# Not used by default; enable only when needed: +# PUID=999 +# PGID=999 +# LOG_LEVEL=info +# IS_DISABLE_ANONYMOUS_TELEMETRY=false +# OPEN_TELEMETRY_URL= diff --git a/apps/databasus/CHANGELOG.md b/apps/databasus/CHANGELOG.md index 582cf46c5..41955e4d3 100644 --- a/apps/databasus/CHANGELOG.md +++ b/apps/databasus/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-17 +- Updated `W9_VERSION` from `latest` to `v3.57.1` and aligned `variables.json.edition` with the current upstream tag. +- Normalized `.env` and `docker-compose.yml` to current repository policy: braced `${VAR}` references, a port purpose comment, and no image/docs source comments. +- Added `DATABASUS_URL` wired to `W9_URL` and set `W9_URL_REPLACE=true`. +- Added `upstream.docs`, `upstream.releases`, and `variables.json.access`; added `tests/cases.yml` with the system health check; generated the README. diff --git a/apps/databasus/README.md b/apps/databasus/README.md new file mode 100644 index 000000000..cd11f5c46 --- /dev/null +++ b/apps/databasus/README.md @@ -0,0 +1,90 @@ +# Databasus on Docker + +## Quick Start + +### Deploy Verification + +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Databasus**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + + +### Usage + +1. Open the Databasus URL and create the first administrator account (email and password) on the sign-up screen. +2. Add a database connection, choose a storage destination, and create a backup job. +3. Run the first backup and check its status on the dashboard. + +### Change Password + +1. Sign in to Databasus and change the password from the user profile. +2. If you cannot sign in, reset it from the host: + `docker exec -it databasus ./main --new-password="YourNewPassword" --email="admin"`. + + +## Configuration Reference + +Websoft9 packages this app from the official [Databasus Docker image](https://hub.docker.com/r/databasus/databasus) and makes some improvements below. + + +The package wires `DATABASUS_URL` to `W9_URL` so links Databasus generates use the public address. Optional SMTP, OAuth, telemetry, and logging settings are listed as comments at the end of `.env`. + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: v3.57.1, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 4005 | + + +### Data Directory + + +Data is persisted in the `databasus-data` volume, mounted at `/databasus-data`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Databasus Administrator Guide](https://support.websoft9.com/docs/databasus) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/databasus/databasus) + +- [Releases](https://github.com/databasus/databasus/releases) + +- [Official docs](https://databasus.com/installation) + +- [Official docs](https://databasus.com/advanced-config) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`; the first startup can take up to two minutes. + +**First backup fails?** +- Ensure the target database accepts connections from the container and the credentials have the required dump privileges. + +**Permission denied on the data volume?** +- Set `PUID` / `PGID` in `.env` to match the mount owner and rebuild the app. + diff --git a/apps/databasus/docker-compose.yml b/apps/databasus/docker-compose.yml index 8560432fb..ea0224b79 100644 --- a/apps/databasus/docker-compose.yml +++ b/apps/databasus/docker-compose.yml @@ -1,20 +1,18 @@ -# image,docs: https://databasus.com/installation - services: databasus: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + restart: unless-stopped env_file: .env ports: - - "$W9_HTTP_PORT_SET:4005" + - "${W9_HTTP_PORT_SET}:4005" # Web Console volumes: - databasus-data:/databasus-data - restart: unless-stopped volumes: databasus-data: - + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true diff --git a/apps/databasus/tests/cases.yml b/apps/databasus/tests/cases.yml new file mode 100644 index 000000000..05a327445 --- /dev/null +++ b/apps/databasus/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: system-health + type: web-access + path: /api/v1/system/health + expect_status: 200 diff --git a/apps/databasus/variables.json b/apps/databasus/variables.json index 33b4ae78e..edf3931ad 100644 --- a/apps/databasus/variables.json +++ b/apps/databasus/variables.json @@ -2,21 +2,35 @@ "name": "databasus", "trademark": "Databasus", "release": false, + "upstream": { + "image": "https://hub.docker.com/r/databasus/databasus", + "releases": "https://github.com/databasus/databasus/releases", + "docs": [ + "https://databasus.com/installation", + "https://databasus.com/advanced-config" + ] + }, "edition": [ { "dist": "community", "version": [ - "v3.9.0", + "v3.57.1", "latest" ] } ], + "access": { + "web": { + "port": 4005, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "10" }, - "upstream": { - "image": "https://hub.docker.com/r/databasus/databasus" + "env": { + "first_startup_only": [] } } diff --git a/apps/dolibarr/.env b/apps/dolibarr/.env index 22e5195d3..502e68ec3 100644 --- a/apps/dolibarr/.env +++ b/apps/dolibarr/.env @@ -1,32 +1,56 @@ W9_REPO=tuxgasy/dolibarr W9_DIST=community -W9_VERSION=18 +W9_VERSION=19.0.2 -W9_POWER_PASSWORD=spJNF09yzwWJaG! +W9_POWER_PASSWORD="spJNF09yzwWJaG!" + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### + W9_ID=dolibarr -W9_HTTP_PORT_SET=9001 + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=80 +W9_HTTP_PORT_SET=9001 + +# Dependency helpers: uncomment when the package bundles a dependency service. +W9_DB_EXPOSE=mariadb +W9_DB_VERSION=12.3 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. W9_LOGIN_USER=admin -W9_LOGIN_PASSWORD=$W9_POWER_PASSWORD -W9_DB_EXPOSE="mariadb" -W9_MARIADB_VERSION=latest -W9_URL=example.youdomain.com +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} +W9_URL=appname.example.com + W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### -# Below envs is from official Image +# ============================================================ +# Dolibarr image environment variables +# Docs: https://github.com/tuxgasy/docker-dolibarr +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ -DOLI_URL_ROOT='http://0.0.0.0' -PHP_INI_DATE_TIMEZONE='Europe/Paris' +# Used by docker-compose.yml: +DOLI_URL_ROOT=http://0.0.0.0 +DOLI_DB_HOST=${W9_ID}-mariadb +DOLI_DB_USER=${W9_ID} +DOLI_DB_PASSWORD=${W9_POWER_PASSWORD} +DOLI_DB_NAME=${W9_ID} +DOLI_ADMIN_LOGIN=${W9_LOGIN_USER} +DOLI_ADMIN_PASSWORD=${W9_POWER_PASSWORD} +PHP_INI_DATE_TIMEZONE=Europe/Paris PHP_INI_MEMORY_LIMIT=512M -DOLI_AUTH=dolibarr - -DOLI_DB_HOST=$W9_ID-mariadb -DOLI_DB_USER=$W9_ID -DOLI_DB_PASSWORD=$W9_POWER_PASSWORD -DOLI_DB_NAME=$W9_ID -DOLI_ADMIN_LOGIN=$W9_LOGIN_USER -DOLI_ADMIN_PASSWORD=$W9_POWER_PASSWORD \ No newline at end of file + +# Not used by default; enable only when needed: +# DOLI_AUTH=dolibarr +# DOLI_INSTALL_AUTO=1 +# DOLI_DB_PORT=3306 +# PHP_INI_UPLOAD_MAX_FILESIZE=2M +# PHP_INI_MAX_EXECUTION_TIME=120 diff --git a/apps/dolibarr/CHANGELOG.md b/apps/dolibarr/CHANGELOG.md index 582cf46c5..4c8ca2ac9 100644 --- a/apps/dolibarr/CHANGELOG.md +++ b/apps/dolibarr/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-17 +- Fixed the version drift: pinned `W9_VERSION` from `18` to `19.0.2`, matching `variables.json.edition` and the current upstream release. +- Replaced the non-standard `W9_MARIADB_VERSION` with `W9_DB_VERSION` and pinned the bundled MariaDB to `12.3` (LTS); `11.4` and `11.8` were also verified against Dolibarr 19.0.2. +- Normalized `.env` and `docker-compose.yml` to current repository policy: braced `${VAR}` references, a port purpose comment, no image/docs source comments, and no obsolete compose `version` key. +- Replaced the legacy `links` dependency with the shared `websoft9` network plus `depends_on`. +- Added `upstream.docs`, `variables.json.access`, `help.db`, and a login-page functional check in `tests/cases.yml`. +- Regenerated the README. diff --git a/apps/dolibarr/README.md b/apps/dolibarr/README.md index b3385bc4f..5258b02eb 100644 --- a/apps/dolibarr/README.md +++ b/apps/dolibarr/README.md @@ -1,26 +1,91 @@ -# Dolibarr on Docker +# Dolibarr on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Dolibarr: +## Quick Start +### Deploy Verification - - community: 19.0.2, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Dolibarr**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Dolibarr runs its installer on first start and creates the administrator account from `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD`. +2. Sign in at `/index.php` and complete the initial company setup. +3. Try a core feature. -The following are the minimal [recommended requirements](https://github.com/tuxgasy/docker-dolibarr): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to Dolibarr and change the password from the user profile. +2. `W9_LOGIN_PASSWORD` seeds the administrator password on first startup; changing `.env` later does not change an existing password. +3. If you cannot sign in, reset the password in the database. + -## Install +## Configuration Reference -You can install this Dolibarr by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Dolibarr Docker image](https://hub.docker.com/r/tuxgasy/dolibarr) and makes some improvements below. -If you want use Dolibarr with **Websoft9 Business Support** free, you can [subscribe Dolibarr](https://www.websoft9.com/apps) on Cloud platform + +The package bundles MariaDB and passes the database connection plus the initial administrator credentials through `.env` (`DOLI_*`). The image installs Dolibarr automatically on first start. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[Dolibarr Administrator Guide](https://support.websoft9.com/docs/dolibarr) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 19.0.2, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 80 | + + +### Data Directory + + +- `dolibarr_html` → `/var/www/html` +- `dolibarr_documents` → `/var/www/documents` +- `mariadb` → `/var/lib/mysql` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_LOGIN_PASSWORD` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Dolibarr Administrator Guide](https://support.websoft9.com/docs/dolibarr) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/tuxgasy/dolibarr) + +- [GitHub docs](https://github.com/tuxgasy/docker-dolibarr) + +- [GitHub docs](https://github.com/Dolibarr/dolibarr) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/dolibarr/docker-compose.yml b/apps/dolibarr/docker-compose.yml index 81f93f02a..e7f08f283 100644 --- a/apps/dolibarr/docker-compose.yml +++ b/apps/dolibarr/docker-compose.yml @@ -1,41 +1,35 @@ -# image: https://hub.docker.com/r/tuxgasy/dolibarr -# docs: https://github.com/tuxgasy/docker-dolibarr - -version: '3.8' - services: - dolibarr: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID - restart: unless-stopped - env_file: - - .env - ports: - - "$W9_HTTP_PORT_SET:80" - volumes: - - dolibarr_html:/var/www/html - - dolibarr_documents:/var/www/documents - links: - - mariadb + dolibarr: + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + restart: unless-stopped + env_file: .env + ports: + - "${W9_HTTP_PORT_SET}:80" # Web Console + volumes: + - dolibarr_html:/var/www/html + - dolibarr_documents:/var/www/documents + depends_on: + - mariadb - mariadb: - image: mariadb:$W9_MARIADB_VERSION - container_name: $W9_ID-mariadb - restart: unless-stopped - environment: - - MARIADB_DATABASE=$W9_ID - - MARIADB_USER=$W9_ID - - MARIADB_PASSWORD=$W9_POWER_PASSWORD - - MARIADB_ROOT_PASSWORD=$W9_POWER_PASSWORD - volumes: - - mariadb:/var/lib/mysql + mariadb: + image: mariadb:${W9_DB_VERSION} + container_name: ${W9_ID}-mariadb + restart: unless-stopped + environment: + MARIADB_DATABASE: ${W9_ID} + MARIADB_USER: ${W9_ID} + MARIADB_PASSWORD: ${W9_POWER_PASSWORD} + MARIADB_ROOT_PASSWORD: ${W9_POWER_PASSWORD} + volumes: + - mariadb:/var/lib/mysql networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true volumes: mariadb: dolibarr_html: - dolibarr_documents: \ No newline at end of file + dolibarr_documents: diff --git a/apps/dolibarr/tests/cases.yml b/apps/dolibarr/tests/cases.yml new file mode 100644 index 000000000..98008132e --- /dev/null +++ b/apps/dolibarr/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: login-page + type: web-access + path: /index.php + expect_status: 200 diff --git a/apps/dolibarr/variables.json b/apps/dolibarr/variables.json index 228d12c9e..3aeaa586a 100644 --- a/apps/dolibarr/variables.json +++ b/apps/dolibarr/variables.json @@ -2,6 +2,13 @@ "name": "dolibarr", "trademark": "Dolibarr", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/tuxgasy/dolibarr", + "docs": [ + "https://github.com/tuxgasy/docker-dolibarr", + "https://github.com/Dolibarr/dolibarr" + ] + }, "edition": [ { "dist": "community", @@ -11,12 +18,27 @@ ] } ], + "access": { + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/admin" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" }, - "upstream": { - "image": "https://hub.docker.com/r/tuxgasy/dolibarr" + "env": { + "first_startup_only": [ + "W9_LOGIN_PASSWORD" + ] + }, + "help": { + "db": "Dolibarr requires MariaDB or MySQL. This package bundles MariaDB for a single-node deployment." } } diff --git a/apps/uptimekuma/.env b/apps/uptimekuma/.env index 389194099..cf6bd0758 100644 --- a/apps/uptimekuma/.env +++ b/apps/uptimekuma/.env @@ -1,19 +1,40 @@ - - W9_REPO=louislam/uptime-kuma -W9_DIST='community' +W9_DIST=community +W9_VERSION=2.5.5 -# latest version is 1 -W9_VERSION='2.0.2' +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='uptimekuma' -W9_HTTP_PORT_SET='9001' + +W9_ID=uptimekuma + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=3001 -W9_URL='example.yourdomain.com' +W9_HTTP_PORT_SET=9001 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +# Uptime Kuma creates the first admin account interactively in the browser, so no login pair is seeded. +W9_URL=example.yourdomain.com + W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### +# ============================================================ +# Uptime Kuma image environment variables +# Docs: https://github.com/louislam/uptime-kuma/wiki/Environment-Variables +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: -# Below environment is created by uptime-kuma: https://github.com/louislam/uptime-kuma/wiki/Environment-Variables +# Not used by default; enable only when needed: +# UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN=false +# UPTIME_KUMA_WS_ORIGIN_CHECK=cors-like +# UPTIME_KUMA_SQLITE_SINGLE_CONNECTION=true +# NOTIFICATION_PROXY= +# NODE_TLS_REJECT_UNAUTHORIZED=0 diff --git a/apps/uptimekuma/CHANGELOG.md b/apps/uptimekuma/CHANGELOG.md index 582cf46c5..62be72ed9 100644 --- a/apps/uptimekuma/CHANGELOG.md +++ b/apps/uptimekuma/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Updated `W9_VERSION` from `2.0.2` to `2.5.5` and aligned `variables.json.edition` with the current upstream tag (upstream publishes no `x.x` tag, so the patch pin is intentional). +- Normalized `.env` and `docker-compose.yml` to current repository policy: braced `${VAR}` references, a port purpose comment, no image/docs source comments, and no obsolete compose `version` key. +- Added `upstream.releases`, `upstream.docs`, and `variables.json.access`. +- Added `tests/cases.yml` with an entry-page API check. +- Regenerated `README.md` from `variables.json` and `docker-compose.yml`. diff --git a/apps/uptimekuma/Notes.md b/apps/uptimekuma/Notes.md deleted file mode 100644 index 1df64c597..000000000 --- a/apps/uptimekuma/Notes.md +++ /dev/null @@ -1 +0,0 @@ -# Uptime Kuma diff --git a/apps/uptimekuma/README.md b/apps/uptimekuma/README.md index 397424a00..e74213e5a 100644 --- a/apps/uptimekuma/README.md +++ b/apps/uptimekuma/README.md @@ -1,26 +1,83 @@ -# Uptime Kuma on Docker +# Uptime Kuma on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Uptime Kuma: +## Quick Start +### Deploy Verification - - community: 1.23.15, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Uptime Kuma**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the app URL and create the first admin account (Uptime Kuma creates it interactively in the browser). +2. Add a monitor to confirm monitoring works. -The following are the minimal [recommended requirements](https://github.com/louislam/uptime-kuma/wiki): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to the Uptime Kuma console. +2. Open **Settings** → **Security** and update the password. + -## Install +## Configuration Reference -You can install this Uptime Kuma by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Uptime Kuma Docker image](https://hub.docker.com/r/louislam/uptime-kuma) and makes some improvements below. -If you want use Uptime Kuma with **Websoft9 Business Support** free, you can [subscribe Uptime Kuma](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Uptime Kuma Administrator Guide](https://support.websoft9.com/docs/uptimekuma) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 2.5.5, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 3001 | + + +### Data Directory + + +Data is persisted in the `uptime-kuma` volume, mounted at `/app/data`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Uptime Kuma Administrator Guide](https://support.websoft9.com/docs/uptimekuma) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/louislam/uptime-kuma) + +- [Releases](https://github.com/louislam/uptime-kuma/releases) + +- [GitHub docs](https://github.com/louislam/uptime-kuma/wiki/Environment-Variables) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/uptimekuma/docker-compose.yml b/apps/uptimekuma/docker-compose.yml index 1da7d29fe..0305d971c 100644 --- a/apps/uptimekuma/docker-compose.yml +++ b/apps/uptimekuma/docker-compose.yml @@ -1,24 +1,18 @@ -# docs: https://github.com/louislam/uptime-kuma -# image: https://hub.docker.com/r/louislam/uptime-kuma -# compose: https://github.com/louislam/uptime-kuma/blob/master/compose.yaml - -version: '3.8' - services: uptime-kuma: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped ports: - - $W9_HTTP_PORT_SET:3001 + - "${W9_HTTP_PORT_SET}:3001" # Web Console env_file: .env volumes: - uptime-kuma:/app/data -volumes: - uptime-kuma: - networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true + +volumes: + uptime-kuma: diff --git a/apps/uptimekuma/tests/cases.yml b/apps/uptimekuma/tests/cases.yml new file mode 100644 index 000000000..01c9e884d --- /dev/null +++ b/apps/uptimekuma/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: entry-page-api + type: web-access + path: /api/entry-page + expect_status: 200 diff --git a/apps/uptimekuma/variables.json b/apps/uptimekuma/variables.json index 17be8c7e9..84aedd625 100644 --- a/apps/uptimekuma/variables.json +++ b/apps/uptimekuma/variables.json @@ -2,21 +2,31 @@ "name": "uptimekuma", "trademark": "Uptime Kuma", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/louislam/uptime-kuma", + "releases": "https://github.com/louislam/uptime-kuma/releases", + "docs": [ + "https://github.com/louislam/uptime-kuma/wiki/Environment-Variables" + ] + }, "edition": [ { "dist": "community", "version": [ - "2.0.2", - "1" + "2.5.5", + "latest" ] } ], + "access": { + "web": { + "port": 3001, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/louislam/uptime-kuma" } } diff --git a/apps/varnish/.env b/apps/varnish/.env index 1f65ac8b8..5b25eca65 100644 --- a/apps/varnish/.env +++ b/apps/varnish/.env @@ -1,15 +1,32 @@ -W9_REPO="varnish" -W9_DIST='community' -W9_VERSION='8.0' -W9_HTTP_PORT_SET='9001' +W9_REPO=varnish +W9_DIST=community +W9_VERSION=9.0 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='varnish' + +W9_ID=varnish W9_HTTP_PORT=80 -W9_URL='example.youdomain.com' +W9_HTTP_PORT_SET=9001 +W9_URL=example.youdomain.com W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### -# Below environment is created by this app +# ============================================================ +# Varnish image environment variables +# Docs: https://hub.docker.com/_/varnish +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: VARNISH_SIZE=2G + +# Not used by default; enable only when needed: +# VARNISH_BACKEND_HOST=https://appname.example.com/ +# VARNISH_FILESERVER=true +# VARNISH_VCL_FILE=/etc/varnish/default.vcl diff --git a/apps/varnish/CHANGELOG.md b/apps/varnish/CHANGELOG.md index 582cf46c5..ccdf5ec98 100644 --- a/apps/varnish/CHANGELOG.md +++ b/apps/varnish/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG -## Release -### Fixes and Enhancements - +## 2026-09-20 +- Updated Varnish to `9.0` (official `varnish` image, currently 9.0.4). +- Replaced the stale `src/default.vcl` (VCL 4.0 with an unresolvable backend that prevented the container from starting) with the upstream 9.0 default VCL; the backend is now configured through `VARNISH_BACKEND_HOST`. +- Exposed the Varnish image variables in `.env` (`VARNISH_SIZE` plus optional `VARNISH_BACKEND_HOST`, `VARNISH_FILESERVER`, `VARNISH_VCL_FILE`, `VARNISH_HTTP_PORT`, `VARNISH_PROXY_PORT`). +- Normalized `.env` and `docker-compose.yml` to current repository policy rules (braced `${VAR}` references, port purpose comment, removal of the source comment). +- Added `tests/cases.yml` with a Varnish smoke check. +- Updated `Notes.md` and regenerated the README. diff --git a/apps/varnish/Notes.md b/apps/varnish/Notes.md index afa7a55db..13bfb6791 100644 --- a/apps/varnish/Notes.md +++ b/apps/varnish/Notes.md @@ -5,21 +5,33 @@ 1. 分别在 Websoft9 控制台安装 WordPress 和 Varnish 两个应用 > 确保 Varnish 配置的域名是最终提供给用户访问的域名 -2. 编辑 Varnish 应用的 `./src/default.vcl` 文件中相关参数,将 WordPress 容器名和容器端口作为连接点 +2. 编辑 Varnish 应用的 `.env` 文件,通过 `VARNISH_BACKEND_HOST` 将后端指向 WordPress 容器 ``` - backend default { - .host = "wordpress_shlez"; - .port = "80"; - } + VARNISH_BACKEND_HOST=http://wordpress_shlez:80/ ``` 3. 重建 Varnish 应用后,Varnish 已经将 WordPress 缓存 -4. 访问 Varnish 所绑定的域名,便发现访问速度大大提升 +4. 访问 Varnish 所绑定的域名,便发现访问速度大大提升 + +## Varnish 禁止爬虫访问 + +1. 编辑 Varnish 应用的 `./src/default.vcl` 文件,在 `sub vcl_recv` 中增加如下内容,其中 `Sogou web spider` 改成你想要禁用的爬虫名: + ``` + sub vcl_recv { + if (req.http.user-agent ~ "Sogou web spider") { + return (synth(403, "Forbidden")); + } + } + ``` + +2. 重建 Varnish 应用后,Varnish 已经对爬虫禁用 ## 配置选项 -- 缓存大小:通过 VARNISH_SIZE 环境变量设置 -- 配置文件:`./src/default.vcl` +- 缓存大小:通过 `VARNISH_SIZE` 环境变量设置 +- 后端地址:通过 `VARNISH_BACKEND_HOST` 环境变量设置(未设置时提供一个本地占位页面) +- 文件服务模式:设置 `VARNISH_FILESERVER=true` 后,Varnish 直接提供 `/var/www/html` 下的静态文件 +- 配置文件:`./src/default.vcl`,可自定义 VCL 规则 ## FAQ diff --git a/apps/varnish/README.md b/apps/varnish/README.md index 71f1ac600..3c5c7a52c 100644 --- a/apps/varnish/README.md +++ b/apps/varnish/README.md @@ -1,26 +1,84 @@ -# Varnish on Docker +# Varnish on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Varnish: +## Quick Start +### Deploy Verification - - community: 7.7, stable, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Varnish**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +Varnish is an HTTP cache and reverse proxy; it has no built-in login. Until a backend is configured it serves a local placeholder page. -The following are the minimal [recommended requirements](https://varnish-cache.org): +1. Set `VARNISH_BACKEND_HOST` in `.env` to your origin, for example `http://wordpress_shlez:80/`. +2. Rebuild the app. Requests to the Varnish URL are now cached and served from your origin. -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 8 GB of free space -* **bandwidth**: more fluent experience over 100M +To customize caching rules, edit `./src/default.vcl` and rebuild. -## Install +### Configuration -You can install this Varnish by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +- Cache size: `VARNISH_SIZE` in `.env`. +- Backend origin: `VARNISH_BACKEND_HOST` in `.env`. +- Static file mode: `VARNISH_FILESERVER=true` in `.env`. + -If you want use Varnish with **Websoft9 Business Support** free, you can [subscribe Varnish](https://www.websoft9.com/apps) on Cloud platform +## Configuration Reference -## Documentation +Websoft9 packages this app from the official [Varnish Docker image](https://hub.docker.com/_/varnish) and makes some improvements below. -[Varnish Administrator Guide](https://support.websoft9.com/docs/varnish) powered by Websoft9 \ No newline at end of file + + + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 9.0, stable, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web | 80 | + + +### Data Directory + + +Data is kept inside the container; a named volume is recommended for persistence. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/default.vcl` to `/etc/varnish/default.vcl`. + + +## References + +- [Varnish Administrator Guide](https://support.websoft9.com/docs/varnish) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/_/varnish) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/varnish/docker-compose.yml b/apps/varnish/docker-compose.yml index 956613380..516d5ba13 100644 --- a/apps/varnish/docker-compose.yml +++ b/apps/varnish/docker-compose.yml @@ -1,19 +1,17 @@ -# image,docs: https://hub.docker.com/_/varnish - services: varnish: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped ports: - - $W9_HTTP_PORT_SET:80 + - "${W9_HTTP_PORT_SET}:80" # Web volumes: - ./src/default.vcl:/etc/varnish/default.vcl:ro env_file: .env tmpfs: - /var/lib/varnish/varnishd:exec - + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true diff --git a/apps/varnish/src/default.vcl b/apps/varnish/src/default.vcl index bbdef9acb..ae2c67628 100644 --- a/apps/varnish/src/default.vcl +++ b/apps/varnish/src/default.vcl @@ -1,40 +1,58 @@ -# -# This is an example VCL file for Varnish. -# -# It does not do anything by default, delegating control to the -# builtin VCL. The builtin VCL is called when there is no explicit -# return statement. -# -# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/ -# and https://www.varnish-cache.org/trac/wiki/VCLExamples for more examples. - -# Marker to tell the VCL compiler that this VCL has been adapted to the -# new 4.0 format. -vcl 4.0; - -# Default backend definition. Set this to point to your content server. -backend default { - .host = "wordpress_zm4pm"; - .port = "80"; +# Important documentation links: +# - general entry point: https://www.varnish-cache.org/docs/ +# - VCL primer: https://varnish-cache.org/docs/2.1/tutorial/vcl.html +# - more VCL information: https://www.varnish-software.com/developers/tutorials/varnish-configuration-language-vcl/ +# - logging: https://docs.varnish-software.com/tutorials/vsl-query/ + +vcl 4.1; + +import fileserver; +import reqwest; +import std; + +# https://github.com/varnish/toolbox/tree/master/vcls/hit-miss +include "hit-miss.vcl"; + +backend default none; + +sub vcl_init { + # sanity check to fail the VCL loading if VARNISH_BACKEND_HOST + # doesn't look right + if (std.getenv("VARNISH_BACKEND_HOST") && + std.getenv("VARNISH_BACKEND_HOST") !~ "^https?://") { + return(fail("VARNISH_BACKEND_HOST is set but doesn't start with http:// or https://")); + } + new http_backend = reqwest.client(base_url = std.getenv("VARNISH_BACKEND_HOST")); + new file_backend = fileserver.root("/var/www/html"); } sub vcl_recv { - # Happens before we check if we have this in cache already. - # - # Typically you clean up the request here, removing cookies you don't need, - # rewriting the request, etc. + if (std.getenv("VARNISH_BACKEND_HOST")) { + # if VARNISH_BACKEND_HOST is set, use the HTTP backend + set req.backend_hint = http_backend.backend(); + } else if (std.getenv("VARNISH_FILESERVER")) { + # if VARNISH_FILESERVER, act as a fileserver + set req.backend_hint = file_backend.backend(); + } else { + # otherwise, force the path to our default page and serve it + # from disk + set req.backend_hint = file_backend.backend(); + set req.url = "/index.html"; + } +} + +# if the request goes to the backend, unset the host header and let +# vmod-reqwest set it, according to VARNISH_BACKEND_HOST (and it doesn't +# matter for file_backend) +sub vcl_backend_fetch { + unset bereq.http.host; } +# vcl_backend_response is the opportunity to set/unset backend response headers +# (beresp.http.*) before they enter the cache sub vcl_backend_response { - # Happens after we have read the response headers from the backend. - # - # Here you clean the response headers, removing silly Set-Cookie headers - # and other mistakes your backend does. + set beresp.http.varnish-default-vcl = "true"; } -sub vcl_deliver { - # Happens when we have all the pieces we need, and are about to send the - # response to the client. - # - # You can do accounting or modifying the final object here. -} \ No newline at end of file +# https://github.com/varnish/toolbox/tree/master/vcls/verbose_builtin +include "verbose_builtin.vcl"; diff --git a/apps/varnish/tests/cases.yml b/apps/varnish/tests/cases.yml new file mode 100644 index 000000000..4556ebf3d --- /dev/null +++ b/apps/varnish/tests/cases.yml @@ -0,0 +1,4 @@ +optional: + - id: varnish-smoke + type: script + script: smoke.sh diff --git a/apps/varnish/tests/smoke.sh b/apps/varnish/tests/smoke.sh new file mode 100644 index 000000000..db814d4a7 --- /dev/null +++ b/apps/varnish/tests/smoke.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +status="$(curl -s -o /dev/null -w '%{http_code}' "${BASE_URL}/")" +if [ "${status}" != "200" ]; then + echo "unexpected status from ${BASE_URL}/: ${status}" >&2 + exit 1 +fi + +headers="$(curl -sI "${BASE_URL}/")" +if ! grep -qi '^varnish-default-vcl: true' <<<"${headers}"; then + echo "missing varnish-default-vcl header" >&2 + exit 1 +fi +if ! grep -qi '^via: .*Varnish/' <<<"${headers}"; then + echo "missing Via: ... Varnish header" >&2 + exit 1 +fi + +echo "varnish serving ${BASE_URL}/ (status=${status})" diff --git a/apps/varnish/variables.json b/apps/varnish/variables.json index dbfe7b578..8b7dbfaa6 100644 --- a/apps/varnish/variables.json +++ b/apps/varnish/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "8.0", + "9.0", "stable", "latest" ] diff --git a/apps/vault/.env b/apps/vault/.env index d24393273..24d3038da 100644 --- a/apps/vault/.env +++ b/apps/vault/.env @@ -1,13 +1,36 @@ -# This image have no latest -W9_VERSION='1.21' -W9_DIST='community' W9_REPO=hashicorp/vault +W9_DIST=community +W9_VERSION=2.1 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='vault' + +W9_ID=vault + +# Vault serves its web UI and HTTP API on 8200. +W9_HTTP_PORT_SET=9001 W9_HTTP_PORT=8200 -W9_HTTP_PORT_SET='9001' -W9_URL='appname.example.com' +W9_URL=appname.example.com W9_NETWORK=websoft9 -W9_LOGIN_GET_TOKEN="Get Token from your Vault Container logs" -#### --------------------------------------------------------------------------------------- #### +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Vault image environment variables +# Docs: https://hub.docker.com/r/hashicorp/vault +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +VAULT_LOCAL_CONFIG={} + +# Not used by default; enable only when needed: +# VAULT_DEV_ROOT_TOKEN_ID=root +# VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 +# VAULT_LOG_LEVEL=info +# VAULT_DISABLE_MLOCK=true +# VAULT_ADDR=http://127.0.0.1:8200 diff --git a/apps/vault/CHANGELOG.md b/apps/vault/CHANGELOG.md index 582cf46c5..8eda9135e 100644 --- a/apps/vault/CHANGELOG.md +++ b/apps/vault/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Updated Vault from `1.21` to `2.1`, the latest stable upstream minor (upstream `2.1.1`). +- Pinned `W9_VERSION` to `2.1` and declared it in `variables.json`. +- Kept the package in Vault dev mode (`server -dev`), matching the current image default. +- Replaced the legacy `W9_LOGIN_GET_TOKEN` hint with a declarative `variables.json.credentials.password` source (`container-log`, pattern `Root Token:`). +- Aligned `.env` and `docker-compose.yml` with current repository policy: braced variable references, inline published-port comment, and the `.env` section banner with a Docs URL. +- Removed the obsolete `version:` key and the `# image:` / `# docs:` source comments; moved `VAULT_LOCAL_CONFIG` into `.env`. +- Added a healthcheck against `/v1/sys/health`. +- Added `tests/cases.yml` with a Vault health check. +- Added upstream releases and documentation references to `variables.json`. +- Regenerated `README.md`. diff --git a/apps/vault/README.md b/apps/vault/README.md index 4dc1adb55..d1f610f85 100644 --- a/apps/vault/README.md +++ b/apps/vault/README.md @@ -1,26 +1,87 @@ -# Vault on Docker +# Vault on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Vault: +## Quick Start +### Deploy Verification - - community: 1.19, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Vault**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the Vault URL from the Websoft9 **Access** tab; the web UI and HTTP API are served on port 8200. +2. Get the root token from the container logs: run `docker logs vault` and look for the `Root Token:` line. +3. Paste the token into the Vault sign-in page. -The following are the minimal [recommended requirements](https://learn.hashicorp.com/tutorials/vault): +### Change Token -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +This package runs Vault in dev mode, so all data is held in memory and a new root token is generated on every restart. To use a fixed token, set `VAULT_DEV_ROOT_TOKEN_ID` in `.env` and rebuild. + -## Install +## Configuration Reference -You can install this Vault by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Vault Docker image](https://hub.docker.com/r/hashicorp/vault) and makes some improvements below. -If you want use Vault with **Websoft9 Business Support** free, you can [subscribe Vault](https://www.websoft9.com/apps) on Cloud platform + +This package runs Vault in dev mode (`server -dev`): storage is in memory and the root token is printed in the container logs. Do not use it for production data. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[Vault Administrator Guide](https://support.websoft9.com/docs/vault) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 2.1, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Vault UI and HTTP API | 8200 | + + +### Data Directory + + +- `vault-logs` → `/vault/logs` +- `vault-file` → `/vault/file` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Vault Administrator Guide](https://support.websoft9.com/docs/vault) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/hashicorp/vault) + +- [Releases](https://github.com/hashicorp/vault/releases) + +- [Official docs](https://developer.hashicorp.com/vault/docs) + +- [Official docs](https://hub.docker.com/r/hashicorp/vault) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/vault/docker-compose.yml b/apps/vault/docker-compose.yml index ea61f23e1..312778fab 100644 --- a/apps/vault/docker-compose.yml +++ b/apps/vault/docker-compose.yml @@ -1,23 +1,22 @@ -# image: https://hub.docker.com/r/hashicorp/vault -# docs: https://www.vaultproject.io/ - -version: '3.8' - services: vault: image: ${W9_REPO}:${W9_VERSION} container_name: ${W9_ID} - cap_add: + cap_add: - IPC_LOCK ports: - - '${W9_HTTP_PORT_SET}:8200' + - "${W9_HTTP_PORT_SET}:8200" # Vault UI and HTTP API and CLI env_file: - .env - environment: - - VAULT_LOCAL_CONFIG={} volumes: - - 'vault-logs:/vault/logs' - - 'vault-file:/vault/file' + - vault-logs:/vault/logs + - vault-file:/vault/file + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8200/v1/sys/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s networks: default: diff --git a/apps/vault/tests/cases.yml b/apps/vault/tests/cases.yml new file mode 100644 index 000000000..ad82476e4 --- /dev/null +++ b/apps/vault/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: vault-health + type: web-access + path: /v1/sys/health + expect_status: 200 diff --git a/apps/vault/variables.json b/apps/vault/variables.json index 8014b1044..3ed1d8341 100644 --- a/apps/vault/variables.json +++ b/apps/vault/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "1.21", + "2.1", "latest" ] } @@ -16,7 +16,18 @@ "memory": "2", "disk": "1" }, + "credentials": { + "password": { + "source": "container-log", + "pattern": "Root Token:" + } + }, "upstream": { - "image": "https://hub.docker.com/r/hashicorp/vault" + "image": "https://hub.docker.com/r/hashicorp/vault", + "releases": "https://github.com/hashicorp/vault/releases", + "docs": [ + "https://developer.hashicorp.com/vault/docs", + "https://hub.docker.com/r/hashicorp/vault" + ] } } diff --git a/apps/vaultwarden/.env b/apps/vaultwarden/.env index f82e5541b..9c4f4eaaf 100644 --- a/apps/vaultwarden/.env +++ b/apps/vaultwarden/.env @@ -1,24 +1,51 @@ W9_REPO=vaultwarden/server -W9_DIST='community' -W9_VERSION='1.34.3' -W9_DB_VERSION=10.4 +W9_DIST=community +W9_VERSION=1.37.3 + +# Optional password seed: drives the bundled MariaDB password and the admin token. W9_POWER_PASSWORD='fPgk6t8tPVHH!jyh' W9_RCODE='pH3A0atXKks3V' -W9_HTTP_PORT_SET='9001' -W9_ID='vaultwarden' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### + +W9_ID=vaultwarden + +# Web/internal ports and bundled dependency shape. +W9_HTTP_PORT_SET=9001 W9_HTTP_PORT=80 +W9_DB_VERSION=11.4 W9_DB_EXPOSE=mariadb -W9_URL='example.youdomain.com' + +# Public URL helpers. +W9_URL=example.youdomain.com W9_URL_REPLACE=true W9_URL_WITH_PORT=false + W9_NETWORK=websoft9 -#you can find more environment variables information in https://github.com/dani-garcia/vaultwarden/wiki +#### ----------------------------------------------------------------------------------------- #### -SIGNUPS_ALLOWED=true # Deactivate this with "false" after you have created your account so that no strangers can register +# ============================================================ +# Vaultwarden image environment variables +# Docs: https://github.com/dani-garcia/vaultwarden/wiki +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +SIGNUPS_ALLOWED=true DATABASE_URL=mysql://vaultwarden:${W9_RCODE}@${W9_ID}-mariadb:3306/vaultwarden -# It is used in W9_URL/admin to access the admin page -# How to set it? refer to https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#using-argon2 -# You should redeploy the app -ADMIN_TOKEN=$W9_POWER_PASSWORD +ADMIN_TOKEN=${W9_POWER_PASSWORD} +DOMAIN=https://${W9_URL} RUST_BACKTRACE=1 + +# Not used by default; enable only when needed: +# SIGNUPS_DOMAINS_WHITELIST=example.com,example.net +# INVITATIONS_ALLOWED=true +# DISABLE_ADMIN_TOKEN=false +# SHOW_PASSWORD_HINT=false +# SSO_ENABLED=false diff --git a/apps/vaultwarden/CHANGELOG.md b/apps/vaultwarden/CHANGELOG.md index 582cf46c5..2bf17faf6 100644 --- a/apps/vaultwarden/CHANGELOG.md +++ b/apps/vaultwarden/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Updated Vaultwarden from `1.34.3` to `1.37.3`, the latest stable upstream release (includes the 1.35.5 / 1.36.0 / 1.37.0 security fixes). +- Pinned `W9_VERSION` to `1.37.3` and declared it in `variables.json`. +- Bumped the bundled MariaDB dependency from `10.4` (EOL) to `11.4` (LTS). +- Fixed the URL contract by wiring `DOMAIN=https://${W9_URL}`, which the previous `W9_URL_REPLACE=true` declaration lacked (policy gate failure). +- Aligned `.env` and `docker-compose.yml` with current repository policy: braced variable references, inline published-port comment, and the `.env` section banner with a Docs URL. +- Removed the obsolete `version:` key and the `# image:` / `# docs:` source comments. +- Added a healthcheck against `/alive`. +- Added `tests/cases.yml` with an `/alive` check. +- Added upstream releases and documentation references to `variables.json`. +- Regenerated `README.md`. diff --git a/apps/vaultwarden/README.md b/apps/vaultwarden/README.md index f46dee7f5..fdfd992e9 100644 --- a/apps/vaultwarden/README.md +++ b/apps/vaultwarden/README.md @@ -1,26 +1,88 @@ -# Vaultwarden on Docker +# Vaultwarden on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Vaultwarden: +## Quick Start +### Deploy Verification - - community: 1.33.2, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Vaultwarden**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the Vaultwarden URL from the Websoft9 **Access** tab. +2. Create the first account in the web vault, then set `SIGNUPS_ALLOWED=false` in `.env` and rebuild to stop open registration. +3. The admin page is at `/admin`; sign in with the `ADMIN_TOKEN` value from `.env`. -The following are the minimal [recommended requirements](): +### Change Password -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +The admin token is `W9_POWER_PASSWORD` in `.env`; update it and rebuild to rotate it. User account passwords are managed inside the web vault. + -## Install +## Configuration Reference -You can install this Vaultwarden by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Vaultwarden Docker image](https://hub.docker.com/r/vaultwarden/server) and makes some improvements below. -If you want use Vaultwarden with **Websoft9 Business Support** free, you can [subscribe Vaultwarden](https://www.websoft9.com/apps) on Cloud platform + +Vaultwarden needs HTTPS for the browser Web Crypto API (WebAuthn), so publish it behind a TLS-enabled domain. This package bundles MariaDB 11.4; deployments created on the previous MariaDB 10.4 volume must follow the MariaDB stepwise upgrade path before starting this version. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[Vaultwarden Administrator Guide](https://support.websoft9.com/docs/vaultwarden) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 1.37.3, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web vault and API | 80 | + + +### Data Directory + + +- `vaultwarden_vol` → `/data/` +- `mariadb_vol` → `/var/lib/mysql` +- `/etc/localtime` → `/etc/localtime` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Vaultwarden Administrator Guide](https://support.websoft9.com/docs/vaultwarden) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/vaultwarden/server) + +- [Releases](https://github.com/dani-garcia/vaultwarden/releases) + +- [GitHub docs](https://github.com/dani-garcia/vaultwarden/wiki) + +- [GitHub docs](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/vaultwarden/docker-compose.yml b/apps/vaultwarden/docker-compose.yml index f4c00f955..56b50d25b 100644 --- a/apps/vaultwarden/docker-compose.yml +++ b/apps/vaultwarden/docker-compose.yml @@ -1,34 +1,38 @@ -# image: https://hub.docker.com/r/vaultwarden/server -# docs: https://github.com/dani-garcia/vaultwarden/wiki - -version: '3.8' services: vaultwarden: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped ports: - - $W9_HTTP_PORT_SET:80 - env_file: .env + - "${W9_HTTP_PORT_SET}:80" # Web vault and API + env_file: + - .env volumes: - - "vaultwarden_vol:/data/" + - vaultwarden_vol:/data/ + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/alive || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s mariadb: - image: mariadb:$W9_DB_VERSION - container_name: "$W9_ID-mariadb" + image: mariadb:${W9_DB_VERSION} + container_name: ${W9_ID}-mariadb restart: unless-stopped volumes: - - "mariadb_vol:/var/lib/mysql" - - "/etc/localtime:/etc/localtime:ro" + - mariadb_vol:/var/lib/mysql + - /etc/localtime:/etc/localtime:ro environment: - - "MYSQL_ROOT_PASSWORD=$W9_RCODE" - - "MYSQL_PASSWORD=$W9_RCODE" - - "MYSQL_DATABASE=vaultwarden" - - "MYSQL_USER=vaultwarden" + - MYSQL_ROOT_PASSWORD=${W9_RCODE} + - MYSQL_PASSWORD=${W9_RCODE} + - MYSQL_DATABASE=vaultwarden + - MYSQL_USER=vaultwarden + volumes: vaultwarden_vol: mariadb_vol: - + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true diff --git a/apps/vaultwarden/tests/cases.yml b/apps/vaultwarden/tests/cases.yml new file mode 100644 index 000000000..7ddb8df96 --- /dev/null +++ b/apps/vaultwarden/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: alive + type: web-access + path: /alive + expect_status: 200 diff --git a/apps/vaultwarden/variables.json b/apps/vaultwarden/variables.json index 1827828cc..3a70c2364 100644 --- a/apps/vaultwarden/variables.json +++ b/apps/vaultwarden/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "1.34.3", + "1.37.3", "latest" ] } @@ -17,6 +17,11 @@ "disk": "1" }, "upstream": { - "image": "https://hub.docker.com/r/vaultwarden/server" + "image": "https://hub.docker.com/r/vaultwarden/server", + "releases": "https://github.com/dani-garcia/vaultwarden/releases", + "docs": [ + "https://github.com/dani-garcia/vaultwarden/wiki", + "https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page" + ] } } diff --git a/apps/vespa/.env b/apps/vespa/.env index 9b2b7ff8f..3c4c2b905 100644 --- a/apps/vespa/.env +++ b/apps/vespa/.env @@ -1,9 +1,33 @@ -W9_VERSION=latest -W9_ID=vespa W9_REPO=vespaengine/vespa +W9_DIST=community +W9_VERSION=8.751.13 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### + +W9_ID=vespa + +# Vespa serves the query/document HTTP API on 8080, and the config server / deployment API on 19071. +# The HTTP API on 8080 only starts after an application package is deployed. +W9_HTTP_PORT=8080 +W9_HTTP_PORT_SET=9001 +W9_API_PORT_SET=9002 + W9_NETWORK=websoft9 -# Port configurations -W9_HTTP_PORT=19071 -W9_HTTP_PORT_SET=19071 +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Vespa image environment variables +# Docs: https://docs.vespa.ai/en/operations/self-managed/docker-containers.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +# (none) +# Not used by default; enable only when needed: diff --git a/apps/vespa/CHANGELOG.md b/apps/vespa/CHANGELOG.md index 09c4e7bd0..2f9e568df 100644 --- a/apps/vespa/CHANGELOG.md +++ b/apps/vespa/CHANGELOG.md @@ -1,5 +1,13 @@ -# CHANGELOG +# Changelog -## Release +## 2026-09-20 -### Fixes and Enhancements +- Pin the Vespa image from floating `latest` to `8.751.13`, the newest release with a published image. +- Point `upstream.image` at the Docker Hub registry and add upstream releases and docs references so version scanning works again. +- Rework the compose topology to the official single-container shape running `configserver,services`. +- Map `W9_HTTP_PORT_SET` to the query/document HTTP API (`8080`) and expose the config server / deployment API (`19071`) on `W9_API_PORT_SET`, the endpoint needed for `vespa deploy`. +- Fix data persistence paths to the official `/opt/vespa/var` and `/opt/vespa/logs`; the previous `/var/lib/vespa` and `/etc/vespa` paths do not exist in the image. +- Add a healthcheck against the config server `/state/v1/health`, which is up before any application package is deployed. +- Skip the default web check in `tests/cases.yml`, since the HTTP API on `8080` is not available until an application is deployed. +- Document in the README that Vespa has no web UI or built-in authentication, and how the ports are used. +- Normalize `.env` and `docker-compose.yml` to current repository policy and regenerate `README.md`. diff --git a/apps/vespa/Notes.md b/apps/vespa/Notes.md index 20c52464b..2853e964d 100644 --- a/apps/vespa/Notes.md +++ b/apps/vespa/Notes.md @@ -1 +1,8 @@ -# vespa +# Vespa + +- 自托管 Vespa 没有 Web UI,也没有内置用户名/密码认证。 +- `8080`:应用 HTTP API(查询 / 文档)。**必须先在 `19071` 部署应用包后才会监听**。 +- `19071`:config server / 部署端点。全新安装即可用,但**未认证**,暴露到公网需自行加访问控制。 +- 部署应用包:`vespa deploy --target http://:19071 ./app` +- 查询示例:`curl 'http://:8080/search/?yql=select * from sources * where true'` +- 启用认证:在应用包 `services.xml` 配置 TLS + 客户端证书(mTLS)或自定义 filter chain;Vespa 内部通信可用 `VESPA_TLS_CONFIG_FILE` 走 mTLS。 diff --git a/apps/vespa/README.md b/apps/vespa/README.md index f246ba2c0..d7687cdff 100644 --- a/apps/vespa/README.md +++ b/apps/vespa/README.md @@ -1,26 +1,84 @@ -# Vespa on Docker +# Vespa on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Vespa: +## Quick Start +### Deploy Verification - - community: latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Vespa**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +Vespa has no web UI or built-in login; use the Vespa CLI or the REST API. -The following are the minimal [recommended requirements](https://github.com/vespa-engine/docker-image-dev#vespa-development-on-almalinux-8): +1. Deploy an application package to the config server on port 19071, for example `vespa deploy --target http://:19071 ./app`. +2. After the application is deployed, the query and document API becomes available on port 8080. +3. Query it with `curl 'http://:8080/search/?yql=select * from sources * where true'`. + -* **RAM**: 8 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 128 GB of free space -* **bandwidth**: more fluent experience over 100M +## Configuration Reference -## Install +Websoft9 packages this app from the official [Vespa Docker image](https://hub.docker.com/r/vespaengine/vespa) and makes some improvements below. -You can install this Vespa by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). + +Vespa has no built-in authentication. The HTTP API on port 8080 is open by default and only starts after an application package is deployed. The config server on port 19071 is an unauthenticated deployment endpoint; expose it only to trusted networks. Secure access with TLS, client certificates (mTLS), or HTTP filter chains in the application package. + -If you want use Vespa with **Websoft9 Business Support** free, you can [subscribe Vespa](https://www.websoft9.com/apps) on Cloud platform +Apps run as containers; rebuild after any configuration change. -## Documentation +### Version Support -[Vespa Administrator Guide](https://support.websoft9.com/docs/vespa) powered by Websoft9 \ No newline at end of file +Supported versions: 8.751.13, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| HTTP API (query/document) | 8080 | +| Config server (deployment API) | 19071 | + + +### Data Directory + + +- `vespa-var` → `/opt/vespa/var` +- `vespa-logs` → `/opt/vespa/logs` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Vespa Administrator Guide](https://support.websoft9.com/docs/vespa) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/vespaengine/vespa) + +- [Releases](https://github.com/vespa-engine/vespa/releases) + +- [Official docs](https://docs.vespa.ai/en/operations/self-managed/docker-containers.html) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/vespa/docker-compose.yml b/apps/vespa/docker-compose.yml index 830dc0df9..c8295b132 100644 --- a/apps/vespa/docker-compose.yml +++ b/apps/vespa/docker-compose.yml @@ -1,34 +1,28 @@ -# image: https://hub.docker.com/r/vespaengine/vespa -# doc: https://github.com/vespa-engine/vespa - version: '3.8' services: vespa: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID - hostname: vespa-app - command: services - depends_on: - - configserver - volumes: - - vespa-data:/var/lib/vespa - environment: - VESPA_CONFIGSERVERS: vespa-config-server - + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + hostname: vespa-container + command: ["configserver,services"] restart: unless-stopped - - configserver: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID-config-server - hostname: vespa-config-server - command: configserver ports: - - $W9_HTTP_PORT_SET:19071 + - "${W9_HTTP_PORT_SET}:8080" # HTTP API (query/document) + - "${W9_API_PORT_SET}:19071" # Config server (deployment API) volumes: - - vespa-config:/etc/vespa + - vespa-var:/opt/vespa/var + - vespa-logs:/opt/vespa/logs environment: - VESPA_CONFIGSERVERS: vespa-config-server + VESPA_CONFIGSERVERS: vespa-container + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:19071/state/v1/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 120s + env_file: + - .env networks: default: @@ -36,5 +30,5 @@ networks: external: true volumes: - vespa-config: - vespa-data: + vespa-var: + vespa-logs: diff --git a/apps/vespa/tests/cases.yml b/apps/vespa/tests/cases.yml new file mode 100644 index 000000000..e24b793ac --- /dev/null +++ b/apps/vespa/tests/cases.yml @@ -0,0 +1,2 @@ +skip: + - id: web-access diff --git a/apps/vespa/variables.json b/apps/vespa/variables.json index 8edc5e8a8..91b8d095e 100644 --- a/apps/vespa/variables.json +++ b/apps/vespa/variables.json @@ -1,11 +1,12 @@ { "name": "vespa", "trademark": "Vespa", - "release": false, + "release": true, "edition": [ { "dist": "community", "version": [ + "8.751.13", "latest" ] } @@ -13,9 +14,13 @@ "requirements": { "cpu": "2", "memory": "8", - "disk": "128" + "disk": "5" }, "upstream": { - "image": "https://github.com/vespa-engine/vespa" + "image": "https://hub.docker.com/r/vespaengine/vespa", + "releases": "https://github.com/vespa-engine/vespa/releases", + "docs": [ + "https://docs.vespa.ai/en/operations/self-managed/docker-containers.html" + ] } } diff --git a/apps/wazuh/.env b/apps/wazuh/.env index 770cbd6c1..998f53b4a 100644 --- a/apps/wazuh/.env +++ b/apps/wazuh/.env @@ -1,53 +1,62 @@ -W9_REPO="wordpress" +W9_REPO=wazuh/wazuh-dashboard W9_DIST=community -W9_VERSION="latest" - -W9_POWER_PASSWORD="1PrMxExC45LsCT" - -# Environments which for user settings when create application -# Named expression: W9_xxx_xxx_SET, xxx refer to file fields -W9_HTTP_PORT_SET=9001 -# W9_HTTPS_PORT_SET=9002 -# W9_DB_PORT_SET=3306 -# W9_SSH_PORT_SET=23 -W9_KEY_SET="dfsjdkjf77xjxcjcj" +W9_VERSION=4.14.7 #### -- Not allowed to edit below environments when recreate app based on existing data -- #### W9_ID=wazuh -# W9_HTTP_PORT or W9_HTTPS_PORT is need at leaset and used for proxy for web application -# Some container (e.g teleport) need HTTPS access, then need to set this pra -W9_HTTP_PORT=80 -W9_HTTPS_PORT=81 +# Web/internal ports +W9_HTTPS_PORT=5601 +W9_HTTPS_PORT_SET=9443 +W9_AGENT_PORT_SET=1514 +W9_ENROLLMENT_PORT_SET=1515 +# Optional; enable together with the matching port in docker-compose.yml: +# W9_SYSLOG_UDP_PORT_SET=514 +# W9_API_PORT_SET=55000 +# Built-in login and URL helpers W9_LOGIN_USER=admin -# use https://1password.com/zh-cn/password-generator/ to genarate 14 bit password -# this password can also use password file -W9_LOGIN_PASSWORD=$W9_POWER_PASSWORD -W9_ADMIN_PATH="/wp-login" - -# Container name's suffix must use one of the value -W9_DB_EXPOSE="mysql,postgresql,mariadb,mongodb,redis" - -# It is used when the application APP needs to set an external URL, which can be IP(or domain), IP:PORT -# If have protocols, should be set it in the APP's ENV -W9_URL=example.youdomain.com -# modifies W9_URL on init when it is true -W9_URL_REPLACE=true +W9_LOGIN_PASSWORD=SecretPassword +W9_URL=appname.example.com W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### - -# Below environment is created by this app - -WORDPRESS_DB_HOST=$W9_ID-mariadb -WORDPRESS_DB_USER=wordpress #if use postgresql, it need set to postgres -WORDPRESS_DB_PASSWORD=$W9_POWER_PASSWORD -WORDPRESS_DB_NAME=wordpress - - -#W9_NAME="" -#W9_RCODE="" \ No newline at end of file +# ============================================================ +# Wazuh image environment variables +# Docs: https://documentation.wazuh.com/current/deployment-options/docker/index.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: + +# Wazuh indexer +OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g + +# Wazuh manager +INDEXER_URL=https://wazuh.indexer:9200 +INDEXER_USERNAME=admin +INDEXER_PASSWORD=SecretPassword +FILEBEAT_SSL_VERIFICATION_MODE=full +SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/wazuh/root-ca-manager.pem +SSL_CERTIFICATE=/etc/ssl/wazuh/wazuh.manager.pem +SSL_KEY=/etc/ssl/wazuh/wazuh.manager-key.pem + +# Wazuh API user, shared by the manager and the dashboard +API_USERNAME=wazuh-wui +API_PASSWORD=MyS3cr37P450r.*- + +# Wazuh dashboard +WAZUH_API_URL=https://wazuh.manager +DASHBOARD_USERNAME=kibanaserver +DASHBOARD_PASSWORD=kibanaserver + +# Not used by default; enable only when needed: +# CERT_TOOL_VERSION=4.14 +# WAZUH_CLUSTER_KEY= +# WAZUH_REGISTRATION_PASSWORD= +# WAZUH_API_PORT=55000 diff --git a/apps/wazuh/CHANGELOG.md b/apps/wazuh/CHANGELOG.md index 582cf46c5..32404a131 100644 --- a/apps/wazuh/CHANGELOG.md +++ b/apps/wazuh/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Rebuild the package as the official Wazuh single-node stack (manager, indexer and dashboard at 4.14.7). +- Replace the placeholder WordPress package with Wazuh configs, ports, environment variables and tests. +- Generate the indexer TLS certificates at deploy time with `wazuh/wazuh-certs-generator`. +- Raise `vm.max_map_count` with a one-shot privileged `busybox` helper before the indexer starts. +- Publish only the required ports (dashboard 5601, agent 1514/1515); keep syslog 514/udp and manager API 55000 commented for optional use. +- Isolate the stack on a per-instance `${W9_ID}-internal` network so multiple Wazuh instances can share the host without hostname/TLS collisions. diff --git a/apps/wazuh/Dockerfile b/apps/wazuh/Dockerfile deleted file mode 100644 index 4364b46fb..000000000 --- a/apps/wazuh/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -# image: https://hub.docker.com/r/websoft9dev/discuzq - -FROM ccr.ccs.tencentyun.com/discuzq/dzq:latest - -LABEL org.opencontainers.image.authors="https://www.websoft9.com" \ - org.opencontainers.image.description="Application packaged by Websoft9" \ - org.opencontainers.image.source="https://github.com/Websoft9/docker-library/tree/main/apps/opencart" \ - org.opencontainers.image.title="OpenCart" \ - org.opencontainers.image.vendor="Websoft9 Inc." \ - org.opencontainers.image.version="4.0.1.1" - -ENV DISCUZQ_MYSQL_HOST=mysql -ENV DISCUZQ_MYSQL_USER=discuzq -ENV DISCUZQ_MYSQL_PASSWORD=discuzq -ENV DISCUZQ_MYSQL_DATABASE=discuzq -ENV DISCUZQ_SITENAME=DiscuzQ - -COPY cmd.sh /tmp -RUN chmod +x /tmp/cmd.sh - -CMD ["/tmp/cmd.sh"] diff --git a/apps/wazuh/Notes.md b/apps/wazuh/Notes.md index 1fdbbb29d..5d55b7e2e 100644 --- a/apps/wazuh/Notes.md +++ b/apps/wazuh/Notes.md @@ -1,2 +1,109 @@ -# Appname -## FAQ +# Wazuh Notes + +> Internal maintenance notes. Customer-facing documentation lives in `README.md`. + +## Sources + +- Official images: `wazuh/wazuh-manager`, `wazuh/wazuh-indexer`, `wazuh/wazuh-dashboard` +- Official deployment: https://github.com/wazuh/wazuh-docker (single-node, tag `v4.14.7`) +- Docs: https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html + +## Version coupling + +The manager, indexer and dashboard images must always share the same version. `W9_VERSION` drives +all three images and the `CERT_TOOL_VERSION` used by the certificate generator (`4.14` for the +`4.14.x` line). Update them together. + +Wazuh publishes full patch tags only (`4.14.7`), so the package intentionally pins `x.x.x` instead +of an `x.x` tag. + +## Host prerequisite: vm.max_map_count + +The indexer (OpenSearch based) needs `vm.max_map_count >= 262144`. The `busybox` service runs a +privileged one-shot `sysctl -w vm.max_map_count=262144` before the indexer starts. If the host +already reports a higher value, the command is a no-op. On hosts that block this, set it manually: + +```bash +echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf && sudo sysctl -p +``` + +## TLS certificates + +The `wazuh-certs` service runs `wazuh/wazuh-certs-generator:0.0.4`, which downloads +`wazuh-certs-tool.sh` from `packages.wazuh.com` and writes the certificates into the bind-mounted +`src/wazuh_indexer_ssl_certs/` directory. Generation runs only when `wazuh.indexer.pem` is absent, +and the stale `/wazuh-certificates` work dir is removed first, so repeated `docker compose up` does +not rotate the certificates. + +Do not switch this back to a named volume: `wazuh/wazuh-indexer` ships stock demo certificates at +`/usr/share/wazuh-indexer/config/certs/`, and Docker would copy them into a fresh named volume +before the generator runs, making the guard skip generation and leaving the indexer without the +`wazuh.indexer.pem` it expects. + +- The generated directory is forced to `755` after generation so the `wazuh` (999) and + `wazuh-indexer`/`wazuh-dashboard` (1000) users can traverse it. +- To rotate the certificates, delete `src/wazuh_indexer_ssl_certs/*` and recreate the stack: + `docker compose down && docker compose up -d`. +- The container paths differ from upstream because the whole volume is mounted as a directory: + the manager reads from `/etc/ssl/wazuh/`, the indexer from + `/usr/share/wazuh-indexer/config/certs/`, the dashboard from + `/usr/share/wazuh-dashboard/certs/`. + +## Startup ordering + +The dashboard must not start before the indexer can serve requests, otherwise its first saved +objects migration (`.kibana_1`) times out and the dashboard waits forever. The indexer therefore +defines a healthcheck (`_cluster/health` with the admin credentials) and the manager and dashboard +depend on `service_healthy`. + +If a deployment is interrupted and the dashboard reports +`Another OpenSearch Dashboards instance appears to be migrating the index`, delete the broken index +and restart the dashboard: + +```bash +docker exec ${W9_ID}-indexer curl -k -s -u admin:${W9_LOGIN_PASSWORD} -X DELETE https://localhost:9200/.kibana_1 +docker restart ${W9_ID} +``` + +## Multi-instance isolation + +The upstream single-node stack uses fixed hostnames (`wazuh.indexer`, `wazuh.manager`, +`wazuh.dashboard`) that are baked into the certificates and configs. On the shared `websoft9` +network these names collide with any other Wazuh instance, causing clients to reach the wrong +indexer/manager and fail with `SSLHandshakeException (unknown_ca)`. + +To keep instances isolated, the manager, indexer and dashboard join a per-instance private bridge +network `${W9_ID}-internal`; only the dashboard is also attached to `websoft9` for platform access. +Keep it this way: dropping the private network reintroduces the collision. `hostname` alone cannot +fix it because Compose always registers the service name as a network alias. + +## Credentials + +- Dashboard login: `admin` / `SecretPassword` (`W9_LOGIN_USER` / `W9_LOGIN_PASSWORD`). +- The password is defined by the bcrypt hash in `src/internal_users.yml`, which the indexer loads + only when it initializes a fresh security index. Changing `W9_LOGIN_PASSWORD` in `.env` does not + rewrite an existing deployment. +- Internal service accounts keep the upstream defaults: `kibanaserver`/`kibanaserver` and + `wazuh-wui`/`MyS3cr37P450r.*-`. +- Rotate credentials with the Wazuh `wazuh-passwords-tool.sh` inside the manager, or by recreating + the stack with an updated `src/internal_users.yml` hash. + +## Ports + +| Purpose | Host variable | Container | +| --- | --- | --- | +| Web Console (HTTPS) | `W9_HTTPS_PORT_SET` | 5601 | +| Agent Connection | `W9_AGENT_PORT_SET` | 1514 | +| Agent Enrollment | `W9_ENROLLMENT_PORT_SET` | 1515 | +| Syslog Collection (optional) | `W9_SYSLOG_UDP_PORT_SET` | 514/udp | +| Manager API (optional) | `W9_API_PORT_SET` | 55000 | + +The syslog and API ports are commented out in `docker-compose.yml` (and their `_SET` vars are commented +in `.env`). The dashboard reaches the manager API over the Docker network, so 55000 only needs to be +published for external automation; 514/udp only for external syslog sources. + +## Known limits + +- The dashboard is HTTPS-only, so the package has no `W9_HTTP_PORT_SET`; the deploy test uses the + `tests/check.sh` HTTPS probe instead of the adaptive HTTP web-access check. +- First startup takes a few minutes while the indexer builds its security index. diff --git a/apps/wazuh/README.md b/apps/wazuh/README.md index f75ff7075..d6a7c9411 100644 --- a/apps/wazuh/README.md +++ b/apps/wazuh/README.md @@ -1,26 +1,119 @@ -# Wazuh on Docker +# Wazuh on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Wazuh: +## Quick Start +### Deploy Verification - - community: latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Wazuh**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the dashboard at `https://:${W9_HTTPS_PORT_SET}` and accept the self-signed certificate. +2. Sign in with `admin` / `SecretPassword`. +3. Deploy agents and point them at the manager: agent connection on port `1514`, enrollment on `1515`, syslog on `514/udp`. -The following are the minimal [recommended requirements](https://wazuh.com): +### Change Password -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 10 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update `W9_LOGIN_PASSWORD` in `.env` and the matching bcrypt hash in `src/internal_users.yml`. +3. Recreate the stack so the indexer rebuilds its security index (`docker compose down -v && docker compose up -d`). + -## Install +## Configuration Reference -You can install this Wazuh by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Wazuh Docker image](https://hub.docker.com/r/wazuh/wazuh-dashboard) and makes some improvements below. -If you want use Wazuh with **Websoft9 Business Support** free, you can [subscribe Wazuh](https://www.websoft9.com/apps) on Cloud platform + +- The manager, indexer and dashboard must run the same version; `W9_VERSION` drives all three images. +- TLS certificates are generated on first start by `wazuh/wazuh-certs-generator` and stored in the `wazuh-certs` volume. +- The indexer requires `vm.max_map_count=262144`; the `busybox` helper service sets it before startup. +- `W9_LOGIN_PASSWORD` takes effect only when the indexer initializes a fresh security index. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[Wazuh Administrator Guide](https://support.websoft9.com/docs/wazuh) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 4.14.7. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Agent Connection | 1514 | +| Agent Enrollment | 1515 | +| Web Console | 5601 | + + +### Data Directory + + +- `wazuh-indexer-data` → `/var/lib/wazuh-indexer` +- `wazuh_api_configuration` → `/var/ossec/api/configuration` +- `wazuh_etc` → `/var/ossec/etc` +- `wazuh_logs` → `/var/ossec/logs` +- `wazuh_queue` → `/var/ossec/queue` +- `wazuh_var_multigroups` → `/var/ossec/var/multigroups` +- `wazuh_integrations` → `/var/ossec/integrations` +- `wazuh_active_response` → `/var/ossec/active-response/bin` +- `wazuh_agentless` → `/var/ossec/agentless` +- `wazuh_wodles` → `/var/ossec/wodles` +- `filebeat_etc` → `/etc/filebeat` +- `filebeat_var` → `/var/lib/filebeat` +- `wazuh-dashboard-config` → `/usr/share/wazuh-dashboard/data/wazuh/config` +- `wazuh-dashboard-custom` → `/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +- `./src/wazuh_indexer_ssl_certs` → `/certificates` +- `./src/certs.yml` → `/config/certs.yml` +- `./src/wazuh_indexer_ssl_certs` → `/usr/share/wazuh-indexer/config/certs` +- `./src/wazuh.indexer.yml` → `/usr/share/wazuh-indexer/config/opensearch.yml` +- `./src/internal_users.yml` → `/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml` +- `./src/wazuh_indexer_ssl_certs` → `/etc/ssl/wazuh` +- `./src/wazuh_manager.conf` → `/wazuh-config-mount/etc/ossec.conf` +- `./src/wazuh_indexer_ssl_certs` → `/usr/share/wazuh-dashboard/certs` +- `./src/opensearch_dashboards.yml` → `/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml` +- `./src/wazuh.yml` → `/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml` + + + +## References + +- [Wazuh Administrator Guide](https://support.websoft9.com/docs/wazuh) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/wazuh/wazuh-dashboard) + +- [Releases](https://github.com/wazuh/wazuh/releases) + +- [Official compose](https://raw.githubusercontent.com/wazuh/wazuh-docker/v4.14.7/single-node/docker-compose.yml) + +- [Official docs](https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html) + +- [GitHub docs](https://github.com/wazuh/wazuh-docker) + + + +## Troubleshooting + +**Indexer exits because `vm.max_map_count` is too low?** +- Run `sudo sysctl -w vm.max_map_count=262144` on the host, then restart the stack. + +**Dashboard unreachable or certificate error?** +- The dashboard is HTTPS-only. Use `https://:${W9_HTTPS_PORT_SET}` and accept the self-signed certificate. + +**App fails to start?** +- Check `docker compose logs wazuh.indexer wazuh.manager wazuh.dashboard`. + diff --git a/apps/wazuh/docker-compose.yml b/apps/wazuh/docker-compose.yml index 1c1948688..bacf6b3d5 100644 --- a/apps/wazuh/docker-compose.yml +++ b/apps/wazuh/docker-compose.yml @@ -1,50 +1,148 @@ -# image,docs: https://hub.docker.com/_/wordpress/ - services: + wazuh-certs: + image: wazuh/wazuh-certs-generator:0.0.4 + container_name: ${W9_ID}-certs + restart: "no" + environment: + CERT_TOOL_VERSION: "4.14" + entrypoint: ["/bin/bash", "-c"] + command: + - | + if [ ! -f /certificates/wazuh.indexer.pem ]; then + rm -rf /wazuh-certificates + /entrypoint.sh + fi + chmod 755 /certificates + volumes: + - ./src/wazuh_indexer_ssl_certs:/certificates + - ./src/certs.yml:/config/certs.yml + networks: + - wazuh-internal - wordpress: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID - restart: unless-stopped - #This is for access host from container - # extra_hosts: ["host.docker.internal:host-gateway"] - # command: | - # /bin/bash -c "ping -c 3 host.docker.internal" - logging: - driver: "json-file" - options: - max-file: "5" - max-size: 10m - deploy: - resources: - limits: - memory: 5g - cpus: '0.7' - ports: - - $W9_HTTP_PORT_SET:80 + busybox: + image: busybox + container_name: ${W9_ID}-sysctl + restart: "no" + command: /bin/sh -c "sysctl -w vm.max_map_count=262144 || true" + privileged: true + networks: + - wazuh-internal + + wazuh.indexer: + image: wazuh/wazuh-indexer:${W9_VERSION} + container_name: ${W9_ID}-indexer + hostname: wazuh.indexer + restart: always env_file: .env + depends_on: + wazuh-certs: + condition: service_completed_successfully + busybox: + condition: service_completed_successfully + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + healthcheck: + test: ["CMD-SHELL", "curl -k -s -u admin:$${INDEXER_PASSWORD} https://localhost:9200/_cluster/health | grep -q '\"status\"'"] + interval: 10s + timeout: 10s + retries: 30 + start_period: 60s volumes: - - wordpress:/var/www/html - - ./src/php_exra.ini:/usr/local/etc/php/conf.d/php_exra.ini + - wazuh-indexer-data:/var/lib/wazuh-indexer + - ./src/wazuh_indexer_ssl_certs:/usr/share/wazuh-indexer/config/certs + - ./src/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml + - ./src/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml + networks: + - wazuh-internal - mariadb: - image: mariadb:10.4 - container_name: $W9_ID-mariadb - restart: unless-stopped - command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --innodb_log_buffer_size=30M + wazuh.manager: + image: wazuh/wazuh-manager:${W9_VERSION} + container_name: ${W9_ID}-manager + hostname: wazuh.manager + restart: always + env_file: .env + depends_on: + wazuh.indexer: + condition: service_healthy + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 655360 + hard: 655360 + ports: + - "${W9_AGENT_PORT_SET}:1514" # Agent Connection + - "${W9_ENROLLMENT_PORT_SET}:1515" # Agent Enrollment + # Uncomment to collect syslog from external devices: + # - "${W9_SYSLOG_UDP_PORT_SET}:514/udp" # Syslog Collection + # Uncomment to expose the Wazuh REST API to external automation: + # - "${W9_API_PORT_SET}:55000" # Manager API volumes: - - mysql_data:/var/lib/mysql - environment: - MYSQL_DATABASE: $WORDPRESS_DB_NAME - MYSQL_USER: $WORDPRESS_DB_USER - MYSQL_PASSWORD: $W9_POWER_PASSWORD - MYSQL_ROOT_PASSWORD: $W9_POWER_PASSWORD + - wazuh_api_configuration:/var/ossec/api/configuration + - wazuh_etc:/var/ossec/etc + - wazuh_logs:/var/ossec/logs + - wazuh_queue:/var/ossec/queue + - wazuh_var_multigroups:/var/ossec/var/multigroups + - wazuh_integrations:/var/ossec/integrations + - wazuh_active_response:/var/ossec/active-response/bin + - wazuh_agentless:/var/ossec/agentless + - wazuh_wodles:/var/ossec/wodles + - filebeat_etc:/etc/filebeat + - filebeat_var:/var/lib/filebeat + - ./src/wazuh_indexer_ssl_certs:/etc/ssl/wazuh + - ./src/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf + networks: + - wazuh-internal + + wazuh.dashboard: + image: wazuh/wazuh-dashboard:${W9_VERSION} + container_name: ${W9_ID} + hostname: wazuh.dashboard + restart: always + env_file: .env + depends_on: + wazuh.indexer: + condition: service_healthy + wazuh.manager: + condition: service_started + ports: + - "${W9_HTTPS_PORT_SET}:5601" # Web Console + volumes: + - ./src/wazuh_indexer_ssl_certs:/usr/share/wazuh-dashboard/certs + - ./src/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml + - ./src/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml + - wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config + - wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom + networks: + - wazuh-internal + - default volumes: - wordpress: - mysql_data: - + wazuh-indexer-data: + wazuh_api_configuration: + wazuh_etc: + wazuh_logs: + wazuh_queue: + wazuh_var_multigroups: + wazuh_integrations: + wazuh_active_response: + wazuh_agentless: + wazuh_wodles: + filebeat_etc: + filebeat_var: + wazuh-dashboard-config: + wazuh-dashboard-custom: + networks: + wazuh-internal: + name: ${W9_ID}-internal + driver: bridge default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true diff --git a/apps/wazuh/src/certs.yml b/apps/wazuh/src/certs.yml new file mode 100755 index 000000000..c3e017be1 --- /dev/null +++ b/apps/wazuh/src/certs.yml @@ -0,0 +1,16 @@ +nodes: + # Wazuh indexer server nodes + indexer: + - name: wazuh.indexer + ip: wazuh.indexer + + # Wazuh server nodes + # Use node_type only with more than one Wazuh manager + server: + - name: wazuh.manager + ip: wazuh.manager + + # Wazuh dashboard node + dashboard: + - name: wazuh.dashboard + ip: wazuh.dashboard diff --git a/apps/wazuh/src/internal_users.yml b/apps/wazuh/src/internal_users.yml new file mode 100644 index 000000000..d9f05b343 --- /dev/null +++ b/apps/wazuh/src/internal_users.yml @@ -0,0 +1,56 @@ +--- +# This is the internal user database +# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh + +_meta: + type: "internalusers" + config_version: 2 + +# Define your internal users here + +## Demo users + +admin: + hash: "$2y$12$K/SpwjtB.wOHJ/Nc6GVRDuc1h0rM1DfvziFRNPtk27P.c4yDr9njO" + reserved: true + backend_roles: + - "admin" + description: "Demo admin user" + +kibanaserver: + hash: "$2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H." + reserved: true + description: "Demo kibanaserver user" + +kibanaro: + hash: "$2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC" + reserved: false + backend_roles: + - "kibanauser" + - "readall" + attributes: + attribute1: "value1" + attribute2: "value2" + attribute3: "value3" + description: "Demo kibanaro user" + +logstash: + hash: "$2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2" + reserved: false + backend_roles: + - "logstash" + description: "Demo logstash user" + +readall: + hash: "$2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2" + reserved: false + backend_roles: + - "readall" + description: "Demo readall user" + +snapshotrestore: + hash: "$2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W" + reserved: false + backend_roles: + - "snapshotrestore" + description: "Demo snapshotrestore user" diff --git a/apps/wazuh/src/opensearch_dashboards.yml b/apps/wazuh/src/opensearch_dashboards.yml new file mode 100644 index 000000000..903045eb1 --- /dev/null +++ b/apps/wazuh/src/opensearch_dashboards.yml @@ -0,0 +1,16 @@ +server.host: 0.0.0.0 +server.port: 5601 +opensearch.hosts: https://wazuh.indexer:9200 +opensearch.ssl.verificationMode: certificate +opensearch.requestHeadersWhitelist: ["securitytenant","Authorization"] +opensearch_security.multitenancy.enabled: false +opensearch_security.readonly_mode.roles: ["kibana_read_only"] +server.ssl.enabled: true +server.ssl.key: "/usr/share/wazuh-dashboard/certs/wazuh.dashboard-key.pem" +server.ssl.certificate: "/usr/share/wazuh-dashboard/certs/wazuh.dashboard.pem" +opensearch.ssl.certificateAuthorities: ["/usr/share/wazuh-dashboard/certs/root-ca.pem"] +uiSettings.overrides.defaultRoute: /app/wz-home +# Session expiration settings +opensearch_security.cookie.ttl: 900000 +opensearch_security.session.ttl: 900000 +opensearch_security.session.keepalive: true diff --git a/apps/wazuh/src/wazuh.indexer.yml b/apps/wazuh/src/wazuh.indexer.yml new file mode 100644 index 000000000..21b8e978c --- /dev/null +++ b/apps/wazuh/src/wazuh.indexer.yml @@ -0,0 +1,36 @@ +network.host: "0.0.0.0" +node.name: "wazuh.indexer" +cluster.name: "wazuh-cluster" +path.data: /var/lib/wazuh-indexer +path.logs: /var/log/wazuh-indexer +discovery.type: single-node +compatibility.override_main_response_version: true +plugins.security.ssl.http.pemcert_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem +plugins.security.ssl.http.pemkey_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer-key.pem +plugins.security.ssl.http.pemtrustedcas_filepath: /usr/share/wazuh-indexer/config/certs/root-ca.pem +plugins.security.ssl.transport.pemcert_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem +plugins.security.ssl.transport.pemkey_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer-key.pem +plugins.security.ssl.transport.pemtrustedcas_filepath: /usr/share/wazuh-indexer/config/certs/root-ca.pem +plugins.security.ssl.http.enabled: true +plugins.security.ssl.transport.enforce_hostname_verification: false +plugins.security.ssl.transport.resolve_hostname: false +plugins.security.ssl.http.enabled_ciphers: + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" +plugins.security.ssl.http.enabled_protocols: + - "TLSv1.2" +plugins.security.authcz.admin_dn: +- "CN=admin,OU=Wazuh,O=Wazuh,L=California,C=US" +plugins.security.check_snapshot_restore_write_privileges: true +plugins.security.enable_snapshot_restore_privilege: true +plugins.security.nodes_dn: +- "CN=wazuh.indexer,OU=Wazuh,O=Wazuh,L=California,C=US" +plugins.security.restapi.roles_enabled: +- "all_access" +- "security_rest_api_access" +plugins.security.system_indices.enabled: true +plugins.security.system_indices.indices: [".opendistro-alerting-config", ".opendistro-alerting-alert*", ".opendistro-anomaly-results*", ".opendistro-anomaly-detector*", ".opendistro-anomaly-checkpoints", ".opendistro-anomaly-detection-state", ".opendistro-reports-*", ".opendistro-notifications-*", ".opendistro-notebooks", ".opensearch-observability", ".opendistro-asynchronous-search-response*", ".replication-metadata-store"] +plugins.security.allow_default_init_securityindex: true +cluster.routing.allocation.disk.threshold_enabled: false \ No newline at end of file diff --git a/apps/wazuh/src/wazuh.yml b/apps/wazuh/src/wazuh.yml new file mode 100644 index 000000000..5ff4e2bef --- /dev/null +++ b/apps/wazuh/src/wazuh.yml @@ -0,0 +1,7 @@ +hosts: + - 1513629884013: + url: "https://wazuh.manager" + port: 55000 + username: wazuh-wui + password: "MyS3cr37P450r.*-" + run_as: true diff --git a/apps/wazuh/src/wazuh_indexer_ssl_certs/.gitkeep b/apps/wazuh/src/wazuh_indexer_ssl_certs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wazuh/src/wazuh_manager.conf b/apps/wazuh/src/wazuh_manager.conf new file mode 100644 index 000000000..5ff2c0762 --- /dev/null +++ b/apps/wazuh/src/wazuh_manager.conf @@ -0,0 +1,311 @@ + + + yes + yes + no + no + no + smtp.example.wazuh.com + wazuh@example.wazuh.com + recipient@example.wazuh.com + 12 + alerts.log + 10m + 0 + + + + 3 + 12 + + + + + plain + + + + secure + 1514 + tcp + 131072 + + + + + no + yes + yes + yes + yes + yes + yes + yes + + + 43200 + + etc/rootcheck/rootkit_files.txt + etc/rootcheck/rootkit_trojans.txt + + yes + + + + yes + 1800 + 1d + yes + + wodles/java + wodles/ciscat + + + + + yes + yes + /var/log/osquery/osqueryd.results.log + /etc/osquery/osquery.conf + yes + + + + + no + 1h + yes + yes + yes + yes + yes + yes + yes + + + + 10 + + + + + yes + yes + 12h + yes + + + + yes + yes + 60m + + + + yes + + https://wazuh.indexer:9200 + + + + /etc/ssl/wazuh/root-ca-manager.pem + + /etc/ssl/wazuh/wazuh.manager.pem + /etc/ssl/wazuh/wazuh.manager-key.pem + + + + + + no + + + 43200 + + yes + + + yes + + + no + + + /etc,/usr/bin,/usr/sbin + /bin,/sbin,/boot + + + /etc/mtab + /etc/hosts.deny + /etc/mail/statistics + /etc/random-seed + /etc/random.seed + /etc/adjtime + /etc/httpd/logs + /etc/utmpx + /etc/wtmpx + /etc/cups/certs + /etc/dumpdates + /etc/svc/volatile + + + .log$|.swp$ + + + /etc/ssl/private.key + + yes + yes + yes + yes + + + 10 + + + 100 + + + + yes + 5m + 1h + 10 + + + + + + 127.0.0.1 + ^localhost.localdomain$ + + + + disable-account + disable-account + yes + + + + restart-wazuh + restart-wazuh + + + + firewall-drop + firewall-drop + yes + + + + host-deny + host-deny + yes + + + + route-null + route-null + yes + + + + win_route-null + route-null.exe + yes + + + + netsh + netsh.exe + yes + + + + + + + command + df -P + 360 + + + + full_command + netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d + netstat listening ports + 360 + + + + full_command + last -n 20 + 360 + + + + + ruleset/decoders + ruleset/rules + 0215-policy_rules.xml + etc/lists/audit-keys + etc/lists/amazon/aws-eventnames + etc/lists/security-eventchannel + etc/lists/malicious-ioc/malicious-ip + etc/lists/malicious-ioc/malicious-domains + etc/lists/malicious-ioc/malware-hashes + + + etc/decoders + etc/rules + + + + yes + 1 + 64 + 15m + + + + + no + 1515 + no + yes + no + HIGH:!ADH:!EXP:!MD5:!RC4:!3DES:!CAMELLIA:@STRENGTH + + no + etc/sslmanager.cert + etc/sslmanager.key + no + + + + wazuh + node01 + master + aa093264ef885029653eea20dfcf51ae + 1516 + 0.0.0.0 + + wazuh.manager + + no + yes + + + + + + + syslog + /var/ossec/logs/active-responses.log + + + diff --git a/apps/wazuh/tests/cases.yml b/apps/wazuh/tests/cases.yml new file mode 100644 index 000000000..f16490388 --- /dev/null +++ b/apps/wazuh/tests/cases.yml @@ -0,0 +1,9 @@ +# The Wazuh dashboard is HTTPS-only, so the adaptive HTTP web-access check does +# not apply. The custom script waits for the dashboard over HTTPS instead. +skip: + - id: web-access + +optional: + - id: dashboard-https + type: script + script: check.sh diff --git a/apps/wazuh/tests/check.sh b/apps/wazuh/tests/check.sh new file mode 100755 index 000000000..25d4dd76d --- /dev/null +++ b/apps/wazuh/tests/check.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -uo pipefail + +port="${W9_HTTPS_PORT_SET:-9443}" +base="${BASE_URL:-https://localhost:${port}}" +deadline=$((SECONDS + 300)) +code="000" + +while [ "$SECONDS" -lt "$deadline" ]; do + code=$(curl -k -s -o /dev/null -w "%{http_code}" --max-time 15 "${base}/" || true) + case "$code" in + 200|301|302) + echo "wazuh dashboard ${base}/ -> ${code}" + exit 0 + ;; + esac + sleep 5 +done + +echo "wazuh dashboard ${base}/ -> ${code} (timeout)" +exit 1 diff --git a/apps/wazuh/variables.json b/apps/wazuh/variables.json index eadd7bf01..71218f435 100644 --- a/apps/wazuh/variables.json +++ b/apps/wazuh/variables.json @@ -1,21 +1,35 @@ { "name": "wazuh", "trademark": "Wazuh", - "release": false, + "release": true, + "upstream": { + "image": "https://hub.docker.com/r/wazuh/wazuh-dashboard", + "releases": "https://github.com/wazuh/wazuh/releases", + "compose": { + "compose": "https://raw.githubusercontent.com/wazuh/wazuh-docker/v4.14.7/single-node/docker-compose.yml" + }, + "docs": [ + "https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html", + "https://github.com/wazuh/wazuh-docker" + ] + }, "edition": [ { "dist": "community", "version": [ - "latest" + "4.14.7" ] } ], + "access": { + "web": { + "port": 5601, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "10" - }, - "upstream": { - "image": "https://hub.docker.com/r/wazuh/wazuh-manager" } } diff --git a/apps/weaviate/.env b/apps/weaviate/.env index f0be13916..cbdfb1888 100644 --- a/apps/weaviate/.env +++ b/apps/weaviate/.env @@ -1,18 +1,41 @@ W9_REPO=semitechnologies/weaviate W9_DIST=community -W9_VERSION=latest +W9_VERSION=1.39.5 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### + W9_ID=weaviate -# Environments which for user settings when create applications + +# Weaviate exposes an HTTP API (8080) and a gRPC API (50051) used by clients. W9_HTTP_PORT_SET=8080 W9_HTTP_PORT=8080 +W9_GRPC_PORT_SET=50051 W9_URL=example.youdomain.com W9_NETWORK=websoft9 +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Weaviate image environment variables +# Docs: https://weaviate.io/developers/weaviate/installation/docker-compose +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: QUERY_DEFAULTS_LIMIT=25 AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED='true' PERSISTENCE_DATA_PATH='/var/lib/weaviate' DEFAULT_VECTORIZER_MODULE='none' -ENABLE_API_BASED_MODULES='true' CLUSTER_HOSTNAME='node1' + +# Not used by default; enable only when needed: +# AUTHENTICATION_APIKEY_ENABLED='false' +# AUTHENTICATION_APIKEY_ALLOWED_KEYS='user-a-key,user-b-key' +# AUTHENTICATION_APIKEY_USERS='user-a,user-b' +# AUTHORIZATION_ENABLE_RBAC='true' +# AUTHORIZATION_RBAC_ROOT_USERS='user-a' diff --git a/apps/weaviate/CHANGELOG.md b/apps/weaviate/CHANGELOG.md index 09c4e7bd0..4fd05e6a0 100644 --- a/apps/weaviate/CHANGELOG.md +++ b/apps/weaviate/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG -## Release +## 2026-09-20 -### Fixes and Enhancements +- Updated Weaviate from `1.26.6` to `1.39.5`, the latest stable upstream release. +- Pinned `W9_VERSION` to `1.39.5` and declared it in `variables.json`. +- Added the gRPC API port (`W9_GRPC_PORT_SET`, 50051) required by Weaviate clients. +- Removed `ENABLE_API_BASED_MODULES`, which upstream removed in v1.33. +- Aligned `.env` and `docker-compose.yml` with current repository policy: braced variable references, inline published-port comments, and the `.env` section banner with a Docs URL. +- Added a readiness healthcheck against `/v1/.well-known/ready`. +- Added `tests/cases.yml` with an app-specific readiness check. +- Added upstream releases and documentation references to `variables.json`. +- Regenerated `README.md`. diff --git a/apps/weaviate/Notes.md b/apps/weaviate/Notes.md deleted file mode 100644 index 122ecbbb8..000000000 --- a/apps/weaviate/Notes.md +++ /dev/null @@ -1 +0,0 @@ -# Weaviate diff --git a/apps/weaviate/README.md b/apps/weaviate/README.md index 6947c4e14..0d3a7948e 100644 --- a/apps/weaviate/README.md +++ b/apps/weaviate/README.md @@ -1,26 +1,87 @@ -# Weaviate on Docker +# Weaviate on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Weaviate: +## Quick Start +### Deploy Verification - - community: 1.26.6, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Weaviate**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the Weaviate admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://weaviate.io/developers/weaviate/current/): +### Change Password -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 10 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this Weaviate by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Weaviate Docker image](https://hub.docker.com/r/semitechnologies/weaviate) and makes some improvements below. -If you want use Weaviate with **Websoft9 Business Support** free, you can [subscribe Weaviate](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Weaviate Administrator Guide](https://support.websoft9.com/docs/weaviate) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 1.39.5, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| HTTP API | 8080 | +| gRPC API | 50051 | + + +### Data Directory + + +Data is persisted in the `weaviate_data` volume, mounted at `/var/lib/weaviate`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Weaviate Administrator Guide](https://support.websoft9.com/docs/weaviate) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/semitechnologies/weaviate) + +- [Releases](https://github.com/weaviate/weaviate/releases) + +- [Official docs](https://weaviate.io/developers/weaviate/installation/docker-compose) + +- [Official docs](https://weaviate.io/developers/weaviate/config-refs/env-vars) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/weaviate/docker-compose.yml b/apps/weaviate/docker-compose.yml index 66db30a71..ab9c2b4f5 100644 --- a/apps/weaviate/docker-compose.yml +++ b/apps/weaviate/docker-compose.yml @@ -1,27 +1,33 @@ -services: - weaviate: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID - command: - - --host - - 0.0.0.0 - - --port - - '8080' - - --scheme - - http - ports: - - $W9_HTTP_PORT_SET:8080 - - volumes: - - weaviate_data:/var/lib/weaviate - restart: unless-stopped - env_file: - - .env - -networks: - default: - name: $W9_NETWORK - external: true - -volumes: - weaviate_data: +services: + weaviate: + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + command: + - --host + - 0.0.0.0 + - --port + - '8080' + - --scheme + - http + ports: + - "${W9_HTTP_PORT_SET}:8080" # HTTP API + - "${W9_GRPC_PORT_SET}:50051" # gRPC API + volumes: + - weaviate_data:/var/lib/weaviate + restart: unless-stopped + env_file: + - .env + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/v1/.well-known/ready || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +networks: + default: + name: ${W9_NETWORK} + external: true + +volumes: + weaviate_data: diff --git a/apps/weaviate/tests/cases.yml b/apps/weaviate/tests/cases.yml new file mode 100644 index 000000000..3d7522aaa --- /dev/null +++ b/apps/weaviate/tests/cases.yml @@ -0,0 +1,8 @@ +skip: + - id: web-access + +optional: + - id: readiness + type: web-access + path: /v1/.well-known/ready + expect_status: 200 diff --git a/apps/weaviate/variables.json b/apps/weaviate/variables.json index 3fbf8ad51..5f4d2bfdf 100644 --- a/apps/weaviate/variables.json +++ b/apps/weaviate/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "1.26.6", + "1.39.5", "latest" ] } @@ -17,6 +17,11 @@ "disk": "10" }, "upstream": { - "image": "https://hub.docker.com/r/semitechnologies/weaviate" + "image": "https://hub.docker.com/r/semitechnologies/weaviate", + "releases": "https://github.com/weaviate/weaviate/releases", + "docs": [ + "https://weaviate.io/developers/weaviate/installation/docker-compose", + "https://weaviate.io/developers/weaviate/config-refs/env-vars" + ] } } diff --git a/apps/webcheck/src/nginx-proxy.conf.template b/apps/webcheck/src/nginx-proxy.conf.template deleted file mode 100644 index 951364ffe..000000000 --- a/apps/webcheck/src/nginx-proxy.conf.template +++ /dev/null @@ -1,57 +0,0 @@ -proxy_busy_buffers_size 512k; -proxy_buffers 4 512k; -proxy_buffer_size 256k; -client_max_body_size 50m; -# override default location / -location / { - add_header X-Served-By $host; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Scheme $scheme; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Real-IP $remote_addr; - proxy_pass $forward_scheme://$server:$port$request_uri; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection upgrade; - } - -location /console { - proxy_pass http://$server:8080; - proxy_http_version 1.1; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; -} - -# for echo some useful information -location / { - default_type text/plain; - return 200 'Hello World'; -} - -location /oida/ { -# this is the address and port of the ORDS installation -proxy_pass http://127.0.0.1:8080/ords/; - -# set Origin to blank to avoid Chrome problems with CORS -proxy_set_header Origin "" ; - -# pass along some header variables with the public host name/port/and so on -proxy_set_header Host $host; -proxy_set_header X-Forwarded-Host $host:$server_port; -proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -proxy_set_header X-Forwarded-Proto $scheme; - -# this reverse proxies any "location" headers being passed in the response -proxy_redirect http://$host/ords/ https://$host/oida/; - -# also tell cookies their public path -proxy_cookie_path /ords/ /oida/; - -# reverse proxy links included in response (ie from ORDS webservice) -sub_filter_types application/json ; -sub_filter http://$host/ords/ https://$host/oida/; -sub_filter_once off; -} diff --git a/apps/webcheck/src/php_exra.ini b/apps/webcheck/src/php_exra.ini deleted file mode 100644 index b253d5718..000000000 --- a/apps/webcheck/src/php_exra.ini +++ /dev/null @@ -1,8 +0,0 @@ -file_uploads = On -max_input_time = 800 -max_execution_time = 300 -memory_limit = 600M -upload_max_filesize = 900M -post_max_size = 900M -max_file_uploads = 200 -error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT \ No newline at end of file diff --git a/apps/youtrack/.env b/apps/youtrack/.env index f4423def6..0f1106a22 100644 --- a/apps/youtrack/.env +++ b/apps/youtrack/.env @@ -1,13 +1,32 @@ W9_REPO=jetbrains/youtrack -W9_DIST='community' -W9_VERSION='2025.2.89748' -W9_ID='youtrack' -W9_HTTP_PORT_SET='9001' +W9_DIST=community +W9_VERSION=2026.2.18991 + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### + +W9_ID=youtrack + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=8080 -W9_URL='example.youdomain.com' -W9_URL_REPLACE=false -W9_URL_WITH_PORT=false +W9_HTTP_PORT_SET=9001 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +W9_URL=example.youdomain.com + W9_NETWORK=websoft9 -# config and Envrioment -# https://www.jetbrains.com/help/youtrack/server/youtrack-java-start-parameters.html +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# YouTrack image environment variables +# Docs: https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +# (none) + +# Not used by default; enable only when needed: diff --git a/apps/youtrack/CHANGELOG.md b/apps/youtrack/CHANGELOG.md index 582cf46c5..89903e80a 100644 --- a/apps/youtrack/CHANGELOG.md +++ b/apps/youtrack/CHANGELOG.md @@ -1,5 +1,10 @@ -# CHANGELOG +# Changelog -## Release -### Fixes and Enhancements +## 2026-09-20 +- Update YouTrack community image from `2025.2.89748` to `2026.2.18991`. +- Remove dead `W9_URL_REPLACE` and `W9_URL_WITH_PORT` helpers that failed the policy gate. +- Normalize `.env` and `docker-compose.yml` to current repository policy, including braced variable references and port purpose comments. +- Raise the documented memory requirement to 1.5 GB to match upstream. +- Declare the first-run Configuration Wizard token as a `container-log` credential source. +- Regenerate README. diff --git a/apps/youtrack/Notes.md b/apps/youtrack/Notes.md deleted file mode 100644 index f1d50c332..000000000 --- a/apps/youtrack/Notes.md +++ /dev/null @@ -1,7 +0,0 @@ -# YouTrack - -# Installation - -you can followed the url https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html. - -## FAQ diff --git a/apps/youtrack/README.md b/apps/youtrack/README.md index 68ea12b43..32d047152 100644 --- a/apps/youtrack/README.md +++ b/apps/youtrack/README.md @@ -1,26 +1,84 @@ -# YouTrack on Docker +# YouTrack on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for YouTrack: +## Quick Start +### Deploy Verification - - community: 2025.1.76253 +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **YouTrack**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the YouTrack admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://www.jetbrains.com/help/youtrack/server/youtrack-supported-environments.html#hardware-requirements): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1.5 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this YouTrack by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [YouTrack Docker image](https://hub.docker.com/r/jetbrains/youtrack) and makes some improvements below. -If you want use YouTrack with **Websoft9 Business Support** free, you can [subscribe YouTrack](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[YouTrack Administrator Guide](https://support.websoft9.com/docs/youtrack) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 2026.2.18991. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | + + +### Data Directory + + +- `conf` → `/opt/youtrack/conf` +- `data` → `/opt/youtrack/data` +- `logs` → `/opt/youtrack/logs` +- `backups` → `/opt/youtrack/backups` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [YouTrack Administrator Guide](https://support.websoft9.com/docs/youtrack) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/jetbrains/youtrack) + +- [Official docs](https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/youtrack/docker-compose.yml b/apps/youtrack/docker-compose.yml index f6dc97947..3761b42bc 100644 --- a/apps/youtrack/docker-compose.yml +++ b/apps/youtrack/docker-compose.yml @@ -1,12 +1,9 @@ -#image: https://hub.docker.com/r/jetbrains/youtrack -#docs: https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html - version: '3.8' services: youtrack: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped volumes: - conf:/opt/youtrack/conf @@ -14,16 +11,17 @@ services: - logs:/opt/youtrack/logs - backups:/opt/youtrack/backups ports: - - $W9_HTTP_PORT_SET:8080 - env_file: .env - + - "${W9_HTTP_PORT_SET}:8080" # Web Console + env_file: + - .env + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true volumes: conf: data: logs: - backups: \ No newline at end of file + backups: diff --git a/apps/youtrack/variables.json b/apps/youtrack/variables.json index 6aade8086..b9c76dee8 100644 --- a/apps/youtrack/variables.json +++ b/apps/youtrack/variables.json @@ -6,16 +6,28 @@ { "dist": "community", "version": [ - "2025.2.89748" + "2026.2.18991" ] } ], + "credentials": { + "password": { + "source": "container-file", + "pattern": "/opt/youtrack/conf/internal/services/configurationWizard/wizard_token.txt" + } + }, "requirements": { "cpu": "2", - "memory": "1", - "disk": "1.5" + "memory": "1.5", + "disk": "10" + }, + "env": { + "first_startup_only": [] }, "upstream": { - "image": "https://hub.docker.com/r/jetbrains/youtrack" + "image": "https://hub.docker.com/r/jetbrains/youtrack", + "docs": [ + "https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html" + ] } } diff --git a/apps/zammad/.env b/apps/zammad/.env index 674b79665..cce3fd078 100644 --- a/apps/zammad/.env +++ b/apps/zammad/.env @@ -1,39 +1,63 @@ -W9_DIST='community' -# don't forget to add the minus before the version -W9_VERSION='6.5' -W9_REPO=zammad/zammad-docker-compose +W9_REPO=ghcr.io/zammad/zammad +W9_DIST=community +W9_VERSION=7.1 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. W9_POWER_PASSWORD='Yy6!CK!BebD1dzKn' +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### + W9_ID='zammad' -W9_HTTP_PORT_SET='9001' W9_HTTP_PORT=8080 +W9_HTTP_PORT_SET='9001' + +# Dependency helpers: bundled PostgreSQL stores Zammad data. +W9_DB_EXPOSE='postgresql' +W9_DB_VERSION='17.11-alpine' + +W9_URL='zammad.example.com' -W9_DB_EXPOSE="postgresql" -W9_URL='' W9_NETWORK=websoft9 # It need to modify for every creating application W9_RCODE='ZqTXurwg4e9zD' #### --------------------------------------------------------------------------------------- #### -# zammad environments: https://docs.zammad.org/en/latest/install/docker-compose/environment.html - -MEMCACHE_SERVERS=$W9_ID-memcached:11211 -MEMCACHE_VERSION=1.6.20-alpine - -ELASTICSEARCH_ENABLED=false - -# This is for init container, not for postgresql container -# postgresql connection have some trouble: host and password -POSTGRESQL_DB=zammad -POSTGRESQL_HOST=$W9_RCODE-postgresql +# ============================================================ +# Zammad image environment variables +# Docs: https://docs.zammad.org/en/latest/install/docker-compose/environment.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +MEMCACHE_SERVERS=${W9_ID}-memcached:11211 +MEMCACHE_VERSION='1.6-alpine' +REDIS_URL=redis://${W9_ID}-redis:6379 +REDIS_VERSION='8.10-alpine' +POSTGRESQL_DB=zammad_production +# This environment variable is not allowed to use _ in the name, so we use W9_RCODE +POSTGRESQL_HOST=${W9_RCODE}-postgresql POSTGRESQL_USER=zammad -POSTGRESQL_PASS=$W9_RCODE +POSTGRESQL_PASS=${W9_RCODE} POSTGRESQL_PORT=5432 -POSTGRESQL_VERSION=15.3-alpine +POSTGRESQL_OPTIONS=?pool=50 POSTGRESQL_DB_CREATE=false - - -REDIS_URL=redis://$W9_ID-redis:6379 -REDIS_VERSION=7.0.5-alpine +ELASTICSEARCH_ENABLED=true +ELASTICSEARCH_HOST=${W9_ID}-elasticsearch +ELASTICSEARCH_PORT=9200 +ELASTICSEARCH_SCHEMA=http +ELASTICSEARCH_NAMESPACE=${W9_ID} +ELASTICSEARCH_REINDEX=true +ELASTICSEARCH_VERSION='9.5.3' + +# Not used by default; enable only when needed: +# NGINX_SERVER_NAME= +# NGINX_SERVER_SCHEME=https +# ZAMMAD_FQDN= +# ZAMMAD_HTTP_TYPE=https diff --git a/apps/zammad/CHANGELOG.md b/apps/zammad/CHANGELOG.md index 582cf46c5..c450092ee 100644 --- a/apps/zammad/CHANGELOG.md +++ b/apps/zammad/CHANGELOG.md @@ -1,5 +1,7 @@ # CHANGELOG -## Release -### Fixes and Enhancements - +## 2026-09-20 +- Update Zammad from 6.5 to 7.1 and switch the image to the official `ghcr.io/zammad/zammad`. +- Rework `docker-compose.yml` and `.env` to the current upstream stack: PostgreSQL 17, Redis 8.10, Memcached 1.6, and a dedicated non-superuser `zammad` role/database provisioned on first init. +- Bundle Elasticsearch 9 and enable it (`ELASTICSEARCH_ENABLED=true`) so search, reports, and attachment indexing work out of the box; scope the ES index namespace to the instance (`ELASTICSEARCH_NAMESPACE=${W9_ID}`) so multiple deployments can safely share one external ES; administrator account is created through the first-run setup wizard. +- Add `tests/cases.yml` with a guided-setup API smoke check. diff --git a/apps/zammad/README.md b/apps/zammad/README.md index ae01d6fd8..ebf62f60c 100644 --- a/apps/zammad/README.md +++ b/apps/zammad/README.md @@ -1,26 +1,97 @@ -# Zammad on Docker +# Zammad on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Zammad: +## Quick Start +### Deploy Verification - - community: 6.4, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Zammad**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the Zammad web console from the **Access** tab. +2. Complete the first-run setup wizard to create the administrator account. +3. Configure an email channel and create your first ticket to confirm the flow. -The following are the minimal [recommended requirements](https://github.com/zammad-contrib/docker-zammad/blob/master/README.md): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 2 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Change the administrator password from the user profile inside Zammad. +2. `W9_POWER_PASSWORD` and `W9_RCODE` seed the bundled PostgreSQL credentials and only take effect on first startup; to rotate them, update the roles in PostgreSQL (or recreate the `postgresql-data` volume) and then rebuild the app. + -## Install +## Configuration Reference -You can install this Zammad by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Zammad Docker image](https://ghcr.io/zammad/zammad) and makes some improvements below. -If you want use Zammad with **Websoft9 Business Support** free, you can [subscribe Zammad](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Zammad Administrator Guide](https://support.websoft9.com/docs/zammad) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 7.1, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | + + +### Data Directory + + +- `zammad-storage` → `/opt/zammad/storage` +- `elasticsearch-data` → `/usr/share/elasticsearch/data` +- `redis-data` → `/data` +- `postgresql-data` → `/var/lib/postgresql/data` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_POWER_PASSWORD`, `W9_RCODE` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/postgresql_init.sh` to `/docker-entrypoint-initdb.d/postgresql_init.sh`. + + +## References + +- [Zammad Administrator Guide](https://support.websoft9.com/docs/zammad) by Websoft9 + +- [GHCR image](https://ghcr.io/zammad/zammad) + +- [Releases](https://github.com/zammad/zammad-docker-compose/releases) + +- [Official compose](https://github.com/zammad/zammad-docker-compose/blob/v17.2.0/docker-compose.yml) + +- [Official env example](https://github.com/zammad/zammad-docker-compose/blob/v17.2.0/.env.dist) + +- [Official docs](https://docs.zammad.org/en/latest/install/docker-compose.html) + +- [Official docs](https://docs.zammad.org/en/latest/install/docker-compose/environment.html) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/zammad/docker-compose.yml b/apps/zammad/docker-compose.yml index 3c4ad42f5..92666d022 100644 --- a/apps/zammad/docker-compose.yml +++ b/apps/zammad/docker-compose.yml @@ -1,92 +1,124 @@ -# image: https://hub.docker.com/r/zammad/zammad-docker-compose -# docs: https://docs.zammad.org/en/latest/install/docker-compose.html#getting-started-with-zammad-docker-compose -# github: https://github.com/zammad/zammad-docker-compose - -version: '3.8' - x-shared: zammad-service: &zammad-service image: ${W9_REPO}:${W9_VERSION} + init: true restart: unless-stopped env_file: .env volumes: - zammad-storage:/opt/zammad/storage - - zammad-var:/opt/zammad/var depends_on: - - zammad-postgresql - - zammad-redis + zammad-memcached: + condition: service_healthy + zammad-postgresql: + condition: service_healthy + zammad-redis: + condition: service_healthy services: + zammad-nginx: <<: *zammad-service - container_name: $W9_ID + container_name: ${W9_ID} command: ["zammad-nginx"] ports: - - $W9_HTTP_PORT_SET:8080 - expose: - - "8080" + - "${W9_HTTP_PORT_SET}:8080" # Web Console depends_on: - - zammad-railsserver - volumes: - - zammad-var:/opt/zammad/var:ro # required for the zammad-ready check file + zammad-railsserver: + condition: service_healthy zammad-init: <<: *zammad-service + container_name: ${W9_ID}-init command: ["zammad-init"] - container_name: $W9_ID-init - depends_on: - - zammad-postgresql restart: on-failure user: 0:0 + zammad-elasticsearch: + image: elasticsearch:${ELASTICSEARCH_VERSION} + container_name: ${W9_ID}-elasticsearch + restart: unless-stopped + environment: + discovery.type: single-node + xpack.security.enabled: "false" + ES_JAVA_OPTS: "-Xms1g -Xmx1g" + volumes: + - elasticsearch-data:/usr/share/elasticsearch/data + zammad-railsserver: - container_name: $W9_ID-railsserver <<: *zammad-service + container_name: ${W9_ID}-railsserver command: ["zammad-railsserver"] + healthcheck: + test: ["CMD", "curl", "-sf", "http://127.0.0.1:3000"] + interval: 30s + timeout: 5s + start_period: 120s + retries: 3 zammad-scheduler: <<: *zammad-service - container_name: $W9_ID-scheduler + container_name: ${W9_ID}-scheduler command: ["zammad-scheduler"] zammad-websocket: <<: *zammad-service - container_name: $W9_ID-websocket + container_name: ${W9_ID}-websocket command: ["zammad-websocket"] zammad-memcached: - command: memcached -m 256M - container_name: $W9_ID-memcached image: memcached:${MEMCACHE_VERSION} + container_name: ${W9_ID}-memcached + restart: unless-stopped + command: memcached -m 256M + healthcheck: + test: ["CMD", "nc", "-z", "127.0.0.1", "11211"] + interval: 10s + timeout: 5s + start_period: 10s + retries: 5 zammad-redis: - container_name: $W9_ID-redis image: redis:${REDIS_VERSION} + container_name: ${W9_ID}-redis + restart: unless-stopped volumes: - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + start_period: 10s + retries: 5 zammad-postgresql: - container_name: $W9_ID-postgresql - hostname: $W9_RCODE-postgresql + image: postgres:${W9_DB_VERSION} + container_name: ${W9_ID}-postgresql + hostname: ${W9_RCODE}-postgresql + restart: unless-stopped environment: - POSTGRES_DB: zammad POSTGRES_USER: postgres POSTGRES_PASSWORD: ${W9_POWER_PASSWORD} - POSTGRES_ZAMMAD_PASSWORD: ${W9_RCODE} - image: postgres:${POSTGRESQL_VERSION} + ZAMMAD_DB: zammad_production + ZAMMAD_DB_USER: zammad + ZAMMAD_DB_PASS: ${W9_RCODE} volumes: - postgresql-data:/var/lib/postgresql/data - - ./src/postgresql_init.sh:/docker-entrypoint-initdb.d/postgresql_init.sh + - ./src/postgresql_init.sh:/docker-entrypoint-initdb.d/postgresql_init.sh:ro + healthcheck: + test: + - CMD-SHELL + - |- + PGPASSWORD="$${ZAMMAD_DB_PASS}" psql --no-password --quiet --output /dev/null --variable ON_ERROR_STOP=1 --host 127.0.0.1 --username "$${ZAMMAD_DB_USER}" --dbname "$${ZAMMAD_DB}" --command "SELECT 1" + interval: 10s + timeout: 5s + start_period: 60s + retries: 5 volumes: + elasticsearch-data: postgresql-data: - driver: local redis-data: - driver: local zammad-storage: - driver: local - zammad-var: - driver: local networks: default: diff --git a/apps/zammad/src/postgresql_init.sh b/apps/zammad/src/postgresql_init.sh old mode 100644 new mode 100755 index b90b2af73..dc9f4576b --- a/apps/zammad/src/postgresql_init.sh +++ b/apps/zammad/src/postgresql_init.sh @@ -1,10 +1,35 @@ #!/bin/bash -set -a - -echo "POSTGRES_USER is set to: '${POSTGRES_USER}'" -echo "POSTGRES_ZAMMAD_PASSWORD is set to: '${POSTGRES_ZAMMAD_PASSWORD}'" -# create zammad user and database -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL - CREATE USER zammad WITH PASSWORD '${POSTGRES_ZAMMAD_PASSWORD}' SUPERUSER; -EOSQL \ No newline at end of file +set -o errexit +set -o pipefail + +zammad_abort() { + echo "$1" >&2 + echo "Correct this, then remove the postgresql-data volume and start again. The" >&2 + echo " database directory is already initialised, so this will not run twice." >&2 + exit 1 +} + +# Zammad must not reuse the bootstrap role, which is always a superuser. +if [ "${ZAMMAD_DB_USER}" = "${POSTGRES_USER}" ]; then + zammad_abort "ZAMMAD_DB_USER and POSTGRES_USER must differ, the latter is a superuser." +fi + +# The system databases exist already, so they would keep the bootstrap role as owner. +case "${ZAMMAD_DB}" in + postgres | template0 | template1) + zammad_abort "ZAMMAD_DB must not be one of PostgreSQL's system databases." + ;; +esac + +echo "Creating the '${ZAMMAD_DB_USER}' role and the '${ZAMMAD_DB}' database..." + +psql --variable ON_ERROR_STOP=1 \ + --username "${POSTGRES_USER}" \ + --dbname "${POSTGRES_DB:-postgres}" \ + --variable role="${ZAMMAD_DB_USER}" \ + --variable pass="${ZAMMAD_DB_PASS}" \ + --variable db="${ZAMMAD_DB}" <<'EOSQL' +CREATE ROLE :"role" LOGIN PASSWORD :'pass'; +CREATE DATABASE :"db" OWNER :"role"; +EOSQL diff --git a/apps/zammad/tests/cases.yml b/apps/zammad/tests/cases.yml new file mode 100644 index 000000000..9aa4cb34c --- /dev/null +++ b/apps/zammad/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: guided-setup-api + type: web-access + path: /api/v1/getting_started + expect_status: 200 diff --git a/apps/zammad/variables.json b/apps/zammad/variables.json index f0abc837e..b6767e36a 100644 --- a/apps/zammad/variables.json +++ b/apps/zammad/variables.json @@ -2,21 +2,46 @@ "name": "zammad", "trademark": "Zammad", "release": true, + "upstream": { + "image": "ghcr.io/zammad/zammad", + "releases": "https://github.com/zammad/zammad-docker-compose/releases", + "compose": { + "compose": "https://github.com/zammad/zammad-docker-compose/blob/v17.2.0/docker-compose.yml", + "env": "https://github.com/zammad/zammad-docker-compose/blob/v17.2.0/.env.dist" + }, + "docs": [ + "https://docs.zammad.org/en/latest/install/docker-compose.html", + "https://docs.zammad.org/en/latest/install/docker-compose/environment.html" + ] + }, "edition": [ { "dist": "community", "version": [ - "6.5", + "7.1", "latest" ] } ], + "access": { + "web": { + "port": 8080, + "path": "/" + } + }, "requirements": { - "cpu": "1", - "memory": "2", - "disk": "2" + "cpu": "2", + "memory": "4", + "disk": "8" }, - "upstream": { - "image": "https://hub.docker.com/r/zammad/zammad-docker-compose" + "credentials": {}, + "env": { + "first_startup_only": [ + "W9_POWER_PASSWORD", + "W9_RCODE" + ] + }, + "help": { + "db": "Bundled PostgreSQL 17 stores Zammad data; bundled Elasticsearch 9 powers search and reports." } } diff --git a/apps/zentao/.env b/apps/zentao/.env index 215dedce4..e946b0748 100644 --- a/apps/zentao/.env +++ b/apps/zentao/.env @@ -1,24 +1,51 @@ W9_REPO=easysoft/zentao -W9_DIST='community' -W9_VERSION='21.7' -W9_POWER_PASSWORD='78VCi6!ZrZ8T46xM' +W9_DIST=community +W9_VERSION=22.6 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. +W9_POWER_PASSWORD="78VCi6!ZrZ8T46xM" #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='zentao' -# Environments which for user settings when create applications -W9_HTTP_PORT_SET='9001' + +W9_ID=zentao + +# Web/internal ports: uncomment the ones the package actually uses. W9_HTTP_PORT=80 -W9_URL='example.youdomain.com' -W9_DB_EXPOSE="mysql" +W9_HTTP_PORT_SET=9001 + +# Dependency helpers: uncomment when the package bundles a dependency service. +W9_DB_EXPOSE=mysql +W9_DB_VERSION=5.7 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +W9_URL=example.youdomain.com + W9_NETWORK=websoft9 + #### ------------------------------------------------------------------------------------ #### +# ============================================================ +# ZenTao image environment variables +# Docs: https://github.com/easysoft/zentaopms +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ -##--------------- Zentao environments for user ---------------------------------------------- ## -ZT_MYSQL_HOST=$W9_ID-mysql +# Used by docker-compose.yml: +ZT_MYSQL_HOST=${W9_ID}-mysql ZT_MYSQL_PORT=3306 -# it need root for privilege + +# It needs root for privilege during the install wizard. ZT_MYSQL_USER=root -ZT_MYSQL_PASSWORD=$W9_POWER_PASSWORD -# Not suggest use W9_ID which will can not connect when wizard +ZT_MYSQL_PASSWORD=${W9_POWER_PASSWORD} ZT_MYSQL_DB=zentao + +# Not used by default; enable only when needed: +# PHP_MAX_EXECUTION_TIME= +# PHP_MAX_INPUT_VARS= +# PHP_MEMORY_LIMIT= +# PHP_POST_MAX_SIZE= +# PHP_UPLOAD_MAX_FILESIZE= diff --git a/apps/zentao/CHANGELOG.md b/apps/zentao/CHANGELOG.md index 582cf46c5..e4df29653 100644 --- a/apps/zentao/CHANGELOG.md +++ b/apps/zentao/CHANGELOG.md @@ -1,5 +1,7 @@ -# CHANGELOG +# Changelog -## Release -### Fixes and Enhancements +## 2026-09-20 +- Update ZenTao community image from `21.7` to `22.6`. +- Normalize `.env` and `docker-compose.yml` to current repository policy, including braced variable references and dependency tag management. +- Add app-local test coverage metadata for the install-wizard flow. diff --git a/apps/zentao/Notes.md b/apps/zentao/Notes.md deleted file mode 100644 index 989d330fe..000000000 --- a/apps/zentao/Notes.md +++ /dev/null @@ -1,11 +0,0 @@ -## ZenTao - -- ZenTao 需要用户自助完成安装向导流程 -- 18.8 以后无需 cmd.sh 修改密码配置 - -## FAQ - - -#### 如何修改 php 配置文件? - -目前没有环境变量方案,官方提供的路径: /etc/php/7.0/apache2 \ No newline at end of file diff --git a/apps/zentao/README.md b/apps/zentao/README.md index 876de0da0..834bd3c36 100644 --- a/apps/zentao/README.md +++ b/apps/zentao/README.md @@ -1,26 +1,84 @@ -# ZenTao on Docker +# ZenTao on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for ZenTao: +## Quick Start +### Deploy Verification - - community: 21.7, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **ZenTao**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the ZenTao admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://hub.docker.com/r/easysoft/zentao): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 4 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this ZenTao by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [ZenTao Docker image](https://hub.docker.com/r/easysoft/zentao) and makes some improvements below. -If you want use ZenTao with **Websoft9 Business Support** free, you can [subscribe ZenTao](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[ZenTao Administrator Guide](https://support.websoft9.com/docs/zentao) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 22.6, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 80 | + + +### Data Directory + + +- `zentao` → `/data` +- `mysql_data` → `/var/lib/mysql` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [ZenTao Administrator Guide](https://support.websoft9.com/docs/zentao) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/easysoft/zentao) + +- [GitHub docs](https://github.com/easysoft/zentaopms) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/zentao/docker-compose.yml b/apps/zentao/docker-compose.yml index 9e409716f..355de0599 100644 --- a/apps/zentao/docker-compose.yml +++ b/apps/zentao/docker-compose.yml @@ -1,23 +1,20 @@ -# image: https://hub.docker.com/r/easysoft/zentao -# docs: https://github.com/easysoft/zentaopms - version: '3.8' services: zentao: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} ports: - - $W9_HTTP_PORT_SET:80 + - "${W9_HTTP_PORT_SET}:80" # Web Console volumes: - zentao:/data env_file: - .env restart: unless-stopped - + mysql: - image: mysql:5.7 - container_name: $W9_ID-mysql + image: mysql:${W9_DB_VERSION} + container_name: ${W9_ID}-mysql restart: unless-stopped command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci env_file: @@ -26,13 +23,13 @@ services: - mysql_data:/var/lib/mysql environment: MYSQL_DATABASE: zentao - MYSQL_ROOT_PASSWORD: $W9_POWER_PASSWORD - + MYSQL_ROOT_PASSWORD: ${W9_POWER_PASSWORD} + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true volumes: zentao: - mysql_data: \ No newline at end of file + mysql_data: diff --git a/apps/zentao/tests/cases.yml b/apps/zentao/tests/cases.yml new file mode 100644 index 000000000..3d69311c0 --- /dev/null +++ b/apps/zentao/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: install-wizard + type: web-access + path: /install.php + expect_status: 200 diff --git a/apps/zentao/variables.json b/apps/zentao/variables.json index 2304bba6e..6ed0f020c 100644 --- a/apps/zentao/variables.json +++ b/apps/zentao/variables.json @@ -6,17 +6,24 @@ { "dist": "community", "version": [ - "21.7", + "22.6", "latest" ] } ], + "credentials": {}, "requirements": { "cpu": "1", "memory": "1", "disk": "4" }, + "env": { + "first_startup_only": [] + }, "upstream": { - "image": "https://hub.docker.com/r/easysoft/zentao" + "image": "https://hub.docker.com/r/easysoft/zentao", + "docs": [ + "https://github.com/easysoft/zentaopms" + ] } } diff --git a/apps/zulip/.env b/apps/zulip/.env index 6c0a6e491..f1f6d368a 100644 --- a/apps/zulip/.env +++ b/apps/zulip/.env @@ -1,6 +1,6 @@ -W9_REPO=zulip/docker-zulip -W9_DIST='community' -W9_VERSION=latest +W9_REPO=ghcr.io/zulip/zulip-server +W9_DIST=community +W9_VERSION=12.2-0 W9_POWER_PASSWORD=1PrMxExC45LsCT @@ -8,25 +8,49 @@ W9_HTTPS_PORT_SET=9443 W9_HTTPS_PORT=443 #### -- Not allowed to edit below environments when recreate app based on existing data -- #### + W9_ID=zulip W9_URL=example.youdomain.com W9_URL_REPLACE=true +W9_ADMIN_PATH=/login/ +W9_LOGIN_USER=admin@example.com +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} W9_NETWORK=websoft9 -W9_RCODE="UGz0IARz117ssO" +W9_RCODE=UGz0IARz117ssO #### ----------------------------------------------------------------------------------------- #### -DB_HOST=$W9_ID-postgresql -DB_HOST_PORT="5432" -DB_USER="zulip" -SSL_CERTIFICATE_GENERATION="self-signed" -SETTING_ZULIP_ADMINISTRATOR="admin@example.com" -SETTING_MEMCACHED_LOCATION=$W9_ID-memcached:11211 -SETTING_RABBITMQ_HOST=$W9_ID-rabbitmq -SETTING_REDIS_HOST=$W9_ID-redis -SECRETS_rabbitmq_password=$W9_RCODE -SECRETS_postgres_password=$W9_POWER_PASSWORD -SECRETS_memcached_password=$W9_POWER_PASSWORD -SECRETS_redis_password=$W9_POWER_PASSWORD -SECRETS_secret_key="Fz7!dJ3q@vP#2Lk5^Wn8*Rm6Tp4&Yb9^Xc1$Hj0%Ql7!Gz8@Vr2" -SETTING_EXTERNAL_HOST=$W9_URL + +# ============================================================ +# Zulip image environment variables +# Docs: https://zulip.readthedocs.io/projects/docker/en/latest/reference/environment-vars.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +SETTING_REMOTE_POSTGRES_HOST=${W9_ID}-postgresql +SETTING_MEMCACHED_LOCATION=${W9_ID}-memcached:11211 +SETTING_RABBITMQ_HOST=${W9_ID}-rabbitmq +SETTING_REDIS_HOST=${W9_ID}-redis +SETTING_ZULIP_ADMINISTRATOR=${W9_LOGIN_USER} +CERTIFICATES="self-signed" +SECRETS_rabbitmq_password=${W9_RCODE} +SECRETS_postgres_password=${W9_POWER_PASSWORD} +SECRETS_memcached_password=${W9_POWER_PASSWORD} +SECRETS_redis_password=${W9_POWER_PASSWORD} +SECRETS_secret_key="Fz7!dJ3q@vP#2Lk5^Wn8*Rm6Tp4&Hb9^Xc1Qj0%Ql7!Gz8@Vr2" +SETTING_EXTERNAL_HOST=${W9_URL} +SETTING_FAKE_EMAIL_DOMAIN="zulip.example.com" ZULIP_AUTH_BACKENDS="EmailAuthBackend" +W9_ZULIP_REALM_NAME="Default Organization" +W9_ZULIP_REALM_STRING_ID="" +W9_ZULIP_ADMIN_FULL_NAME="Administrator" + +# Not used by default; enable only when needed: +# CONFIG_application_server__queue_workers_multiprocess=False +# LOADBALANCER_IPS= +# TRUST_GATEWAY_IP=True +# SETTING_EMAIL_HOST= +# SETTING_EMAIL_HOST_USER= +# SECRETS_email_password= diff --git a/apps/zulip/CHANGELOG.md b/apps/zulip/CHANGELOG.md index 582cf46c5..70147d9b6 100644 --- a/apps/zulip/CHANGELOG.md +++ b/apps/zulip/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-18 +- Fixed the first-start organization bootstrap on the 12.x image: the password file is now written as root before being chowned to `zulip`, so the post-setup script no longer fails with `Permission denied` when the container root lacks DAC override. +- Fixed the `create_realm` call to pass all positional arguments together, so Zulip 12.2 no longer rejects the owner email and full name as unrecognized arguments. +- Declared `SETTING_FAKE_EMAIL_DOMAIN=zulip.example.com` so first-start realm creation succeeds when `W9_URL` is an IP address rather than a domain. +- Declared `W9_ADMIN_PATH=/login/` in `.env` and the matching `access` block (web `/`, admin `/login/` on port 443) in `variables.json`. +- Migrated the package from the legacy Docker Hub image line to `ghcr.io/zulip/zulip-server:12.2-0`, updating the compose wiring and 12.x environment variable names so the main container can start again. +- Switched the first-start organization bootstrap from a custom container entrypoint to an official `post-setup.d` script that creates a default organization plus owner account after Zulip finishes initialization. +- Declared first-start organization and login variables in `.env`, documented them in metadata, and normalized compose/env variable references to the braced repository style. diff --git a/apps/zulip/Notes.md b/apps/zulip/Notes.md index 82450d00d..920a46ae4 100644 --- a/apps/zulip/Notes.md +++ b/apps/zulip/Notes.md @@ -2,7 +2,18 @@ ## When installing, it is necessary to bind the domain name, otherwise the installation will fail ## After installation, it is necessary to apply for SSL certificate from nginx -## Create a Zulip organization +## Zulip 12.x packaging +- The package now uses `ghcr.io/zulip/zulip-server:12.2-0`. +- 11.x legacy env names like `DB_HOST` and `SSL_CERTIFICATE_GENERATION` are no longer valid in Zulip 12.x; use `SETTING_*`, `CONFIG_*`, and `CERTIFICATES`. + +## First startup organization bootstrap +- The package now creates a default organization and owner account automatically on the first successful startup. +- The organization name, optional subdomain, owner full name, and owner email/password come from `W9_ZULIP_REALM_NAME`, `W9_ZULIP_REALM_STRING_ID`, `W9_ZULIP_ADMIN_FULL_NAME`, `W9_LOGIN_USER`, and `W9_LOGIN_PASSWORD`. +- These values are first-start only; changing them later does not modify an existing Zulip database. +- `SETTING_FAKE_EMAIL_DOMAIN=zulip.example.com` is required because `W9_URL` may resolve to an IP address; Zulip rejects an IP as the fake email domain during owner creation. +- Admin entry is declared as `W9_ADMIN_PATH=/login/`; the app store `access` block mirrors it (web `/`, admin `/login/` on port 443). + +## Manual organization creation ``` docker exec -it container_name bash su zulip -c /home/zulip/deployments/current/manage.py generate_realm_creation_link diff --git a/apps/zulip/README.md b/apps/zulip/README.md index c065272ff..f52109908 100644 --- a/apps/zulip/README.md +++ b/apps/zulip/README.md @@ -1,26 +1,91 @@ -# Zulip on Docker +# Zulip on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Zulip: +## Quick Start +### Deploy Verification - - community: 9.1-3, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Zulip**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the Zulip admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://zulip.readthedocs.io/en/latest/production/requirements.html): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 10 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this Zulip by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Zulip Docker image](https://ghcr.io/zulip/zulip-server) and makes some improvements below. -If you want use Zulip with **Websoft9 Business Support** free, you can [subscribe Zulip](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Zulip Administrator Guide](https://support.websoft9.com/docs/zulip) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 12.2-0. + + +### Ports + +| Purpose | Port | +| --- | --- | +| HTTPS | 443 | + + +### Data Directory + + +Data is kept inside the container; a named volume is recommended for persistence. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_LOGIN_USER`, `W9_LOGIN_PASSWORD`, `W9_ZULIP_REALM_NAME`, `W9_ZULIP_REALM_STRING_ID`, `W9_ZULIP_ADMIN_FULL_NAME` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/create-default-realm.sh` to `/data/post-setup.d/10-create-default-realm.sh`. + + +## References + +- [Zulip Administrator Guide](https://support.websoft9.com/docs/zulip) by Websoft9 + +- [GHCR image](https://ghcr.io/zulip/zulip-server) + +- [Releases](https://github.com/zulip/docker-zulip/releases) + +- [GitHub docs](https://github.com/zulip/docker-zulip/blob/main/README.md) + +- [Official docs](https://zulip.readthedocs.io/projects/docker/en/latest/how-to/compose-upgrading-from-legacy.html) + +- [Official docs](https://zulip.readthedocs.io/projects/docker/en/latest/reference/environment-vars.html) + +- [Official docs](https://zulip.readthedocs.io/en/latest/production/management-commands.html) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/zulip/docker-compose.yml b/apps/zulip/docker-compose.yml index 67990e4a0..bb59c8fbd 100644 --- a/apps/zulip/docker-compose.yml +++ b/apps/zulip/docker-compose.yml @@ -1,23 +1,18 @@ -# docker:https://hub.docker.com/r/zulip/docker-zulip -# docs: https://github.com/zulip/docker-zulip/blob/main/README.md - -version: "3.8" - services: database: image: "zulip/zulip-postgresql:14" - container_name: $W9_ID-postgresql + container_name: ${W9_ID}-postgresql restart: unless-stopped environment: POSTGRES_DB: "zulip" POSTGRES_USER: "zulip" - POSTGRES_PASSWORD: $W9_POWER_PASSWORD + POSTGRES_PASSWORD: ${W9_POWER_PASSWORD} volumes: - "postgresql-14:/var/lib/postgresql/data:rw" memcached: image: "memcached:alpine" - container_name: $W9_ID-memcached + container_name: ${W9_ID}-memcached restart: unless-stopped command: - "sh" @@ -30,21 +25,25 @@ services: environment: SASL_CONF_PATH: "/home/memcache/memcached.conf" MEMCACHED_SASL_PWDB: "/home/memcache/memcached-sasl-db" - MEMCACHED_PASSWORD: $W9_POWER_PASSWORD + MEMCACHED_PASSWORD: ${W9_POWER_PASSWORD} rabbitmq: - image: "rabbitmq:3.7.7" - container_name: $W9_ID-rabbitmq + image: "rabbitmq:4.2" + container_name: ${W9_ID}-rabbitmq restart: unless-stopped - environment: - RABBITMQ_DEFAULT_USER: "zulip" - RABBITMQ_DEFAULT_PASS: $W9_RCODE + command: + - "sh" + - "-euc" + - | + echo 'default_user = zulip' >> /etc/rabbitmq/rabbitmq.conf + echo 'default_pass = ${W9_RCODE}' >> /etc/rabbitmq/rabbitmq.conf + exec docker-entrypoint.sh rabbitmq-server volumes: - "rabbitmq:/var/lib/rabbitmq:rw" redis: image: "redis:alpine" - container_name: $W9_ID-redis + container_name: ${W9_ID}-redis restart: unless-stopped command: - "sh" @@ -53,23 +52,29 @@ services: echo "requirepass '$$REDIS_PASSWORD'" > /etc/redis.conf exec redis-server /etc/redis.conf environment: - REDIS_PASSWORD: $W9_POWER_PASSWORD + REDIS_PASSWORD: ${W9_POWER_PASSWORD} volumes: - "redis:/data:rw" zulip: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped ports: - - $W9_HTTPS_PORT_SET:443 + - "${W9_HTTPS_PORT_SET}:443" # HTTPS env_file: .env volumes: - "zulip:/data:rw" + - ./src/create-default-realm.sh:/data/post-setup.d/10-create-default-realm.sh:ro ulimits: nofile: soft: 1000000 hard: 1048576 + depends_on: + - database + - memcached + - rabbitmq + - redis volumes: zulip: @@ -80,5 +85,5 @@ volumes: networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true diff --git a/apps/zulip/src/README.md b/apps/zulip/src/README.md index cdbd7a0b9..49c0c4ff5 100644 --- a/apps/zulip/src/README.md +++ b/apps/zulip/src/README.md @@ -1,3 +1,3 @@ -# About +# Local overrides -This folder includes files mount to container and used by Websoft9 +- `create-default-realm.sh` is mounted into `/data/post-setup.d/` so the official Zulip 12.x entrypoint runs it after configuration and database migrations. It creates a default organization plus owner account on first startup. diff --git a/apps/zulip/src/create-default-realm.sh b/apps/zulip/src/create-default-realm.sh new file mode 100755 index 000000000..404a7255b --- /dev/null +++ b/apps/zulip/src/create-default-realm.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -eu + +REALM_MARKER="/data/.realm-created" +ZULIP_MANAGE="/home/zulip/deployments/current/manage.py" + +if [ -f "${REALM_MARKER}" ]; then + exit 0 +fi + +if [ -z "${W9_ZULIP_REALM_NAME:-}" ] || [ -z "${W9_LOGIN_USER:-}" ] || [ -z "${W9_LOGIN_PASSWORD:-}" ]; then + echo "Skipping default Zulip organization creation because required W9_ZULIP_REALM_* or W9_LOGIN_* settings are missing." + exit 0 +fi + +if su zulip -c "${ZULIP_MANAGE} list_realms | awk 'NR > 2 && \$2 != \"zulipinternal\" { found = 1 } END { exit found ? 0 : 1 }'"; then + touch "${REALM_MARKER}" + exit 0 +fi + +password_file="$(mktemp)" +cleanup() { + rm -f "${password_file}" +} +trap cleanup EXIT INT TERM +printf '%s' "${W9_LOGIN_PASSWORD}" > "${password_file}" +chown zulip:zulip "${password_file}" +chmod 600 "${password_file}" + +echo "Creating default Zulip organization \"${W9_ZULIP_REALM_NAME}\" and owner ${W9_LOGIN_USER} ..." +su zulip -c "${ZULIP_MANAGE} create_realm \"${W9_ZULIP_REALM_NAME}\" \"${W9_LOGIN_USER}\" \"${W9_ZULIP_ADMIN_FULL_NAME:-Administrator}\" --string-id \"${W9_ZULIP_REALM_STRING_ID:-}\" --password-file \"${password_file}\"" +touch "${REALM_MARKER}" diff --git a/apps/zulip/tests/cases.yml b/apps/zulip/tests/cases.yml new file mode 100644 index 000000000..9e10fd4d4 --- /dev/null +++ b/apps/zulip/tests/cases.yml @@ -0,0 +1,7 @@ +skip: + - id: web-access + +custom: + - id: login-page + type: script + script: login-page.sh diff --git a/apps/zulip/tests/login-page.sh b/apps/zulip/tests/login-page.sh new file mode 100755 index 000000000..382f1e431 --- /dev/null +++ b/apps/zulip/tests/login-page.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -eu + +base_url="${BASE_URL:-}" +if [ -z "${base_url}" ]; then + base_url="https://127.0.0.1:${W9_HTTPS_PORT_SET}" +fi + +curl -kfsSL "${base_url}/login/" | grep -qi "zulip" diff --git a/apps/zulip/variables.json b/apps/zulip/variables.json index ba40935bf..5c52a4669 100644 --- a/apps/zulip/variables.json +++ b/apps/zulip/variables.json @@ -6,17 +6,47 @@ { "dist": "community", "version": [ - "9.1-3", - "latest" + "12.2-0" ] } ], + "access": { + "web": { + "port": 443, + "path": "/" + }, + "admin": { + "port": 443, + "path": "/login/" + } + }, "requirements": { "cpu": "2", "memory": "2", "disk": "10" }, + "credentials": {}, + "env": { + "first_startup_only": [ + "W9_LOGIN_USER", + "W9_LOGIN_PASSWORD", + "W9_ZULIP_REALM_NAME", + "W9_ZULIP_REALM_STRING_ID", + "W9_ZULIP_ADMIN_FULL_NAME" + ] + }, + "help": { + "db": "Bundled PostgreSQL, RabbitMQ, Redis, and Memcached credentials are initialized from the first-start settings on the initial deployment.", + "login": "On first startup the package creates a default Zulip organization and owner account from W9_ZULIP_REALM_* and W9_LOGIN_*; later edits do not backfill existing data." + }, "upstream": { - "image": "https://hub.docker.com/r/zulip/docker-zulip" + "image": "https://ghcr.io/zulip/zulip-server", + "releases": "https://github.com/zulip/docker-zulip/releases", + "docs": [ + "https://github.com/zulip/docker-zulip/blob/main/README.md", + "https://zulip.readthedocs.io/projects/docker/en/latest/how-to/compose-upgrading-from-legacy.html", + "https://zulip.readthedocs.io/projects/docker/en/latest/reference/environment-vars.html", + "https://zulip.readthedocs.io/en/latest/production/management-commands.html" + ] } } diff --git a/apps/v2ray/.env b/archive/apps/v2ray/.env similarity index 100% rename from apps/v2ray/.env rename to archive/apps/v2ray/.env diff --git a/apps/v2ray/CHANGELOG.md b/archive/apps/v2ray/CHANGELOG.md similarity index 100% rename from apps/v2ray/CHANGELOG.md rename to archive/apps/v2ray/CHANGELOG.md diff --git a/apps/v2ray/Notes.md b/archive/apps/v2ray/Notes.md similarity index 100% rename from apps/v2ray/Notes.md rename to archive/apps/v2ray/Notes.md diff --git a/apps/v2ray/README.md b/archive/apps/v2ray/README.md similarity index 100% rename from apps/v2ray/README.md rename to archive/apps/v2ray/README.md diff --git a/apps/v2ray/docker-compose.yml b/archive/apps/v2ray/docker-compose.yml similarity index 100% rename from apps/v2ray/docker-compose.yml rename to archive/apps/v2ray/docker-compose.yml diff --git a/apps/v2ray/src/README.md b/archive/apps/v2ray/src/README.md similarity index 100% rename from apps/v2ray/src/README.md rename to archive/apps/v2ray/src/README.md diff --git a/apps/v2ray/src/config.json b/archive/apps/v2ray/src/config.json similarity index 100% rename from apps/v2ray/src/config.json rename to archive/apps/v2ray/src/config.json diff --git a/apps/v2ray/variables.json b/archive/apps/v2ray/variables.json similarity index 100% rename from apps/v2ray/variables.json rename to archive/apps/v2ray/variables.json diff --git a/apps/webcheck/.env b/archive/apps/webcheck/.env similarity index 100% rename from apps/webcheck/.env rename to archive/apps/webcheck/.env diff --git a/apps/webcheck/CHANGELOG.md b/archive/apps/webcheck/CHANGELOG.md similarity index 100% rename from apps/webcheck/CHANGELOG.md rename to archive/apps/webcheck/CHANGELOG.md diff --git a/apps/webcheck/Notes.md b/archive/apps/webcheck/Notes.md similarity index 100% rename from apps/webcheck/Notes.md rename to archive/apps/webcheck/Notes.md diff --git a/apps/webcheck/README.md b/archive/apps/webcheck/README.md similarity index 100% rename from apps/webcheck/README.md rename to archive/apps/webcheck/README.md diff --git a/apps/webcheck/docker-compose.yml b/archive/apps/webcheck/docker-compose.yml similarity index 100% rename from apps/webcheck/docker-compose.yml rename to archive/apps/webcheck/docker-compose.yml diff --git a/apps/webcheck/src/README.md b/archive/apps/webcheck/src/README.md similarity index 100% rename from apps/webcheck/src/README.md rename to archive/apps/webcheck/src/README.md diff --git a/apps/wazuh/src/nginx-proxy.conf.template b/archive/apps/webcheck/src/nginx-proxy.conf.template similarity index 100% rename from apps/wazuh/src/nginx-proxy.conf.template rename to archive/apps/webcheck/src/nginx-proxy.conf.template diff --git a/apps/wazuh/src/php_exra.ini b/archive/apps/webcheck/src/php_exra.ini similarity index 100% rename from apps/wazuh/src/php_exra.ini rename to archive/apps/webcheck/src/php_exra.ini diff --git a/apps/webcheck/variables.json b/archive/apps/webcheck/variables.json similarity index 100% rename from apps/webcheck/variables.json rename to archive/apps/webcheck/variables.json diff --git a/apps/wikijs/.env b/archive/apps/wikijs/.env similarity index 100% rename from apps/wikijs/.env rename to archive/apps/wikijs/.env diff --git a/apps/wikijs/CHANGELOG.md b/archive/apps/wikijs/CHANGELOG.md similarity index 100% rename from apps/wikijs/CHANGELOG.md rename to archive/apps/wikijs/CHANGELOG.md diff --git a/apps/wikijs/Notes.md b/archive/apps/wikijs/Notes.md similarity index 100% rename from apps/wikijs/Notes.md rename to archive/apps/wikijs/Notes.md diff --git a/apps/wikijs/README.md b/archive/apps/wikijs/README.md similarity index 100% rename from apps/wikijs/README.md rename to archive/apps/wikijs/README.md diff --git a/apps/wikijs/docker-compose.yml b/archive/apps/wikijs/docker-compose.yml similarity index 100% rename from apps/wikijs/docker-compose.yml rename to archive/apps/wikijs/docker-compose.yml diff --git a/apps/wikijs/src/README.md b/archive/apps/wikijs/src/README.md similarity index 100% rename from apps/wikijs/src/README.md rename to archive/apps/wikijs/src/README.md diff --git a/apps/wikijs/variables.json b/archive/apps/wikijs/variables.json similarity index 100% rename from apps/wikijs/variables.json rename to archive/apps/wikijs/variables.json diff --git a/apps/windmill/.env b/archive/apps/windmill/.env similarity index 100% rename from apps/windmill/.env rename to archive/apps/windmill/.env diff --git a/apps/windmill/CHANGELOG.md b/archive/apps/windmill/CHANGELOG.md similarity index 100% rename from apps/windmill/CHANGELOG.md rename to archive/apps/windmill/CHANGELOG.md diff --git a/apps/windmill/Notes.md b/archive/apps/windmill/Notes.md similarity index 100% rename from apps/windmill/Notes.md rename to archive/apps/windmill/Notes.md diff --git a/apps/windmill/README.md b/archive/apps/windmill/README.md similarity index 100% rename from apps/windmill/README.md rename to archive/apps/windmill/README.md diff --git a/apps/windmill/docker-compose.yml b/archive/apps/windmill/docker-compose.yml similarity index 100% rename from apps/windmill/docker-compose.yml rename to archive/apps/windmill/docker-compose.yml diff --git a/apps/windmill/src/Caddyfile b/archive/apps/windmill/src/Caddyfile similarity index 100% rename from apps/windmill/src/Caddyfile rename to archive/apps/windmill/src/Caddyfile diff --git a/apps/windmill/src/README.md b/archive/apps/windmill/src/README.md similarity index 100% rename from apps/windmill/src/README.md rename to archive/apps/windmill/src/README.md diff --git a/apps/windmill/variables.json b/archive/apps/windmill/variables.json similarity index 100% rename from apps/windmill/variables.json rename to archive/apps/windmill/variables.json diff --git a/apps/wireguard/.env b/archive/apps/wireguard/.env similarity index 100% rename from apps/wireguard/.env rename to archive/apps/wireguard/.env diff --git a/apps/wireguard/CHANGELOG.md b/archive/apps/wireguard/CHANGELOG.md similarity index 100% rename from apps/wireguard/CHANGELOG.md rename to archive/apps/wireguard/CHANGELOG.md diff --git a/apps/wireguard/Notes.md b/archive/apps/wireguard/Notes.md similarity index 100% rename from apps/wireguard/Notes.md rename to archive/apps/wireguard/Notes.md diff --git a/apps/wireguard/README.md b/archive/apps/wireguard/README.md similarity index 100% rename from apps/wireguard/README.md rename to archive/apps/wireguard/README.md diff --git a/apps/wireguard/docker-compose.yml b/archive/apps/wireguard/docker-compose.yml similarity index 100% rename from apps/wireguard/docker-compose.yml rename to archive/apps/wireguard/docker-compose.yml diff --git a/apps/wireguard/getkeys.sh b/archive/apps/wireguard/getkeys.sh similarity index 100% rename from apps/wireguard/getkeys.sh rename to archive/apps/wireguard/getkeys.sh diff --git a/apps/wireguard/src/README.md b/archive/apps/wireguard/src/README.md similarity index 100% rename from apps/wireguard/src/README.md rename to archive/apps/wireguard/src/README.md diff --git a/apps/wireguard/src/nginx_proxy.conf b/archive/apps/wireguard/src/nginx_proxy.conf similarity index 100% rename from apps/wireguard/src/nginx_proxy.conf rename to archive/apps/wireguard/src/nginx_proxy.conf diff --git a/apps/wireguard/variables.json b/archive/apps/wireguard/variables.json similarity index 100% rename from apps/wireguard/variables.json rename to archive/apps/wireguard/variables.json diff --git a/apps/zabbix/.env b/archive/apps/zabbix/.env similarity index 100% rename from apps/zabbix/.env rename to archive/apps/zabbix/.env diff --git a/apps/zabbix/CHANGELOG.md b/archive/apps/zabbix/CHANGELOG.md similarity index 100% rename from apps/zabbix/CHANGELOG.md rename to archive/apps/zabbix/CHANGELOG.md diff --git a/apps/zabbix/Notes.md b/archive/apps/zabbix/Notes.md similarity index 100% rename from apps/zabbix/Notes.md rename to archive/apps/zabbix/Notes.md diff --git a/apps/zabbix/README.md b/archive/apps/zabbix/README.md similarity index 100% rename from apps/zabbix/README.md rename to archive/apps/zabbix/README.md diff --git a/apps/zabbix/docker-compose.yml b/archive/apps/zabbix/docker-compose.yml similarity index 100% rename from apps/zabbix/docker-compose.yml rename to archive/apps/zabbix/docker-compose.yml diff --git a/apps/zabbix/src/README.md b/archive/apps/zabbix/src/README.md similarity index 100% rename from apps/zabbix/src/README.md rename to archive/apps/zabbix/src/README.md diff --git a/apps/zabbix/variables.json b/archive/apps/zabbix/variables.json similarity index 100% rename from apps/zabbix/variables.json rename to archive/apps/zabbix/variables.json diff --git a/apps/zerotier/.env b/archive/apps/zerotier/.env similarity index 100% rename from apps/zerotier/.env rename to archive/apps/zerotier/.env diff --git a/apps/zerotier/CHANGELOG.md b/archive/apps/zerotier/CHANGELOG.md similarity index 100% rename from apps/zerotier/CHANGELOG.md rename to archive/apps/zerotier/CHANGELOG.md diff --git a/apps/zerotier/Notes.md b/archive/apps/zerotier/Notes.md similarity index 100% rename from apps/zerotier/Notes.md rename to archive/apps/zerotier/Notes.md diff --git a/apps/zerotier/README.md b/archive/apps/zerotier/README.md similarity index 100% rename from apps/zerotier/README.md rename to archive/apps/zerotier/README.md diff --git a/apps/zerotier/docker-compose.yml b/archive/apps/zerotier/docker-compose.yml similarity index 100% rename from apps/zerotier/docker-compose.yml rename to archive/apps/zerotier/docker-compose.yml diff --git a/apps/zerotier/src/README.md b/archive/apps/zerotier/src/README.md similarity index 100% rename from apps/zerotier/src/README.md rename to archive/apps/zerotier/src/README.md diff --git a/apps/zerotier/variables.json b/archive/apps/zerotier/variables.json similarity index 100% rename from apps/zerotier/variables.json rename to archive/apps/zerotier/variables.json diff --git a/apps/zitadel/.env b/archive/apps/zitadel/.env similarity index 100% rename from apps/zitadel/.env rename to archive/apps/zitadel/.env diff --git a/apps/zitadel/CHANGELOG.md b/archive/apps/zitadel/CHANGELOG.md similarity index 100% rename from apps/zitadel/CHANGELOG.md rename to archive/apps/zitadel/CHANGELOG.md diff --git a/apps/zitadel/Notes.md b/archive/apps/zitadel/Notes.md similarity index 100% rename from apps/zitadel/Notes.md rename to archive/apps/zitadel/Notes.md diff --git a/apps/zitadel/README.md b/archive/apps/zitadel/README.md similarity index 100% rename from apps/zitadel/README.md rename to archive/apps/zitadel/README.md diff --git a/apps/zitadel/docker-compose.yml b/archive/apps/zitadel/docker-compose.yml similarity index 100% rename from apps/zitadel/docker-compose.yml rename to archive/apps/zitadel/docker-compose.yml diff --git a/apps/zitadel/src/README.md b/archive/apps/zitadel/src/README.md similarity index 100% rename from apps/zitadel/src/README.md rename to archive/apps/zitadel/src/README.md diff --git a/apps/zitadel/variables.json b/archive/apps/zitadel/variables.json similarity index 100% rename from apps/zitadel/variables.json rename to archive/apps/zitadel/variables.json diff --git a/apps/zookeeper/.env b/archive/apps/zookeeper/.env similarity index 100% rename from apps/zookeeper/.env rename to archive/apps/zookeeper/.env diff --git a/apps/zookeeper/CHANGELOG.md b/archive/apps/zookeeper/CHANGELOG.md similarity index 100% rename from apps/zookeeper/CHANGELOG.md rename to archive/apps/zookeeper/CHANGELOG.md diff --git a/apps/zookeeper/Notes.md b/archive/apps/zookeeper/Notes.md similarity index 100% rename from apps/zookeeper/Notes.md rename to archive/apps/zookeeper/Notes.md diff --git a/apps/zookeeper/README.md b/archive/apps/zookeeper/README.md similarity index 100% rename from apps/zookeeper/README.md rename to archive/apps/zookeeper/README.md diff --git a/apps/zookeeper/docker-compose.yml b/archive/apps/zookeeper/docker-compose.yml similarity index 100% rename from apps/zookeeper/docker-compose.yml rename to archive/apps/zookeeper/docker-compose.yml diff --git a/apps/zookeeper/src/README.md b/archive/apps/zookeeper/src/README.md similarity index 100% rename from apps/zookeeper/src/README.md rename to archive/apps/zookeeper/src/README.md diff --git a/apps/zookeeper/variables.json b/archive/apps/zookeeper/variables.json similarity index 100% rename from apps/zookeeper/variables.json rename to archive/apps/zookeeper/variables.json diff --git a/cli/README.md b/cli/README.md index 811694445..4d57c5348 100644 --- a/cli/README.md +++ b/cli/README.md @@ -34,7 +34,7 @@ Windows PowerShell: Or without activating: ```bash -make libs ARGS="check --app wordpress --json" +make libs ARGS="app-check --app wordpress --json" ``` One-off without install: diff --git a/i18n/translation.json b/i18n/translation.json index 790e36a43..a16bfc26c 100644 --- a/i18n/translation.json +++ b/i18n/translation.json @@ -7,10 +7,18 @@ "App HTTP Port", "应用 HTTP 端口" ], + "W9_GRPC_PORT_SET": [ + "", + "" + ], "W9_AGENT_PORT_SET": [ "App Agent Port", "应用代理端口" ], + "W9_ENROLLMENT_PORT_SET": [ + "", + "" + ], "W9_SSH_PORT_SET": [ "App SSH Port", "应用 SSH 端口" diff --git a/list-check.md b/list-check.md new file mode 100644 index 000000000..6767c9af2 --- /dev/null +++ b/list-check.md @@ -0,0 +1,327 @@ +# List Check + +- Source app list: `.venv/bin/libs list --json` +- Data source: `https://theirstack.com/en/technology/` +- Total apps checked: 317 +- Found on TheirStack: 174 +- Not found on TheirStack: 143 + +| App | TheirStack Slug | Users/Companies | Status | Notes | +| --- | --- | ---: | --- | --- | +| `activemq` | `activemq` | 7,438 | found | | +| `activepieces` | | | not found | No TheirStack technology slug matched this app name. | +| `affine` | `affine` | 7,901 | found | | +| `airbyte` | | | not found | No TheirStack technology slug matched this app name. | +| `airflow` | `airflow` | 68,961 | found | | +| `akeneo` | `akeneo` | 1,577 | found | | +| `anythingllm` | | | not found | No TheirStack technology slug matched this app name. | +| `apache` | | | not found | No TheirStack technology slug matched this app name. | +| `apisix` | | | not found | No TheirStack technology slug matched this app name. | +| `appsmith` | `appsmith` | 252 | found | | +| `arangodb` | `arangodb` | 900 | found | | +| `bigbluebutton` | `bigbluebutton` | 219 | found | | +| `bitwarden` | `bitwarden` | 465 | found | | +| `bookstack` | | | not found | No TheirStack technology slug matched this app name. | +| `browserless` | | | not found | No TheirStack technology slug matched this app name. | +| `budibase` | `budibase` | 72 | found | | +| `bunkerweb` | | | not found | No TheirStack technology slug matched this app name. | +| `bytebase` | `bytebase` | 16 | found | | +| `caddy` | `caddy` | 2,508 | found | | +| `canvas` | `canvas` | 8,787 | found | | +| `chatwoot` | `chatwoot` | 194 | found | | +| `checkmate` | | | not found | No TheirStack technology slug matched this app name. | +| `chroma` | `chroma` | 4,614 | found | | +| `ckan` | | | not found | No TheirStack technology slug matched this app name. | +| `clamav` | `clamav` | 280 | found | | +| `clickhouse` | `clickhouse` | 8,053 | found | | +| `cloudbeaver` | | | not found | No TheirStack technology slug matched this app name. | +| `cloudreve` | | | not found | No TheirStack technology slug matched this app name. | +| `cmak` | | | not found | No TheirStack technology slug matched this app name. | +| `codeserver` | | | not found | No TheirStack technology slug matched this app name. | +| `collabora` | | | not found | No TheirStack technology slug matched this app name. | +| `commafeed` | | | not found | No TheirStack technology slug matched this app name. | +| `compreface` | | | not found | No TheirStack technology slug matched this app name. | +| `consul` | `consul` | 4,831 | found | | +| `coredns` | `coredns` | 255 | found | | +| `couchbase` | `couchbase` | 4,086 | found | | +| `couchdb` | `couchdb` | 2,014 | found | | +| `countly` | `countly` | 37 | found | | +| `coze` | | | not found | No TheirStack technology slug matched this app name. | +| `crowdsec` | | | not found | No TheirStack technology slug matched this app name. | +| `dashy` | | | not found | No TheirStack technology slug matched this app name. | +| `databasus` | | | not found | No TheirStack technology slug matched this app name. | +| `datahub` | `datahub` | 3,580 | found | | +| `ddnsgo` | | | not found | No TheirStack technology slug matched this app name. | +| `debezium` | `debezium` | 2,188 | found | | +| `dgraph` | `dgraph` | 220 | found | | +| `dify` | | | not found | No TheirStack technology slug matched this app name. | +| `directus` | `directus` | 503 | found | | +| `discourse` | `discourse` | 7,420 | found | | +| `django` | `django` | 70,614 | found | | +| `docker` | `docker` | 260,811 | found | | +| `dockermailserver` | | | not found | No TheirStack technology slug matched this app name. | +| `docusaurus` | `docusaurus` | 602 | found | | +| `docuseal` | | | not found | No TheirStack technology slug matched this app name. | +| `dolibarr` | `dolibarr` | 406 | found | | +| `doris` | | | not found | No TheirStack technology slug matched this app name. | +| `dotnet` | `dot-net` | 277,871 | found | Used normalized slug match `dot-net`. TheirStack page title: .NET. | +| `drawio` | `drawio` | 537 | found | | +| `drupal` | `drupal` | 29,292 | found | | +| `duplicati` | | | not found | No TheirStack technology slug matched this app name. | +| `ejbca` | | | not found | No TheirStack technology slug matched this app name. | +| `elasticsearch` | `elasticsearch` | 45,768 | found | | +| `emqx` | `emqx` | 233 | found | | +| `erpnext` | `erpnext` | 2,798 | found | | +| `espocrm` | `espo-crm` | 53 | found | Used normalized slug match `espo-crm`. | +| `excalidraw` | `excalidraw` | 154 | found | | +| `falcon` | `falcon` | 9,126 | found | | +| `ffmpeg` | `ffmpeg` | 3,550 | found | | +| `filepizza` | | | not found | No TheirStack technology slug matched this app name. | +| `filerun` | | | not found | No TheirStack technology slug matched this app name. | +| `firecrawl` | | | not found | No TheirStack technology slug matched this app name. | +| `flowise` | | | not found | No TheirStack technology slug matched this app name. | +| `fluentbit` | `fluent-bit` | 1,360 | found | Used normalized slug match `fluent-bit`. | +| `fluentd` | `fluentd` | 2,596 | found | | +| `focalboard` | `focalboard` | 1 | found | | +| `formbricks` | | | not found | No TheirStack technology slug matched this app name. | +| `frappe` | | | not found | No TheirStack technology slug matched this app name. | +| `freshrss` | | | not found | No TheirStack technology slug matched this app name. | +| `frigate` | | | not found | No TheirStack technology slug matched this app name. | +| `frp` | | | not found | No TheirStack technology slug matched this app name. | +| `geoserver` | `geoserver` | 2,153 | found | | +| `ghost` | `ghost` | 3,859 | found | | +| `gitea` | `gitea` | 580 | found | | +| `gitlab` | `gitlab` | 105,721 | found | | +| `go` | | | not found | No TheirStack technology slug matched this app name. | +| `gocd` | `go-cd` | 1,100 | found | Used normalized slug match `go-cd`. | +| `grafana` | `grafana` | 79,690 | found | | +| `graylog` | `graylog` | 3,404 | found | | +| `haproxy` | `haproxy` | 5,986 | found | | +| `headscale` | | | not found | No TheirStack technology slug matched this app name. | +| `hermes` | `hermes` | 3,382 | found | | +| `homeassistant` | `home-assistant` | 1,242 | found | Used normalized slug match `home-assistant`. | +| `immich` | | | not found | No TheirStack technology slug matched this app name. | +| `influxdb` | `influxdb` | 6,096 | found | | +| `jellyfin` | | | not found | No TheirStack technology slug matched this app name. | +| `jenkins` | `jenkins` | 124,245 | found | | +| `jihu` | | | not found | No TheirStack technology slug matched this app name. | +| `jitsi` | `jitsi` | 353 | found | | +| `jitsu` | `jitsu` | 1,114 | found | | +| `jupyterhub` | | | not found | No TheirStack technology slug matched this app name. | +| `k0s` | `k0s` | 76 | found | | +| `kafka` | `kafka` | 92,263 | found | | +| `kasmweb` | | | not found | No TheirStack technology slug matched this app name. | +| `kavita` | | | not found | No TheirStack technology slug matched this app name. | +| `keep` | | | not found | No TheirStack technology slug matched this app name. | +| `keila` | | | not found | No TheirStack technology slug matched this app name. | +| `kener` | | | not found | No TheirStack technology slug matched this app name. | +| `keploy` | `keploy` | 6 | found | | +| `kestra` | | | not found | No TheirStack technology slug matched this app name. | +| `keycloak` | `keycloak` | 8,790 | found | | +| `khoj` | | | not found | No TheirStack technology slug matched this app name. | +| `kibana` | `kibana` | 21,045 | found | | +| `killbill` | `killbill` | 10 | found | | +| `knowage` | `knowage` | 27 | found | | +| `kodbox` | | | not found | No TheirStack technology slug matched this app name. | +| `koishi` | | | not found | No TheirStack technology slug matched this app name. | +| `komga` | | | not found | No TheirStack technology slug matched this app name. | +| `kong` | `kong` | 4,851 | found | | +| `kopia` | | | not found | No TheirStack technology slug matched this app name. | +| `lago` | | | not found | No TheirStack technology slug matched this app name. | +| `langflow` | | | not found | No TheirStack technology slug matched this app name. | +| `langfuse` | | | not found | No TheirStack technology slug matched this app name. | +| `laravel` | `laravel` | 74,194 | found | | +| `lemmy` | | | not found | No TheirStack technology slug matched this app name. | +| `librechat` | | | not found | No TheirStack technology slug matched this app name. | +| `linkwarden` | | | not found | No TheirStack technology slug matched this app name. | +| `listmonk` | `listmonk` | 14 | found | | +| `litellm` | | | not found | No TheirStack technology slug matched this app name. | +| `lobehub` | | | not found | No TheirStack technology slug matched this app name. | +| `localai` | | | not found | No TheirStack technology slug matched this app name. | +| `logstash` | `logstash` | 9,217 | found | | +| `mage` | | | not found | No TheirStack technology slug matched this app name. | +| `mailu` | | | not found | No TheirStack technology slug matched this app name. | +| `manageiq` | `manageiq` | 72 | found | | +| `mariadb` | `mariadb` | 23,672 | found | | +| `mathesar` | | | not found | No TheirStack technology slug matched this app name. | +| `matlab` | `matlab` | 56,344 | found | | +| `matomo` | `matomo` | 3,632 | found | | +| `mattermost` | `mattermost` | 766 | found | | +| `mautic` | `mautic` | 466 | found | | +| `maven` | | | not found | No TheirStack technology slug matched this app name. | +| `meilisearch` | `meilisearch` | 305 | found | | +| `memadmin` | | | not found | No TheirStack technology slug matched this app name. | +| `memcached` | `memcached` | 6,681 | found | | +| `metabase` | `metabase` | 10,567 | found | | +| `milvus` | `milvus` | 5,933 | found | | +| `miniflux` | | | not found | No TheirStack technology slug matched this app name. | +| `minio` | `minio` | 3,112 | found | | +| `mixpost` | | | not found | No TheirStack technology slug matched this app name. | +| `mlflow` | `mlflow` | 23,636 | found | | +| `modsecurity` | `modsecurity` | 288 | found | | +| `mongocompass` | | | not found | No TheirStack technology slug matched this app name. | +| `mongodb` | `mongodb` | 136,560 | found | | +| `mongoexpress` | | | not found | No TheirStack technology slug matched this app name. | +| `monkeycode` | | | not found | No TheirStack technology slug matched this app name. | +| `moodle` | `moodle` | 14,256 | found | | +| `mosquitto` | `mosquitto` | 297 | found | | +| `mqttx` | | | not found | No TheirStack technology slug matched this app name. | +| `mysql` | `mysql` | 246,670 | found | | +| `mysqlworkbench` | `mysql-workbench` | 983 | found | Used normalized slug match `mysql-workbench`. | +| `n8n` | `n8n` | 32,448 | found | | +| `navidrome` | | | not found | No TheirStack technology slug matched this app name. | +| `neko` | | | not found | No TheirStack technology slug matched this app name. | +| `neo4j` | `neo4j` | 10,563 | found | | +| `netdata` | `netdata` | 258 | found | | +| `nextchat` | | | not found | No TheirStack technology slug matched this app name. | +| `nextcloud` | `nextcloud` | 1,251 | found | | +| `nexterm` | | | not found | No TheirStack technology slug matched this app name. | +| `nextjs` | `next-js` | 75,804 | found | Used normalized slug match `next-js`. | +| `nextterminal` | | | not found | No TheirStack technology slug matched this app name. | +| `nexus` | | | not found | No TheirStack technology slug matched this app name. | +| `nginx` | `nginx` | 35,744 | found | | +| `nginxproxymanager` | | | not found | No TheirStack technology slug matched this app name. | +| `nifi` | | | not found | No TheirStack technology slug matched this app name. | +| `nocobase` | | | not found | No TheirStack technology slug matched this app name. | +| `nocodb` | `nocodb` | 75 | found | | +| `nodejs` | `nodejs` | 257,523 | found | | +| `nodered` | `nodered` | 1,456 | found | | +| `nomad` | | | not found | No TheirStack technology slug matched this app name. | +| `nopcommerce` | `nopcommerce` | 300 | found | | +| `odoo` | `odoo` | 41,878 | found | | +| `ollama` | | | not found | No TheirStack technology slug matched this app name. | +| `oneapi` | | | not found | No TheirStack technology slug matched this app name. | +| `onlyofficedocs` | | | not found | No TheirStack technology slug matched this app name. | +| `onyx` | | | not found | No TheirStack technology slug matched this app name. | +| `openclaw` | `openclaw` | 1,281 | found | | +| `opencost` | `opencost` | 211 | found | | +| `opencv` | `opencv` | 16,919 | found | | +| `opengauss` | | | not found | No TheirStack technology slug matched this app name. | +| `openhands` | | | not found | No TheirStack technology slug matched this app name. | +| `openjdk` | `openjdk` | 677 | found | | +| `openproject` | `openproject` | 405 | found | | +| `opensearch` | `opensearch` | 14,172 | found | | +| `opentelemetry` | `opentelemetry` | 18,242 | found | | +| `openwebui` | | | not found | No TheirStack technology slug matched this app name. | +| `oracle` | `oracle` | 249,952 | found | | +| `outline` | | | not found | No TheirStack technology slug matched this app name. | +| `owncloud` | `owncloud` | 203 | found | | +| `palworld` | | | not found | No TheirStack technology slug matched this app name. | +| `pangolin` | | | not found | No TheirStack technology slug matched this app name. | +| `paperlessngx` | | | not found | No TheirStack technology slug matched this app name. | +| `papermc` | | | not found | No TheirStack technology slug matched this app name. | +| `passbolt` | `passbolt` | 54 | found | | +| `penpot` | `penpot` | 217 | found | | +| `pgadmin` | | | not found | No TheirStack technology slug matched this app name. | +| `pgvector` | | | not found | No TheirStack technology slug matched this app name. | +| `photoprism` | | | not found | No TheirStack technology slug matched this app name. | +| `php` | `php` | 240,406 | found | | +| `phpfpmapache` | | | not found | No TheirStack technology slug matched this app name. | +| `phpfpmnginx` | | | not found | No TheirStack technology slug matched this app name. | +| `phpmyadmin` | `phpmyadmin` | 1,039 | found | | +| `pimcore` | `pimcore` | 1,242 | found | | +| `plane` | | | not found | No TheirStack technology slug matched this app name. | +| `plausible` | `plausible` | 264 | found | | +| `plex` | | | not found | No TheirStack technology slug matched this app name. | +| `pmm` | | | not found | No TheirStack technology slug matched this app name. | +| `pocketbase` | | | not found | No TheirStack technology slug matched this app name. | +| `portainer` | `portainer` | 698 | found | | +| `portkey` | | | not found | No TheirStack technology slug matched this app name. | +| `posteio` | | | not found | No TheirStack technology slug matched this app name. | +| `postgresql` | `postgresql` | 230,506 | found | | +| `postgrest` | `postgrest` | 182 | found | | +| `posthog` | `posthog` | 3,129 | found | | +| `prestashop` | `prestashop` | 8,959 | found | | +| `prometheus` | `prometheus` | 68,111 | found | | +| `puter` | | | not found | No TheirStack technology slug matched this app name. | +| `pydio` | | | not found | No TheirStack technology slug matched this app name. | +| `python` | `python` | 567,319 | found | | +| `rabbitmq` | `rabbitmq` | 48,703 | found | | +| `ragflow` | | | not found | No TheirStack technology slug matched this app name. | +| `rancher` | `rancher` | 8,404 | found | | +| `rclone` | | | not found | No TheirStack technology slug matched this app name. | +| `redash` | `redash` | 1,275 | found | | +| `redis` | `redis` | 82,959 | found | | +| `redisinsight` | | | not found | No TheirStack technology slug matched this app name. | +| `redmine` | `redmine` | 5,809 | found | | +| `redpandaconsole` | | | not found | No TheirStack technology slug matched this app name. | +| `registry` | | | not found | No TheirStack technology slug matched this app name. | +| `rethinkdb` | `rethinkdb` | 97 | found | | +| `rocketchat` | `rocketchat` | 153 | found | | +| `rocketmq` | `rocketmq` | 24 | found | TheirStack page title: Apache RocketMQ. | +| `rowy` | | | not found | No TheirStack technology slug matched this app name. | +| `ruby` | `ruby` | 61,812 | found | | +| `rudderstack` | `rudderstack` | 615 | found | | +| `rundeck` | `rundeck` | 2,741 | found | | +| `runtime` | | | not found | No TheirStack technology slug matched this app name. | +| `rustdesk` | | | not found | No TheirStack technology slug matched this app name. | +| `safeline` | | | not found | No TheirStack technology slug matched this app name. | +| `sakai` | | | not found | No TheirStack technology slug matched this app name. | +| `saleor` | `saleor` | 64 | found | | +| `scmmanager` | | | not found | No TheirStack technology slug matched this app name. | +| `seafile` | `seafile` | 62 | found | | +| `selenium` | `selenium` | 84,299 | found | | +| `semaphore` | `semaphore` | 631 | found | | +| `signoz` | | | not found | No TheirStack technology slug matched this app name. | +| `snapdrop` | | | not found | No TheirStack technology slug matched this app name. | +| `snipeit` | `snipeit` | 161 | found | | +| `sonarqube` | `sonarqube` | 24,054 | found | | +| `springboot` | `spring-boot` | 82,535 | found | Used normalized slug match `spring-boot`. | +| `sqlserver` | | | not found | No TheirStack technology slug matched this app name. | +| `squid` | `squid` | 1,834 | found | | +| `srs` | | | not found | No TheirStack technology slug matched this app name. | +| `stirlingpdf` | | | not found | No TheirStack technology slug matched this app name. | +| `strapi` | `strapi` | 3,897 | found | | +| `streampipes` | | | not found | No TheirStack technology slug matched this app name. | +| `suitecrm` | `suitecrm` | 331 | found | | +| `supabase` | `supabase` | 10,696 | found | | +| `superset` | `superset` | 5,329 | found | | +| `supertokens` | `super-tokens` | 29 | found | Used normalized slug match `super-tokens`. | +| `syncthing` | `syncthing` | 10 | found | | +| `taskingai` | | | not found | No TheirStack technology slug matched this app name. | +| `teamcity` | `teamcity` | 9,117 | found | | +| `teleport` | `teleport` | 702 | found | | +| `tensorflow` | `tensorflow` | 81,596 | found | | +| `theia` | `theia` | 352 | found | | +| `thingsboard` | | | not found | No TheirStack technology slug matched this app name. | +| `tinyproxy` | | | not found | No TheirStack technology slug matched this app name. | +| `tomcat` | `tomcat` | 16,288 | found | TheirStack page title: Apache Tomcat. | +| `tomee` | | | not found | No TheirStack technology slug matched this app name. | +| `tooljet` | `tooljet` | 34 | found | | +| `traccar` | | | not found | No TheirStack technology slug matched this app name. | +| `traefik` | `traefik` | 1,876 | found | | +| `trafficserver` | | | not found | No TheirStack technology slug matched this app name. | +| `triggerdev` | `trigger-dev` | 167 | found | Used normalized slug match `trigger-dev`. | +| `trivy` | `trivy` | 3,498 | found | | +| `ttrss` | | | not found | No TheirStack technology slug matched this app name. | +| `twenty` | | | not found | No TheirStack technology slug matched this app name. | +| `tyk` | `tyk` | 846 | found | | +| `typesense` | `typesense` | 261 | found | | +| `typo3` | `typo3` | 7,813 | found | | +| `umami` | `umami` | 421 | found | | +| `umbraco` | `umbraco` | 4,193 | found | | +| `unleash` | | | not found | No TheirStack technology slug matched this app name. | +| `uptimekuma` | `uptime-kuma` | 101 | found | Used normalized slug match `uptime-kuma`. | +| `v2ray` | | | not found | No TheirStack technology slug matched this app name. | +| `varnish` | `varnish` | 3,201 | found | | +| `vault` | `vault` | 45,237 | found | | +| `vaultwarden` | | | not found | No TheirStack technology slug matched this app name. | +| `vespa` | `vespa` | 915 | found | | +| `wazuh` | `wazuh` | 2,500 | found | | +| `weaviate` | | | not found | No TheirStack technology slug matched this app name. | +| `webcheck` | | | not found | No TheirStack technology slug matched this app name. | +| `wikijs` | `wiki-js` | 54 | found | Used normalized slug match `wiki-js`. | +| `windmill` | | | not found | No TheirStack technology slug matched this app name. | +| `wireguard` | | | not found | No TheirStack technology slug matched this app name. | +| `woocommerce` | `woocommerce` | 34,509 | found | | +| `wordpress` | `wordpress` | 295,062 | found | | +| `wordpresspro` | | | not found | No TheirStack technology slug matched this app name. | +| `xwiki` | `xwiki` | 170 | found | | +| `youtrack` | `youtrack` | 1,153 | found | | +| `zabbix` | `zabbix` | 23,050 | found | | +| `zammad` | `zammad` | 260 | found | | +| `zentao` | `zentao` | 40 | found | | +| `zerotier` | | | not found | No TheirStack technology slug matched this app name. | +| `zitadel` | `zitadel` | 78 | found | | +| `zookeeper` | `zookeeper` | 4,417 | found | | +| `zulip` | `zulip` | 81 | found | | diff --git a/metadata/archive.yaml b/metadata/archive.yaml index fd8c8af31..95a079e8e 100644 --- a/metadata/archive.yaml +++ b/metadata/archive.yaml @@ -57,8 +57,17 @@ apps: - screego - sscms - tailscale +- v2ray +- webcheck - weblate +- wikijs +- windmill +- wireguard - xinference +- zabbix +- zerotier +- zitadel +- zookeeper overrides: aitable: archive_reason: Low user demand @@ -146,3 +155,21 @@ overrides: archive_reason: Low user demand mastodon: archive_reason: Low user demand + zerotier: + archive_reason: Low user demand + wireguard: + archive_reason: Low user demand + windmill: + archive_reason: Low user demand + wikijs: + archive_reason: Low user demand + zookeeper: + archive_reason: Low user demand + zitadel: + archive_reason: Low user demand + zabbix: + archive_reason: Low user demand + webcheck: + archive_reason: Low user demand + v2ray: + archive_reason: Low user demand diff --git a/metadata/catalog/vespa.json b/metadata/catalog/vespa.json new file mode 100644 index 000000000..666536860 --- /dev/null +++ b/metadata/catalog/vespa.json @@ -0,0 +1,13 @@ +{ + "trademark": "Vespa", + "catalogBindings": [ + { + "parentKey": "database", + "childKey": "vector-databases" + } + ], + "summary": "Open-source AI search and vector database.", + "overview": "Vespa is an open-source search engine and vector database for AI, serving low-latency search and ranking at scale.", + "description": "Vespa is an open-source, distributed compute engine for search and AI. It combines vector search, text search, and structured filtering with machine-learned ranking, and serves low-latency queries over large datasets.\n\nThis package deploys the official vespaengine/vespa image as a single container running both the config server and the services role. The config server / deployment API is published on port 19071, and the query and document HTTP API on port 8080. Application data and logs are persisted in named volumes mounted at /opt/vespa/var and /opt/vespa/logs. Vespa starts serving the HTTP API on port 8080 only after an application package is deployed to the config server.\n\nTypical use cases include vector and hybrid search, retrieval-augmented generation (RAG) for AI applications, recommendation and ranking systems, and large-scale document search.", + "websiteurl": "https://vespa.ai" +} diff --git a/metadata/maintenance.yaml b/metadata/maintenance.yaml index 30a22b9c6..5ddd19296 100644 --- a/metadata/maintenance.yaml +++ b/metadata/maintenance.yaml @@ -34,7 +34,6 @@ cadence: - woocommerce - wordpress - wordpresspro - - zabbix update_policy: {} lifecycle: frozen: [] diff --git a/skills/app-update/SKILL.md b/skills/app-update/SKILL.md index d31bef66e..a9d71e77c 100644 --- a/skills/app-update/SKILL.md +++ b/skills/app-update/SKILL.md @@ -7,6 +7,8 @@ description: Use when the user wants to implement an approved app update after a Implement one approved app update with minimal app-local changes. +When the caller provides only an app name, first detect a candidate target version and gather upstream references, then present that candidate for owner confirmation before implementation. Do not skip confirmation unless the target version is already fixed by an issue, an approved assessment, or an explicit owner instruction in the current conversation. + This skill follows `docs/ai-sdlc/03-update-pipeline.md`, `docs/ai-sdlc/05-quality-gates.md`, and `docs/ai-sdlc/06-test-report-format.md`. Supporting files in this skill: @@ -18,32 +20,33 @@ Supporting files in this skill: ## Inputs - app name (required) -- target version (required unless already fixed by the issue or approved assessment) -- upstream references (required) +- target version (optional at entry; required before editing files unless already fixed by the issue, an approved assessment, or an explicit owner confirmation after detection) +- upstream references (required before editing files) ## Steps 1. Read repository facts from `apps//`, `metadata/maintenance.yaml`, and the app notes when relevant. -2. Read `docs/w9-env-spec.md` before touching `.env` or `docker-compose.yml`; use it as the canonical `W9_*` reference, then mirror `metadata/templates/new-app/.env.tmpl` for layout. -3. Read the approved assessment result, if one exists. -4. Read upstream release notes, upgrade notes, image tags, and requirements. -5. Update only the files required for the target version and any app-local files that must change to satisfy current repository quality gates or generation rules, typically `.env`, `docker-compose.yml`, `variables.json`, `README.md`, `CHANGELOG.md`, and `src/`. -6. Keep changes app-local unless the task explicitly requires cross-repo updates. -7. Apply the version tag policy from `docs/devops-spec.md`: prefer `x.x`, use `x.x.x` only when exact patch pinning is required. -8. If new translatable env keys are introduced, register them in `i18n/translation.json`. -9. When `.env` is touched, keep the "image environment variables" section intact and mirror the template layout in `metadata/templates/new-app/.env.tmpl`: keep the section banner, the Docs URL, the "Used by docker-compose.yml" group, and the commented "Not used by default" group. Refresh the single Docs URL if the upstream changed, keep only the variables required by the current package shape plus any user-facing essentials, keep the used vars aligned with `docker-compose.yml`, and keep commented unused vars at no more than 5. Follow the decision rules in `docs/w9-env-spec.md` for `W9_URL`, `W9_URL_REPLACE`, login pairs, `_SET` ports, and dependency helpers. Use a domain-style `W9_URL` placeholder (for example `appname.example.com` or `example.youdomain.com`); do not use `internet_ip:${W9_HTTP_PORT_SET}`. Whenever `.env` is touched at all, convert every environment-variable reference in the whole file to the braced form `${VAR}`; do not leave bare `$VAR` in the file even on lines that were already present. -10. When `docker-compose.yml` is touched, ensure every published port line carries an inline `# purpose` comment and that no `# image:` / `# docs:` source comments remain — image and documentation sources live only in `variables.json` `upstream`. Convert every environment-variable reference in the whole file to the braced form `${VAR}` (for example `${W9_REPO}`, `${W9_HTTP_PORT_SET}`), not just the lines being changed. -11. When a credential or config env var only takes effect on first container startup (the image's entrypoint uses a marker file, e.g. `webconsole.security.enabled`), record that fact in `variables.json` as `env.first_startup_only` (a list of such env names). The README generator then auto-renders the warning; keep the "how to rotate" solution in the hand-written README Change Password section or Notes instead of in metadata. -12. When an app does not control a built-in admin password in `.env` but the password or token can be resolved after startup from inside the application container or from its logs, declare a declarative source in `variables.json.credentials.password` using `container-file` or `container-log`. Prefer this over adding the legacy `W9_LOGIN_GET_PASSWORD` command string. -13. Healthchecks should default to the main app container only. Add healthchecks to sidecar or dependency containers only when the official upstream compose explicitly defines them or the task explicitly requires them. -14. If the target app has app-local drift against the current repository rules (for example template, metadata, env policy, or generated README expectations), fix the minimum blocking or directly relevant items as part of the same update. -15. Keep `apps//CHANGELOG.md` as the single source of app change history. Use a pure-date heading `## YYYY-MM-DD` as the first-level heading for each change batch; list all changes for that date below it. Do not duplicate changelog content into `README.md`. -16. Run `.venv/bin/libs app-gen-readme --app --json` after metadata or README marker content changes so generated sections stay current. -17. For dependency images such as PostgreSQL, MySQL, MariaDB, Redis, or pgvector, prefer `x.x` tags even when upstream examples show `x.x.x`, unless exact patch pinning is demonstrably required. Hard-coded dependency `x.x.x` tags in `docker-compose.yml` are policy drift and should be normalized before handoff. -18. Verify braced references: scan the touched `.env` and `docker-compose.yml` for any remaining bare `$VAR` reference (for example `grep -nE '\$W9_[A-Z_]+'`); fix every hit to `${VAR}` before handoff. A file that was touched must contain no bare `$VAR` anywhere. -19. Ensure `apps//tests/cases.yml` exists and reflects the app's real functional path. Follow `docs/app-tests.md`: keep the built-in adaptive checks and add the minimum app-specific cases the defaults cannot cover, such as `http-basic` for an authenticated console or API, `web-access` for a dedicated health endpoint, or `script` only when built-ins are insufficient. Do not add a case that duplicates a default check. -20. Run the `deploy-validation` skill for the changed app. -21. Produce a short test report. +2. If the caller did not provide a target version, detect a candidate target version first by checking the current package version, the upstream image tags, and the upstream release notes or changelog. If the candidate is not already fixed by issue context or approved assessment, stop and obtain owner confirmation before editing files. +3. Read `docs/w9-env-spec.md` before touching `.env` or `docker-compose.yml`; use it as the canonical `W9_*` reference, then mirror `metadata/templates/new-app/.env.tmpl` for layout. +4. Read the approved assessment result, if one exists. +5. Read upstream release notes, upgrade notes, image tags, and requirements. +6. Update only the files required for the target version and any app-local files that must change to satisfy current repository quality gates or generation rules, typically `.env`, `docker-compose.yml`, `variables.json`, `README.md`, `CHANGELOG.md`, and `src/`. +7. Keep changes app-local unless the task explicitly requires cross-repo updates. +8. Apply the version tag policy from `docs/devops-spec.md`: prefer `x.x`, use `x.x.x` only when exact patch pinning is required. +9. If new translatable env keys are introduced, register them in `i18n/translation.json`. +10. When `.env` is touched, keep the "image environment variables" section intact and mirror the template layout in `metadata/templates/new-app/.env.tmpl`: keep the section banner, the Docs URL, the "Used by docker-compose.yml" group, and the commented "Not used by default" group. Refresh the single Docs URL if the upstream changed, keep only the variables required by the current package shape plus any user-facing essentials, keep the used vars aligned with `docker-compose.yml`, and keep commented unused vars at no more than 5. Follow the decision rules in `docs/w9-env-spec.md` for `W9_URL`, `W9_URL_REPLACE`, login pairs, `_SET` ports, and dependency helpers. Use a domain-style `W9_URL` placeholder (for example `appname.example.com` or `example.youdomain.com`); do not use `internet_ip:${W9_HTTP_PORT_SET}`. Whenever `.env` is touched at all, convert every environment-variable reference in the whole file to the braced form `${VAR}`; do not leave bare `$VAR` in the file even on lines that were already present. +11. When `docker-compose.yml` is touched, ensure every published port line carries an inline `# purpose` comment and that no `# image:` / `# docs:` source comments remain — image and documentation sources live only in `variables.json` `upstream`. Convert every environment-variable reference in the whole file to the braced form `${VAR}` (for example `${W9_REPO}`, `${W9_HTTP_PORT_SET}`), not just the lines being changed. +12. When a credential or config env var only takes effect on first container startup (the image's entrypoint uses a marker file, e.g. `webconsole.security.enabled`), record that fact in `variables.json` as `env.first_startup_only` (a list of such env names). The README generator then auto-renders the warning; keep the "how to rotate" solution in the hand-written README Change Password section or Notes instead of in metadata. +13. When an app does not control a built-in admin password in `.env` but the password or token can be resolved after startup from inside the application container or from its logs, declare a declarative source in `variables.json.credentials.password` using `container-file` or `container-log`. Prefer this over adding the legacy `W9_LOGIN_GET_PASSWORD` command string. +14. Healthchecks should default to the main app container only. Add healthchecks to sidecar or dependency containers only when the official upstream compose explicitly defines them or the task explicitly requires them. +15. If the target app has app-local drift against the current repository rules (for example template, metadata, env policy, or generated README expectations), fix the minimum blocking or directly relevant items as part of the same update. +16. Keep `apps//CHANGELOG.md` as the single source of app change history. Use a pure-date heading `## YYYY-MM-DD` as the first-level heading for each change batch; list all changes for that date below it. Do not duplicate changelog content into `README.md`. +17. Run `.venv/bin/libs app-gen-readme --app --json` after metadata or README marker content changes so generated sections stay current. +18. For dependency images such as PostgreSQL, MySQL, MariaDB, Redis, or pgvector, prefer `x.x` tags even when upstream examples show `x.x.x`, unless exact patch pinning is demonstrably required. Hard-coded dependency `x.x.x` tags in `docker-compose.yml` are policy drift and should be normalized before handoff. +19. Verify braced references: scan the touched `.env` and `docker-compose.yml` for any remaining bare `$VAR` reference (for example `grep -nE '\$W9_[A-Z_]+'`); fix every hit to `${VAR}` before handoff. A file that was touched must contain no bare `$VAR` anywhere. +20. Ensure `apps//tests/cases.yml` exists and reflects the app's real functional path. Follow `docs/app-tests.md`: keep the built-in adaptive checks and add the minimum app-specific cases the defaults cannot cover, such as `http-basic` for an authenticated console or API, `web-access` for a dedicated health endpoint, or `script` only when built-ins are insufficient. Do not add a case that duplicates a default check. +21. Run the `deploy-validation` skill for the changed app. +22. Produce a short test report. ## Output @@ -56,6 +59,7 @@ Supporting files in this skill: ## Rules - Do not start implementation for a `review-first` candidate unless the owner has approved continuation. +- When only an app name is provided, discovery is allowed, but file edits must wait until the owner confirms the detected candidate version unless approval already exists in issue context or a prior assessment. - Keep the smallest correct change. - The update is not a blind version bump. The changed app must still pass the current quality gates after the work is complete. - Do not perform broad cosmetic template re-alignment. Fix only the app-local conformance items that are blocking, directly relevant to the update, or required by current gates and generators. diff --git a/skills/deploy-validation/SKILL.md b/skills/deploy-validation/SKILL.md index dfb84f501..9de6f22f1 100644 --- a/skills/deploy-validation/SKILL.md +++ b/skills/deploy-validation/SKILL.md @@ -26,7 +26,7 @@ Supporting files in this skill: ## Steps -1. Run the gates locally: `make --no-print-directory libs ARGS="check --app --json"`. On failure, stop and report the first blocking error. +1. Run the gates locally: `make --no-print-directory libs ARGS="app-check --app --json"`. On failure, stop and report the first blocking error. 2. Run `make --no-print-directory libs ARGS="app-deploy --app [--target ] [--ssh-host --ssh-user --ssh-secret-path --deploy-root ] --json"` to perform the compose deployment primitive. It handles local vs remote resolution, sync, network creation, `config`, `up -d`, and `ps` evidence. 3. Run `make --no-print-directory libs ARGS="app-tests --app [--base-url ] [--ssh-host --ssh-user --ssh-secret-path --deploy-root ] --json"` to perform functional checks. When `tests/cases.yml` is absent, the command still runs the default required checks; report the absence as an app-local test gap when the app's core path needs an app-specific check. 4. Check container logs for blocking errors. From 03c2911c7d55593523d96889f1eed6e91eb5759d Mon Sep 17 00:00:00 2001 From: zhaojing1987 Date: Mon, 21 Sep 2026 10:36:35 +0800 Subject: [PATCH 2/7] fix: hash catalog content for dataset version and restore production field --- build/fetch_catalog.py | 1 + build/library_publish.py | 16 ++++++++++++++-- tests/build/test_fetch_catalog.py | 4 ++++ tests/build/test_library_publish.py | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/build/fetch_catalog.py b/build/fetch_catalog.py index 99ea8639d..9187b0fa5 100644 --- a/build/fetch_catalog.py +++ b/build/fetch_catalog.py @@ -55,6 +55,7 @@ description screenshots distribution + production vcpu memory storage diff --git a/build/library_publish.py b/build/library_publish.py index 4962417b0..bebbd966f 100644 --- a/build/library_publish.py +++ b/build/library_publish.py @@ -144,6 +144,19 @@ def write_checksum_file(path: Path) -> str: return checksum_path.name +def compute_catalog_dataset_version(catalog_dir: Path) -> str: + """Derive catalog datasetVersion from the actual content of the catalog files. + + Must hash file contents, not checksum file names, otherwise the version is a + constant and consumers can never detect catalog changes. + """ + parts = [ + f"{file_name}={sha256_file(catalog_dir / file_name)}" + for file_name in CATALOG_FILE_NAMES + ] + return _hash_content(",".join(parts)) + + def create_zip_from_directory(source_dir: Path, destination_zip: Path) -> None: with ZipFile(destination_zip, "w", compression=ZIP_DEFLATED) as archive: for path in sorted(path for path in source_dir.rglob("*") if path.is_file()): @@ -563,8 +576,7 @@ def build_v2_appstore_artifacts( elif file_name == "product_zh.json": catalog_checksums["productZh"] = write_checksum_file(destination) - catalog_checksum_values = ",".join(f"{k}={v}" for k, v in sorted(catalog_checksums.items())) - catalog_dsv = _hash_content(catalog_checksum_values) + catalog_dsv = compute_catalog_dataset_version(catalog_dir) # ── catalog full package ───────────────────────────────── catalog_full_dir = catalog_dir / "full" diff --git a/tests/build/test_fetch_catalog.py b/tests/build/test_fetch_catalog.py index 075e205af..ecce9eddf 100644 --- a/tests/build/test_fetch_catalog.py +++ b/tests/build/test_fetch_catalog.py @@ -45,6 +45,10 @@ def test_fetch_product_entries_uses_stable_id_ordering(): assert "order: sys_id_ASC" in fetch_catalog.PRODUCT_QUERY +def test_product_query_requests_production_field(): + assert "\n production\n" in fetch_catalog.PRODUCT_QUERY + + def test_fetch_product_entries_rejects_duplicate_ids_across_pages(monkeypatch): def fake_run_query(token, query, variables): return { diff --git a/tests/build/test_library_publish.py b/tests/build/test_library_publish.py index 8743ba753..3a7711b54 100644 --- a/tests/build/test_library_publish.py +++ b/tests/build/test_library_publish.py @@ -17,6 +17,22 @@ def test_hash_content_is_deterministic_and_input_sensitive(): assert len(library_publish._hash_content("a")) == 16 +def test_compute_catalog_dataset_version_is_content_sensitive(tmp_path: Path): + catalog_dir = tmp_path / "catalog" + catalog_dir.mkdir() + for file_name in library_publish.CATALOG_FILE_NAMES: + (catalog_dir / file_name).write_text("[]\n", encoding="utf-8") + + baseline = library_publish.compute_catalog_dataset_version(catalog_dir) + assert len(baseline) == 16 + assert baseline == library_publish.compute_catalog_dataset_version(catalog_dir) + + (catalog_dir / "product_en.json").write_text('[{"key": "lobechat"}]\n', encoding="utf-8") + changed = library_publish.compute_catalog_dataset_version(catalog_dir) + + assert changed != baseline + + def test_summarize_versions_deduplicates_and_preserves_order(): editions = [ {"dist": "community", "version": ["1.0", "1.0", "2.0"]}, From fe55a67e0408e8ca56d4903c3324c02e76132312 Mon Sep 17 00:00:00 2001 From: zhaojing1987 Date: Mon, 21 Sep 2026 11:41:32 +0800 Subject: [PATCH 3/7] fix: fill locale-specific default logo when product logo is missing --- build/fetch_catalog.py | 19 ++++++++++++++++++- tests/build/test_fetch_catalog.py | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/build/fetch_catalog.py b/build/fetch_catalog.py index 9187b0fa5..76fe99ebf 100644 --- a/build/fetch_catalog.py +++ b/build/fetch_catalog.py @@ -16,6 +16,12 @@ "en-US": "en", "zh-CN": "zh", } +# Fallback logos used when a Contentful product entry has no logo, so consumers +# do not reject the whole catalog for incomplete data. Keyed by locale short code. +DEFAULT_LOGO_URLS = { + "en": "https://libs.websoft9.com/Websoft9/logo/product/websoft9-en.png", + "zh": "https://libs.websoft9.com/Websoft9/logo/product/websoft9.png", +} CATALOG_QUERY = """ query($locale: String!) { catalog(id: \"2Yp0TY3kBHgG6VDjsHZNpK\", locale: $locale) { @@ -121,6 +127,15 @@ def fetch_catalog_entries(token: str, locale: str) -> list[dict]: return collection.get("items") or [] +def apply_default_logo(entry: dict, locale_code: str) -> dict: + """Return the entry with a usable logo URL, filling in a locale default when absent.""" + logo = entry.get("logo") or {} + if logo.get("imageurl"): + return entry + fallback = DEFAULT_LOGO_URLS.get(locale_code) or DEFAULT_LOGO_URLS["en"] + return {**entry, "logo": {"imageurl": fallback}} + + def fetch_product_entries(token: str, locale: str, production: bool | None) -> list[dict]: items: list[dict] = [] skip = 0 @@ -141,7 +156,9 @@ def fetch_product_entries(token: str, locale: str, production: bool | None) -> l duplicate_ids = sorted({entry_id for entry_id in entry_ids if entry_id and entry_ids.count(entry_id) > 1}) if duplicate_ids: raise SystemExit(f"duplicate Contentful product sys.id values: {', '.join(duplicate_ids)}") - return items + + locale_code = LOCALES.get(locale, "en") + return [apply_default_logo(entry, locale_code) for entry in items] def write_json(path: Path, payload: list[dict]) -> None: diff --git a/tests/build/test_fetch_catalog.py b/tests/build/test_fetch_catalog.py index ecce9eddf..22a820bf3 100644 --- a/tests/build/test_fetch_catalog.py +++ b/tests/build/test_fetch_catalog.py @@ -49,6 +49,25 @@ def test_product_query_requests_production_field(): assert "\n production\n" in fetch_catalog.PRODUCT_QUERY +def test_apply_default_logo_fills_missing_logo_per_locale(): + en = fetch_catalog.apply_default_logo({"key": "vespa", "logo": None}, "en") + assert en["logo"] == {"imageurl": fetch_catalog.DEFAULT_LOGO_URLS["en"]} + + zh = fetch_catalog.apply_default_logo({"key": "vespa", "logo": None}, "zh") + assert zh["logo"] == {"imageurl": fetch_catalog.DEFAULT_LOGO_URLS["zh"]} + + assert fetch_catalog.DEFAULT_LOGO_URLS["en"] != fetch_catalog.DEFAULT_LOGO_URLS["zh"] + + for logo in ({}, {"imageurl": ""}): + filled = fetch_catalog.apply_default_logo({"key": "vespa", "logo": logo}, "zh") + assert filled["logo"]["imageurl"] == fetch_catalog.DEFAULT_LOGO_URLS["zh"] + + +def test_apply_default_logo_keeps_existing_logo(): + existing = {"key": "gitea", "logo": {"imageurl": "https://libs.websoft9.com/gitea.png"}} + assert fetch_catalog.apply_default_logo(existing, "en") is existing + + def test_fetch_product_entries_rejects_duplicate_ids_across_pages(monkeypatch): def fake_run_query(token, query, variables): return { From 70d8eb93a73ced3473de53220de1282360d6d3a1 Mon Sep 17 00:00:00 2001 From: zhaojing1987 Date: Mon, 21 Sep 2026 14:16:03 +0800 Subject: [PATCH 4/7] feat: add per-app updatedAt and align apps-index datasetVersion - apps-index entries carry updatedAt derived from the git author date - apps-index datasetVersion now derives from its own content instead of the catalog version - document the updatedAt contract and the consumer read path --- build/library_publish.py | 68 ++++++++++++++++++++++++++--- docs/appstore-release-spec.md | 13 +++++- tests/build/test_library_publish.py | 50 ++++++++++++++++++++- 3 files changed, 124 insertions(+), 7 deletions(-) diff --git a/build/library_publish.py b/build/library_publish.py index bebbd966f..1040f491d 100644 --- a/build/library_publish.py +++ b/build/library_publish.py @@ -144,6 +144,17 @@ def write_checksum_file(path: Path) -> str: return checksum_path.name +def to_utc_z(value: str) -> str: + """Normalize an ISO 8601 timestamp to UTC with a Z suffix.""" + try: + parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError: + return value + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=timezone.utc) + return parsed.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + + def compute_catalog_dataset_version(catalog_dir: Path) -> str: """Derive catalog datasetVersion from the actual content of the catalog files. @@ -207,7 +218,38 @@ def build_app_checksum_entry(app_name: str) -> dict: return {"latest": f"apps/{app_name}/{APP_PACKAGE_NAME}.sha256"} -def build_apps_index(dataset_version: str, channel: str, generated_at: str) -> dict: +def build_app_updated_at_map() -> dict[str, str]: + """Map each app to the commit date of its last change under apps/. + + git log lists commits newest first, so the first time an app path appears is + its most recent change. Author date is used because it survives rebases, + unlike the committer date. + """ + try: + output = run_git("log", "--format=@@%aI", "--name-only", "--", "apps") + except (subprocess.CalledProcessError, FileNotFoundError): + return {} + + updated: dict[str, str] = {} + current: str | None = None + for line in output.splitlines(): + if line.startswith("@@"): + current = line[2:].strip() + continue + if not current or not line.startswith("apps/"): + continue + parts = line.split("/", 2) + if len(parts) >= 2 and parts[1] and parts[1] not in updated: + updated[parts[1]] = to_utc_z(current) + return updated + + +def build_apps_index( + channel: str, + generated_at: str, + app_updated_at: dict[str, str] | None = None, +) -> dict: + updated_map = app_updated_at or {} apps = [] for app_dir in sorted(path for path in APPS_DIR.iterdir() if path.is_dir()): variables = load_variables_json(app_dir) @@ -221,6 +263,7 @@ def build_apps_index(dataset_version: str, channel: str, generated_at: str) -> d "versions": summarize_versions(variables.get("edition", [])), "path": f"apps/{app_name}", "hash": current_app_fingerprint(app_dir), + "updatedAt": updated_map.get(app_name, generated_at), "package": build_app_package_entry(app_name), "checksum": build_app_checksum_entry(app_name), } @@ -228,7 +271,6 @@ def build_apps_index(dataset_version: str, channel: str, generated_at: str) -> d return { "schemaVersion": "1", - "datasetVersion": dataset_version, "channel": channel, "generatedAt": generated_at, "appCount": len(apps), @@ -236,6 +278,23 @@ def build_apps_index(dataset_version: str, channel: str, generated_at: str) -> d } +def build_apps_index_with_version( + channel: str, + generated_at: str, + app_updated_at: dict[str, str] | None = None, +) -> tuple[dict, str]: + """Build the apps index and derive its datasetVersion from its own content. + + The version has to be computed before it is stored, otherwise the file would + carry a version that does not describe it (for example the catalog version). + """ + apps_index = build_apps_index(channel, generated_at, app_updated_at) + serialized = json.dumps(apps_index, sort_keys=True, ensure_ascii=False) + dataset_version = _hash_content(serialized) + apps_index["datasetVersion"] = dataset_version + return apps_index, dataset_version + + def apps_in_ref(from_ref: str | None) -> set[str]: if not from_ref: return set() @@ -605,9 +664,8 @@ def build_v2_appstore_artifacts( apps_packages_dir.mkdir(parents=True, exist_ok=True) # ── library – compute index & delta BEFORE per-app zips ── - apps_index = build_apps_index(catalog_dsv, channel, generated_at) - serialized_index = json.dumps(apps_index, sort_keys=True, ensure_ascii=False) - library_dsv = _hash_content(serialized_index) + app_updated_at = build_app_updated_at_map() + apps_index, library_dsv = build_apps_index_with_version(channel, generated_at, app_updated_at) full_latest_name = V2_FULL_LATEST_NAME with tempfile.TemporaryDirectory() as tmp_dir_name: diff --git a/docs/appstore-release-spec.md b/docs/appstore-release-spec.md index f2d8f44a4..00952fe1e 100644 --- a/docs/appstore-release-spec.md +++ b/docs/appstore-release-spec.md @@ -277,6 +277,7 @@ artifact/appstore// "hash": "d80b08f74959...", "versions": ["1.26", "latest"], "path": "apps/nginx", + "updatedAt": "2026-09-20T09:52:48Z", "package": { "latest": "apps/nginx/latest.zip" }, "checksum": { "latest": "apps/nginx/latest.zip.sha256" } } @@ -284,6 +285,8 @@ artifact/appstore// 客户端通过对比 `hash` 字段判断 app 是否变更。 +`updatedAt` 是该应用安装模板(`apps/` 整个目录)最后一次内容变更的时间,取自 Git 提交的 author date,统一为 UTC。它只用于展示"最近更新",不参与增量判断;应用没有可用的 Git 记录时回退为本次发布的 `generatedAt`。 + --- ## 6. 发布流程 @@ -406,7 +409,10 @@ artifact/appstore// → 校验 full/latest.zip.sha256 → 解压到本地 apps/ 目录 -5. 保存本次下载的 appstore-manifest.json 到本地作为状态锚点 +5. 下载并缓存 apps-index-.json + → 读取每个应用的 updatedAt(模板最后变更时间) + +6. 保存本次下载的 appstore-manifest.json 到本地作为状态锚点 ``` ### 11.2 增量更新 @@ -418,6 +424,8 @@ artifact/appstore// → 如果 catalog.datasetVersion 不同: 下载 catalog/manifest.json → 逐个对比 checksum → 下载变更的 JSON → 如果 library.datasetVersion 不同: + 下载 apps-index-.json 并替换本地缓存 + (未变更应用的 updatedAt 只能从这里获得) if supportsPartialUpdate: 下载 apps-delta → 下载 changed/added app 的 latest.zip else: @@ -435,6 +443,8 @@ artifact/appstore// | 仅 catalog 变了 1 个文件 | 3(根 manifest + catalog manifest + 1 个 JSON) | | 仅 library 变了 2 个 app | 4(根 manifest + library manifest + delta + 2 个 zip) | +> 需要展示 `updatedAt` 时,library 更新会额外包含 1 次 `apps-index` 请求(约 136 KB,gzip 后约 24 KB)。 + ### 11.3 异常恢复 ``` @@ -454,6 +464,7 @@ schemaVersion 不在本地支持列表中: | `appstore-manifest.json` | Cache-Control: 60s | 入口文件,需及时感知更新 | | `catalog/manifest.json` | Cache-Control: 60s | 体积极小,变更频率低 | | `catalog/full/latest.zip` | Cache-Control: 60s | 冷启动与兜底恢复入口,覆盖发布后需尽快生效 | +| `apps-index-*.json` | Cache-Control: 60s | 应用索引与 updatedAt,仅在 datasetVersion 变化时重新下载 | | `apps-delta-*.json` | Cache-Control: 60s | 体积极小 | | `apps/{app}/latest.zip` | Cache-Control: 60s | 模板文件体积极小,变更后需立即生效 | | `library/full/latest.zip` | Cache-Control: 60s | library 全量固定入口 | diff --git a/tests/build/test_library_publish.py b/tests/build/test_library_publish.py index 3a7711b54..79db585f1 100644 --- a/tests/build/test_library_publish.py +++ b/tests/build/test_library_publish.py @@ -17,6 +17,29 @@ def test_hash_content_is_deterministic_and_input_sensitive(): assert len(library_publish._hash_content("a")) == 16 +def test_to_utc_z_normalizes_offsets(): + assert library_publish.to_utc_z("2022-11-29T10:49:58+08:00") == "2022-11-29T02:49:58Z" + assert library_publish.to_utc_z("2026-09-20T09:52:48Z") == "2026-09-20T09:52:48Z" + + +def test_build_app_updated_at_map_keeps_latest_per_app(monkeypatch): + output = "\n".join( + [ + "@@2026-09-20T09:52:48Z", + "apps/alpha/docker-compose.yml", + "apps/beta/README.md", + "@@2026-08-01T00:00:00+08:00", + "apps/alpha/.env", + ] + ) + monkeypatch.setattr(library_publish, "run_git", lambda *args, **kwargs: output) + + mapping = library_publish.build_app_updated_at_map() + + assert mapping["alpha"] == "2026-09-20T09:52:48Z" + assert mapping["beta"] == "2026-09-20T09:52:48Z" + + def test_compute_catalog_dataset_version_is_content_sensitive(tmp_path: Path): catalog_dir = tmp_path / "catalog" catalog_dir.mkdir() @@ -47,13 +70,28 @@ def test_app_package_and_checksum_entry_shapes(): assert library_publish.build_app_checksum_entry("demo") == {"latest": "apps/demo/latest.zip.sha256"} +def test_apps_index_dataset_version_describes_its_own_content(build_fixture, monkeypatch): + monkeypatch.setattr(library_publish, "APPS_DIR", build_fixture / "apps") + monkeypatch.setattr(library_publish, "ROOT", build_fixture) + + index, dataset_version = library_publish.build_apps_index_with_version("dev", "2026-01-01T00:00:00Z") + + assert index["datasetVersion"] == dataset_version + assert len(dataset_version) == 16 + + body = {key: value for key, value in index.items() if key != "datasetVersion"} + expected = library_publish._hash_content(json.dumps(body, sort_keys=True, ensure_ascii=False)) + assert dataset_version == expected + + def test_build_apps_index_with_fixture_apps(build_fixture, monkeypatch): monkeypatch.setattr(library_publish, "APPS_DIR", build_fixture / "apps") monkeypatch.setattr(library_publish, "ROOT", build_fixture) - index = library_publish.build_apps_index("2026.01.01", "dev", "2026-01-01T00:00:00Z") + index = library_publish.build_apps_index("dev", "2026-01-01T00:00:00Z") assert index["schemaVersion"] == "1" + assert "datasetVersion" not in index assert index["appCount"] == 1 entry = index["apps"][0] assert entry["app"] == "demo" @@ -62,11 +100,21 @@ def test_build_apps_index_with_fixture_apps(build_fixture, monkeypatch): assert entry["release"] is True assert entry["versions"] == ["1.0", "latest"] assert entry["path"] == "apps/demo" + assert entry["updatedAt"] == "2026-01-01T00:00:00Z" assert entry["package"] == {"latest": "apps/demo/latest.zip"} assert entry["checksum"] == {"latest": "apps/demo/latest.zip.sha256"} assert len(entry["hash"]) == 64 +def test_build_apps_index_uses_provided_updated_at(build_fixture, monkeypatch): + monkeypatch.setattr(library_publish, "APPS_DIR", build_fixture / "apps") + monkeypatch.setattr(library_publish, "ROOT", build_fixture) + + index = library_publish.build_apps_index("dev", "2026-01-01T00:00:00Z", {"demo": "2026-05-05T00:00:00Z"}) + + assert index["apps"][0]["updatedAt"] == "2026-05-05T00:00:00Z" + + def test_build_apps_delta_computes_added_changed_removed(monkeypatch): apps_index = {"apps": [{"app": "b"}, {"app": "c"}]} monkeypatch.setattr(library_publish, "apps_in_ref", lambda from_ref: {"a", "c"}) From 6a3420858e2eedb3765f7440d0bc4060946354fd Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Sep 2026 09:48:32 +0000 Subject: [PATCH 5/7] upgrade apps --- Notes.md | 2 +- apps/syncthing/.env | 35 ++++-- apps/syncthing/CHANGELOG.md | 7 +- apps/syncthing/README.md | 87 +++++++++++--- apps/syncthing/docker-compose.yml | 27 ++--- apps/syncthing/src/init-syncthing.sh | 17 +++ apps/syncthing/tests/cases.yml | 7 ++ apps/syncthing/tests/check.sh | 19 +++ apps/syncthing/variables.json | 14 ++- apps/teamcity/.env | 44 +++++-- apps/teamcity/CHANGELOG.md | 10 +- apps/teamcity/README.md | 108 +++++++++++++++--- apps/teamcity/docker-compose.yml | 17 +-- apps/teamcity/tests/cases.yml | 7 ++ apps/teamcity/tests/check.sh | 25 ++++ apps/teamcity/variables.json | 10 +- apps/teleport/.env | 33 ++++-- apps/teleport/CHANGELOG.md | 10 +- apps/teleport/Notes.md | 12 +- apps/teleport/README.md | 84 +++++++++++--- apps/teleport/docker-compose.yml | 31 ++--- apps/teleport/src/config/bootstrap.yaml | 31 +++++ apps/teleport/src/config/teleport.yaml | 87 ++++++++------ apps/teleport/tests/cases.yml | 10 ++ apps/teleport/tests/check.sh | 19 +++ apps/teleport/variables.json | 17 ++- apps/tensorflow/.env | 30 +++-- apps/tensorflow/CHANGELOG.md | 6 +- apps/tensorflow/README.md | 4 +- apps/tensorflow/docker-compose.yml | 10 +- apps/tensorflow/tests/cases.yml | 5 + apps/tensorflow/variables.json | 2 +- apps/thingsboard/.env | 34 ++++-- apps/thingsboard/CHANGELOG.md | 9 +- apps/thingsboard/README.md | 88 +++++++++++--- apps/thingsboard/docker-compose.yml | 10 +- apps/thingsboard/tests/cases.yml | 4 + apps/thingsboard/tests/check.sh | 28 +++++ apps/thingsboard/variables.json | 9 +- apps/tomcat/.env | 30 ++++- apps/tomcat/CHANGELOG.md | 9 +- apps/tomcat/Notes.md | 49 +++++++- apps/tomcat/README.md | 96 +++++++++++++--- apps/tomcat/docker-compose.yml | 26 +++-- apps/tomcat/src/cmd.sh | 11 -- apps/tomcat/src/entrypoint.d/10-webapps.sh | 14 +++ apps/tomcat/src/entrypoint.sh | 50 ++++++++ apps/tomcat/src/start.sh | 7 ++ apps/tomcat/tests/cases.yml | 10 ++ apps/tomcat/tests/smoke.sh | 14 +++ apps/tomcat/tests/war-deploy.sh | 48 ++++++++ apps/tomcat/variables.json | 24 ++-- apps/traefik/.env | 38 ++++-- apps/traefik/CHANGELOG.md | 6 +- apps/traefik/Notes.md | 4 - apps/traefik/README.md | 89 ++++++++++++--- apps/traefik/docker-compose.yml | 10 +- apps/traefik/tests/cases.yml | 8 ++ apps/traefik/variables.json | 9 +- apps/trivy/.env | 45 +++++++- apps/trivy/CHANGELOG.md | 8 ++ apps/trivy/Notes.md | 26 +++-- apps/trivy/README.md | 91 ++++++++++++--- apps/trivy/docker-compose.yml | 30 ++--- apps/trivy/tests/cases.yml | 8 ++ apps/trivy/variables.json | 21 +++- apps/typesense/.env | 44 +++++-- apps/typesense/CHANGELOG.md | 8 +- apps/typesense/Notes.md | 2 - apps/typesense/README.md | 98 +++++++++++++--- apps/typesense/docker-compose.yml | 17 +-- apps/typesense/tests/cases.yml | 8 ++ apps/typesense/variables.json | 2 +- apps/typo3/.env | 56 +++++++-- apps/typo3/CHANGELOG.md | 11 +- apps/typo3/Notes.md | 3 - apps/typo3/README.md | 100 +++++++++++++--- apps/typo3/docker-compose.yml | 31 +++-- apps/typo3/src/entrypoint.sh | 36 ++++++ apps/typo3/tests/cases.yml | 12 ++ apps/typo3/variables.json | 24 +++- apps/umami/.env | 56 +++++++-- apps/umami/CHANGELOG.md | 7 +- apps/umami/Notes.md | 8 -- apps/umami/README.md | 96 +++++++++++++--- apps/umami/docker-compose.yml | 30 ++--- apps/umami/tests/cases.yml | 7 ++ apps/umami/tests/check.sh | 35 ++++++ apps/umami/variables.json | 22 +++- apps/umbraco/.env | 47 +++++++- apps/umbraco/CHANGELOG.md | 9 +- apps/umbraco/Dockerfile | 23 ++++ apps/umbraco/Notes.md | 7 +- apps/umbraco/README.md | 91 ++++++++++++--- apps/umbraco/docker-compose.yml | 40 +++++-- apps/umbraco/tests/cases.yml | 5 + apps/umbraco/variables.json | 34 +++++- apps/varnish/CHANGELOG.md | 7 +- apps/varnish/Notes.md | 37 ------ apps/varnish/README.md | 8 +- apps/varnish/variables.json | 8 +- apps/xwiki/.env | 44 ++++--- apps/xwiki/CHANGELOG.md | 7 +- apps/xwiki/Notes.md | 41 +++---- apps/xwiki/README.md | 100 +++++++++++++--- apps/xwiki/docker-compose.yml | 27 +++-- apps/xwiki/src/mysql_init.sql | 1 + apps/xwiki/tests/cases.yml | 7 ++ apps/xwiki/tests/check.sh | 21 ++++ apps/xwiki/variables.json | 23 +++- {apps => archive/apps}/taskingai/.env | 0 {apps => archive/apps}/taskingai/CHANGELOG.md | 0 {apps => archive/apps}/taskingai/Notes.md | 0 {apps => archive/apps}/taskingai/README.md | 0 .../apps}/taskingai/docker-compose.yml | 0 .../apps}/taskingai/src/README.md | 0 .../apps}/taskingai/src/proxy.conf | 0 .../apps}/taskingai/variables.json | 0 {apps => archive/apps}/theia/.env | 0 {apps => archive/apps}/theia/CHANGELOG.md | 0 {apps => archive/apps}/theia/Notes.md | 0 {apps => archive/apps}/theia/README.md | 0 .../apps}/theia/docker-compose.yml | 0 {apps => archive/apps}/theia/src/filelist | 0 {apps => archive/apps}/theia/variables.json | 0 {apps => archive/apps}/tinyproxy/.env | 0 {apps => archive/apps}/tinyproxy/CHANGELOG.md | 0 {apps => archive/apps}/tinyproxy/Notes.md | 0 {apps => archive/apps}/tinyproxy/README.md | 0 .../apps}/tinyproxy/docker-compose.yml | 0 .../apps}/tinyproxy/src/README.md | 0 .../apps}/tinyproxy/src/tinyproxy.conf | 0 .../apps}/tinyproxy/variables.json | 0 {apps => archive/apps}/tomee/.env | 0 {apps => archive/apps}/tomee/CHANGELOG.md | 0 {apps => archive/apps}/tomee/Notes.md | 0 {apps => archive/apps}/tomee/README.md | 0 .../apps}/tomee/docker-compose.yml | 0 {apps => archive/apps}/tomee/src/README.md | 0 {apps => archive/apps}/tomee/src/cmd.sh | 0 {apps => archive/apps}/tomee/variables.json | 0 {apps => archive/apps}/tooljet/.env | 0 {apps => archive/apps}/tooljet/CHANGELOG.md | 0 {apps => archive/apps}/tooljet/README.md | 0 .../apps}/tooljet/docker-compose.yml | 0 {apps => archive/apps}/tooljet/src/README.md | 0 .../apps}/tooljet/src/nginx-proxy.conf | 0 .../apps}/tooljet/tests/cases.yml | 0 {apps => archive/apps}/tooljet/variables.json | 0 {apps => archive/apps}/traccar/.env | 0 {apps => archive/apps}/traccar/CHANGELOG.md | 0 {apps => archive/apps}/traccar/Notes.md | 0 {apps => archive/apps}/traccar/README.md | 0 .../apps}/traccar/docker-compose.yml | 0 {apps => archive/apps}/traccar/src/README.md | 0 {apps => archive/apps}/traccar/variables.json | 0 {apps => archive/apps}/trafficserver/.env | 0 .../apps}/trafficserver/CHANGELOG.md | 0 {apps => archive/apps}/trafficserver/Notes.md | 0 .../apps}/trafficserver/README.md | 0 .../apps}/trafficserver/docker-compose.yml | 0 .../apps}/trafficserver/src/README.md | 0 .../apps}/trafficserver/src/records.yaml | 0 .../apps}/trafficserver/src/remap.config | 0 .../apps}/trafficserver/src/storage.config | 0 .../apps}/trafficserver/variables.json | 0 {apps => archive/apps}/triggerdev/.env | 0 .../apps}/triggerdev/CHANGELOG.md | 0 {apps => archive/apps}/triggerdev/Notes.md | 0 .../apps}/triggerdev/docker-compose.yml | 0 .../apps}/triggerdev/src/README.md | 0 .../apps}/triggerdev/variables.json | 0 {apps => archive/apps}/ttrss/.env | 0 {apps => archive/apps}/ttrss/CHANGELOG.md | 0 {apps => archive/apps}/ttrss/Notes.md | 0 {apps => archive/apps}/ttrss/README.md | 0 .../apps}/ttrss/docker-compose.yml | 0 {apps => archive/apps}/ttrss/src/README.md | 0 {apps => archive/apps}/ttrss/variables.json | 0 {apps => archive/apps}/twenty/.env | 0 {apps => archive/apps}/twenty/CHANGELOG.md | 0 {apps => archive/apps}/twenty/Notes.md | 0 {apps => archive/apps}/twenty/README.md | 0 .../apps}/twenty/docker-compose.yml | 0 {apps => archive/apps}/twenty/src/README.md | 0 {apps => archive/apps}/twenty/variables.json | 0 {apps => archive/apps}/tyk/.env | 0 {apps => archive/apps}/tyk/CHANGELOG.md | 0 {apps => archive/apps}/tyk/Notes.md | 0 {apps => archive/apps}/tyk/README.md | 0 {apps => archive/apps}/tyk/docker-compose.yml | 0 {apps => archive/apps}/tyk/src/README.md | 0 {apps => archive/apps}/tyk/src/tyk.conf | 0 {apps => archive/apps}/tyk/variables.json | 0 {apps => archive/apps}/unleash/.env | 0 {apps => archive/apps}/unleash/CHANGELOG.md | 0 {apps => archive/apps}/unleash/Notes.md | 0 {apps => archive/apps}/unleash/README.md | 0 .../apps}/unleash/docker-compose.yml | 0 {apps => archive/apps}/unleash/src/README.md | 0 {apps => archive/apps}/unleash/variables.json | 0 .../__pycache__/fetch_catalog.cpython-312.pyc | Bin 6715 -> 7625 bytes .../library_publish.cpython-312.pyc | Bin 33388 -> 36396 bytes .../__pycache__/app_tests.cpython-312.pyc | Bin 20643 -> 22028 bytes cli/libs/app_tests.py | 35 +++++- ...est_app_tests.cpython-312-pytest-9.1.1.pyc | Bin 39262 -> 48277 bytes ...t_dblifecycle.cpython-312-pytest-9.1.1.pyc | Bin 10589 -> 10682 bytes cli/tests/test_app_tests.py | 73 ++++++++++++ cli/tests/test_dblifecycle.py | 4 +- docs/app-tests.md | 7 ++ i18n/translation.json | 4 + metadata/archive.yaml | 36 ++++++ ...fetch_catalog.cpython-312-pytest-9.1.1.pyc | Bin 15376 -> 19828 bytes ...brary_publish.cpython-312-pytest-9.1.1.pyc | Bin 32287 -> 43182 bytes 214 files changed, 2612 insertions(+), 637 deletions(-) create mode 100755 apps/syncthing/src/init-syncthing.sh create mode 100644 apps/syncthing/tests/cases.yml create mode 100755 apps/syncthing/tests/check.sh create mode 100644 apps/teamcity/tests/cases.yml create mode 100644 apps/teamcity/tests/check.sh create mode 100644 apps/teleport/src/config/bootstrap.yaml create mode 100644 apps/teleport/tests/cases.yml create mode 100755 apps/teleport/tests/check.sh create mode 100644 apps/tensorflow/tests/cases.yml create mode 100644 apps/thingsboard/tests/cases.yml create mode 100644 apps/thingsboard/tests/check.sh delete mode 100644 apps/tomcat/src/cmd.sh create mode 100644 apps/tomcat/src/entrypoint.d/10-webapps.sh create mode 100644 apps/tomcat/src/entrypoint.sh create mode 100644 apps/tomcat/src/start.sh create mode 100644 apps/tomcat/tests/cases.yml create mode 100644 apps/tomcat/tests/smoke.sh create mode 100644 apps/tomcat/tests/war-deploy.sh delete mode 100644 apps/traefik/Notes.md create mode 100644 apps/traefik/tests/cases.yml create mode 100644 apps/trivy/tests/cases.yml delete mode 100644 apps/typesense/Notes.md create mode 100644 apps/typesense/tests/cases.yml delete mode 100644 apps/typo3/Notes.md create mode 100644 apps/typo3/src/entrypoint.sh create mode 100644 apps/typo3/tests/cases.yml delete mode 100644 apps/umami/Notes.md create mode 100644 apps/umami/tests/cases.yml create mode 100644 apps/umami/tests/check.sh create mode 100644 apps/umbraco/Dockerfile create mode 100644 apps/umbraco/tests/cases.yml delete mode 100644 apps/varnish/Notes.md create mode 100644 apps/xwiki/src/mysql_init.sql create mode 100644 apps/xwiki/tests/cases.yml create mode 100644 apps/xwiki/tests/check.sh rename {apps => archive/apps}/taskingai/.env (100%) rename {apps => archive/apps}/taskingai/CHANGELOG.md (100%) rename {apps => archive/apps}/taskingai/Notes.md (100%) rename {apps => archive/apps}/taskingai/README.md (100%) rename {apps => archive/apps}/taskingai/docker-compose.yml (100%) rename {apps => archive/apps}/taskingai/src/README.md (100%) rename {apps => archive/apps}/taskingai/src/proxy.conf (100%) rename {apps => archive/apps}/taskingai/variables.json (100%) rename {apps => archive/apps}/theia/.env (100%) rename {apps => archive/apps}/theia/CHANGELOG.md (100%) rename {apps => archive/apps}/theia/Notes.md (100%) rename {apps => archive/apps}/theia/README.md (100%) rename {apps => archive/apps}/theia/docker-compose.yml (100%) rename {apps => archive/apps}/theia/src/filelist (100%) rename {apps => archive/apps}/theia/variables.json (100%) rename {apps => archive/apps}/tinyproxy/.env (100%) rename {apps => archive/apps}/tinyproxy/CHANGELOG.md (100%) rename {apps => archive/apps}/tinyproxy/Notes.md (100%) rename {apps => archive/apps}/tinyproxy/README.md (100%) rename {apps => archive/apps}/tinyproxy/docker-compose.yml (100%) rename {apps => archive/apps}/tinyproxy/src/README.md (100%) rename {apps => archive/apps}/tinyproxy/src/tinyproxy.conf (100%) rename {apps => archive/apps}/tinyproxy/variables.json (100%) rename {apps => archive/apps}/tomee/.env (100%) rename {apps => archive/apps}/tomee/CHANGELOG.md (100%) rename {apps => archive/apps}/tomee/Notes.md (100%) rename {apps => archive/apps}/tomee/README.md (100%) rename {apps => archive/apps}/tomee/docker-compose.yml (100%) rename {apps => archive/apps}/tomee/src/README.md (100%) rename {apps => archive/apps}/tomee/src/cmd.sh (100%) rename {apps => archive/apps}/tomee/variables.json (100%) rename {apps => archive/apps}/tooljet/.env (100%) rename {apps => archive/apps}/tooljet/CHANGELOG.md (100%) rename {apps => archive/apps}/tooljet/README.md (100%) rename {apps => archive/apps}/tooljet/docker-compose.yml (100%) rename {apps => archive/apps}/tooljet/src/README.md (100%) rename {apps => archive/apps}/tooljet/src/nginx-proxy.conf (100%) rename {apps => archive/apps}/tooljet/tests/cases.yml (100%) rename {apps => archive/apps}/tooljet/variables.json (100%) rename {apps => archive/apps}/traccar/.env (100%) rename {apps => archive/apps}/traccar/CHANGELOG.md (100%) rename {apps => archive/apps}/traccar/Notes.md (100%) rename {apps => archive/apps}/traccar/README.md (100%) rename {apps => archive/apps}/traccar/docker-compose.yml (100%) rename {apps => archive/apps}/traccar/src/README.md (100%) rename {apps => archive/apps}/traccar/variables.json (100%) rename {apps => archive/apps}/trafficserver/.env (100%) rename {apps => archive/apps}/trafficserver/CHANGELOG.md (100%) rename {apps => archive/apps}/trafficserver/Notes.md (100%) rename {apps => archive/apps}/trafficserver/README.md (100%) rename {apps => archive/apps}/trafficserver/docker-compose.yml (100%) rename {apps => archive/apps}/trafficserver/src/README.md (100%) rename {apps => archive/apps}/trafficserver/src/records.yaml (100%) rename {apps => archive/apps}/trafficserver/src/remap.config (100%) rename {apps => archive/apps}/trafficserver/src/storage.config (100%) rename {apps => archive/apps}/trafficserver/variables.json (100%) rename {apps => archive/apps}/triggerdev/.env (100%) rename {apps => archive/apps}/triggerdev/CHANGELOG.md (100%) rename {apps => archive/apps}/triggerdev/Notes.md (100%) rename {apps => archive/apps}/triggerdev/docker-compose.yml (100%) rename {apps => archive/apps}/triggerdev/src/README.md (100%) rename {apps => archive/apps}/triggerdev/variables.json (100%) rename {apps => archive/apps}/ttrss/.env (100%) rename {apps => archive/apps}/ttrss/CHANGELOG.md (100%) rename {apps => archive/apps}/ttrss/Notes.md (100%) rename {apps => archive/apps}/ttrss/README.md (100%) rename {apps => archive/apps}/ttrss/docker-compose.yml (100%) rename {apps => archive/apps}/ttrss/src/README.md (100%) rename {apps => archive/apps}/ttrss/variables.json (100%) rename {apps => archive/apps}/twenty/.env (100%) rename {apps => archive/apps}/twenty/CHANGELOG.md (100%) rename {apps => archive/apps}/twenty/Notes.md (100%) rename {apps => archive/apps}/twenty/README.md (100%) rename {apps => archive/apps}/twenty/docker-compose.yml (100%) rename {apps => archive/apps}/twenty/src/README.md (100%) rename {apps => archive/apps}/twenty/variables.json (100%) rename {apps => archive/apps}/tyk/.env (100%) rename {apps => archive/apps}/tyk/CHANGELOG.md (100%) rename {apps => archive/apps}/tyk/Notes.md (100%) rename {apps => archive/apps}/tyk/README.md (100%) rename {apps => archive/apps}/tyk/docker-compose.yml (100%) rename {apps => archive/apps}/tyk/src/README.md (100%) rename {apps => archive/apps}/tyk/src/tyk.conf (100%) rename {apps => archive/apps}/tyk/variables.json (100%) rename {apps => archive/apps}/unleash/.env (100%) rename {apps => archive/apps}/unleash/CHANGELOG.md (100%) rename {apps => archive/apps}/unleash/Notes.md (100%) rename {apps => archive/apps}/unleash/README.md (100%) rename {apps => archive/apps}/unleash/docker-compose.yml (100%) rename {apps => archive/apps}/unleash/src/README.md (100%) rename {apps => archive/apps}/unleash/variables.json (100%) diff --git a/Notes.md b/Notes.md index 6f709198c..78aa9c256 100644 --- a/Notes.md +++ b/Notes.md @@ -35,7 +35,7 @@ Semaphore cloudreve,compreface,commafeed, coze,dashy,ejbca, frigate, falcon Qdrant -e2e test for: zammad, vaultwarden, wazuh, varnish +e2e test for: vaultwarden, varnish varnish not have config file pangolin.net RustDesk diff --git a/apps/syncthing/.env b/apps/syncthing/.env index 01bc17aa5..6d6d95bcf 100644 --- a/apps/syncthing/.env +++ b/apps/syncthing/.env @@ -1,15 +1,36 @@ -W9_DIST='community' -W9_VERSION='2.0' W9_REPO=syncthing/syncthing +W9_DIST=community +W9_VERSION=2.1 +W9_POWER_PASSWORD='nM7xQ2pL8vR4sK6d' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='syncthing' + +W9_ID=syncthing W9_HTTP_PORT=8384 -W9_HTTP_PORT_SET='8384' -W9_URL='appname.example.com' +W9_HTTP_PORT_SET=8384 +W9_LOGIN_USER=admin +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} +W9_URL=appname.example.com W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Syncthing image environment variables +# Docs: https://docs.syncthing.net/intro/getting-started.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: PUID=1000 PGID=1000 -SYNC_PATH="/data/websoft9/syncthing" +SYNC_PATH=/data/websoft9/syncthing +STGUIADDRESS=0.0.0.0:8384 + +# Not used by default; enable only when needed: +# STNOUPGRADE=true diff --git a/apps/syncthing/CHANGELOG.md b/apps/syncthing/CHANGELOG.md index 582cf46c5..9e71fce4a 100644 --- a/apps/syncthing/CHANGELOG.md +++ b/apps/syncthing/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Update Syncthing to 2.1 +- Align `.env` and `docker-compose.yml` with current repository env and variable-reference rules +- Refresh metadata links and add app-specific test coverage +- Add first-start GUI username/password initialization from `W9_LOGIN_USER` and `W9_LOGIN_PASSWORD` diff --git a/apps/syncthing/README.md b/apps/syncthing/README.md index bafb6a0fc..1643af5e8 100644 --- a/apps/syncthing/README.md +++ b/apps/syncthing/README.md @@ -1,26 +1,85 @@ -# Syncthing on Docker +# Syncthing on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Syncthing: +## Quick Start +### Deploy Verification - - community: 1.29.4, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Syncthing**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the Syncthing admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://github.com/syncthing/syncthing/blob/main/README-Docker.md): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 2 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this Syncthing by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Syncthing Docker image](https://hub.docker.com/r/syncthing/syncthing) and makes some improvements below. -If you want use Syncthing with **Websoft9 Business Support** free, you can [subscribe Syncthing](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Syncthing Administrator Guide](https://support.websoft9.com/docs/syncthing) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 2.1, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8384 | + + +### Data Directory + + +Data is kept inside the container; a named volume is recommended for persistence. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_LOGIN_USER`, `W9_LOGIN_PASSWORD` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/init-syncthing.sh` to `/websoft9/init-syncthing.sh`. + + +## References + +- [Syncthing Administrator Guide](https://support.websoft9.com/docs/syncthing) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/syncthing/syncthing) + +- [Official docs](https://docs.syncthing.net/intro/getting-started.html) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/syncthing/docker-compose.yml b/apps/syncthing/docker-compose.yml index b3ed613b9..ebba6223e 100644 --- a/apps/syncthing/docker-compose.yml +++ b/apps/syncthing/docker-compose.yml @@ -1,28 +1,23 @@ -# image: https://hub.docker.com/r/syncthing/syncthing -# compose: https://github.com/syncthing/syncthing/blob/main/README-Docker.md -# docs: https://docs.syncthing.net/intro/getting-started.html - -version: "3.8" - services: syncthing: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} hostname: syncthing restart: unless-stopped + entrypoint: + - /bin/sh + - /websoft9/init-syncthing.sh env_file: .env volumes: - - $SYNC_PATH/$W9_ID:/var/syncthing + - ${SYNC_PATH}/${W9_ID}:/var/syncthing + - ./src/init-syncthing.sh:/websoft9/init-syncthing.sh:ro ports: - - ${W9_HTTP_PORT_SET}:8384 + - "${W9_HTTP_PORT_SET}:8384" # Web Console # - 22000:22000/tcp # TCP file transfers # - 22000:22000/udp # QUIC file transfers # - 21027:21027/udp # Receive local discovery broadcasts networks: - default: - name: ${W9_NETWORK} - external: true - -volumes: - sync: + default: + name: ${W9_NETWORK} + external: true diff --git a/apps/syncthing/src/init-syncthing.sh b/apps/syncthing/src/init-syncthing.sh new file mode 100755 index 000000000..37648ce40 --- /dev/null +++ b/apps/syncthing/src/init-syncthing.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu + +CONFIG_DIR="${STHOMEDIR:-/var/syncthing/config}" +CONFIG_FILE="${CONFIG_DIR}/config.xml" + +if [ ! -f "${CONFIG_FILE}" ] && [ -n "${W9_LOGIN_USER:-}" ] && [ -n "${W9_LOGIN_PASSWORD:-}" ]; then + mkdir -p "${CONFIG_DIR}" + syncthing generate \ + --home="${CONFIG_DIR}" \ + --gui-user="${W9_LOGIN_USER}" \ + --gui-password="${W9_LOGIN_PASSWORD}" \ + --no-port-probing >/dev/null +fi + +exec /bin/entrypoint.sh /bin/syncthing diff --git a/apps/syncthing/tests/cases.yml b/apps/syncthing/tests/cases.yml new file mode 100644 index 000000000..a24753cc3 --- /dev/null +++ b/apps/syncthing/tests/cases.yml @@ -0,0 +1,7 @@ +skip: + - id: web-access + +custom: + - id: gui-auth + type: script + script: check.sh diff --git a/apps/syncthing/tests/check.sh b/apps/syncthing/tests/check.sh new file mode 100755 index 000000000..0159d6de9 --- /dev/null +++ b/apps/syncthing/tests/check.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +base="${BASE_URL:?BASE_URL is required}" +deadline=$((SECONDS + 120)) +status="000" + +while [ "$SECONDS" -lt "$deadline" ]; do + status="$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "${base}/rest/system/config" || true)" + [ "${status}" != "000" ] && break + sleep 3 +done + +if [ "${status}" != "403" ]; then + echo "expected anonymous GUI API access to be blocked with HTTP 403, got ${status}" >&2 + exit 1 +fi + +echo "Syncthing GUI API blocks anonymous access as expected" diff --git a/apps/syncthing/variables.json b/apps/syncthing/variables.json index 8f5ee9001..cf846abe1 100644 --- a/apps/syncthing/variables.json +++ b/apps/syncthing/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "2.0", + "2.1", "latest" ] } @@ -16,7 +16,17 @@ "memory": "2", "disk": "2" }, + "env": { + "first_startup_only": [ + "W9_LOGIN_USER", + "W9_LOGIN_PASSWORD" + ] + }, "upstream": { - "image": "https://hub.docker.com/r/syncthing/syncthing" + "image": "https://hub.docker.com/r/syncthing/syncthing", + "docs": [ + "https://docs.syncthing.net/intro/getting-started.html" + ], + "version_notes": "https://github.com/syncthing/syncthing/releases/tag/v2.1.5" } } diff --git a/apps/teamcity/.env b/apps/teamcity/.env index b0759fcfa..ef1104df8 100644 --- a/apps/teamcity/.env +++ b/apps/teamcity/.env @@ -1,18 +1,42 @@ -W9_VERSION='2025.07' -W9_DIST='community' W9_REPO=jetbrains/teamcity-server +W9_DIST=community +W9_VERSION=2026.2 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. W9_POWER_PASSWORD='!cQT8pI4iLmcsnlU' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='teamcity' -W9_HTTP_PORT_SET='8111' + +W9_ID=teamcity W9_HTTP_PORT=8111 -W9_URL='example.youdomain.com' -W9_DB_EXPOSE="mysql" -W9_DB_VERSION="5.7" +W9_HTTP_PORT_SET=8111 + +W9_DB_EXPOSE=mysql +W9_DB_VERSION=8.4 + +# Shown in the Websoft9 console as reference for the TeamCity database setup wizard. +W9_LOGIN_MYSQL_CONNECTION_STRING_PASSWORD="jdbc:mysql://${W9_ID}-mysql:3306/teamcity?user=teamcity&password=${W9_POWER_PASSWORD}" + +W9_URL=example.youdomain.com + +# Random hostname used by the TeamCity server and build agent (no underscores allowed). W9_RCODE='YNUOeQHG8rNmT' + W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### -# Below is Teamcity environments: +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# TeamCity image environment variables +# Docs: https://hub.docker.com/r/jetbrains/teamcity-server +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# ============================================================ + +# Used by docker-compose.yml: +TEAMCITY_SERVER_MEM_OPTS="-Xmx2g -XX:ReservedCodeCacheSize=640m" -TEAMCITY_SERVER_MEM_OPTS="-Xmx2g -XX:MaxPermSize=270m -XX:ReservedCodeCacheSize=640m" +# Not used by default; enable only when needed: diff --git a/apps/teamcity/CHANGELOG.md b/apps/teamcity/CHANGELOG.md index 582cf46c5..e0d4d4f7d 100644 --- a/apps/teamcity/CHANGELOG.md +++ b/apps/teamcity/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Update TeamCity to 2026.2 and the bundled MySQL to 8.4. +- Add upstream releases and official docs references to `variables.json`. +- Refresh `.env` to the current template layout; drop the obsolete `-XX:MaxPermSize` JVM flag. +- Normalize `docker-compose.yml` references to `${VAR}`, remove image source comments, and annotate the published port. +- Regenerate `README.md` so the advertised version matches `2026.2` and document first-run setup and agent authorization. +- Add `tests/cases.yml` and `tests/check.sh` covering the TeamCity HTTP endpoint. +- Add `W9_LOGIN_MYSQL_CONNECTION_STRING` so the Websoft9 console shows the MySQL connection string during first-run setup. diff --git a/apps/teamcity/README.md b/apps/teamcity/README.md index c5f3817aa..ac9379c2c 100644 --- a/apps/teamcity/README.md +++ b/apps/teamcity/README.md @@ -1,26 +1,106 @@ -# TeamCity on Docker +# TeamCity on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for TeamCity: +## Quick Start +### Deploy Verification - - community: 2025.03.1, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **TeamCity**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### First-run setup -## System Requirements +1. Open the app URL; TeamCity shows a maintenance page titled **Confirming TeamCity first start**. +2. Click **I'm a server administrator, show me the details**, then **Proceed**. +3. On **Setting up database connection**, choose **MySQL** and use the **Download** button to fetch the JDBC driver (the image does not bundle it). To install it manually, put `mysql-connector-j-*.jar` in the `teamcity_data` volume under `lib/jdbc`, then click **Refresh JDBC drivers**. +4. Enter the connection details: host `teamcity-mysql`, port `3306`, database `teamcity`, user `teamcity`, password from `W9_POWER_PASSWORD` in `.env`. +5. Accept the driver license, then create the first administrator account. -The following are the minimal [recommended requirements](https://github.com/JetBrains/teamcity-docker-server): +### Build agents -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +The bundled `teamcity-agent` connects to the server automatically but must be authorized: after signing in, open **Agents → Unauthorized** and click **Authorize** for the agent. -## Install +### Change Password -You can install this TeamCity by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +TeamCity accounts are managed inside the application. Change the administrator password from the account profile, or reset other users under **Administration → Users**. + -If you want use TeamCity with **Websoft9 Business Support** free, you can [subscribe TeamCity](https://www.websoft9.com/apps) on Cloud platform +## Configuration Reference -## Documentation +Websoft9 packages this app from the official [TeamCity Docker image](https://hub.docker.com/r/jetbrains/teamcity-server) and makes some improvements below. -[TeamCity Administrator Guide](https://support.websoft9.com/docs/teamcity) powered by Websoft9 \ No newline at end of file + + + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 2026.2, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8111 | + + +### Data Directory + + +- `teamcity_data` → `/data/teamcity_server/datadir` +- `teamcity_logs` → `/opt/teamcity/logs` +- `teamcity_temp` → `/opt/teamcity/temp` +- `agent_conf` → `/data/teamcity_agent/conf` +- `/var/run/docker.sock` → `/var/run/docker.sock` +- `agent_work` → `/opt/buildagent/work` +- `agent_temp` → `/opt/buildagent/temp` +- `agent_tools` → `/opt/buildagent/tools` +- `agent_plugins` → `/opt/buildagent/plugins` +- `agent_system` → `/opt/buildagent/system` +- `agent_logs` → `/opt/buildagent/logs` +- `agent_docker` → `/var/lib/docker` +- `mysql_data` → `/var/lib/mysql` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [TeamCity Administrator Guide](https://support.websoft9.com/docs/teamcity) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/jetbrains/teamcity-server) + +- [Releases](https://www.jetbrains.com/teamcity/download/) + +- [Official docs](https://www.jetbrains.com/help/teamcity/teamcity-documentation.html) + +- [GitHub docs](https://github.com/JetBrains/teamcity-docker-server) + +- [GitHub docs](https://github.com/JetBrains/teamcity-docker-samples) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/teamcity/docker-compose.yml b/apps/teamcity/docker-compose.yml index aec625570..5ec9ab265 100644 --- a/apps/teamcity/docker-compose.yml +++ b/apps/teamcity/docker-compose.yml @@ -1,13 +1,6 @@ -# image: https://hub.docker.com/r/jetbrains/teamcity-server -# docs: https://www.jetbrains.com/help/teamcity/teamcity-documentation.html -# https://github.com/JetBrains/teamcity-docker-server -# compose: https://github.com/JetBrains/teamcity-docker-samples/blob/master/compose-ubuntu/docker-compose.yml -# volumes: https://github.com/JetBrains/teamcity-docker-images/blob/master/dockerhub/teamcity-agent/README.md - -version: '3.8' services: teamcity-server: - image: jetbrains/teamcity-server:${W9_VERSION} + image: ${W9_REPO}:${W9_VERSION} container_name: ${W9_ID} restart: unless-stopped hostname: ${W9_RCODE} @@ -15,7 +8,7 @@ services: - teamcity-mysql env_file: .env ports: - - "${W9_HTTP_PORT_SET}:8111" + - "${W9_HTTP_PORT_SET}:8111" # Web Console volumes: - teamcity_data:/data/teamcity_server/datadir - teamcity_logs:/opt/teamcity/logs @@ -27,7 +20,7 @@ services: restart: unless-stopped privileged: true environment: - - SERVER_URL=${W9_RCODE}:8111 # If use W9_ID which include _, agent can't connect Teamcity + - SERVER_URL=${W9_RCODE}:8111 # TeamCity hostname must not contain underscores - AGENT_NAME=${W9_ID}-agent - OWN_PORT=9090 - DOCKER_IN_DOCKER=start @@ -41,9 +34,9 @@ services: - agent_system:/opt/buildagent/system - agent_logs:/opt/buildagent/logs - agent_docker:/var/lib/docker - + teamcity-mysql: - image: mysql:$W9_DB_VERSION + image: mysql:${W9_DB_VERSION} container_name: ${W9_ID}-mysql environment: - MYSQL_ROOT_PASSWORD=${W9_POWER_PASSWORD} diff --git a/apps/teamcity/tests/cases.yml b/apps/teamcity/tests/cases.yml new file mode 100644 index 000000000..f2eb1661a --- /dev/null +++ b/apps/teamcity/tests/cases.yml @@ -0,0 +1,7 @@ +skip: + - id: web-access + +optional: + - id: server-http + type: script + script: check.sh diff --git a/apps/teamcity/tests/check.sh b/apps/teamcity/tests/check.sh new file mode 100644 index 000000000..403e2d2c5 --- /dev/null +++ b/apps/teamcity/tests/check.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +base="${BASE_URL:?BASE_URL is required}" +body="$(mktemp)" +trap 'rm -f "$body"' EXIT + +status="$(curl -sS -o "$body" -w '%{http_code}' "${base}/")" + +# TeamCity serves HTTP 503 from its maintenance page until the first start is +# confirmed and the database connection is configured; it serves 200 afterwards. +case "$status" in + 200 | 503) ;; + *) + echo "unexpected HTTP ${status} from TeamCity" >&2 + exit 1 + ;; +esac + +if ! grep -qi 'TeamCity' "$body"; then + echo "response does not look like a TeamCity page" >&2 + exit 1 +fi + +echo "TeamCity server responded with HTTP ${status}" diff --git a/apps/teamcity/variables.json b/apps/teamcity/variables.json index b79836878..b1df48cfb 100644 --- a/apps/teamcity/variables.json +++ b/apps/teamcity/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "2025.07", + "2026.2", "latest" ] } @@ -17,6 +17,12 @@ "disk": "1" }, "upstream": { - "image": "https://hub.docker.com/r/jetbrains/teamcity-server" + "image": "https://hub.docker.com/r/jetbrains/teamcity-server", + "releases": "https://www.jetbrains.com/teamcity/download/", + "docs": [ + "https://www.jetbrains.com/help/teamcity/teamcity-documentation.html", + "https://github.com/JetBrains/teamcity-docker-server", + "https://github.com/JetBrains/teamcity-docker-samples" + ] } } diff --git a/apps/teleport/.env b/apps/teleport/.env index b2e4f8eb1..a80b26e8a 100644 --- a/apps/teleport/.env +++ b/apps/teleport/.env @@ -1,18 +1,35 @@ -W9_REPO=public.ecr.aws/gravitational/teleport +W9_REPO=public.ecr.aws/gravitational/teleport-distroless W9_DIST=community -# get version from: https://gallery.ecr.aws/gravitational/teleport -W9_VERSION=14.0 +# get version from: https://gallery.ecr.aws/gravitational/teleport-distroless +W9_VERSION=18.10 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### + W9_ID=teleport W9_HTTPS_PORT=3080 W9_HTTPS_PORT_SET=9001 -W9_LOGIN_GET_USER="Run command at Teleport container: [tctl users add admin --roles=editor,auditor,access --logins=root,ubuntu,ec2-user]" +W9_LOGIN_GET_USER="The admin user is created automatically. Run at the Teleport container to get the password setup link: [tctl users reset admin]" -# Must use Domain, IP can not use -W9_URL=example.domain.com +# Teleport requires a domain name; an IP address cannot be used. +W9_URL=example.yourdomain.com W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### -## Environment of Teleport, need research \ No newline at end of file +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Teleport image environment variables +# Docs: https://goteleport.com/docs/installation/single-machine/docker/ +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +# Teleport 18 refuses to disable the second factor unless this escape hatch is set. +TELEPORT_ALLOW_NO_SECOND_FACTOR=true + +# Not used by default; enable only when needed: diff --git a/apps/teleport/CHANGELOG.md b/apps/teleport/CHANGELOG.md index 582cf46c5..3e4fd24ef 100644 --- a/apps/teleport/CHANGELOG.md +++ b/apps/teleport/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Bump Teleport from `14.0` to `18.10` and switch the image to the production `public.ecr.aws/gravitational/teleport-distroless`; the old `public.ecr.aws/gravitational/teleport` image stops at `14.4.x`. +- Regenerate `src/config/teleport.yaml` for the v18 schema. Teleport 18 rejects `second_factor: off` unless `TELEPORT_ALLOW_NO_SECOND_FACTOR=true`; the package sets that escape hatch and disables MFA for local users. +- Auto-create the first administrator and a full-access `admin` role on first start from `src/config/bootstrap.yaml` (`--bootstrap`); generate the password setup link with `tctl users reset admin`. +- Add `restart: unless-stopped` and a `tctl status` healthcheck; remove the deprecated `version:` key and the unused `teleport_config` volume. +- Add `upstream` metadata and the web access entry to `variables.json`. +- Add `tests/cases.yml` with an HTTPS `/webapi/ping` check. +- Note: existing 14.x cluster data cannot be upgraded directly to 18.x; upstream requires one major version at a time (14→15→16→17→18). Fresh deployments are unaffected. diff --git a/apps/teleport/Notes.md b/apps/teleport/Notes.md index 867cfb887..324c50489 100644 --- a/apps/teleport/Notes.md +++ b/apps/teleport/Notes.md @@ -1,5 +1,11 @@ # Teleport -- Need create teleport.yaml before create container: https://goteleport.com/docs/installation/#running-teleport-on-docker -- Need HTTPS access -- Need set configure item [proxy_service - public_addr:url:443], 443 is need otherwise url will add 3080 \ No newline at end of file +- Teleport Community Edition 18 runs from the production `public.ecr.aws/gravitational/teleport-distroless` image (no shell). Use `docker exec /usr/local/bin/tctl ...` for in-container commands. +- On first start the container creates the `admin` user and a full-access `admin` role from `src/config/bootstrap.yaml` (applied with `--bootstrap`, ignored on later starts). +- The admin has no password yet. Generate the setup link with `docker exec /usr/local/bin/tctl users reset admin` and open it to set the password. MFA is disabled. +- MFA is disabled for local users with `authentication.second_factor: off` plus `TELEPORT_ALLOW_NO_SECOND_FACTOR=true`. Remove both to require MFA. +- The config file `src/config/teleport.yaml` is mounted at `/etc/teleport/teleport.yaml`. Set `W9_URL`, `teleport.nodename`, `auth_service.cluster_name`, and `proxy_service.public_addr` to the domain that resolves to this host. Keep the `:443` port in `proxy_service.public_addr` so generated URLs omit the internal `3080` port. +- The Web UI and API are HTTPS-only on port `3080` with a self-signed certificate. Check them with: + `curl -k https://:/webapi/ping` +- Regenerate a starter config with: + `docker run --rm --entrypoint /usr/local/bin/teleport public.ecr.aws/gravitational/teleport-distroless:18.10 configure --roles=proxy,auth,ssh` diff --git a/apps/teleport/README.md b/apps/teleport/README.md index 5ea84ea4f..e217e1cbb 100644 --- a/apps/teleport/README.md +++ b/apps/teleport/README.md @@ -1,26 +1,82 @@ -# Teleport on Docker +# Teleport on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Teleport: +## Quick Start +### Deploy Verification - - community: 14.0, 13.0 +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Teleport**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Set `W9_URL` and `proxy_service.public_addr` in `src/config/teleport.yaml` to the domain that resolves to this host. +2. Start the app. On first run it creates the `admin` user automatically. +3. Generate the password setup link with `docker exec /usr/local/bin/tctl users reset admin`, then open it to set the admin password. MFA is disabled; enable it later from the Web UI if needed. -The following are the minimal [recommended requirements](https://gallery.ecr.aws/gravitational/teleport): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Reset the user from inside the container: `docker exec /usr/local/bin/tctl users reset `. +2. Open the printed reset URL to set a new password. + -## Install +## Configuration Reference -You can install this Teleport by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Teleport Docker image](https://gallery.ecr.aws/gravitational/teleport-distroless) and makes some improvements below. -If you want use Teleport with **Websoft9 Business Support** free, you can [subscribe Teleport](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Teleport Administrator Guide](https://support.websoft9.com/docs/teleport) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 18.10, 18. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Teleport Proxy HTTPS (Web UI + API) | 3080 | + + +### Data Directory + + +Data is persisted in the `teleport_data` volume, mounted at `/var/lib/teleport`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/config` to `/etc/teleport`. + + +## References + +- [Teleport Administrator Guide](https://support.websoft9.com/docs/teleport) by Websoft9 + +- [Docker Hub image](https://gallery.ecr.aws/gravitational/teleport-distroless) + +- [Releases](https://github.com/gravitational/teleport/releases) + +- [Official docs](https://goteleport.com/docs/installation/single-machine/docker/) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/teleport/docker-compose.yml b/apps/teleport/docker-compose.yml index 80400213d..31ce5bef5 100644 --- a/apps/teleport/docker-compose.yml +++ b/apps/teleport/docker-compose.yml @@ -1,23 +1,25 @@ -# image: https://gallery.ecr.aws/gravitational/teleport -# docs: https://goteleport.com/docs/management/guides/docker/ -# docs: https://goteleport.com/docs/try-out-teleport/docker-compose/ - - -version: '3.8' - services: + teleport: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} hostname: localhost + restart: unless-stopped + entrypoint: ["/usr/bin/dumb-init", "/usr/local/bin/teleport", "start", "-c", "/etc/teleport/teleport.yaml", "--bootstrap=/etc/teleport/bootstrap.yaml"] + env_file: .env ports: - - ${W9_HTTPS_PORT_SET}:3080 # HTTPS for API - #- 3025:3025 # SSH port - #- 3023:3023 # Node Tunneling + - "${W9_HTTPS_PORT_SET}:3080" # Teleport Proxy HTTPS (Web UI + API) + # - "3025:3025" # Auth Service (internal) + # - "3023:3023" # Node Tunneling (internal) volumes: - ./src/config:/etc/teleport - teleport_data:/var/lib/teleport - env_file: .env + healthcheck: + test: ["CMD", "/usr/local/bin/tctl", "status"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s networks: default: @@ -25,5 +27,4 @@ networks: external: true volumes: - teleport_config: - teleport_data: \ No newline at end of file + teleport_data: diff --git a/apps/teleport/src/config/bootstrap.yaml b/apps/teleport/src/config/bootstrap.yaml new file mode 100644 index 000000000..b488743bd --- /dev/null +++ b/apps/teleport/src/config/bootstrap.yaml @@ -0,0 +1,31 @@ +# +# Bootstrap resources, applied once on the cluster's first start. +# +# Creates the first administrator and a full-access role. The account has no +# password yet: generate a setup link with +# docker exec /usr/local/bin/tctl users reset admin +# and open it to set the password. MFA is disabled (see teleport.yaml). +# +# Applied with --bootstrap, so it is ignored once the cluster is initialized. +# +kind: role +version: v7 +metadata: + name: admin +spec: + options: + max_session_ttl: 12h + allow: + logins: ["root", "ubuntu", "ec2-user"] + node_labels: + "*": "*" + rules: + - resources: ["*"] + verbs: ["*"] +--- +kind: user +version: v2 +metadata: + name: admin +spec: + roles: ["admin"] diff --git a/apps/teleport/src/config/teleport.yaml b/apps/teleport/src/config/teleport.yaml index 9c1a6333c..2ce218174 100644 --- a/apps/teleport/src/config/teleport.yaml +++ b/apps/teleport/src/config/teleport.yaml @@ -1,38 +1,49 @@ -# -# A Sample Teleport configuration file. -# -# Things to update: -# 1. license.pem: Retrieve a license from your Teleport account https://teleport.sh -# if you are an Enterprise customer. -# -version: v3 -teleport: - nodename: localhost - data_dir: /var/lib/teleport - log: - output: stderr - severity: INFO - format: - output: text - ca_pin: "" - diag_addr: "" -auth_service: - enabled: "yes" - listen_addr: 0.0.0.0:3025 - proxy_listener_mode: multiplex - authentication: - type: local - second_factor: off -ssh_service: - enabled: "yes" - commands: - - name: hostname - command: [hostname] - period: 1m0s -proxy_service: - enabled: "yes" - https_keypairs: [] - https_keypairs_reload_interval: 0s - acme: {} - public_addr: - - 'example.yourdomain.com:443' \ No newline at end of file +# +# Teleport configuration file (v18 schema). +# +# Things to update: +# 1. teleport.nodename / auth_service.cluster_name / proxy_service.public_addr: +# set them to the domain that resolves to this host. +# 2. proxy_service.public_addr: keep the ":443" port so generated URLs omit the +# internal 3080 port. +# 3. license.pem: retrieve a license from https://teleport.sh only if you are an +# Enterprise customer. +# +# MFA is disabled for local users (second_factor: off). Teleport 18 refuses this +# unless the container runs with TELEPORT_ALLOW_NO_SECOND_FACTOR=true, which the +# package sets in .env. Remove both to require MFA. +# +# Regenerate a starter file with: +# docker run --rm --entrypoint /usr/local/bin/teleport \ +# public.ecr.aws/gravitational/teleport-distroless:18.10 \ +# configure --roles=proxy,auth,ssh +# +version: v3 +teleport: + nodename: localhost + data_dir: /var/lib/teleport + log: + output: stderr + severity: INFO + format: + output: text + ca_pin: "" + diag_addr: "" +auth_service: + enabled: "yes" + listen_addr: 0.0.0.0:3025 + cluster_name: teleport.example.com + proxy_listener_mode: multiplex + authentication: + type: local + second_factor: off +ssh_service: + enabled: "yes" +proxy_service: + enabled: "yes" + web_listen_addr: 0.0.0.0:3080 + https_keypairs: [] + https_keypairs_reload_interval: 0s + acme: {} + public_addr: + - 'example.yourdomain.com:443' diff --git a/apps/teleport/tests/cases.yml b/apps/teleport/tests/cases.yml new file mode 100644 index 000000000..ae859c5d0 --- /dev/null +++ b/apps/teleport/tests/cases.yml @@ -0,0 +1,10 @@ +# Teleport serves its Web UI and API over HTTPS with a self-signed certificate, +# so the adaptive HTTP web-access check does not apply. The custom script waits +# for the web API over HTTPS instead. +skip: + - id: web-access + +optional: + - id: webapi-https + type: script + script: check.sh diff --git a/apps/teleport/tests/check.sh b/apps/teleport/tests/check.sh new file mode 100755 index 000000000..1a4c0afb3 --- /dev/null +++ b/apps/teleport/tests/check.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -uo pipefail + +port="${W9_HTTPS_PORT_SET:-9001}" +base="${BASE_URL:-https://localhost:${port}}" +deadline=$((SECONDS + 300)) +code="000" + +while [ "$SECONDS" -lt "$deadline" ]; do + code=$(curl -k -s -o /dev/null -w "%{http_code}" --max-time 15 "${base}/webapi/ping" || true) + if [ "$code" = "200" ]; then + echo "teleport webapi ${base}/webapi/ping -> ${code}" + exit 0 + fi + sleep 5 +done + +echo "teleport webapi ${base}/webapi/ping -> ${code} (timeout)" +exit 1 diff --git a/apps/teleport/variables.json b/apps/teleport/variables.json index 403108a44..49cde453a 100644 --- a/apps/teleport/variables.json +++ b/apps/teleport/variables.json @@ -2,15 +2,28 @@ "name": "teleport", "trademark": "Teleport", "release": true, + "upstream": { + "image": "https://gallery.ecr.aws/gravitational/teleport-distroless", + "releases": "https://github.com/gravitational/teleport/releases", + "docs": [ + "https://goteleport.com/docs/installation/single-machine/docker/" + ] + }, "edition": [ { "dist": "community", "version": [ - "14.0", - "13.0" + "18.10", + "18" ] } ], + "access": { + "web": { + "port": 3080, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", diff --git a/apps/tensorflow/.env b/apps/tensorflow/.env index 29e117fd1..5df25bbca 100644 --- a/apps/tensorflow/.env +++ b/apps/tensorflow/.env @@ -1,12 +1,28 @@ -W9_VERSION='2.19.0-jupyter' -W9_DIST='community' W9_REPO=tensorflow/tensorflow +W9_DIST=community +W9_VERSION=2.20.0-jupyter + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='tensorflow' +W9_ID=tensorflow W9_HTTP_PORT=8888 -W9_HTTP_PORT_SET='8888' -W9_GUI_PORT_SET='6006' -W9_URL='appname.example.com' +W9_HTTP_PORT_SET=8888 +W9_GUI_PORT_SET=6006 +W9_URL=appname.example.com W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Tensorflow image environment variables +# Docs: https://hub.docker.com/r/tensorflow/tensorflow +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: + +# Not used by default; enable only when needed: diff --git a/apps/tensorflow/CHANGELOG.md b/apps/tensorflow/CHANGELOG.md index 582cf46c5..8f29d2e5c 100644 --- a/apps/tensorflow/CHANGELOG.md +++ b/apps/tensorflow/CHANGELOG.md @@ -1,5 +1,7 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Updated Tensorflow community package from `2.19.0-jupyter` to `2.20.0-jupyter`. +- Normalized `.env` and `docker-compose.yml` to current repository policy for variable formatting and port comments. +- Added app-specific functional test coverage metadata for the Jupyter entry path. diff --git a/apps/tensorflow/README.md b/apps/tensorflow/README.md index e6b01a697..64bd8fb2d 100644 --- a/apps/tensorflow/README.md +++ b/apps/tensorflow/README.md @@ -3,7 +3,7 @@ This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Tensorflow: - - community: 2.18.0-jupyter, latest-jupyter + - community: 2.20.0-jupyter, latest-jupyter ## System Requirements @@ -23,4 +23,4 @@ If you want use Tensorflow with **Websoft9 Business Support** free, you can [sub ## Documentation -[Tensorflow Administrator Guide](https://support.websoft9.com/docs/tensorflow) powered by Websoft9 \ No newline at end of file +[Tensorflow Administrator Guide](https://support.websoft9.com/docs/tensorflow) powered by Websoft9 diff --git a/apps/tensorflow/docker-compose.yml b/apps/tensorflow/docker-compose.yml index 70d4086bf..23c19e02b 100644 --- a/apps/tensorflow/docker-compose.yml +++ b/apps/tensorflow/docker-compose.yml @@ -1,17 +1,11 @@ -# image: https://hub.docker.com/r/tensorflow/tensorflow -# docs: https://github.com/tensorflow/tensorflow - -version: '3.8' - services: tensorflow: image: ${W9_REPO}:${W9_VERSION} container_name: ${W9_ID} restart: unless-stopped ports: - # tensorboard port is 6006, but you need to start it on container by yourself - - ${W9_HTTP_PORT_SET}:8888 - - ${W9_GUI_PORT_SET}:6006 + - "${W9_HTTP_PORT_SET}:8888" # Jupyter Notebook + - "${W9_GUI_PORT_SET}:6006" # TensorBoard env_file: .env volumes: - tensorflow:/tf/notebooks diff --git a/apps/tensorflow/tests/cases.yml b/apps/tensorflow/tests/cases.yml new file mode 100644 index 000000000..7902908f0 --- /dev/null +++ b/apps/tensorflow/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: notebook-ui + type: web-access + path: /lab + expect_status: 200 diff --git a/apps/tensorflow/variables.json b/apps/tensorflow/variables.json index 8d1fd4af3..94d93997f 100644 --- a/apps/tensorflow/variables.json +++ b/apps/tensorflow/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "2.19.0-jupyter", + "2.20.0-jupyter", "latest-jupyter" ] } diff --git a/apps/thingsboard/.env b/apps/thingsboard/.env index 7736adf6d..290743e63 100644 --- a/apps/thingsboard/.env +++ b/apps/thingsboard/.env @@ -1,19 +1,39 @@ -W9_DIST='community' W9_REPO=thingsboard/tb-node -W9_VERSION='4.2.0' +W9_DIST=community +W9_VERSION=4.3.1 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='thingsboard' + +W9_ID=thingsboard W9_HTTP_PORT=8080 -W9_HTTP_PORT_SET='9009' -W9_URL='appname.example.com' +W9_HTTP_PORT_SET=9009 + +W9_DB_EXPOSE=postgresql +W9_DB_VERSION=18 + W9_LOGIN_USER=sysadmin@thingsboard.org W9_LOGIN_PASSWORD=sysadmin + +W9_URL=appname.example.com + W9_NETWORK=websoft9 -W9_DB_EXPOSE='postgresql' -#### --------------------------------------------------------------------------------------- #### + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# ThingsBoard image environment variables +# Docs: https://thingsboard.io/docs/installation/docker/ +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# ============================================================ + +# Used by docker-compose.yml: POSTGRESQL_USER=postgres POSTGRESQL_PASSWORD=postgres POSTGRESQL_DATABASE_NAME=thingsboard POSTGRESQL_HOST=${W9_ID}-postgres POSTGRESQL_PORT=5432 + +# Not used by default; enable only when needed: diff --git a/apps/thingsboard/CHANGELOG.md b/apps/thingsboard/CHANGELOG.md index 582cf46c5..2696e2f5f 100644 --- a/apps/thingsboard/CHANGELOG.md +++ b/apps/thingsboard/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG -## Release +## 2026-09-21 ### Fixes and Enhancements - +- Update ThingsBoard to 4.3.1 and the bundled PostgreSQL to 18. +- Correct `variables.json` upstream image from `thingsboard/tb-postgres` to `thingsboard/tb-node`; add releases and official docs references. +- Refresh `.env` to the current template layout and add `W9_DB_VERSION`. +- Normalize `docker-compose.yml` references to `${VAR}`, remove image source comments, and annotate the published port. +- Regenerate `README.md` so the advertised version matches `4.3.1`. +- Add `tests/cases.yml` and `tests/check.sh` covering the ThingsBoard REST login. diff --git a/apps/thingsboard/README.md b/apps/thingsboard/README.md index 78e343702..c64353322 100644 --- a/apps/thingsboard/README.md +++ b/apps/thingsboard/README.md @@ -1,26 +1,86 @@ -# ThingsBoard on Docker +# ThingsBoard on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for ThingsBoard: +## Quick Start +### Deploy Verification - - community: 3.9.1, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **ThingsBoard**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the app URL and sign in with the default System Administrator account `sysadmin@thingsboard.org` / `sysadmin`. +2. The bundled init service loads demo tenants, devices, and dashboards so you can explore the platform right away. -The following are the minimal [recommended requirements](https://thingsboard.io/docs/user-guide/install/docker): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 2 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to the ThingsBoard web UI. +2. Open the account menu in the top-right corner, choose **Account**, and change the password. +3. Administrators can reset other users' passwords under **Users**. + -## Install +## Configuration Reference -You can install this ThingsBoard by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [ThingsBoard Docker image](https://hub.docker.com/r/thingsboard/tb-node) and makes some improvements below. -If you want use ThingsBoard with **Websoft9 Business Support** free, you can [subscribe ThingsBoard](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[ThingsBoard Administrator Guide](https://support.websoft9.com/docs/thingsboard) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 4.3.1, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | + + +### Data Directory + + +Data is persisted in the `postgres-data` volume, mounted at `/var/lib/postgresql`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [ThingsBoard Administrator Guide](https://support.websoft9.com/docs/thingsboard) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/thingsboard/tb-node) + +- [Releases](https://github.com/thingsboard/thingsboard/releases) + +- [Official docs](https://thingsboard.io/docs/installation/docker/) + +- [GitHub docs](https://github.com/thingsboard/thingsboard) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/thingsboard/docker-compose.yml b/apps/thingsboard/docker-compose.yml index b06dc09fa..35955c3ad 100644 --- a/apps/thingsboard/docker-compose.yml +++ b/apps/thingsboard/docker-compose.yml @@ -1,7 +1,3 @@ -# image: https://hub.docker.com/r/thingsboard/tb-postgres/ -# docs: https://thingsboard.io/docs/user-guide/install/docker/ - - services: thingsboard-ce: restart: unless-stopped @@ -10,7 +6,7 @@ services: env_file: - .env ports: - - ${W9_HTTP_PORT_SET}:8080 + - "${W9_HTTP_PORT_SET}:8080" # Web Console logging: driver: "json-file" options: @@ -25,14 +21,14 @@ services: postgres: restart: unless-stopped - image: postgres:16 + image: postgres:${W9_DB_VERSION} container_name: ${W9_ID}-postgres environment: - POSTGRES_USER=${POSTGRESQL_USER} - POSTGRES_DB=${POSTGRESQL_DATABASE_NAME} - POSTGRES_PASSWORD=${POSTGRESQL_PASSWORD} volumes: - - postgres-data:/var/lib/postgresql/data + - postgres-data:/var/lib/postgresql healthcheck: test: ["CMD", "pg_isready", "-U", "${POSTGRESQL_USER}", "-d", "${POSTGRESQL_DATABASE_NAME}"] interval: 5s diff --git a/apps/thingsboard/tests/cases.yml b/apps/thingsboard/tests/cases.yml new file mode 100644 index 000000000..95b4ad144 --- /dev/null +++ b/apps/thingsboard/tests/cases.yml @@ -0,0 +1,4 @@ +optional: + - id: api-login + type: script + script: check.sh diff --git a/apps/thingsboard/tests/check.sh b/apps/thingsboard/tests/check.sh new file mode 100644 index 000000000..7d23329a2 --- /dev/null +++ b/apps/thingsboard/tests/check.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +base="${BASE_URL:?BASE_URL is required}" +user="${W9_LOGIN_USER:?W9_LOGIN_USER is required}" +password="${W9_LOGIN_PASSWORD:?W9_LOGIN_PASSWORD is required}" + +response_file="$(mktemp)" +trap 'rm -f "$response_file"' EXIT + +status="$(curl -sS -o "$response_file" -w '%{http_code}' \ + -X POST "${base}/api/auth/login" \ + -H 'Content-Type: application/json' \ + --data "{\"username\":\"${user}\",\"password\":\"${password}\"}")" + +if [ "$status" != "200" ]; then + echo "login failed with HTTP ${status}" >&2 + cat "$response_file" >&2 + exit 1 +fi + +if ! grep -q '"token"' "$response_file"; then + echo "login response has no token" >&2 + cat "$response_file" >&2 + exit 1 +fi + +echo "ThingsBoard API login succeeded" diff --git a/apps/thingsboard/variables.json b/apps/thingsboard/variables.json index 51c19764a..465a4b4e9 100644 --- a/apps/thingsboard/variables.json +++ b/apps/thingsboard/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "4.2.0", + "4.3.1", "latest" ] } @@ -17,6 +17,11 @@ "disk": "2" }, "upstream": { - "image": "https://hub.docker.com/r/thingsboard/tb-postgres" + "image": "https://hub.docker.com/r/thingsboard/tb-node", + "releases": "https://github.com/thingsboard/thingsboard/releases", + "docs": [ + "https://thingsboard.io/docs/installation/docker/", + "https://github.com/thingsboard/thingsboard" + ] } } diff --git a/apps/tomcat/.env b/apps/tomcat/.env index 4d3ecb7ce..f0d5c53f8 100644 --- a/apps/tomcat/.env +++ b/apps/tomcat/.env @@ -1,10 +1,36 @@ W9_REPO=tomcat W9_DIST=community -W9_VERSION=10 +W9_VERSION=11.0-jdk21-temurin + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### W9_ID=tomcat + +# Web/internal ports W9_HTTP_PORT=8080 W9_HTTP_PORT_SET=8080 + +# URL helper W9_URL=example.domain.com -W9_NETWORK=websoft9 \ No newline at end of file +W9_NETWORK=websoft9 + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Tomcat image environment variables +# Docs: https://hub.docker.com/_/tomcat +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# The container runs the official Tomcat image with a mounted entrypoint +# (src/entrypoint.sh) that runs hooks before Tomcat starts. +# ============================================================ + +# Used by docker-compose.yml: + +# Not used by default; enable only when needed: +# CATALINA_OPTS= +# JAVA_OPTS= +# CATALINA_OUT= +# CATALINA_TMPDIR= diff --git a/apps/tomcat/CHANGELOG.md b/apps/tomcat/CHANGELOG.md index 582cf46c5..ba16b4ade 100644 --- a/apps/tomcat/CHANGELOG.md +++ b/apps/tomcat/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Update Tomcat from the floating `10` major tag to `11.0-jdk21-temurin` (Tomcat 11 stable line with JDK21 LTS). +- Refresh the `variables.json` supported tags to the current upstream temurin variants (`11.0` / `10.1` / `9.0` with jdk25/21/17/11/8); drop the `corretto` tags that upstream no longer publishes. +- Align `.env` and `docker-compose.yml` with current repository policy: braced variable references, inline published-port comment, image-env section banner, removal of the `# image:` source comment and obsolete `version`, and a main-container healthcheck. +- Add `apps/tomcat/tests/cases.yml` with a welcome-page smoke test and a WAR auto-deploy test (`war-deploy.sh`) that builds a tiny WAR inside the container and verifies the context. +- Adopt the shared runtime startup mechanism: `src/entrypoint.sh` orchestrator + `src/entrypoint.d/10-webapps.sh` + `src/start.sh`, replacing `src/cmd.sh`. User hooks can be added under `/usr/local/tomcat/.w9/entrypoint.d` without rebuilding, and Tomcat now starts via `exec` as PID 1. +- Regenerate `README.md` from `variables.json` and `docker-compose.yml`. diff --git a/apps/tomcat/Notes.md b/apps/tomcat/Notes.md index 3e7bf9463..37abe7bdb 100644 --- a/apps/tomcat/Notes.md +++ b/apps/tomcat/Notes.md @@ -1,14 +1,51 @@ -## Tomcat +# Tomcat Notes -### 运行 war 包 +> 内部维护说明;面向客户的文档以 `README.md` 为准。 -进入 **tomcat** 容器,下载官方示例,会自动解压 +## 来源 +- 官方镜像:https://hub.docker.com/_/tomcat +- 镜像源码:https://github.com/docker-library/tomcat +- 版本列表:https://hub.docker.com/_/tomcat/tags + +## 版本 + +- `W9_VERSION=11.0-jdk21-temurin`:Tomcat 11 稳定线 + JDK21 LTS。 +- `variables.json` 只保留当前上游仍发布的 temurin 变体(`11.0` / `10.1` / `9.0`);官方已移除 `corretto` 变体,故不再列出。 +- 数据卷 `tomcat:/usr/local/tomcat` 持久化整个 Tomcat 目录(含 `webapps` 与 `conf`)。 + +## 启动机制(runtime-app 约定) + +参照 `docs/runtime-app-spec.md`,与 `springboot` 一致,但**不**引入非 root 用户/permissions 侧车(Tomcat 官方镜像以 root 运行,改动风险大)。 + +``` +src/entrypoint.sh # orchestrator,挂到 /opt/websoft9/entrypoint.sh +src/entrypoint.d/*.sh # 包内钩子,挂到 /opt/websoft9/entrypoint.d(只读) +src/start.sh # 默认启动,挂到 /opt/websoft9/start.sh(只读) ``` -cd /usr/local/tomcat/webapps && wget https://tomcat.apache.org/tomcat-10.0-doc/appdev/sample/sample.war -cd /usr/local/tomcat/webapps && wget https://tomcat.apache.org/tomcat-10.0-doc/appdev/sample/sample.war -O ROOT.war +- 钩子来源两处,同名用户钩子覆盖包内钩子,按文件名排序,每次启动都执行且必须幂等: + - 包内:`/opt/websoft9/entrypoint.d` + - 用户:`${APP_DIR}/.w9/entrypoint.d`,即 `/usr/local/tomcat/.w9/entrypoint.d` +- `APP_DIR=/usr/local/tomcat`(复用数据卷);用户可放 `${APP_DIR}/.w9/start.sh` 覆盖默认启动。 +- 默认钩子 `10-webapps.sh`:`cp -a webapps.dist/. webapps/`,恢复 ROOT/docs/examples 默认应用。 +- `start.sh` 用 `exec catalina.sh run`,保证 Tomcat 是 PID1、能收到 SIGTERM。 +- 与 runtime-app-spec 的差异:不使用 `DATABASE_URL` 覆盖,不使用非 root 用户。 +## 运行 war 包 + +进入 **tomcat** 容器,下载官方示例,会自动解压: + +``` +cd /usr/local/tomcat/webapps && wget https://tomcat.apache.org/tomcat-11.0-doc/appdev/sample/sample.war +cd /usr/local/tomcat/webapps && wget https://tomcat.apache.org/tomcat-11.0-doc/appdev/sample/sample.war -O ROOT.war ``` -ROOT.war 会自动解压到根目录,而不包含路径 \ No newline at end of file +`ROOT.war` 会自动解压到根目录(不包含路径)。 + +## 测试 + +- `tests/cases.yml`:默认自适应检查(compose-config / container-up / container-healthy / web-access `/`)之外,加两个 `script` 用例: + - `smoke.sh`:校验欢迎页内容,证明 `10-webapps.sh` 的默认应用已恢复。 + - `war-deploy.sh`:在容器内用 `jar` 造一个极小 WAR,放进 `webapps/`,等待自动解压并校验 context,验证真实 WAR 部署路径。 +- `script` 用例默认在**部署目标**执行(remote 时走 SSH,见 `docs/app-tests.md`),因此 `war-deploy.sh` 可以使用远端 `docker exec`;`BASE_URL` 在远端被改写为 `http://localhost:${W9_HTTP_PORT_SET}`。 diff --git a/apps/tomcat/README.md b/apps/tomcat/README.md index 659c6731d..d3cd0bffd 100644 --- a/apps/tomcat/README.md +++ b/apps/tomcat/README.md @@ -1,26 +1,94 @@ -# Tomcat on Docker +# Tomcat on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Tomcat : +## Quick Start +### Deploy Verification - - community: 11.0-jdk21-temurin, 10-jdk21-temurin, 10-jdk17-temurin, 10-jdk11-temurin, 9-jdk21-temurin, 9-jdk17-temurin, 9-jdk11-temurin, 9-jdk8-temurin, 9-jdk21-corretto, 9-jdk17-corretto, 9-jdk11-corretto, 9-jdk8-corretto +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Tomcat**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +Tomcat serves the default webapps (ROOT, docs, examples) on port `8080`; this package has no separate admin console. -The following are the minimal [recommended requirements](https://tomcat.apache.org/): +1. In the Websoft9 console, open **My Apps → Tomcat → Access** to get the URL (`http://:8080`). +2. Open it in a browser; you should see the Apache Tomcat welcome page. -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +### Deploy a WAR -## Install +1. Copy your WAR into the container's `webapps` directory: + `docker cp app.war ${W9_ID}:/usr/local/tomcat/webapps/` +2. Tomcat auto-deploys it; open `http://:8080/app/`. +3. To deploy at the root, name the file `ROOT.war` (it replaces the default welcome page). -You can install this Tomcat by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +### Customize Startup -If you want use Tomcat with **Websoft9 Business Support** free, you can [subscribe Tomcat ](https://www.websoft9.com/apps) on Cloud platform +Startup runs through `src/entrypoint.sh`, which executes hooks in filename order on every start. Package hooks live in `src/entrypoint.d/`. To add your own without rebuilding, put scripts in the `tomcat` volume at `/usr/local/tomcat/.w9/entrypoint.d/`; a `/usr/local/tomcat/.w9/start.sh` replaces the default start command. + -## Documentation +## Configuration Reference -[Tomcat Administrator Guide](https://support.websoft9.com/docs/tomcat) powered by Websoft9 \ No newline at end of file +Websoft9 packages this app from the official [Tomcat Docker image](https://hub.docker.com/_/tomcat) and makes some improvements below. + + +- Tomcat data (including `webapps` and `conf`) is persisted in the `tomcat` volume mounted at `/usr/local/tomcat`. +- Startup runs through `src/entrypoint.sh`, which executes hooks from `/opt/websoft9/entrypoint.d` (package) and `/usr/local/tomcat/.w9/entrypoint.d` (user) before starting Tomcat. Hooks run on every start and must be idempotent. +- The default `10-webapps.sh` hook restores the bundled default webapps (`webapps.dist/*` → `webapps`). +- The default `W9_VERSION=11.0-jdk21-temurin` pins Tomcat 11 on JDK 21 LTS; pick another supported tag to change the Tomcat/JDK combination. + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 11.0-jdk21-temurin, 11.0-jdk25-temurin, 11.0-jdk17-temurin, 10.1-jdk25-temurin, 10.1-jdk21-temurin, 10.1-jdk17-temurin, 10.1-jdk11-temurin, 9.0-jdk25-temurin, 9.0-jdk21-temurin, 9.0-jdk17-temurin, 9.0-jdk11-temurin, 9.0-jdk8-temurin. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Tomcat HTTP | 8080 | + + +### Data Directory + + +Data is persisted in the `tomcat` volume, mounted at `/usr/local/tomcat`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +- `./src/entrypoint.sh` → `/opt/websoft9/entrypoint.sh` +- `./src/entrypoint.d` → `/opt/websoft9/entrypoint.d` +- `./src/start.sh` → `/opt/websoft9/start.sh` + + + +## References + +- [Tomcat Administrator Guide](https://support.websoft9.com/docs/tomcat) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/_/tomcat) + + + +## Troubleshooting + +**Root URL returns 404?** +- The default webapps may not have been restored; confirm the `10-webapps.sh` hook ran and `/usr/local/tomcat/webapps/ROOT` exists in the container. + +**Container stays unhealthy?** +- Tomcat can take about 30 seconds to start; check `docker compose logs ${W9_ID}`. + +**Port not reachable?** +- Confirm `W9_HTTP_PORT_SET` is free and allowed by the firewall / security group. + diff --git a/apps/tomcat/docker-compose.yml b/apps/tomcat/docker-compose.yml index 737ec2823..ebbc45763 100644 --- a/apps/tomcat/docker-compose.yml +++ b/apps/tomcat/docker-compose.yml @@ -1,19 +1,27 @@ -# image: https://hub.docker.com/_/tomcat - -version: '3.8' services: tomcat: container_name: ${W9_ID} - image: $W9_REPO:$W9_VERSION + image: ${W9_REPO}:${W9_VERSION} restart: unless-stopped env_file: .env + working_dir: /usr/local/tomcat + entrypoint: ["/bin/bash", "/opt/websoft9/entrypoint.sh"] + environment: + - APP_DIR=/usr/local/tomcat ports: - - '${W9_HTTP_PORT_SET}:8080' + - "${W9_HTTP_PORT_SET}:8080" # Tomcat HTTP + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:8080/ >/dev/null"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 30s volumes: - - tomcat:/usr/local/tomcat - - ./src/cmd.sh:/usr/src/app/cmd.sh - command: /bin/bash -c "bash /usr/src/app/cmd.sh && catalina.sh run" - + - tomcat:/usr/local/tomcat + - ./src/entrypoint.sh:/opt/websoft9/entrypoint.sh:ro + - ./src/entrypoint.d:/opt/websoft9/entrypoint.d:ro + - ./src/start.sh:/opt/websoft9/start.sh:ro + networks: default: name: ${W9_NETWORK} diff --git a/apps/tomcat/src/cmd.sh b/apps/tomcat/src/cmd.sh deleted file mode 100644 index 8704fcc98..000000000 --- a/apps/tomcat/src/cmd.sh +++ /dev/null @@ -1,11 +0,0 @@ -### This script is running before tomcat starting ############## -### You can add your CI code here, below is example - -cp -r webapps.dist/* webapps - -### Install os packages -# apt update -y && apt install unzip -y - -### Install java sample, access by: http://URL -# cd /usr/local/tomcat/webapps -# wget -O ROOT.war https://tomcat.apache.org/tomcat-10.0-doc/appdev/sample/sample.war diff --git a/apps/tomcat/src/entrypoint.d/10-webapps.sh b/apps/tomcat/src/entrypoint.d/10-webapps.sh new file mode 100644 index 000000000..f68a37927 --- /dev/null +++ b/apps/tomcat/src/entrypoint.d/10-webapps.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +APP_DIR="${APP_DIR:-/usr/local/tomcat}" +cd "${APP_DIR}" + +# Restore the bundled default webapps (ROOT, docs, examples) that the official +# image keeps in webapps.dist. Idempotent: safe to run on every start. +mkdir -p webapps + +if [ -d webapps.dist ]; then + echo "[tomcat-runtime] restoring default webapps into ${APP_DIR}/webapps" + cp -a webapps.dist/. webapps/ +fi diff --git a/apps/tomcat/src/entrypoint.sh b/apps/tomcat/src/entrypoint.sh new file mode 100644 index 000000000..88954b1c8 --- /dev/null +++ b/apps/tomcat/src/entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -euo pipefail + +APP_DIR="${APP_DIR:-/usr/local/tomcat}" +PACKAGE_HOOKS_DIR="/opt/websoft9/entrypoint.d" +USER_HOOKS_DIR="${APP_DIR}/.w9/entrypoint.d" +PACKAGE_START="/opt/websoft9/start.sh" +USER_START="${APP_DIR}/.w9/start.sh" + +log() { + echo "[tomcat-runtime] $*" +} + +run_hooks() { + local -A hooks=() + local dir file name + + for dir in "${PACKAGE_HOOKS_DIR}" "${USER_HOOKS_DIR}"; do + [ -d "${dir}" ] || continue + for file in "${dir}"/*.sh; do + [ -e "${file}" ] || continue + name="$(basename "${file}")" + hooks["${name}"]="${file}" + done + done + + if [ "${#hooks[@]}" -eq 0 ]; then + return 0 + fi + + while IFS= read -r name; do + log "hook: ${name}" + bash "${hooks[${name}]}" + done < <(printf '%s\n' "${!hooks[@]}" | sort) +} + +mkdir -p "${APP_DIR}" +cd "${APP_DIR}" +export APP_DIR + +run_hooks + +# Start must be exec'd so Tomcat becomes PID 1 and receives signals. +if [ -f "${USER_START}" ]; then + log "starting with user start script: ${USER_START}" + exec bash "${USER_START}" +fi + +log "starting with default start script" +exec bash "${PACKAGE_START}" diff --git a/apps/tomcat/src/start.sh b/apps/tomcat/src/start.sh new file mode 100644 index 000000000..e6b2f7f06 --- /dev/null +++ b/apps/tomcat/src/start.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -euo pipefail + +APP_DIR="${APP_DIR:-/usr/local/tomcat}" +cd "${APP_DIR}" + +exec catalina.sh run diff --git a/apps/tomcat/tests/cases.yml b/apps/tomcat/tests/cases.yml new file mode 100644 index 000000000..6480b5ef9 --- /dev/null +++ b/apps/tomcat/tests/cases.yml @@ -0,0 +1,10 @@ +# The adaptive checks cover compose config, container health and the web root. +# Tomcat's core path is deploying a WAR, so war-deploy.sh builds a tiny WAR +# inside the container, waits for auto-deploy and verifies the context serves it. +optional: + - id: welcome-page + type: script + script: smoke.sh + - id: war-deploy + type: script + script: war-deploy.sh diff --git a/apps/tomcat/tests/smoke.sh b/apps/tomcat/tests/smoke.sh new file mode 100644 index 000000000..765b53f1d --- /dev/null +++ b/apps/tomcat/tests/smoke.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +# BASE_URL is provided by `libs app-tests`. +base="${BASE_URL:?BASE_URL is required}" +body="$(curl -fsS --max-time 15 "${base}/")" + +if printf '%s' "${body}" | grep -qi "tomcat"; then + echo "tomcat welcome page served at ${base}/" + exit 0 +fi + +echo "unexpected response from ${base}/" >&2 +exit 1 diff --git a/apps/tomcat/tests/war-deploy.sh b/apps/tomcat/tests/war-deploy.sh new file mode 100644 index 000000000..24310b851 --- /dev/null +++ b/apps/tomcat/tests/war-deploy.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Script cases run on the deployment target (see docs/app-tests.md). This builds +# a tiny WAR inside the Tomcat container, drops it into webapps/, waits for +# Tomcat to auto-deploy it, and verifies the context serves the expected page. + +container="${W9_ID:?W9_ID is required}" +base="${BASE_URL:?BASE_URL is required}" +context="w9smoke" + +cleanup() { + docker exec "${container}" rm -f "/usr/local/tomcat/webapps/${context}.war" >/dev/null 2>&1 || true + docker exec "${container}" rm -rf "/usr/local/tomcat/webapps/${context}" >/dev/null 2>&1 || true +} +trap cleanup EXIT + +cleanup + +docker exec "${container}" bash -c ' + set -e + rm -rf /tmp/w9src /tmp/w9smoke.war + mkdir -p /tmp/w9src/WEB-INF + printf "%s\n" "

Websoft9 WAR smoke OK

" > /tmp/w9src/index.html + ( cd /tmp/w9src && jar cf /tmp/w9smoke.war . ) + cp /tmp/w9smoke.war /usr/local/tomcat/webapps/w9smoke.war +' + +deadline=$((SECONDS + 120)) +code="000" +while [ "${SECONDS}" -lt "${deadline}" ]; do + code="$(curl -s -o /tmp/w9war.html -w '%{http_code}' --max-time 10 "${base}/${context}/" || true)" + [ "${code}" = "200" ] && break + sleep 3 +done + +if [ "${code}" != "200" ]; then + echo "WAR context /${context}/ -> ${code} (timeout)" >&2 + exit 1 +fi + +if ! grep -q "Websoft9 WAR smoke OK" /tmp/w9war.html; then + echo "WAR context served unexpected body:" >&2 + cat /tmp/w9war.html >&2 + exit 1 +fi + +echo "WAR auto-deploy ok at ${base}/${context}/" diff --git a/apps/tomcat/variables.json b/apps/tomcat/variables.json index 2dad74c5c..a8094d465 100644 --- a/apps/tomcat/variables.json +++ b/apps/tomcat/variables.json @@ -1,23 +1,23 @@ { "name": "tomcat", - "trademark": "Tomcat ", + "trademark": "Tomcat", "release": true, "edition": [ { "dist": "community", "version": [ "11.0-jdk21-temurin", - "10-jdk21-temurin", - "10-jdk17-temurin", - "10-jdk11-temurin", - "9-jdk21-temurin", - "9-jdk17-temurin", - "9-jdk11-temurin", - "9-jdk8-temurin", - "9-jdk21-corretto", - "9-jdk17-corretto", - "9-jdk11-corretto", - "9-jdk8-corretto" + "11.0-jdk25-temurin", + "11.0-jdk17-temurin", + "10.1-jdk25-temurin", + "10.1-jdk21-temurin", + "10.1-jdk17-temurin", + "10.1-jdk11-temurin", + "9.0-jdk25-temurin", + "9.0-jdk21-temurin", + "9.0-jdk17-temurin", + "9.0-jdk11-temurin", + "9.0-jdk8-temurin" ] } ], diff --git a/apps/traefik/.env b/apps/traefik/.env index 17e4bcda6..04ce2a3a0 100644 --- a/apps/traefik/.env +++ b/apps/traefik/.env @@ -1,13 +1,33 @@ -W9_VERSION='v3.6' -W9_ID='traefik' - W9_REPO=traefik +W9_DIST=community +W9_VERSION=v3.7 + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -# 80 is external Traffic Port, 8080 is Dashboard and API port -W9_HTTP_PORT=80 -W9_HTTP_PORT_SET='9002' -W9_URL='' +W9_ID=traefik +W9_HTTP_PORT=8080 +W9_HTTP_PORT_SET=9002 +W9_API_PORT_SET=9003 +W9_URL=traefik.example.com +W9_ADMIN_PATH=/dashboard/ W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### -W9_DIST='community' + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Traefik image environment variables +# Docs: https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/ +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: + +# Not used by default; enable only when needed: +# TRAEFIK_LOG_LEVEL=INFO +# TRAEFIK_ACCESSLOG=true +# TRAEFIK_PROVIDERS_DOCKER_NETWORK=websoft9 diff --git a/apps/traefik/CHANGELOG.md b/apps/traefik/CHANGELOG.md index 582cf46c5..eb1825c61 100644 --- a/apps/traefik/CHANGELOG.md +++ b/apps/traefik/CHANGELOG.md @@ -1,5 +1,7 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Update Traefik from `v3.6` to `v3.7`. +- Make the packaged Websoft9 entrypoint target the Traefik dashboard on port `8080` and expose the proxy HTTP entrypoint separately. +- Align app metadata and validation coverage with current repository rules. diff --git a/apps/traefik/Notes.md b/apps/traefik/Notes.md deleted file mode 100644 index e763c4eae..000000000 --- a/apps/traefik/Notes.md +++ /dev/null @@ -1,4 +0,0 @@ -# Traefik - -- 目前的配置文件默认支持 Docker 服务,k8s 下未研究 -- 8080 端口由于安全考虑,没有直接绑定到宿主机。Nginx proxy 的 location /dashboard {} 方案也无法达成目标 diff --git a/apps/traefik/README.md b/apps/traefik/README.md index 521597f90..b55910016 100644 --- a/apps/traefik/README.md +++ b/apps/traefik/README.md @@ -1,26 +1,87 @@ -# Traefik on Docker +# Traefik on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Traefik: +## Quick Start +### Deploy Verification - - community: 3.3, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Traefik**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Make sure you are signed in to the Traefik admin console. +2. Try a core feature. -The following are the minimal [recommended requirements](https://hub.docker.com/_/traefik): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update the password in `.env` and save. +3. Rebuild the app. + -## Install +## Configuration Reference -You can install this Traefik by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Traefik Docker image](https://hub.docker.com/_/traefik) and makes some improvements below. -If you want use Traefik with **Websoft9 Business Support** free, you can [subscribe Traefik](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Traefik Administrator Guide](https://support.websoft9.com/docs/traefik) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: v3.7, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | +| HTTP Entrypoint | 80 | + + +### Data Directory + + +Data is persisted in the `/var/run/docker.sock` volume, mounted at `/var/run/docker.sock`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/traefik.yml` to `/etc/traefik/traefik.yml`. + + +## References + +- [Traefik Administrator Guide](https://support.websoft9.com/docs/traefik) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/_/traefik) + +- [Releases](https://github.com/traefik/traefik) + +- [Official docs](https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/) + +- [Official docs](https://doc.traefik.io/traefik/migrate/v3/) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/traefik/docker-compose.yml b/apps/traefik/docker-compose.yml index a75407065..419b66527 100644 --- a/apps/traefik/docker-compose.yml +++ b/apps/traefik/docker-compose.yml @@ -1,8 +1,3 @@ -# image: https://hub.docker.com/_/traefik -# docs: https://doc.traefik.io/traefik/providers/docker/ - -version: '3.8' - services: traefik: image: ${W9_REPO}:${W9_VERSION} @@ -16,9 +11,8 @@ services: - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" ports: - - ${W9_HTTP_PORT_SET}:80 # For HTTP traffic - #- 4433:443 # For HTTPS traffic - - 8080:8080 # ← 已启用 Dashboard 端口 + - "${W9_HTTP_PORT_SET}:8080" # Web Console + - "${W9_API_PORT_SET}:80" # HTTP Entrypoint volumes: - /var/run/docker.sock:/var/run/docker.sock - ./src/traefik.yml:/etc/traefik/traefik.yml diff --git a/apps/traefik/tests/cases.yml b/apps/traefik/tests/cases.yml new file mode 100644 index 000000000..99774c8c2 --- /dev/null +++ b/apps/traefik/tests/cases.yml @@ -0,0 +1,8 @@ +skip: + - id: web-access + +optional: + - id: dashboard + type: web-access + path: /dashboard/ + expect_status: 200 diff --git a/apps/traefik/variables.json b/apps/traefik/variables.json index 6e1f9f484..db41db8cc 100644 --- a/apps/traefik/variables.json +++ b/apps/traefik/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "v3.6", + "v3.7", "latest" ] } @@ -17,6 +17,11 @@ "disk": "1" }, "upstream": { - "image": "https://hub.docker.com/_/traefik" + "image": "https://hub.docker.com/_/traefik", + "releases": "https://github.com/traefik/traefik", + "docs": [ + "https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/", + "https://doc.traefik.io/traefik/migrate/v3/" + ] } } diff --git a/apps/trivy/.env b/apps/trivy/.env index 965339c24..ad11ea7e5 100644 --- a/apps/trivy/.env +++ b/apps/trivy/.env @@ -1,9 +1,44 @@ -W9_VERSION='0.68.1' -W9_DIST='community' W9_REPO=aquasec/trivy +W9_DIST=community +W9_VERSION=0.74.0 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. +W9_POWER_PASSWORD='gyJ2wEL8smpUsA' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='trivy' -W9_SCAN_PATH_SET='/docker/trivy' + +W9_ID=trivy +# Trivy server exposes a single HTTP API port (scan RPC + health endpoints). +W9_HTTP_PORT_SET=4954 + +# Trivy uses a single API token instead of a username/password login; the interface +# surfaces the token through the password field. The username is a display label only. +W9_LOGIN_USER=trivy +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} + W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Trivy image environment variables +# Docs: https://trivy.dev/latest/docs/references/modes/client-server/ +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +# Shared secret for client/server mode; clients must pass the same value via --token. +TRIVY_TOKEN=${W9_LOGIN_PASSWORD} + +# Not used by default; enable only when needed: +# TRIVY_LISTEN=0.0.0.0:4954 +# TRIVY_DEBUG=true +# TRIVY_SKIP_DB_UPDATE=false +# TRIVY_DB_REPOSITORY=ghcr.io/aquasecurity/trivy-db:2 +# TRIVY_INSECURE=false diff --git a/apps/trivy/CHANGELOG.md b/apps/trivy/CHANGELOG.md index 4591f22ad..b7beef716 100644 --- a/apps/trivy/CHANGELOG.md +++ b/apps/trivy/CHANGELOG.md @@ -1,2 +1,10 @@ # CHANGELOG +## 2026-09-21 + +- Switch Trivy from CLI shell mode to **server mode** (`trivy server --listen 0.0.0.0:4954`). +- Bump `aquasec/trivy` from `0.68.1` to `0.74.0`. +- Publish the server API on `${W9_HTTP_PORT_SET}` and persist the vulnerability database in the `trivy_cache` volume. +- Enable token authentication through `TRIVY_TOKEN`, carried by `W9_LOGIN_PASSWORD` so the interface can display it. +- Add a `/healthz` healthcheck and an app-specific `tests/cases.yml`. +- Remove the unused scan-path mount (`W9_SCAN_PATH_SET`) that only applied to the CLI shell mode. diff --git a/apps/trivy/Notes.md b/apps/trivy/Notes.md index 6a9d5f564..a9dcc5dad 100644 --- a/apps/trivy/Notes.md +++ b/apps/trivy/Notes.md @@ -1,14 +1,24 @@ # Trivy -#### how to scan +Trivy runs as a **server**: it keeps the vulnerability database up to date and lets remote clients scan without downloading the DB. -Access into container, run command as following: -``` -trivy fs /scandir -``` -#### quickly scan +#### Server endpoints + +- `http://:/healthz` — health check, returns `ok` +- `http://:/version` — server version information + +#### Scan from a client + +Install the [Trivy CLI](https://trivy.dev/latest/docs/getting-started/installation/) on the machine that runs the scan, then point it at this server: ``` -apk add --no-cache python3 && ln -sf python3 /usr/bin/python -trivy fs --scanners vuln /tmp/usr/share +trivy image --server http://: --token alpine:3.20 +trivy fs --server http://: --token /path/to/project +trivy repo --server http://: --token https://github.com/org/repo ``` + +The token is the `W9_LOGIN_PASSWORD` value in `.env` (also shown in the app's **Access** tab). + +#### Vulnerability database + +The DB is cached in the `trivy_cache` volume (`/root/.cache/trivy`) and refreshed automatically while the server runs. diff --git a/apps/trivy/README.md b/apps/trivy/README.md index 8a631c1f2..a845ad22e 100644 --- a/apps/trivy/README.md +++ b/apps/trivy/README.md @@ -1,26 +1,89 @@ -# Trivy on Docker +# Trivy on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Trivy: +## Quick Start +### Deploy Verification - - community: 0.61.0, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Trivy**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +Trivy is deployed in server mode and exposes an HTTP API on port 4954. -The following are the minimal [recommended requirements](https://aquasecurity.github.io/trivy/v0.53/docs/): +1. Open `http://:4954/healthz` and confirm it returns `ok`. +2. On a client machine, install the [Trivy CLI](https://trivy.dev/latest/docs/getting-started/installation/). +3. Run a scan against this server, for example `trivy image --server http://:4954 --token alpine:3.20`. -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 2 GB of free space -* **bandwidth**: more fluent experience over 100M +### Change Token -## Install +1. In the [Websoft9](https://www.websoft9.com) console, open the app's **Compose** tab. +2. Update `W9_LOGIN_PASSWORD` (or `W9_POWER_PASSWORD`) in `.env` and save. +3. Rebuild the app; clients must use the new token. + -You can install this Trivy by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +## Configuration Reference -If you want use Trivy with **Websoft9 Business Support** free, you can [subscribe Trivy](https://www.websoft9.com/apps) on Cloud platform +Websoft9 packages this app from the official [Trivy Docker image](https://hub.docker.com/r/aquasec/trivy) and makes some improvements below. -## Documentation + -[Trivy Administrator Guide](https://support.websoft9.com/docs/trivy) powered by Websoft9 \ No newline at end of file + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 0.74.0, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Trivy Server API | 4954 | + + +### Data Directory + + +Data is persisted in the `trivy_cache` volume, mounted at `/root/.cache/trivy`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Trivy Administrator Guide](https://support.websoft9.com/docs/trivy) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/aquasec/trivy) + +- [Releases](https://github.com/aquasecurity/trivy/releases) + +- [Official docs](https://trivy.dev/latest/docs/references/modes/client-server/) + +- [Official docs](https://trivy.dev/latest/docs/references/configuration/cli/trivy_server/) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/trivy/docker-compose.yml b/apps/trivy/docker-compose.yml index bbd2bc3b9..f6f8b1232 100644 --- a/apps/trivy/docker-compose.yml +++ b/apps/trivy/docker-compose.yml @@ -1,22 +1,26 @@ -# image: https://hub.docker.com/r/aquasec/trivy -# docs: https://aquasecurity.github.io/trivy/v0.53/getting-started/installation/#docker - -version: '3.8' - services: trivy: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} restart: unless-stopped + command: server --listen 0.0.0.0:4954 env_file: .env + ports: + - "${W9_HTTP_PORT_SET}:4954" # Trivy Server API volumes: - # If you want to scan docker image, you can use the following mount - # - /var/run/docker.sock:/var/run/docker.sock - - $W9_SCAN_PATH_SET:/myproject - entrypoint: tail -f /dev/null - + - trivy_cache:/root/.cache/trivy + healthcheck: + test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:4954/healthz | grep -q ok"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 120s + networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true + +volumes: + trivy_cache: diff --git a/apps/trivy/tests/cases.yml b/apps/trivy/tests/cases.yml new file mode 100644 index 000000000..f524f3ae4 --- /dev/null +++ b/apps/trivy/tests/cases.yml @@ -0,0 +1,8 @@ +skip: + - id: web-access + +optional: + - id: server-health + type: web-access + path: /healthz + expect_status: 200 diff --git a/apps/trivy/variables.json b/apps/trivy/variables.json index 765ca652a..d8b9cd7ce 100644 --- a/apps/trivy/variables.json +++ b/apps/trivy/variables.json @@ -2,21 +2,32 @@ "name": "trivy", "trademark": "Trivy", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/aquasec/trivy", + "releases": "https://github.com/aquasecurity/trivy/releases", + "docs": [ + "https://trivy.dev/latest/docs/references/modes/client-server/", + "https://trivy.dev/latest/docs/references/configuration/cli/trivy_server/" + ] + }, "edition": [ { "dist": "community", "version": [ - "0.68.1", + "0.74.0", "latest" ] } ], + "access": { + "api": { + "port": 4954, + "path": "/healthz" + } + }, "requirements": { "cpu": "2", "memory": "4", - "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/aquasec/trivy" + "disk": "4" } } diff --git a/apps/typesense/.env b/apps/typesense/.env index 13b9bf862..318aa4ad1 100644 --- a/apps/typesense/.env +++ b/apps/typesense/.env @@ -1,19 +1,43 @@ -W9_VERSION='29.0' -W9_DIST='community' W9_REPO=typesense/typesense -W9_POWER_PASSWORD='arwBeGlTzE758!DU' +W9_DIST=community +W9_VERSION=30.2 + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='typesense' + +W9_ID=typesense + +# Web/internal ports W9_HTTP_PORT=8108 -W9_HTTP_PORT_SET='8109' -W9_URL='yourdomain.com' -W9_NETWORK=websoft9 +W9_HTTP_PORT_SET=8109 + +# URL helper +W9_URL=appname.example.com -# Don't use W9_POWER_PASSWORD for api_key -# Some special strings can not used for api, suggest user get key by command [openssl rand -base64 24] +# API key for the Typesense server and the bundled docsearch scraper. +# Do not reuse W9_POWER_PASSWORD here: some special characters are invalid in API keys. +# Generate with: openssl rand -base64 24 W9_LOGIN_API_KEY=cJ9XqddokC3OCRdx1SFQRv+uFj5QHYOT -#### --------------------------------------------------------------------------------------- #### +W9_NETWORK=websoft9 + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Typesense image environment variables +# Docs: https://typesense.org/docs/guide/install-typesense.html +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: TYPESENSE_DATA_DIR=/data TYPESENSE_ENABLE_CORS=true + +# Not used by default; enable only when needed: +# TYPESENSE_LOG_DIR=/data/logs +# TYPESENSE_ENABLE_ACCESS_LOGGING=true +# TYPESENSE_THREAD_POOL_SIZE= +# TYPESENSE_MAX_INDEXING_CONCURRENCY= +# TYPESENSE_FILTER_BY_MAX_OPS= diff --git a/apps/typesense/CHANGELOG.md b/apps/typesense/CHANGELOG.md index 582cf46c5..1afd2a65f 100644 --- a/apps/typesense/CHANGELOG.md +++ b/apps/typesense/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-21 +- Update Typesense from `29.0` to `30.2` (latest stable upstream release). +- Note the v30 behavior changes: synonyms and overrides become top-level Synonym Sets / Curation Sets and analytics rules change shape; existing data is auto-migrated on upgrade, so take a snapshot before upgrading an existing instance. +- Align `.env` with the current repository policy: braced variable references, template layout, image-env section banner, and removal of the unused `W9_POWER_PASSWORD`. +- Add `apps/typesense/tests/cases.yml` with a `/health` reachability check, and drop the source-comment header from `docker-compose.yml` while adding an inline published-port comment. +- Regenerate `README.md` from `variables.json` and `docker-compose.yml`. diff --git a/apps/typesense/Notes.md b/apps/typesense/Notes.md deleted file mode 100644 index 92f212eb4..000000000 --- a/apps/typesense/Notes.md +++ /dev/null @@ -1,2 +0,0 @@ -## Typesense - diff --git a/apps/typesense/README.md b/apps/typesense/README.md index 8249b760b..76ae212f3 100644 --- a/apps/typesense/README.md +++ b/apps/typesense/README.md @@ -1,26 +1,96 @@ -# Typesense on Docker +# Typesense on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Typesense: +## Quick Start +### Deploy Verification - - community: 28.0 +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Typesense**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +Typesense is an HTTP search API server; it has no browser console. In the Websoft9 console, open **My Apps → Typesense → Access** to get the API URL (`http://:8109`). -The following are the minimal [recommended requirements](https://typesense.org/docs/guide/install-typesense.html): +1. Check the server: `curl http://:8109/health` → `{"ok":true}`. +2. Send the API key from `.env` (`W9_LOGIN_API_KEY`) as the `X-TYPESENSE-API-KEY` header on every other request. -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +Example: create a collection and search it: -## Install +```bash +export TYPESENSE_API_KEY= +curl -X POST "http://:8109/collections" \ + -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \ + -H "Content-Type: application/json" \ + -d '{"name":"books","fields":[{"name":"title","type":"string"}]}' +``` -You can install this Typesense by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +### Change API Key -If you want use Typesense with **Websoft9 Business Support** free, you can [subscribe Typesense](https://www.websoft9.com/apps) on Cloud platform +1. In the Websoft9 console, open the app's **Compose** tab. +2. Update `W9_LOGIN_API_KEY` in `.env` and save. +3. Rebuild the app; the bundled `docsearch-scraper` reads the same key. + -## Documentation +## Configuration Reference -[Typesense Administrator Guide](https://support.websoft9.com/docs/typesense) powered by Websoft9 \ No newline at end of file +Websoft9 packages this app from the official [Typesense Docker image](https://hub.docker.com/r/typesense/typesense) and makes some improvements below. + + +- Typesense is an HTTP API server; there is no browser admin UI. Authenticate API calls with the `X-TYPESENSE-API-KEY` header set to `W9_LOGIN_API_KEY`. +- `W9_LOGIN_API_KEY` is the admin API key and is also passed to the bundled `docsearch-scraper`. +- Data is persisted in the `typesense` volume (`/data`). +- The bundled `docsearch-scraper` indexes the Websoft9 documentation site by default; edit its `CONFIG` in `docker-compose.yml` to change the target. + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 30.2. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Typesense HTTP API | 8108 | + + +### Data Directory + + +Data is persisted in the `typesense` volume, mounted at `/data`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Typesense Administrator Guide](https://support.websoft9.com/docs/typesense) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/typesense/typesense) + + + +## Troubleshooting + +**`/health` returns `{"ok":true}` but API calls return 401?** +- Send the `X-TYPESENSE-API-KEY` header with the value of `W9_LOGIN_API_KEY` from `.env`. + +**Port not reachable?** +- Confirm `W9_HTTP_PORT_SET` is free and allowed by the firewall / security group. + +**Upgrading an existing 29.x instance?** +- v30 auto-migrates synonyms, overrides and analytics rules; take a snapshot before upgrading. + diff --git a/apps/typesense/docker-compose.yml b/apps/typesense/docker-compose.yml index 1eeaa3d9b..c2758b4a1 100644 --- a/apps/typesense/docker-compose.yml +++ b/apps/typesense/docker-compose.yml @@ -1,10 +1,3 @@ -# image: https://hub.docker.com/r/typesense/typesense/tags -# docs: https://typesense.org/docs/guide/install-typesense.html -# https://typesense.org/docs/guide/docsearch.html#add-docsearch-meta-tags-optional -# https://typesense.org/docs/0.23.0/api/server-configuration.html#using-command-line-arguments - -version: "3.8" - services: typesense: image: ${W9_REPO}:${W9_VERSION} @@ -13,10 +6,10 @@ services: logging: driver: "json-file" options: - max-file: "5" - max-size: 10m + max-file: "5" + max-size: 10m ports: - - "${W9_HTTP_PORT_SET}:8108" + - "${W9_HTTP_PORT_SET}:8108" # Typesense HTTP API volumes: - typesense:/data env_file: .env @@ -30,8 +23,8 @@ services: logging: driver: "json-file" options: - max-file: "5" - max-size: 10m + max-file: "5" + max-size: 10m deploy: resources: limits: diff --git a/apps/typesense/tests/cases.yml b/apps/typesense/tests/cases.yml new file mode 100644 index 000000000..ac57ac3c4 --- /dev/null +++ b/apps/typesense/tests/cases.yml @@ -0,0 +1,8 @@ +skip: + - id: web-access + +optional: + - id: health + type: web-access + path: /health + expect_status: 200 diff --git a/apps/typesense/variables.json b/apps/typesense/variables.json index 7cae8c29f..4f454fa0a 100644 --- a/apps/typesense/variables.json +++ b/apps/typesense/variables.json @@ -6,7 +6,7 @@ { "dist": "community", "version": [ - "29.0" + "30.2" ] } ], diff --git a/apps/typo3/.env b/apps/typo3/.env index cb9381b3d..fe14acd23 100644 --- a/apps/typo3/.env +++ b/apps/typo3/.env @@ -1,15 +1,53 @@ -W9_DIST='community' -W9_VERSION='13.4' W9_REPO=martinhelmich/typo3 +W9_DIST=community +W9_VERSION=13.4 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. W9_POWER_PASSWORD='VO82vU2TIiI1uJ!L' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='typo3' + +W9_ID=typo3 W9_HTTP_PORT=80 -W9_HTTP_PORT_SET='9001' +W9_HTTP_PORT_SET=9001 + +W9_DB_EXPOSE=mysql +W9_DB_VERSION=8.4 + +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} + +W9_URL=appname.example.com W9_URL_REPLACE=true -W9_URL='appname.example.com' -W9_ADMIN_PATH="/typo3" -W9_DB_EXPOSE="mysql" -W9_DB_VERSION="8" +W9_ADMIN_PATH=/typo3 + W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Typo3 image environment variables +# Docs: https://hub.docker.com/r/martinhelmich/typo3 +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# These variables drive the non-interactive first-run install performed +# by src/entrypoint.sh and take effect on first startup only. +# ============================================================ + +# Used by docker-compose.yml: +TYPO3_DB_DRIVER=mysqli +TYPO3_DB_HOST=${W9_ID}-mysql +TYPO3_DB_PORT=3306 +TYPO3_DB_DBNAME=${W9_ID} +TYPO3_DB_USERNAME=${W9_ID} +TYPO3_DB_PASSWORD=${W9_POWER_PASSWORD} +TYPO3_SETUP_ADMIN_USERNAME=${W9_LOGIN_USER} +TYPO3_SETUP_ADMIN_PASSWORD=${W9_LOGIN_PASSWORD} +TYPO3_SETUP_ADMIN_EMAIL=admin@example.com +TYPO3_PROJECT_NAME=Typo3 +TYPO3_SETUP_CREATE_SITE=http://${W9_URL} +TYPO3_SERVER_TYPE=apache + +# Not used by default; enable only when needed: diff --git a/apps/typo3/CHANGELOG.md b/apps/typo3/CHANGELOG.md index 582cf46c5..a91608e11 100644 --- a/apps/typo3/CHANGELOG.md +++ b/apps/typo3/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG -## Release +## 2026-09-21 ### Fixes and Enhancements - +- Refresh `.env` to the current template layout and add the `TYPO3_*` first-run install variables. +- Normalize `docker-compose.yml` references to `${VAR}`, remove the image source comment, and annotate the published port. +- Regenerate `README.md` so the advertised version matches `13.4`. +- Add `tests/cases.yml` covering the TYPO3 backend login and install tool. +- Add upstream releases and official GitHub/docs references to `variables.json`. +- Pin the bundled MySQL dependency to the `8.4` LTS tag. +- Add non-interactive first-run install via `src/entrypoint.sh`; declare `W9_LOGIN_*` and `W9_URL_REPLACE`. +- Add a healthcheck to the main Typo3 container. diff --git a/apps/typo3/Notes.md b/apps/typo3/Notes.md deleted file mode 100644 index 194870116..000000000 --- a/apps/typo3/Notes.md +++ /dev/null @@ -1,3 +0,0 @@ -# Typo3 - -安装参考:https://github.com/martin-helmich/docker-typo3 diff --git a/apps/typo3/README.md b/apps/typo3/README.md index 3ddba87fe..0496df7a8 100644 --- a/apps/typo3/README.md +++ b/apps/typo3/README.md @@ -1,26 +1,98 @@ -# Typo3 on Docker +# Typo3 on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Typo3: +## Quick Start +### Deploy Verification - - community: 12.4, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Typo3**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### First-run install -## System Requirements +The package runs TYPO3's non-interactive `setup` on first startup, so the database schema, a basic site, and the administrator account are created automatically: -The following are the minimal [recommended requirements](https://docs.typo3.org/m/typo3/tutorial-getting-started/11.5/en-us/Installation/Index.html): +1. Open the app URL and sign in to the backend at `/typo3/` with `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD` from `.env`. +2. Start building content under the auto-generated site. -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +If `TYPO3_SETUP_ADMIN_PASSWORD` is left empty, first startup falls back to the browser install tool at `/typo3/install.php`; use the bundled MySQL service (host `typo3-mysql`, database/user `typo3`, password `W9_POWER_PASSWORD`). -## Install +### Change Password -You can install this Typo3 by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +`W9_LOGIN_PASSWORD` is applied only during the first-run install. To change it later, sign in to the TYPO3 backend at `/typo3/` and update the administrator password under **User Settings**, or reset it from the install tool. + -If you want use Typo3 with **Websoft9 Business Support** free, you can [subscribe Typo3](https://www.websoft9.com/apps) on Cloud platform +## Configuration Reference -## Documentation +Websoft9 packages this app from the official [Typo3 Docker image](https://hub.docker.com/r/martinhelmich/typo3) and makes some improvements below. -[Typo3 Administrator Guide](https://support.websoft9.com/docs/typo3) powered by Websoft9 \ No newline at end of file + + + + +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 13.4, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 80 | + + +### Data Directory + + +- `typo3fileadmin` → `/var/www/html/fileadmin` +- `typo3conf` → `/var/www/html/typo3conf` +- `typo3uploads` → `/var/www/html/uploads` +- `typo3temp` → `/var/www/html/typo3temp` +- `mysql_data` → `/var/lib/mysql` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `TYPO3_DB_DRIVER`, `TYPO3_DB_HOST`, `TYPO3_DB_PORT`, `TYPO3_DB_DBNAME`, `TYPO3_DB_USERNAME`, `TYPO3_DB_PASSWORD`, `TYPO3_SETUP_ADMIN_USERNAME`, `TYPO3_SETUP_ADMIN_PASSWORD`, `TYPO3_SETUP_ADMIN_EMAIL`, `TYPO3_PROJECT_NAME`, `TYPO3_SETUP_CREATE_SITE`, `TYPO3_SERVER_TYPE` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/entrypoint.sh` to `/usr/local/bin/websoft9-entrypoint.sh`. + + +## References + +- [Typo3 Administrator Guide](https://support.websoft9.com/docs/typo3) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/martinhelmich/typo3) + +- [Releases](https://github.com/TYPO3/typo3/releases) + +- [GitHub docs](https://github.com/martin-helmich/docker-typo3) + +- [GitHub docs](https://github.com/TYPO3/typo3) + +- [Official docs](https://docs.typo3.org/) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/typo3/docker-compose.yml b/apps/typo3/docker-compose.yml index 7f00a7643..518111f57 100644 --- a/apps/typo3/docker-compose.yml +++ b/apps/typo3/docker-compose.yml @@ -1,31 +1,38 @@ -# image and compose: https://hub.docker.com/r/martinhelmich/typo3 - -version: '3.8' - services: typo3: image: ${W9_REPO}:${W9_VERSION} container_name: ${W9_ID} + restart: unless-stopped + env_file: .env + entrypoint: ["/bin/bash", "/usr/local/bin/websoft9-entrypoint.sh"] + command: ["apache2-foreground"] ports: - - ${W9_HTTP_PORT_SET}:80 + - "${W9_HTTP_PORT_SET}:80" # Web Console volumes: + - ./src/entrypoint.sh:/usr/local/bin/websoft9-entrypoint.sh:ro - typo3fileadmin:/var/www/html/fileadmin - typo3conf:/var/www/html/typo3conf - typo3uploads:/var/www/html/uploads - typo3temp:/var/www/html/typo3temp - restart: unless-stopped - env_file: .env - + depends_on: + - mysql + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/typo3/install.php >/dev/null || exit 1"] + interval: 10s + timeout: 10s + retries: 12 + start_period: 120s + mysql: - image: mysql:$W9_DB_VERSION + image: mysql:${W9_DB_VERSION} container_name: ${W9_ID}-mysql restart: unless-stopped command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci] volumes: - mysql_data:/var/lib/mysql environment: - MYSQL_DATABASE: typo3 - MYSQL_USER: typo3 + MYSQL_DATABASE: ${W9_ID} + MYSQL_USER: ${W9_ID} MYSQL_PASSWORD: ${W9_POWER_PASSWORD} MYSQL_ROOT_PASSWORD: ${W9_POWER_PASSWORD} @@ -39,4 +46,4 @@ volumes: typo3conf: typo3uploads: typo3temp: - mysql_data: \ No newline at end of file + mysql_data: diff --git a/apps/typo3/src/entrypoint.sh b/apps/typo3/src/entrypoint.sh new file mode 100644 index 000000000..cb82726bb --- /dev/null +++ b/apps/typo3/src/entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +TYPO3_CLI="/var/www/html/typo3/sysext/core/bin/typo3" +SETTINGS_FILE="/var/www/html/typo3conf/system/settings.php" +FIRST_INSTALL_FILE="/var/www/html/FIRST_INSTALL" +DB_HOST="${TYPO3_DB_HOST:-localhost}" +DB_PORT="${TYPO3_DB_PORT:-3306}" +MAX_RETRIES="${TYPO3_SETUP_MAX_RETRIES:-60}" +RETRY_INTERVAL="${TYPO3_SETUP_RETRY_INTERVAL:-5}" + +if [ -n "${TYPO3_SETUP_ADMIN_PASSWORD:-}" ] && [ ! -f "$SETTINGS_FILE" ]; then + echo "websoft9: waiting for database at ${DB_HOST}:${DB_PORT} ..." + retries=0 + until php -r "@\$sock = fsockopen('${DB_HOST}', (int)'${DB_PORT}', \$errno, \$errstr, 1); if (!\$sock) { exit(1); } fclose(\$sock);" >/dev/null 2>&1; do + retries=$((retries + 1)) + if [ "$retries" -ge "$MAX_RETRIES" ]; then + echo "websoft9: database not reachable after $((MAX_RETRIES * RETRY_INTERVAL))s; leaving the browser install tool in place." + break + fi + sleep "$RETRY_INTERVAL" + done + + if [ "$retries" -lt "$MAX_RETRIES" ]; then + echo "websoft9: running non-interactive TYPO3 setup ..." + runuser -u www-data -- "$TYPO3_CLI" setup --force --no-interaction --server-type="${TYPO3_SERVER_TYPE:-apache}" + rm -f "$FIRST_INSTALL_FILE" + echo "websoft9: TYPO3 setup complete." + fi +fi + +if [ "$#" -eq 0 ]; then + set -- apache2-foreground +fi + +exec docker-php-entrypoint "$@" diff --git a/apps/typo3/tests/cases.yml b/apps/typo3/tests/cases.yml new file mode 100644 index 000000000..d8324d581 --- /dev/null +++ b/apps/typo3/tests/cases.yml @@ -0,0 +1,12 @@ +skip: + - id: web-access + +optional: + - id: backend-login + type: web-access + path: /typo3/ + expect_status: [200] + - id: install-tool + type: web-access + path: /typo3/install.php + expect_status: [200] diff --git a/apps/typo3/variables.json b/apps/typo3/variables.json index ebf72f2ad..dfb91e124 100644 --- a/apps/typo3/variables.json +++ b/apps/typo3/variables.json @@ -16,7 +16,29 @@ "memory": "4", "disk": "1" }, + "env": { + "first_startup_only": [ + "TYPO3_DB_DRIVER", + "TYPO3_DB_HOST", + "TYPO3_DB_PORT", + "TYPO3_DB_DBNAME", + "TYPO3_DB_USERNAME", + "TYPO3_DB_PASSWORD", + "TYPO3_SETUP_ADMIN_USERNAME", + "TYPO3_SETUP_ADMIN_PASSWORD", + "TYPO3_SETUP_ADMIN_EMAIL", + "TYPO3_PROJECT_NAME", + "TYPO3_SETUP_CREATE_SITE", + "TYPO3_SERVER_TYPE" + ] + }, "upstream": { - "image": "https://hub.docker.com/r/martinhelmich/typo3" + "image": "https://hub.docker.com/r/martinhelmich/typo3", + "releases": "https://github.com/TYPO3/typo3/releases", + "docs": [ + "https://github.com/martin-helmich/docker-typo3", + "https://github.com/TYPO3/typo3", + "https://docs.typo3.org/" + ] } } diff --git a/apps/umami/.env b/apps/umami/.env index eee18e82b..5b1ff7de6 100644 --- a/apps/umami/.env +++ b/apps/umami/.env @@ -1,19 +1,51 @@ -W9_DIST='community' -W9_REPO=umamisoftware/umami -W9_VERSION='3.0.3' -W9_POWER_PASSWORD='2F!XSIah4D5zhW5P' +W9_REPO=ghcr.io/umami-software/umami +W9_DIST=community +W9_VERSION=3.4 + +W9_POWER_PASSWORD="2F!XSIah4D5zhW5P" + #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='umami' + +W9_ID=umami + +# Web/internal ports W9_HTTP_PORT=3000 -W9_HTTP_PORT_SET='9001' -W9_URL='appname.example.com' +W9_HTTP_PORT_SET=9003 + +# Built-in login and URL helpers W9_LOGIN_USER=admin W9_LOGIN_PASSWORD=umami +W9_URL=appname.example.com + +# Bundled database W9_DB_EXPOSE="postgresql" -W9_RCODE='2GZ5ITB00lhq5' +W9_DB_VERSION=15 + +# Shared secondary secret used by the bundled database and the app +W9_RCODE="2GZ5ITB00lhq5" + W9_NETWORK=websoft9 -#### --------------------------------------------------------------------------------------- #### - -DATABASE_URL="postgresql://umami:$W9_RCODE@$W9_ID-postgresql:5432/umami" + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Umami image environment variables +# Docs: https://umami.is/docs/environment-variables +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: + +DATABASE_URL=postgresql://umami:${W9_RCODE}@${W9_ID}-postgresql:5432/umami DATABASE_TYPE=postgresql -APP_SECRET=a1B2c3D4e5F6g-$W9_POWER_PASSWORD +APP_SECRET=a1B2c3D4e5F6g-${W9_POWER_PASSWORD} +# Required for two-factor authentication; 64 hex characters (openssl rand -hex 32) +TWO_FACTOR_ENCRYPTION_KEY=8355138b8f5ee8f53d96fe785db6c384cf9b27373747aa9aa5d70c7769f960e9 + +# Not used by default; enable only when needed: +# BASE_PATH=/analytics +# REDIS_URL=redis://${W9_ID}-redis:6379 +# SKIP_DB_MIGRATION=1 +# MCP_ENABLED=1 diff --git a/apps/umami/CHANGELOG.md b/apps/umami/CHANGELOG.md index 582cf46c5..709537bd3 100644 --- a/apps/umami/CHANGELOG.md +++ b/apps/umami/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Update Umami to 3.4 and switch the image to the official `ghcr.io/umami-software/umami`. +- Add `TWO_FACTOR_ENCRYPTION_KEY` required by two-factor authentication. +- Model the bundled PostgreSQL version with `W9_DB_VERSION` and wait for a healthy database before starting the app. +- Default the web port to 9003; refresh `variables.json`, README, Notes and add a login/health test. diff --git a/apps/umami/Notes.md b/apps/umami/Notes.md deleted file mode 100644 index 713652830..000000000 --- a/apps/umami/Notes.md +++ /dev/null @@ -1,8 +0,0 @@ -# Plausible - -## to do - -* 密码随机化 -* 数据库规则化表达 - -## FAQ diff --git a/apps/umami/README.md b/apps/umami/README.md index 0f23889fb..23b5866ed 100644 --- a/apps/umami/README.md +++ b/apps/umami/README.md @@ -1,26 +1,94 @@ -# Umami on Docker +# Umami on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Umami: +## Quick Start +### Deploy Verification - - community: 2.17.0, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Umami**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the Umami console and sign in with `admin` / `umami`. +2. Add a website, then copy the tracking script into your site. +3. Open the website report to confirm data is being collected. -The following are the minimal [recommended requirements](https://umami.is/docs/install): +### Change Password -* **RAM**: 1 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to Umami and open **Settings → Profile**. +2. Change the password there; the default `admin` / `umami` account is not controlled by `.env`. + -## Install +## Configuration Reference -You can install this Umami by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Umami Docker image](https://ghcr.io/umami-software/umami) and makes some improvements below. -If you want use Umami with **Websoft9 Business Support** free, you can [subscribe Umami](https://www.websoft9.com/apps) on Cloud platform + +- The admin account is seeded on first start as `admin` / `umami`; change it after the first login. +- `DATABASE_URL` points at the bundled PostgreSQL service `${W9_ID}-postgresql`. +- `TWO_FACTOR_ENCRYPTION_KEY` (64 hex characters) must be set to use two-factor authentication. +- Database migrations run automatically on container start. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[Umami Administrator Guide](https://support.websoft9.com/docs/umami) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 3.4, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 3000 | + + +### Data Directory + + +Data is persisted in the `postgresql` volume, mounted at `/var/lib/postgresql/data`. + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/postgresql_init.sh` to `/docker-entrypoint-initdb.d/postgresql_init.sh`. + + +## References + +- [Umami Administrator Guide](https://support.websoft9.com/docs/umami) by Websoft9 + +- [GHCR image](https://ghcr.io/umami-software/umami) + +- [Releases](https://github.com/umami-software/umami/releases) + +- [Official compose](https://raw.githubusercontent.com/umami-software/umami/v3.4.0/docker-compose.yml) + +- [Official docs](https://umami.is/docs/install) + +- [Official docs](https://umami.is/docs/environment-variables) + + + +## Troubleshooting + +**Container stays unhealthy?** +- The first start runs database migrations; wait a minute and check `docker compose logs ${W9_ID}`. + +**Login fails with the default credentials?** +- The `admin` / `umami` account exists only on a fresh database. If it was changed, reset it from the database or recreate the stack. + +**Database connection error?** +- Confirm `${W9_ID}-postgresql` is healthy and that `DATABASE_URL` matches the bundled database. + diff --git a/apps/umami/docker-compose.yml b/apps/umami/docker-compose.yml index 97f1ca4b1..c00aaaa51 100644 --- a/apps/umami/docker-compose.yml +++ b/apps/umami/docker-compose.yml @@ -1,32 +1,31 @@ -# image: https://github.com/plausible/hosting -# docs: https://umami.is/docs/install -# compose: https://github.com/umami-software/umami/blob/master/docker-compose.yml - -version: '3.8' services: umami: - image: $W9_REPO:$W9_VERSION - container_name: $W9_ID + image: ${W9_REPO}:${W9_VERSION} + container_name: ${W9_ID} + restart: unless-stopped + init: true env_file: - .env ports: - - $W9_HTTP_PORT_SET:3000 + - "${W9_HTTP_PORT_SET}:3000" # Web Console depends_on: - - postgresql - restart: unless-stopped + postgresql: + condition: service_healthy healthcheck: - test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] + test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/heartbeat"] interval: 5s timeout: 5s retries: 5 + start_period: 30s postgresql: - image: postgres:15-alpine - container_name: $W9_ID-postgresql + image: postgres:${W9_DB_VERSION}-alpine + container_name: ${W9_ID}-postgresql + restart: unless-stopped environment: POSTGRES_DB: umami POSTGRES_USER: postgres - POSTGRES_PASSWORD: $W9_POWER_PASSWORD + POSTGRES_PASSWORD: ${W9_POWER_PASSWORD} POSTGRES_UMAMI_PASSWORD: ${W9_RCODE} volumes: - postgresql:/var/lib/postgresql/data @@ -36,7 +35,8 @@ services: interval: 5s timeout: 5s retries: 5 - + start_period: 20s + networks: default: name: ${W9_NETWORK} diff --git a/apps/umami/tests/cases.yml b/apps/umami/tests/cases.yml new file mode 100644 index 000000000..6475a9b85 --- /dev/null +++ b/apps/umami/tests/cases.yml @@ -0,0 +1,7 @@ +# The adaptive checks cover the compose config, container state, the compose +# healthcheck (/api/heartbeat) and the web root. The authenticated path is the +# core of Umami, so it is exercised explicitly. +custom: + - id: login-api + type: script + script: check.sh diff --git a/apps/umami/tests/check.sh b/apps/umami/tests/check.sh new file mode 100644 index 000000000..0861c0b44 --- /dev/null +++ b/apps/umami/tests/check.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -uo pipefail + +# BASE_URL is provided by `libs app-tests`; no package-specific variables needed. +base="${BASE_URL:?BASE_URL is required}" +# Umami seeds this admin account in the database migration; it is not controlled +# by the package .env, so the smoke test uses the documented default. +user="admin" +password="umami" +deadline=$((SECONDS + 240)) + +code="000" +while [ "$SECONDS" -lt "$deadline" ]; do + code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${base}/api/heartbeat" || true) + [ "$code" = "200" ] && break + sleep 5 +done + +if [ "$code" != "200" ]; then + echo "umami heartbeat -> ${code} (timeout)" + exit 1 +fi +echo "umami heartbeat -> 200" + +body=$(curl -s --max-time 15 -X POST "${base}/api/auth/login" \ + -H 'Content-Type: application/json' \ + -d "{\"username\":\"${user}\",\"password\":\"${password}\"}" || true) + +if printf '%s' "$body" | grep -q '"token"'; then + echo "umami login ok" + exit 0 +fi + +echo "umami login failed: ${body}" +exit 1 diff --git a/apps/umami/variables.json b/apps/umami/variables.json index 81f2e6121..eec318510 100644 --- a/apps/umami/variables.json +++ b/apps/umami/variables.json @@ -2,21 +2,35 @@ "name": "umami", "trademark": "Umami", "release": true, + "upstream": { + "image": "https://ghcr.io/umami-software/umami", + "releases": "https://github.com/umami-software/umami/releases", + "compose": { + "compose": "https://raw.githubusercontent.com/umami-software/umami/v3.4.0/docker-compose.yml" + }, + "docs": [ + "https://umami.is/docs/install", + "https://umami.is/docs/environment-variables" + ] + }, "edition": [ { "dist": "community", "version": [ - "3.0.3", + "3.4", "latest" ] } ], + "access": { + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/umamisoftware/umami" } } diff --git a/apps/umbraco/.env b/apps/umbraco/.env index 05b66c29d..121ef3f28 100644 --- a/apps/umbraco/.env +++ b/apps/umbraco/.env @@ -1,3 +1,46 @@ -W9_POWER_PASSWORD=spJNF09yzwWJaG! +W9_REPO=websoft9dev/umbraco +W9_DIST=community +W9_VERSION=18.2.0 + +# Optional password seed: enable only when the package actually controls a DB or built-in login. +# See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. +W9_POWER_PASSWORD='spJNF09yzwWJaG!' + +# Canonical semantics live in docs/w9-env-spec.md. +# This template controls layout; the spec controls meaning and decision rules. + +#### -- Not allowed to edit below environments when recreate app based on existing data -- #### + +W9_ID=umbraco +W9_HTTP_PORT=8080 +W9_HTTP_PORT_SET=9001 + +# Built-in login and URL helpers: keep these in the protected block because the appstore parser +# reads them from the app package metadata surface. +W9_LOGIN_USER=admin@example.com +W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} +W9_URL=umbraco.example.com +W9_URL_REPLACE=true +W9_ADMIN_PATH="/umbraco" + W9_NETWORK=websoft9 -W9_NAME=umbraco + +#### ----------------------------------------------------------------------------------------- #### + +# ============================================================ +# Umbraco image environment variables +# Docs: https://docs.umbraco.com/umbraco-cms/get-started/installation/running-umbraco-on-docker-locally +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ + +# Used by docker-compose.yml: +UMBRACO_DB_DSN=Data Source=/app/umbraco/Data/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True + +# Not used by default; enable only when needed: +# Umbraco__CMS__Global__UseHttps=false +# Umbraco__CMS__Runtime__Mode=Production +# Umbraco__CMS__Hosting__Debug=false +# Umbraco__CMS__Unattended__UnattendedTelemetryLevel=Basic +# Umbraco__CMS__HealthChecks__DisabledChecks__0__Id=E2048C48-21C5-4BE1-A80B-8062162DF124 diff --git a/apps/umbraco/CHANGELOG.md b/apps/umbraco/CHANGELOG.md index 582cf46c5..9de364ef6 100644 --- a/apps/umbraco/CHANGELOG.md +++ b/apps/umbraco/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements - +## 2026-09-20 +- Rebuild the Umbraco package from the official Umbraco Docker guidance as a self-contained app. +- Build the Umbraco CMS 18.2.0 image from `Umbraco.Templates` on `mcr.microsoft.com/dotnet/aspnet:10.0` via `Dockerfile`. +- Use SQLite for storage (`/app/umbraco/Data/Umbraco.sqlite.db`) with a persisted data volume; no external database service. +- Create the administrator account on first start through Umbraco unattended install using `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD`. +- Add `tests/cases.yml` with a backoffice reachability check. diff --git a/apps/umbraco/Dockerfile b/apps/umbraco/Dockerfile new file mode 100644 index 000000000..b26bc55c3 --- /dev/null +++ b/apps/umbraco/Dockerfile @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build + +ARG UMBRACO_VERSION=18.2.0 + +RUN dotnet new install Umbraco.Templates::${UMBRACO_VERSION} + +WORKDIR /src +RUN dotnet new umbraco -n UmbracoApp -o UmbracoApp --no-restore +RUN dotnet publish UmbracoApp/UmbracoApp.csproj -c Release -o /app/publish /p:UseAppHost=false + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final + +WORKDIR /app +COPY --from=build /app/publish . + +RUN mkdir -p /app/umbraco/Data /app/umbraco/Logs /app/wwwroot/media + +ENV ASPNETCORE_URLS=http://+:8080 \ + ASPNETCORE_ENVIRONMENT=Production + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "UmbracoApp.dll"] diff --git a/apps/umbraco/Notes.md b/apps/umbraco/Notes.md index a6cc33a98..107f8f305 100644 --- a/apps/umbraco/Notes.md +++ b/apps/umbraco/Notes.md @@ -1,4 +1,9 @@ # Umbraco -https://our.umbraco.com/documentation/Fundamentals/Setup/Install/ +官方文档:https://docs.umbraco.com/umbraco-cms/get-started/installation/running-umbraco-on-docker-locally +本包基于官方 `Umbraco.Templates` 在构建时生成项目并发布,数据库使用 SQLite,无需额外的数据库容器。 + +- 后台入口:`/umbraco` +- 管理员账号:首次启动时由 unattended install 使用 `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD` 自动创建 +- 数据持久化:`/app/umbraco`(含 SQLite 数据库与日志)、`/app/wwwroot/media` diff --git a/apps/umbraco/README.md b/apps/umbraco/README.md index 12dca39bd..0c9e6289f 100644 --- a/apps/umbraco/README.md +++ b/apps/umbraco/README.md @@ -1,26 +1,89 @@ -# Umbraco on Docker +# Umbraco on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for Umbraco: +## Quick Start +### Deploy Verification - - community: latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **Umbraco**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the Umbraco site from the **Access** tab. +2. Sign in to the backoffice at `/umbraco` with the credentials from the **Access** tab. +3. Create your first content node to confirm the publishing flow. -The following are the minimal [recommended requirements](https://umbraco.com/): +### Change Password -* **RAM**: 4 GB or more -* **CPU**: 2 cores or higher -* **Disk**: at least 1 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Change the administrator password from the user profile inside the Umbraco backoffice. +2. `W9_LOGIN_USER` and `W9_LOGIN_PASSWORD` create the administrator on first start only; changing them afterwards requires updating the user in the backoffice or removing the `umbraco_data` volume and rebuilding. + -## Install +## Configuration Reference -You can install this Umbraco by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [Umbraco Docker image](https://hub.docker.com/r/websoft9dev/umbraco) and makes some improvements below. -If you want use Umbraco with **Websoft9 Business Support** free, you can [subscribe Umbraco](https://www.websoft9.com/apps) on Cloud platform + -## Documentation + -[Umbraco Administrator Guide](https://support.websoft9.com/docs/umbraco) powered by Websoft9 \ No newline at end of file +Apps run as containers; rebuild after any configuration change. + +### Version Support + +Supported versions: 18.2.0. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | + + +### Data Directory + + +- `umbraco_data` → `/app/umbraco` +- `umbraco_media` → `/app/wwwroot/media` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +Note: `W9_LOGIN_USER`, `W9_LOGIN_PASSWORD` take effect on first startup only; changing them after deployment may not take effect until the app is re-initialized. + + +### Configuration Files + + +Configuration files live inside the image; mount a single file read-only to override, and never replace the whole directory. + + +## References + +- [Umbraco Administrator Guide](https://support.websoft9.com/docs/umbraco) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/r/websoft9dev/umbraco) + +- [Releases](https://github.com/umbraco/Umbraco-CMS/releases) + +- [Official docs](https://docs.umbraco.com/umbraco-cms/get-started/installation/running-umbraco-on-docker-locally) + +- [Official docs](https://docs.umbraco.com/umbraco-cms/run-in-production/infrastructure-and-ops/server-setup/running-umbraco-in-docker) + + + +## Troubleshooting + +**App fails to start?** +- Check `docker compose logs`. + +**Port not reachable?** +- Ensure the firewall / security group allows the port. + diff --git a/apps/umbraco/docker-compose.yml b/apps/umbraco/docker-compose.yml index 60c04ffe1..0336a8de2 100644 --- a/apps/umbraco/docker-compose.yml +++ b/apps/umbraco/docker-compose.yml @@ -1,11 +1,35 @@ -version: '3.8' - services: - -networks: - default: - name: ${W9_NAME} + + umbraco: + image: ${W9_REPO}:${W9_VERSION} + build: + context: . + dockerfile: Dockerfile + args: + UMBRACO_VERSION: ${W9_VERSION} + container_name: ${W9_ID} + restart: unless-stopped + env_file: .env + ports: + - "${W9_HTTP_PORT_SET}:8080" # Web Console + environment: + ASPNETCORE_URLS: http://+:8080 + ConnectionStrings__umbracoDbDSN: "${UMBRACO_DB_DSN}" + ConnectionStrings__umbracoDbDSN_ProviderName: Microsoft.Data.Sqlite + Umbraco__CMS__WebRouting__UmbracoApplicationUrl: http://${W9_URL} + Umbraco__CMS__Unattended__InstallUnattended: "true" + Umbraco__CMS__Unattended__UnattendedUserName: Administrator + Umbraco__CMS__Unattended__UnattendedUserEmail: ${W9_LOGIN_USER} + Umbraco__CMS__Unattended__UnattendedUserPassword: ${W9_LOGIN_PASSWORD} + volumes: + - umbraco_data:/app/umbraco + - umbraco_media:/app/wwwroot/media volumes: - mysql: - suitecrm: + umbraco_data: + umbraco_media: + +networks: + default: + name: ${W9_NETWORK} + external: true diff --git a/apps/umbraco/tests/cases.yml b/apps/umbraco/tests/cases.yml new file mode 100644 index 000000000..87493f12c --- /dev/null +++ b/apps/umbraco/tests/cases.yml @@ -0,0 +1,5 @@ +optional: + - id: backoffice + type: web-access + path: /umbraco + expect_status: [200] diff --git a/apps/umbraco/variables.json b/apps/umbraco/variables.json index b78c650ad..61ca2c8fd 100644 --- a/apps/umbraco/variables.json +++ b/apps/umbraco/variables.json @@ -1,18 +1,46 @@ { "name": "umbraco", "trademark": "Umbraco", - "release": false, + "release": true, + "upstream": { + "image": "https://hub.docker.com/r/websoft9dev/umbraco", + "releases": "https://github.com/umbraco/Umbraco-CMS/releases", + "docs": [ + "https://docs.umbraco.com/umbraco-cms/get-started/installation/running-umbraco-on-docker-locally", + "https://docs.umbraco.com/umbraco-cms/run-in-production/infrastructure-and-ops/server-setup/running-umbraco-in-docker" + ] + }, "edition": [ { "dist": "community", "version": [ - "latest" + "18.2.0" ] } ], + "access": { + "web": { + "port": 8080, + "path": "/" + }, + "admin": { + "port": 8080, + "path": "/umbraco" + } + }, "requirements": { "cpu": "2", "memory": "4", - "disk": "1" + "disk": "4" + }, + "credentials": {}, + "env": { + "first_startup_only": [ + "W9_LOGIN_USER", + "W9_LOGIN_PASSWORD" + ] + }, + "help": { + "db": "Bundled SQLite database; the administrator account is created on first start." } } diff --git a/apps/varnish/CHANGELOG.md b/apps/varnish/CHANGELOG.md index ccdf5ec98..3786628b8 100644 --- a/apps/varnish/CHANGELOG.md +++ b/apps/varnish/CHANGELOG.md @@ -1,9 +1,10 @@ # CHANGELOG ## 2026-09-20 -- Updated Varnish to `9.0` (official `varnish` image, currently 9.0.4). +- Updated Varnish to `9.0` (official `varnish` image, currently 9.0.4); the version list is now `9.0` and `latest` (the `stable` tag tracks the unrelated 6.0 LTS line). - Replaced the stale `src/default.vcl` (VCL 4.0 with an unresolvable backend that prevented the container from starting) with the upstream 9.0 default VCL; the backend is now configured through `VARNISH_BACKEND_HOST`. -- Exposed the Varnish image variables in `.env` (`VARNISH_SIZE` plus optional `VARNISH_BACKEND_HOST`, `VARNISH_FILESERVER`, `VARNISH_VCL_FILE`, `VARNISH_HTTP_PORT`, `VARNISH_PROXY_PORT`). +- Exposed the Varnish image variables in `.env` (`VARNISH_SIZE` plus optional `VARNISH_BACKEND_HOST`, `VARNISH_FILESERVER`, `VARNISH_VCL_FILE`). - Normalized `.env` and `docker-compose.yml` to current repository policy rules (braced `${VAR}` references, port purpose comment, removal of the source comment). - Added `tests/cases.yml` with a Varnish smoke check. -- Updated `Notes.md` and regenerated the README. +- Expanded the `upstream` metadata with the GitHub releases and official documentation sources. +- Regenerated the README. diff --git a/apps/varnish/Notes.md b/apps/varnish/Notes.md deleted file mode 100644 index 13bfb6791..000000000 --- a/apps/varnish/Notes.md +++ /dev/null @@ -1,37 +0,0 @@ -# Varnish - -## WordPress 设置 Varnish 教程 - -1. 分别在 Websoft9 控制台安装 WordPress 和 Varnish 两个应用 - > 确保 Varnish 配置的域名是最终提供给用户访问的域名 - -2. 编辑 Varnish 应用的 `.env` 文件,通过 `VARNISH_BACKEND_HOST` 将后端指向 WordPress 容器 - ``` - VARNISH_BACKEND_HOST=http://wordpress_shlez:80/ - ``` - -3. 重建 Varnish 应用后,Varnish 已经将 WordPress 缓存 - -4. 访问 Varnish 所绑定的域名,便发现访问速度大大提升 - -## Varnish 禁止爬虫访问 - -1. 编辑 Varnish 应用的 `./src/default.vcl` 文件,在 `sub vcl_recv` 中增加如下内容,其中 `Sogou web spider` 改成你想要禁用的爬虫名: - ``` - sub vcl_recv { - if (req.http.user-agent ~ "Sogou web spider") { - return (synth(403, "Forbidden")); - } - } - ``` - -2. 重建 Varnish 应用后,Varnish 已经对爬虫禁用 - -## 配置选项 - -- 缓存大小:通过 `VARNISH_SIZE` 环境变量设置 -- 后端地址:通过 `VARNISH_BACKEND_HOST` 环境变量设置(未设置时提供一个本地占位页面) -- 文件服务模式:设置 `VARNISH_FILESERVER=true` 后,Varnish 直接提供 `/var/www/html` 下的静态文件 -- 配置文件:`./src/default.vcl`,可自定义 VCL 规则 - -## FAQ diff --git a/apps/varnish/README.md b/apps/varnish/README.md index 3c5c7a52c..d74e26d54 100644 --- a/apps/varnish/README.md +++ b/apps/varnish/README.md @@ -37,7 +37,7 @@ Apps run as containers; rebuild after any configuration change. ### Version Support -Supported versions: 9.0, stable, latest. +Supported versions: 9.0, latest. The `latest` tag is not guaranteed to remain valid; pin a specific version for production. @@ -72,6 +72,12 @@ Configuration is overridden by mounting `./src/default.vcl` to `/etc/varnish/def - [Docker Hub image](https://hub.docker.com/_/varnish) +- [Releases](https://github.com/varnish/varnish/releases) + +- [Official docs](https://varnish-cache.org/docs/) + +- [GitHub docs](https://github.com/varnish/docker-varnish) + ## Troubleshooting diff --git a/apps/varnish/variables.json b/apps/varnish/variables.json index 8b7dbfaa6..76ad9422d 100644 --- a/apps/varnish/variables.json +++ b/apps/varnish/variables.json @@ -7,7 +7,6 @@ "dist": "community", "version": [ "9.0", - "stable", "latest" ] } @@ -18,6 +17,11 @@ "disk": "8" }, "upstream": { - "image": "https://hub.docker.com/_/varnish" + "image": "https://hub.docker.com/_/varnish", + "releases": "https://github.com/varnish/varnish/releases", + "docs": [ + "https://varnish-cache.org/docs/", + "https://github.com/varnish/docker-varnish" + ] } } diff --git a/apps/xwiki/.env b/apps/xwiki/.env index d385357bf..17e5951d6 100644 --- a/apps/xwiki/.env +++ b/apps/xwiki/.env @@ -1,31 +1,45 @@ W9_REPO=xwiki -W9_DIST='community' +W9_DIST=community +W9_VERSION=18.7 -# This tag is tomcat+mysql runtime, not xwiki version -# xwiki version should set XWIKI_VERSION -W9_VERSION='17.10' -W9_POWER_PASSWORD='fNoyaf5!dgkPBx0E' +W9_POWER_PASSWORD="fNoyaf5!dgkPBx0E" #### -- Not allowed to edit below environments when recreate app based on existing data -- #### -W9_ID='xwiki' -# W9_HTTP_PORT or W9_HTTPS_PORT is need at leaset and used for proxy for web application -# Some container (e.g teleport) need HTTPS access, then need to set this pra +W9_ID=xwiki + +# Web/internal ports W9_HTTP_PORT=8080 -W9_HTTP_PORT_SET='9001' +W9_HTTP_PORT_SET=9004 + +# URL helper +W9_URL=appname.example.com +# Bundled database W9_DB_EXPOSE="mysql" -W9_DB_VERSION="8.3" -W9_URL='example.youdomain.com' +W9_DB_VERSION=8.4 + W9_NETWORK=websoft9 #### ----------------------------------------------------------------------------------------- #### +# ============================================================ +# XWiki image environment variables +# Docs: https://github.com/xwiki/xwiki-docker +# Follow docs/w9-env-spec.md when deciding which vars belong here. +# Enable the vars actually used in docker-compose.yml below; list up +# to 5 unused vars commented out. Full reference lives in the Docs URL. +# ============================================================ -# Below environment is created by xwiki -# default database is xwiki, so it need to create it before install -# XWIKI_VERSION=15.10.4 is for docker build: https://github.com/xwiki/xwiki-docker/blob/master/15/mysql-tomcat/Dockerfile, don't need for docker run -DB_USER=root +# Used by docker-compose.yml: + +DB_USER=xwiki DB_DATABASE=xwiki DB_PASSWORD=${W9_POWER_PASSWORD} DB_HOST=${W9_ID}-mysql + +# Not used by default; enable only when needed: +# XWIKI_VERSION=18.7.0 +# DB_USE_SSL=true +# JDBC_PARAMS=useSSL=false +# XWIKI_MEMORY=2048 diff --git a/apps/xwiki/CHANGELOG.md b/apps/xwiki/CHANGELOG.md index 582cf46c5..f95c42ed9 100644 --- a/apps/xwiki/CHANGELOG.md +++ b/apps/xwiki/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG -## Release -### Fixes and Enhancements +## 2026-09-20 +- Update XWiki to 18.7 (latest stable) and the bundled MySQL to 8.4 LTS. +- Connect as the dedicated `xwiki` database user instead of `root`. +- Add `init: true` and a main-container healthcheck; default the web port to 9004. +- Refresh `variables.json`, README and Notes, and add a webapp smoke test. diff --git a/apps/xwiki/Notes.md b/apps/xwiki/Notes.md index 02755fcf1..531e41e8d 100644 --- a/apps/xwiki/Notes.md +++ b/apps/xwiki/Notes.md @@ -1,29 +1,30 @@ -## XWiki +# XWiki Notes -### 安装 +> 内部维护说明;面向客户的文档以 `README.md` 为准。 -1. MySQL 初始化问题 +## 来源 -官方文档要求初始化时运行 `grant all privileges on *.* to xwiki@'%'`。但即使不运行这段脚本,查询 xwiki 权限发现也具有 ALL PRIVILEGES +- 官方镜像:https://hub.docker.com/_/xwiki +- 镜像源码:https://github.com/xwiki/xwiki-docker +- 版本列表:https://github.com/xwiki/xwiki-platform/releases -``` -mysql> show grants for xwiki@'%'; -+--------------------------------------------------+ -| Grants for xwiki@% | -+--------------------------------------------------+ -| GRANT USAGE ON *.* TO 'xwiki'@'%' | -| GRANT ALL PRIVILEGES ON `xwiki`.* TO 'xwiki'@'%' | -+--------------------------------------------------+ -2 rows in set (0.00 sec) -``` +## 版本与镜像标签 -所以暂时不做权限处理。 +- `W9_VERSION=18.7` 对应官方 `18.7` 标签(即 `18/mysql-tomcat` 变体,`latest`/`stable` 也指向它)。 +- `xwiki` 默认标签就是 mysql-tomcat 变体,无需再加 `-mysql-tomcat` 后缀。 +- 官方 compose 通过 `XWIKI_VERSION` 指定具体 XWiki 版本;本包依赖 `18.7` 浮动标签,因此不设置 `XWIKI_VERSION`,随 18.7.x 自动更新。 +- 18.7 之前的大版本(LTS `17.10`、中间 LTS `18.4`)仍由官方发布,但本包跟随最新稳定线。 -2. Solr service +## 数据库 -By default XWiki ships with an embedded Solr. 但推荐使用外部 solr。官方方案配置外部 solr 还需要挂载一个配置文件,并更改权限,考虑复杂性,暂时不做 +- 内置 MySQL `${W9_ID}-mysql`,镜像 `mysql:${W9_DB_VERSION}`(当前 8.4 LTS)。 +- 连接参数:`DB_USER=xwiki`、`DB_DATABASE=xwiki`、`DB_PASSWORD=${W9_POWER_PASSWORD}`、`DB_HOST=${W9_ID}-mysql`。 +- `src/mysql_init.sql`(官方 `init.sql`)执行 `grant all privileges on *.* to xwiki@'%'`。这一步是**必需**的:XWiki 迁移要读取 `information_schema` 元数据,需要全局 `PROCESS` 权限,仅 `GRANT ALL ON xwiki.*` 会报 `Access denied; you need (at least one of) the PROCESS privilege(s)`,导致数据库迁移失败(`Database is currently in version [0]`)。 +- 启动参数保持官方推荐:`utf8mb4` / `utf8mb4_bin` / `explicit-defaults-for-timestamp=1`(MySQL 8.4 仍接受)。 -3. 安装向导 - -安装向导会在线拉去资源,故时间比较长 +## 首次安装与升级 +- 首次访问会进入 **Distribution Wizard**,会在线下载 Standard Flavor,需要外网,耗时数分钟。 +- 管理员账号在向导中创建,不受 `.env` 控制。 +- 17.x → 18.x 为大版本升级:先备份 MySQL 数据卷与 `xwiki` 数据卷,升级后按向导完成数据库 schema 迁移。 +- 默认使用内嵌 Solr;官方推荐外部 Solr,但需额外挂载配置并处理权限,本包暂不启用。 diff --git a/apps/xwiki/README.md b/apps/xwiki/README.md index 9df37186d..a66528ad7 100644 --- a/apps/xwiki/README.md +++ b/apps/xwiki/README.md @@ -1,26 +1,98 @@ -# XWiki on Docker +# XWiki on Docker -This is an **[Docker Compose template](https://github.com/Websoft9/docker-library)** powered by [Websoft9](https://www.websoft9.com) based on Docker for XWiki: +## Quick Start +### Deploy Verification - - community: 17.4, latest +1. In the [Websoft9](https://www.websoft9.com) console, open **My Apps** and select **XWiki**. +2. In the **Access** tab, get the login URL and credentials. +3. Open the login URL in a browser and sign in to confirm the app works. + +### Usage -## System Requirements +1. Open the XWiki console; the first visit shows the **Distribution Wizard**. +2. Complete the wizard to create the wiki and the first administrator account. +3. After installation, sign in and start creating pages. -The following are the minimal [recommended requirements](https://github.com/xwiki-contrib/docker-xwiki/blob/master/README.md): +### Change Password -* **RAM**: 2 GB or more -* **CPU**: 1 cores or higher -* **Disk**: at least 8 GB of free space -* **bandwidth**: more fluent experience over 100M +1. Sign in to XWiki and open your user profile. +2. Change your own password there, or use **Administration → Users** to reset other accounts. + -## Install +## Configuration Reference -You can install this XWiki by [How to use it?](https://github.com/Websoft9/docker-library#how-to-use-it). +Websoft9 packages this app from the official [XWiki Docker image](https://hub.docker.com/_/xwiki) and makes some improvements below. -If you want use XWiki with **Websoft9 Business Support** free, you can [subscribe XWiki](https://www.websoft9.com/apps) on Cloud platform + +- The first visit runs the XWiki Distribution Wizard; it downloads the standard flavor, so the first install takes several minutes and needs outbound network access. +- `DB_USER` / `DB_PASSWORD` / `DB_DATABASE` / `DB_HOST` configure the bundled MySQL service `${W9_ID}-mysql`. +- XWiki data is persisted in the `xwiki` volume (`/usr/local/xwiki`). +- The administrator account is created interactively in the wizard and is not controlled by `.env`. + -## Documentation +Apps run as containers; rebuild after any configuration change. -[XWiki Administrator Guide](https://support.websoft9.com/docs/xwiki) powered by Websoft9 \ No newline at end of file +### Version Support + +Supported versions: 18.7, latest. + +The `latest` tag is not guaranteed to remain valid; pin a specific version for production. + + +### Ports + +| Purpose | Port | +| --- | --- | +| Web Console | 8080 | + + +### Data Directory + + +- `xwiki` → `/usr/local/xwiki` +- `mysql` → `/var/lib/mysql` + + + +### Environment Variables + +Environment variables are defined in the app's `.env` file; see the reference section at the end of `.env` for supported variables. + + +### Configuration Files + + +Configuration is overridden by mounting `./src/mysql_init.sql` to `/docker-entrypoint-initdb.d/init.sql`. + + +## References + +- [XWiki Administrator Guide](https://support.websoft9.com/docs/xwiki) by Websoft9 + +- [Docker Hub image](https://hub.docker.com/_/xwiki) + +- [Releases](https://github.com/xwiki/xwiki-platform/releases) + +- [Official compose](https://raw.githubusercontent.com/xwiki/xwiki-docker/master/18/mysql-tomcat/docker-compose.yml) + +- [Official env example](https://raw.githubusercontent.com/xwiki/xwiki-docker/master/18/mysql-tomcat/.env) + +- [Official docs](https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/Installation/) + +- [GitHub docs](https://github.com/xwiki/xwiki-docker) + + + +## Troubleshooting + +**The first page keeps showing the Distribution Wizard?** +- Complete the wizard and let it download the standard flavor; it needs outbound network access. + +**Container stays unhealthy?** +- XWiki can take a couple of minutes to start; check `docker compose logs ${W9_ID}`. + +**Database connection error?** +- Confirm `${W9_ID}-mysql` is running and that `DB_USER` / `DB_PASSWORD` / `DB_DATABASE` match the MySQL service. + diff --git a/apps/xwiki/docker-compose.yml b/apps/xwiki/docker-compose.yml index b6f3b36a5..27b4026a7 100644 --- a/apps/xwiki/docker-compose.yml +++ b/apps/xwiki/docker-compose.yml @@ -1,21 +1,23 @@ -# image: https://hub.docker.com/_/xwiki -# docs: https://github.com/xwiki/xwiki-docker/blob/master/README.md - -version: '3.8' - services: xwiki: - image: $W9_REPO:$W9_VERSION + image: ${W9_REPO}:${W9_VERSION} container_name: ${W9_ID} + restart: unless-stopped + init: true + env_file: + - .env ports: - - $W9_HTTP_PORT_SET:8080 + - "${W9_HTTP_PORT_SET}:8080" # Web Console volumes: - xwiki:/usr/local/xwiki - env_file: - - .env depends_on: - db - restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:8080/bin/view/Main/"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 120s db: image: mysql:${W9_DB_VERSION} @@ -27,6 +29,7 @@ services: - "--explicit-defaults-for-timestamp=1" volumes: - mysql:/var/lib/mysql + - ./src/mysql_init.sql:/docker-entrypoint-initdb.d/init.sql environment: MYSQL_DATABASE: xwiki MYSQL_USER: xwiki @@ -35,9 +38,9 @@ services: networks: default: - name: $W9_NETWORK + name: ${W9_NETWORK} external: true volumes: xwiki: - mysql: \ No newline at end of file + mysql: diff --git a/apps/xwiki/src/mysql_init.sql b/apps/xwiki/src/mysql_init.sql new file mode 100644 index 000000000..76dfd8c76 --- /dev/null +++ b/apps/xwiki/src/mysql_init.sql @@ -0,0 +1 @@ +grant all privileges on *.* to xwiki@'%' diff --git a/apps/xwiki/tests/cases.yml b/apps/xwiki/tests/cases.yml new file mode 100644 index 000000000..5a2eabae1 --- /dev/null +++ b/apps/xwiki/tests/cases.yml @@ -0,0 +1,7 @@ +# The adaptive checks cover compose config, container state, the healthcheck and +# the web root. XWiki is served from the ROOT context, so the app-specific check +# follows the redirect chain and asserts the XWiki webapp is actually rendered. +custom: + - id: xwiki-webapp + type: script + script: check.sh diff --git a/apps/xwiki/tests/check.sh b/apps/xwiki/tests/check.sh new file mode 100644 index 000000000..2c713ccfd --- /dev/null +++ b/apps/xwiki/tests/check.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -uo pipefail + +# BASE_URL is provided by `libs app-tests`; no package-specific variables needed. +base="${BASE_URL:?BASE_URL is required}" +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +deadline=$((SECONDS + 300)) +code="000" + +while [ "$SECONDS" -lt "$deadline" ]; do + code=$(curl -s -L -o "$body_file" -w "%{http_code}" --max-time 20 "${base}/" || true) + if [ "$code" = "200" ] && grep -qi "xwiki" "$body_file"; then + echo "xwiki webapp served at ${base}/ (200)" + exit 0 + fi + sleep 5 +done + +echo "xwiki webapp -> ${code} (timeout)" +exit 1 diff --git a/apps/xwiki/variables.json b/apps/xwiki/variables.json index 313c892cc..4598af7cf 100644 --- a/apps/xwiki/variables.json +++ b/apps/xwiki/variables.json @@ -2,21 +2,36 @@ "name": "xwiki", "trademark": "XWiki", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/xwiki", + "releases": "https://github.com/xwiki/xwiki-platform/releases", + "compose": { + "compose": "https://raw.githubusercontent.com/xwiki/xwiki-docker/master/18/mysql-tomcat/docker-compose.yml", + "env": "https://raw.githubusercontent.com/xwiki/xwiki-docker/master/18/mysql-tomcat/.env" + }, + "docs": [ + "https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/Installation/", + "https://github.com/xwiki/xwiki-docker" + ] + }, "edition": [ { "dist": "community", "version": [ - "17.10", + "18.7", "latest" ] } ], + "access": { + "web": { + "port": 8080, + "path": "/xwiki/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "8" - }, - "upstream": { - "image": "https://hub.docker.com/_/xwiki" } } diff --git a/apps/taskingai/.env b/archive/apps/taskingai/.env similarity index 100% rename from apps/taskingai/.env rename to archive/apps/taskingai/.env diff --git a/apps/taskingai/CHANGELOG.md b/archive/apps/taskingai/CHANGELOG.md similarity index 100% rename from apps/taskingai/CHANGELOG.md rename to archive/apps/taskingai/CHANGELOG.md diff --git a/apps/taskingai/Notes.md b/archive/apps/taskingai/Notes.md similarity index 100% rename from apps/taskingai/Notes.md rename to archive/apps/taskingai/Notes.md diff --git a/apps/taskingai/README.md b/archive/apps/taskingai/README.md similarity index 100% rename from apps/taskingai/README.md rename to archive/apps/taskingai/README.md diff --git a/apps/taskingai/docker-compose.yml b/archive/apps/taskingai/docker-compose.yml similarity index 100% rename from apps/taskingai/docker-compose.yml rename to archive/apps/taskingai/docker-compose.yml diff --git a/apps/taskingai/src/README.md b/archive/apps/taskingai/src/README.md similarity index 100% rename from apps/taskingai/src/README.md rename to archive/apps/taskingai/src/README.md diff --git a/apps/taskingai/src/proxy.conf b/archive/apps/taskingai/src/proxy.conf similarity index 100% rename from apps/taskingai/src/proxy.conf rename to archive/apps/taskingai/src/proxy.conf diff --git a/apps/taskingai/variables.json b/archive/apps/taskingai/variables.json similarity index 100% rename from apps/taskingai/variables.json rename to archive/apps/taskingai/variables.json diff --git a/apps/theia/.env b/archive/apps/theia/.env similarity index 100% rename from apps/theia/.env rename to archive/apps/theia/.env diff --git a/apps/theia/CHANGELOG.md b/archive/apps/theia/CHANGELOG.md similarity index 100% rename from apps/theia/CHANGELOG.md rename to archive/apps/theia/CHANGELOG.md diff --git a/apps/theia/Notes.md b/archive/apps/theia/Notes.md similarity index 100% rename from apps/theia/Notes.md rename to archive/apps/theia/Notes.md diff --git a/apps/theia/README.md b/archive/apps/theia/README.md similarity index 100% rename from apps/theia/README.md rename to archive/apps/theia/README.md diff --git a/apps/theia/docker-compose.yml b/archive/apps/theia/docker-compose.yml similarity index 100% rename from apps/theia/docker-compose.yml rename to archive/apps/theia/docker-compose.yml diff --git a/apps/theia/src/filelist b/archive/apps/theia/src/filelist similarity index 100% rename from apps/theia/src/filelist rename to archive/apps/theia/src/filelist diff --git a/apps/theia/variables.json b/archive/apps/theia/variables.json similarity index 100% rename from apps/theia/variables.json rename to archive/apps/theia/variables.json diff --git a/apps/tinyproxy/.env b/archive/apps/tinyproxy/.env similarity index 100% rename from apps/tinyproxy/.env rename to archive/apps/tinyproxy/.env diff --git a/apps/tinyproxy/CHANGELOG.md b/archive/apps/tinyproxy/CHANGELOG.md similarity index 100% rename from apps/tinyproxy/CHANGELOG.md rename to archive/apps/tinyproxy/CHANGELOG.md diff --git a/apps/tinyproxy/Notes.md b/archive/apps/tinyproxy/Notes.md similarity index 100% rename from apps/tinyproxy/Notes.md rename to archive/apps/tinyproxy/Notes.md diff --git a/apps/tinyproxy/README.md b/archive/apps/tinyproxy/README.md similarity index 100% rename from apps/tinyproxy/README.md rename to archive/apps/tinyproxy/README.md diff --git a/apps/tinyproxy/docker-compose.yml b/archive/apps/tinyproxy/docker-compose.yml similarity index 100% rename from apps/tinyproxy/docker-compose.yml rename to archive/apps/tinyproxy/docker-compose.yml diff --git a/apps/tinyproxy/src/README.md b/archive/apps/tinyproxy/src/README.md similarity index 100% rename from apps/tinyproxy/src/README.md rename to archive/apps/tinyproxy/src/README.md diff --git a/apps/tinyproxy/src/tinyproxy.conf b/archive/apps/tinyproxy/src/tinyproxy.conf similarity index 100% rename from apps/tinyproxy/src/tinyproxy.conf rename to archive/apps/tinyproxy/src/tinyproxy.conf diff --git a/apps/tinyproxy/variables.json b/archive/apps/tinyproxy/variables.json similarity index 100% rename from apps/tinyproxy/variables.json rename to archive/apps/tinyproxy/variables.json diff --git a/apps/tomee/.env b/archive/apps/tomee/.env similarity index 100% rename from apps/tomee/.env rename to archive/apps/tomee/.env diff --git a/apps/tomee/CHANGELOG.md b/archive/apps/tomee/CHANGELOG.md similarity index 100% rename from apps/tomee/CHANGELOG.md rename to archive/apps/tomee/CHANGELOG.md diff --git a/apps/tomee/Notes.md b/archive/apps/tomee/Notes.md similarity index 100% rename from apps/tomee/Notes.md rename to archive/apps/tomee/Notes.md diff --git a/apps/tomee/README.md b/archive/apps/tomee/README.md similarity index 100% rename from apps/tomee/README.md rename to archive/apps/tomee/README.md diff --git a/apps/tomee/docker-compose.yml b/archive/apps/tomee/docker-compose.yml similarity index 100% rename from apps/tomee/docker-compose.yml rename to archive/apps/tomee/docker-compose.yml diff --git a/apps/tomee/src/README.md b/archive/apps/tomee/src/README.md similarity index 100% rename from apps/tomee/src/README.md rename to archive/apps/tomee/src/README.md diff --git a/apps/tomee/src/cmd.sh b/archive/apps/tomee/src/cmd.sh similarity index 100% rename from apps/tomee/src/cmd.sh rename to archive/apps/tomee/src/cmd.sh diff --git a/apps/tomee/variables.json b/archive/apps/tomee/variables.json similarity index 100% rename from apps/tomee/variables.json rename to archive/apps/tomee/variables.json diff --git a/apps/tooljet/.env b/archive/apps/tooljet/.env similarity index 100% rename from apps/tooljet/.env rename to archive/apps/tooljet/.env diff --git a/apps/tooljet/CHANGELOG.md b/archive/apps/tooljet/CHANGELOG.md similarity index 100% rename from apps/tooljet/CHANGELOG.md rename to archive/apps/tooljet/CHANGELOG.md diff --git a/apps/tooljet/README.md b/archive/apps/tooljet/README.md similarity index 100% rename from apps/tooljet/README.md rename to archive/apps/tooljet/README.md diff --git a/apps/tooljet/docker-compose.yml b/archive/apps/tooljet/docker-compose.yml similarity index 100% rename from apps/tooljet/docker-compose.yml rename to archive/apps/tooljet/docker-compose.yml diff --git a/apps/tooljet/src/README.md b/archive/apps/tooljet/src/README.md similarity index 100% rename from apps/tooljet/src/README.md rename to archive/apps/tooljet/src/README.md diff --git a/apps/tooljet/src/nginx-proxy.conf b/archive/apps/tooljet/src/nginx-proxy.conf similarity index 100% rename from apps/tooljet/src/nginx-proxy.conf rename to archive/apps/tooljet/src/nginx-proxy.conf diff --git a/apps/tooljet/tests/cases.yml b/archive/apps/tooljet/tests/cases.yml similarity index 100% rename from apps/tooljet/tests/cases.yml rename to archive/apps/tooljet/tests/cases.yml diff --git a/apps/tooljet/variables.json b/archive/apps/tooljet/variables.json similarity index 100% rename from apps/tooljet/variables.json rename to archive/apps/tooljet/variables.json diff --git a/apps/traccar/.env b/archive/apps/traccar/.env similarity index 100% rename from apps/traccar/.env rename to archive/apps/traccar/.env diff --git a/apps/traccar/CHANGELOG.md b/archive/apps/traccar/CHANGELOG.md similarity index 100% rename from apps/traccar/CHANGELOG.md rename to archive/apps/traccar/CHANGELOG.md diff --git a/apps/traccar/Notes.md b/archive/apps/traccar/Notes.md similarity index 100% rename from apps/traccar/Notes.md rename to archive/apps/traccar/Notes.md diff --git a/apps/traccar/README.md b/archive/apps/traccar/README.md similarity index 100% rename from apps/traccar/README.md rename to archive/apps/traccar/README.md diff --git a/apps/traccar/docker-compose.yml b/archive/apps/traccar/docker-compose.yml similarity index 100% rename from apps/traccar/docker-compose.yml rename to archive/apps/traccar/docker-compose.yml diff --git a/apps/traccar/src/README.md b/archive/apps/traccar/src/README.md similarity index 100% rename from apps/traccar/src/README.md rename to archive/apps/traccar/src/README.md diff --git a/apps/traccar/variables.json b/archive/apps/traccar/variables.json similarity index 100% rename from apps/traccar/variables.json rename to archive/apps/traccar/variables.json diff --git a/apps/trafficserver/.env b/archive/apps/trafficserver/.env similarity index 100% rename from apps/trafficserver/.env rename to archive/apps/trafficserver/.env diff --git a/apps/trafficserver/CHANGELOG.md b/archive/apps/trafficserver/CHANGELOG.md similarity index 100% rename from apps/trafficserver/CHANGELOG.md rename to archive/apps/trafficserver/CHANGELOG.md diff --git a/apps/trafficserver/Notes.md b/archive/apps/trafficserver/Notes.md similarity index 100% rename from apps/trafficserver/Notes.md rename to archive/apps/trafficserver/Notes.md diff --git a/apps/trafficserver/README.md b/archive/apps/trafficserver/README.md similarity index 100% rename from apps/trafficserver/README.md rename to archive/apps/trafficserver/README.md diff --git a/apps/trafficserver/docker-compose.yml b/archive/apps/trafficserver/docker-compose.yml similarity index 100% rename from apps/trafficserver/docker-compose.yml rename to archive/apps/trafficserver/docker-compose.yml diff --git a/apps/trafficserver/src/README.md b/archive/apps/trafficserver/src/README.md similarity index 100% rename from apps/trafficserver/src/README.md rename to archive/apps/trafficserver/src/README.md diff --git a/apps/trafficserver/src/records.yaml b/archive/apps/trafficserver/src/records.yaml similarity index 100% rename from apps/trafficserver/src/records.yaml rename to archive/apps/trafficserver/src/records.yaml diff --git a/apps/trafficserver/src/remap.config b/archive/apps/trafficserver/src/remap.config similarity index 100% rename from apps/trafficserver/src/remap.config rename to archive/apps/trafficserver/src/remap.config diff --git a/apps/trafficserver/src/storage.config b/archive/apps/trafficserver/src/storage.config similarity index 100% rename from apps/trafficserver/src/storage.config rename to archive/apps/trafficserver/src/storage.config diff --git a/apps/trafficserver/variables.json b/archive/apps/trafficserver/variables.json similarity index 100% rename from apps/trafficserver/variables.json rename to archive/apps/trafficserver/variables.json diff --git a/apps/triggerdev/.env b/archive/apps/triggerdev/.env similarity index 100% rename from apps/triggerdev/.env rename to archive/apps/triggerdev/.env diff --git a/apps/triggerdev/CHANGELOG.md b/archive/apps/triggerdev/CHANGELOG.md similarity index 100% rename from apps/triggerdev/CHANGELOG.md rename to archive/apps/triggerdev/CHANGELOG.md diff --git a/apps/triggerdev/Notes.md b/archive/apps/triggerdev/Notes.md similarity index 100% rename from apps/triggerdev/Notes.md rename to archive/apps/triggerdev/Notes.md diff --git a/apps/triggerdev/docker-compose.yml b/archive/apps/triggerdev/docker-compose.yml similarity index 100% rename from apps/triggerdev/docker-compose.yml rename to archive/apps/triggerdev/docker-compose.yml diff --git a/apps/triggerdev/src/README.md b/archive/apps/triggerdev/src/README.md similarity index 100% rename from apps/triggerdev/src/README.md rename to archive/apps/triggerdev/src/README.md diff --git a/apps/triggerdev/variables.json b/archive/apps/triggerdev/variables.json similarity index 100% rename from apps/triggerdev/variables.json rename to archive/apps/triggerdev/variables.json diff --git a/apps/ttrss/.env b/archive/apps/ttrss/.env similarity index 100% rename from apps/ttrss/.env rename to archive/apps/ttrss/.env diff --git a/apps/ttrss/CHANGELOG.md b/archive/apps/ttrss/CHANGELOG.md similarity index 100% rename from apps/ttrss/CHANGELOG.md rename to archive/apps/ttrss/CHANGELOG.md diff --git a/apps/ttrss/Notes.md b/archive/apps/ttrss/Notes.md similarity index 100% rename from apps/ttrss/Notes.md rename to archive/apps/ttrss/Notes.md diff --git a/apps/ttrss/README.md b/archive/apps/ttrss/README.md similarity index 100% rename from apps/ttrss/README.md rename to archive/apps/ttrss/README.md diff --git a/apps/ttrss/docker-compose.yml b/archive/apps/ttrss/docker-compose.yml similarity index 100% rename from apps/ttrss/docker-compose.yml rename to archive/apps/ttrss/docker-compose.yml diff --git a/apps/ttrss/src/README.md b/archive/apps/ttrss/src/README.md similarity index 100% rename from apps/ttrss/src/README.md rename to archive/apps/ttrss/src/README.md diff --git a/apps/ttrss/variables.json b/archive/apps/ttrss/variables.json similarity index 100% rename from apps/ttrss/variables.json rename to archive/apps/ttrss/variables.json diff --git a/apps/twenty/.env b/archive/apps/twenty/.env similarity index 100% rename from apps/twenty/.env rename to archive/apps/twenty/.env diff --git a/apps/twenty/CHANGELOG.md b/archive/apps/twenty/CHANGELOG.md similarity index 100% rename from apps/twenty/CHANGELOG.md rename to archive/apps/twenty/CHANGELOG.md diff --git a/apps/twenty/Notes.md b/archive/apps/twenty/Notes.md similarity index 100% rename from apps/twenty/Notes.md rename to archive/apps/twenty/Notes.md diff --git a/apps/twenty/README.md b/archive/apps/twenty/README.md similarity index 100% rename from apps/twenty/README.md rename to archive/apps/twenty/README.md diff --git a/apps/twenty/docker-compose.yml b/archive/apps/twenty/docker-compose.yml similarity index 100% rename from apps/twenty/docker-compose.yml rename to archive/apps/twenty/docker-compose.yml diff --git a/apps/twenty/src/README.md b/archive/apps/twenty/src/README.md similarity index 100% rename from apps/twenty/src/README.md rename to archive/apps/twenty/src/README.md diff --git a/apps/twenty/variables.json b/archive/apps/twenty/variables.json similarity index 100% rename from apps/twenty/variables.json rename to archive/apps/twenty/variables.json diff --git a/apps/tyk/.env b/archive/apps/tyk/.env similarity index 100% rename from apps/tyk/.env rename to archive/apps/tyk/.env diff --git a/apps/tyk/CHANGELOG.md b/archive/apps/tyk/CHANGELOG.md similarity index 100% rename from apps/tyk/CHANGELOG.md rename to archive/apps/tyk/CHANGELOG.md diff --git a/apps/tyk/Notes.md b/archive/apps/tyk/Notes.md similarity index 100% rename from apps/tyk/Notes.md rename to archive/apps/tyk/Notes.md diff --git a/apps/tyk/README.md b/archive/apps/tyk/README.md similarity index 100% rename from apps/tyk/README.md rename to archive/apps/tyk/README.md diff --git a/apps/tyk/docker-compose.yml b/archive/apps/tyk/docker-compose.yml similarity index 100% rename from apps/tyk/docker-compose.yml rename to archive/apps/tyk/docker-compose.yml diff --git a/apps/tyk/src/README.md b/archive/apps/tyk/src/README.md similarity index 100% rename from apps/tyk/src/README.md rename to archive/apps/tyk/src/README.md diff --git a/apps/tyk/src/tyk.conf b/archive/apps/tyk/src/tyk.conf similarity index 100% rename from apps/tyk/src/tyk.conf rename to archive/apps/tyk/src/tyk.conf diff --git a/apps/tyk/variables.json b/archive/apps/tyk/variables.json similarity index 100% rename from apps/tyk/variables.json rename to archive/apps/tyk/variables.json diff --git a/apps/unleash/.env b/archive/apps/unleash/.env similarity index 100% rename from apps/unleash/.env rename to archive/apps/unleash/.env diff --git a/apps/unleash/CHANGELOG.md b/archive/apps/unleash/CHANGELOG.md similarity index 100% rename from apps/unleash/CHANGELOG.md rename to archive/apps/unleash/CHANGELOG.md diff --git a/apps/unleash/Notes.md b/archive/apps/unleash/Notes.md similarity index 100% rename from apps/unleash/Notes.md rename to archive/apps/unleash/Notes.md diff --git a/apps/unleash/README.md b/archive/apps/unleash/README.md similarity index 100% rename from apps/unleash/README.md rename to archive/apps/unleash/README.md diff --git a/apps/unleash/docker-compose.yml b/archive/apps/unleash/docker-compose.yml similarity index 100% rename from apps/unleash/docker-compose.yml rename to archive/apps/unleash/docker-compose.yml diff --git a/apps/unleash/src/README.md b/archive/apps/unleash/src/README.md similarity index 100% rename from apps/unleash/src/README.md rename to archive/apps/unleash/src/README.md diff --git a/apps/unleash/variables.json b/archive/apps/unleash/variables.json similarity index 100% rename from apps/unleash/variables.json rename to archive/apps/unleash/variables.json diff --git a/build/__pycache__/fetch_catalog.cpython-312.pyc b/build/__pycache__/fetch_catalog.cpython-312.pyc index a9191b20edd1e6c7f7486196a6058ea4b6ee5735..b6b5d7ea6c94d9da8a86660974623475cfdc0bc6 100644 GIT binary patch delta 1613 zcmbVMO>7%Q6rNeH^g2jSx{8sB*bm(2+|NWZ5^kmrco0TP)eHGp0%^d#!hF~ z$`1|>1&LN5HP#|E2M#$9gvh0mB@S=^rCbn~{1GCH0|$;ALM>5+3(TzTv`9TM(thvl zH{ZVZ_M5f84gJ_-`^joG1G4_ve=t?>6l@J}zwpvX9+{B&GiHoK+?WMf#;nMLtRJ#t zHgp2nNVFp_vXkgQ4ah;F6Zw#nL>D@VTqM?`MpRFtTks(FbPE8ufdPVks-3p_?;^Z= zqe^YSN&*dFfmvYuO@%`xzj%I9QD)_Hfq*2&<&HTaE@zTTcSj;K6L_n76Ob~gOkfsg zP&T0iYLb_QbjNHuwfNfqEkK{P_!+DxW8pdiMFjtVFj%~%%fY%zyW%GMSO;$uzcD^! zV6ylgcbb8V>bmt-o~gbS4J^5I0KoDWB)JcAV5<76bhEY)tQjy5Wd;HfEHOFenM$Un z#7qth1Le5a6Lr2;7J%H-g-!4&Inw!vY)L1Qk=7xCh{@?hD8h9(4E*fkwIM;t;xw;J z3cQe3@B%+4DwBMS&&sj5B=FRBeq<=x&L>4l64NPOOp{$HlZZ)qfk#3zmX#EKZc-q0 zTqY$tN-TY-WE5v&DItp`&0OMf8=0fz4EIMaj6~m@hz<@6PLRUGezv67I+r-DsxXnr zAOZJMwJ{l!qXoa0{i-R_mq zU9M?|dvT3f4j&k}kLKT>Uor0)8V~I5JC!k<&0K^)-3Ul(W{91?Ce6OEgQSVS-T0=4u&toSEi;->UXH zJ}^|QHLIJBo{J?zbnw!JX#cQggx`icNDCaKFhJoI3K0sgQ)r@an!-5>eFW5?vzfye zNG6jZ0k6K}Y&DOP>|;uG7gwAN9RJEdYy;a0?tFD q*U2th56`sHP#%MN_3M2NlUf9Fk&fbsf!R7L0cj!O>c;KnBYvH#3r<$ z3%7MPZd?m?C4(z>sT*;lJDLALC?d{6;>vT9x=_4uf8Tk0bLQS*K4jlr@PAYkkASOj z<5hhlbl^|G`-8K$_DWvvF8O$nd!9-qg@?Eo)z8D+hZ^7!uAr(s%KfN89^(PjP$kUO zMp_WGgeX+xI!6C4tcMOyiia=`Kge6y69j$8=A~UkCV8Q4|<~#)qc5&+g^G!sA z`7d5|KR^887J{oVv?dWuu%d$W;tqm~WFI*sD{Oj%-Q1w%yTcls6FU z)cBqw!|1Z3i2A=g&6?i1L;kzW^X$%6M>dJoq+>CK09=iBujL zEQ!2K94C%itHYwkv(y>L8Lv|*RwmyS!Wtn;I7j&V%39SJrsg~y?9e}>_l^0XC=O4EG1I#xBWAib848$Y>0u@3d8yR7_^Pc%QPm$Zu zmb5g1o!#PWNFeEU3ro6js-#IaX-m^|+a#MkfRo12p{MD#=WMd+X+>b$g!Z(1?>v2M zN_+MtzH{H5d+*GfxxX3l>!XIJPaE>y%+0k4@cj9w6N68ToXjgRbQn)I{WjJ~@{gK! zmJug$ofgS5QgGC~vz&C1LZDWVZsLZvk}M}4XsgHyQUq-^SxJhamB}hn0&NZHA*ImP zlGUUP+B)LcSx?rGa=;tNT2cY+ENCmCZ6xbR6|_xcJ*hrwitY504MYY)GucRLplu7MVTBm;u}!|zES!Jys{VorLegZEJXc+%@46ZAYSK0Tn0dfY z*LIpRWqi(7~V_@T>lCWI!g+Dna$0AXP$r%fxW4Uf?EJub$hE0L=Fs(s6k&wMqfQ&>J#WVa6bWm%GTWg$AlYBp{(_Hk}=o( zzQdC=3J%Y)mJ?h5{{FLfPLy7*TQXUE=cS^hS8_U&MqnB1Nea;1$PpZ+(AwW+XYpTqX?WvR~{ivqcTZn=}J;X-0Of)?GlXX|GVk<4ZLo z1k-D#!kCTZq@!lkq@v&QM>D0lNzVRVur}iH-I|HmOQ9F=KpNM?gCe}|R|`{uf@Vld zYGRseDRdr>n!u5HWX3SYpFtCA*;)|GhU$KU9;`#R25W~O99h`pAC`mu00iS<9-Prx z!7LAiRP^N_dTK~jL&a6WVld1Q!y(*VCRJLR(jz>VG?4k^G3;xUj{z`G7X)CX6k zirOkG5$>M+jVA=m5IE|vA9KO*a1bK098$R3q4Sxw9v$>=Q_<@|hwkGEBTRlYudPkb zFP+`p4gPgE?QL!7xowf5@R6H_wl->o1@0X+ba&Hx0LxnAhiGJ88=Z^PW&nwT*h+sm z93)$4BoI^-o}d!0)esDNBkJl1ETB%ZDZ}BA8V(Hwm4ppq6hsJ2jIFR~#mLpWxjIEg}s{-IGde>QV*;zB` ztovBVkvfiwGaqXPWLI>N!$3mv*M()_Ly|lo_B2ID@(>*U+EejIlJ|-!{>S?YR<2Ham|(1 zl~?7abH$VL++%A_^o-Y>Tzkb)nRLOx$pXPqIM(~gjY=W6_+5whvcvm!%hF5sPE4J> z1ylL1FIzEn7Nk;V{)z_S)rRtwM&l0+BEUc<8^Xuu!ymkh*FWKbLo*x}74QxS5XJ!^ zYGThcuB&_*LW87QfRjI(ly=IfS(A9F6}4z43=AgCqM2E&sW{h?4g{JRLI?y0qfzhi zWO`};48Ml$0st2H`jM#uk^?|ypKYlwzX90qk@PVD@DrO*SaYRd*4Y(f8`znaD$&Wl*K(w9 zs

f?@`~m?GxH$<*KXY9TSTu%a>lQm_M<7vSQgak3Bc;q= z*;H}eRd?~YiLC>lxMnvy%t^s4nQ0x26fm;ovRlUT0*bp#Cc}+^jF2+lsF77~t;&N$ z1qlvndroGbHkQXEpqVgDXhu!yH^7ETqsbK*3-pQ%;EVMq2(`X zIHX4o4W-MmRNUVN$s%2DLCJ>2{a&6vpgf52$P5vc2WE)XiV8TnwOZpv-l51ynD9s) z@Kbt3_Gh%J`}~lIB0)$dQ2mjh5}+X%0z!QgsFs6=;Y@+~!?m{$+(+g3x- zZ&e~x_3aNHQRuy}B8j}2-{>__++(@{`y?`QU|8W*o!89mi6*i08g|;imJ}afHi+1; zX8f!}68R~G>C#OxqS<4cr|LS!)?do6zHE}G?4A>`OZHip zOtaX_?d5US>J)d;OQ4b)`iE#86l(=~1%TI*Fha4eC$4vpiU&NNMYubGhZ5qI=n|y9 z2OwcZSK?JSy~PHwn+`3D#-qE;UV!uRqO6YRk*OTI6B1gxLNn^9 z3{)^at}&(S@kO0dJCHT@^F?0-UHyw2#ox1s7gyS|8b4y+Sv(6q_GgQ$?D}lSeyzjJ}q4Jy7FAL<+t#bxYQ%NEI9 zKuF}l$!h?jGL$Sd1S?U910_&)1?%dXBdvz_m*Q$ykyuC>l6?pk1AtXJ$DZ!)l-2>u zYw_Q7-zSPMv-K;wq)kBh&-lYDTn4d~on0l1AFv;+>NS*$%+<3j?`2dl7l-*8U@Xwn zBlW@CJMnMze9Pp-U7-~SDiLgEgX>ZLE9+Ywc}U1d;1t;p*EiX@$^AE~dyV~W{X%gI zYuhk?M@E0brPru>rKS7G7;J&~^mAL9e| zZyP$L-N5?G_#Zc1G??e3)MoZ-?>cb@Qr(Oa`B?J$rk&0{Vw<;Y zFb@Kw(-1$q<#mHSf?9AB=?6^hTeiGIOxA12U@LoL=y`lDrPsi<4Ck$k*qiFz~-dv=xq9^G2K+c~YE1KKUuItDGD zxR!#L+K`cTYx!CZacV_aSY9yc^k}J?DJEgk1*k_WWzYJGJ2{@7ktnE*8BI2=G+n;J zZJ<5%sbzo_@{cap%HX5x4O%&V`*Lqu5Vs7UXocFMV=$OS%h4**Wz8g;-L`|Inam8h+0m3$7eIy?QY$(k`cUp2d$vR0;5!)jK=R7^_z(JD1bGiy~iuczG? zQdghR8sgEa;5M!3abvWW6l-R{OHvdmTBlhRgC>m?X_cg~6!gGNTz$Z>56<4ZE$~J4 zT0JR6`rjFjTj92?AzDAQNS&vTh?QE+-~v1*)RYYf>6xZiei~bfODt!VdyC5()J16p za7{0$7%urUcqiUku+ZMp*|W4+gJ=e~Jkdt2QENCTQt`N3E7uzNiZy9XxLu$8gNJRy{`3;@lT!A4W7oK@bni4rN$E}Zkjm4>XtTO9J#XA~aFFU&RH)Tzby_nR zLDdD=LVThv(|pCA@|70w6_~L8mR1a(+M~7b*a^;BK4;&q(N)fTFI=_M$*Wh3>J%(?4@9%SkHbDtc*4F zQl7GGpq2U{U~Z#f47Fnj@Vrf%5OX5tMa+d*GhzjZwE&hd^^b(ZqY}c|yo92gk--WK zPiB zgXhoeV)o<6O?nc=gsErJ2NC<*IrBkWn{l==R2sV_@A6s;qcX+PlVXL$Z-@A?gdW6K zUMXO`tQYTSOw&t0d>uo;bMj9R!-|+<1;fu$6bn;|MGK^8a0sOeE8A3^XO zO7fbPpE~%)ruP(np@C~Pyvb7NKL8(VAFMQg38lK&*1=*AR(SLnzCVNj^Eh3|zBV}9 zoJ#U<4$kYjhD_XY7D0c)iMHhnLjk6!dO^cWnVl$GhoBw-ugi8Iwg<)EU|aSd6KAvT zLvEJbU%_nQ1{)UI+rfI2@5Jv2FBfe$WTCKQqGhsRS^WHgHnF^@ebO^~toLfwoiDDr zxa(4%f3j-tWmEas`V;oJ82QwY-(xgM_lnm9gg1Y`kXdP6^N&2s4R>`IUw6&9tIPPt zd`F+d^j6i9K9_W*L%M6J-XSGbaq`va_J@kelM!$&D`vQU>&Nc=B&NIA7{eP}8 zJ&7^~%Kethrw+l~wC$+XfdKU<3-WvExA+hI`l#z<}{-z{Fv@oEu%tWzRkIFuh{ z$&q3kJ&u!m2${KUP41~NZ5efp8$Z84)eHvzhh6;xHJ+` zb=&F1K5+ZLu$_!iB}&7=0e|3#FC5xS{qzX0liBwUJ|+Dt4EpDI@X#}2D;AHm9@HdE zdn1uBoxpcqQJUxi)Xo_=^;y<^q(h`=QVJAG!MV{J~)WLZwV^hO-}=>e&ZKQiEsnp!sz8UJu?MlO~$pIrdYo&)Q6iX zOTUwz!l+Xnfh#7y-1@+p+YIRxKyuejPBd`E?O5 zEAY@uPhjr?1WaJ@2NuM5{eTzW`Y-IfaCrn@#}QmW!0QoyRq-mmzKP&11o(@Vex3VA z#0;p`il7t$FG_fkvlw5OAmBF<{CV9u@kXk`$g!3{{^Mo$i5xB6w48N#p^{jvH5zLQQRb6cUZ+$;`I`{Sa!X_B34~@ zi(=>X91)2*#bRM{iO@DTX{i>?Q@PHh5!&|)JV^;!_@^eGX5It8%b9K0tw>uha`rs2 zk+Va~*`ZCb=OYUIMc1u>U9XLw8@MsArBHr1!R(Ur%I+zg_V8D>=42_5?UucF zo_?GV_VgwG=Ds`k-nnz<&b&K%Y)1OsQ7P-S%uI^_&#wPCHBuBjo>eIQmvp@D_tE9V zcGS4Lh-8zT8Ii0YxkpXAi^)oo2iy{J1F=KvBCAL~w56n-IG`;ft4RU0ZnB0HLR(Hc zh!ffhvX&G2irpTvk+^|TPi`dT z&^C}RQUPrvw3W~{kxiru+681YsfM;0+8StE*ql_7Ms6at@X`t|<+d%P4(NrXn|PpI z#MVeo|5j2D^kTA&G(fwAY$uJ-E+soi6SQq)Cs_dPGSWkupjZ%oN`l`!avN(g ztGHW|9|rOVvD)fqMCXG*(vtulqh<+BDB}}~mjoz1#r{}xqx>jz{3O;@`y;93b`<4wGt9<&jMG9XZb?sit!{9e6me)oV8< zIt%7?UL^#ItlOQ=K3-@w#0@d)B1hD8LO3ZYIl4J8XiTh&l9%L}lDzzH@`!mRJ<%@; z#~w2ZBuy1mQI(W}q_iO+p&Cf~jA^Ifv2+h&0fSm{Q#PA*cTqw%;UbngT4cPEp2evKX@=aF{+h_tsaw>83x9v zZnuOnKRpD%UqREFZB;mobCHUOkR1be=wCyl<1Z1j%) z8v?q;N8wJ25VZ^B!3O0i+R@_7Jts( zS=ZCXo#Qr6qtNF7nmiQ1Y)YD=SAgp=>K5a+_OD+hr@`_rWh|__uHCtHL(g*WuFf4hyS8>i z7U=GS3^@K;=LR_!2J4JDHdKnT9mw6W_KlYKnf*^Fb)6%CbS<#@K$bp>0(Y^KT}Ant zJn0Kg_&p|l0baqLSJ|swjpCc^i>@VdG01ykt(zu9v5-Boxvi=Q7+Th-pTd%R{bLFZ zKxjS%HO)LR4jUB`!NvZxxkau3!9%e{H#x)#Eq7f0H|cDMKfJG8x9`dnboZ*RAs zK?7EFCmk1v?+TW=qf>4L-d(ZY9giEcasB8Y5WYZYWcFUPvZS{$;{#-Ti13lXMtke5 zJV&2L#uMyx?^3aez0sXK`byfGaV$M8i9e|LXdy7@$DpB!ek}t68NdK|ad%@x zC^z;3BYmsnl^}W|cBbz+$&`g!jV!pQORQs0@7ZbU0LHT-v-Mwe>Y1h4_QR3ro%eBm ziG9_-*>oca{!)x>+WU%R-GWvIag-NW>A3Cl4zdOKErw~cMv`=!O#J3I5a^A{rv0r5-NF66qA|u zA>7RJhZ@`qfYhvU(Q%(LtXT3`~P?4%onHAL_HS`-VzQ2Z8yq z7&|}okSJ}i#QKI8N=9NEDd6=+_EAA`f&zb38+&h?oeealv90c+s)$`FPIlQ7U-?MB zY9TpORzZk3R0kUmRu_~cq#^}MS%MyMv-egOl`Ih=g-T_DsTL;9IF%|^da$k9sb-_G zL_q^(hTAA8wMh-9>QD<<_h{)plB?t=*}&v#Fgbj%(otAawqCLD>BDXAd4uMKj~w+C5u zDhm?>axcr3C5d;nTs5R-G%w`kYO(5uke5Z+;v^+;jvT2{)5DS~Pdii>$;T{PFZ@mQpfoHDN_)+qU9DCV7xM3wj-?xg zNKK@AtW8;tmhn+5q>+`FrRAjulKGK{$b2zfYEjuYrKWJ0npB3veN0d=NBA?c8d9`` z{pZHKMGWL>)!LCY@veDIq)x3Ep1Bn%-K%y8u3^@=3uGI| zo!hx=B6y^H#t>;xHYR3?>k7NX&393yTBSC?Dp#C?;NcT#oVQ=B@%_?>`z0EGOW1|| z(xvOb>RHuBwI-3)(33h#^3}40&ygl&b5f%TvkUfY**UBl;fXAmH*yB4ERGnya)Y9!n7AyP~>5;!pnC02~xj9;Ge@cfMO5O zx-4D=eSmzdjaYVhzl+~4>9>v3d{UVvb+p;sY3 zr1m3xYoUSllx7?W1;+FmSz+UfGkQ(=QiUdXA%eF6iX|w;3n#^mr^#eubUaLZkoz!l z@dl$`v~VNQR}fxBm_|5(5JUJH0c%H!^*-%D!1{)6S_6;>d{ z7dg4UTAD8y^y0NjvnI+SDD~Ku372UIwTsyJM8N_qg6IK+VT3XOtso(vxXgR`wINKi z*~~<AMW76z!X%8fibwj(Hv%*bX4&&av56vUv%$J2%jRfA#VVw zr&!nIca6B!*n5+eY;H1>Mf*(5a@b`#213~qY>`i5t%p0r<~g@eR&i?h2g9%_o*BB( zx$B+I{)?Ucvz_~9+r4ks49u1eTrd{Dn_G6l-8P%MET-PlBsSz$oNhRK_pbRAB0F96(F&)|&z%+;f}ppHcO(Z=;n`qt%}=Qn8Sw+LuolO`9^UGU}+X&*%L z&grc6JPk}Jfum|>>JNv4P|Wk;@kF8B2>4N;c#do7fpBOjL`QuJ3XcbUgMKzUU3CC& zArwn}dJq9uh5H7-UC;nr2r=X7Vc3zVG~_ zd;-k;L(FyS6JnI7eHEHA?hl26^dyeUYkecdYD?c2ocjUx)YFV;7{(evb3D4-Tq!$#v>=M)RC+KF zR`!4wdqM0Fit(AuAGQB#Wj-krg=#5o^1dX+wSI^*FzI(PJ|y4urAe>j{b!;{wQB@W z^qW4%WBC+nrCj8)Y5EdzF0Q8D&)4o6Nj>U%tn~S(!F5=lewP0E;f|&P&Ce)qg@29y zj4`sW>&+k0FK(NkEnp`aKVi7ZN0QywbK5e{0=kiu5T49aw!|7t(I}%Y|;2pIrI~Jk?W^=1D7V@Hq4nmdm81uQvk! zW^x19%godaQptnIF?|RDW@Xn?50?)(ngPy1&w4g^M|t*x82EYs_~ixaupR8aJC@hq zgKGC;Pa^{V{PW_V3|}X)*M?9fvM=sf66K|Uf?X;C79@JD!E1>}@%3?prxDI0@Y3T~ zNWG768R2ULUY+n-g4a9zmzWA1ikC$E_Y+>b@S39oJ8l%=K)?39$8%Xr^YWj7yk5=d zza^mX8kbh$C=~!#(ry?Hk%^$cojwNA@LveWBxbqu0{iWq<#Lf|y1XJbe9SFIhv4Tu z_$7VLfW)s0OXg%G=1fAKW6sRUG@-;jm(EFx;Bd}aIhi5YoO79+%tBckC$ojDoVgrM h<{~$blXk(DHlZWK_oD%(lhCx2s=y>fkTf$*CY}6f$iR;bX=b{6k{`|FM?c%U zd%Jskw|lpH>t~OU{U=GrU+i|P1mB*2pW41`@iY_>I0@BbB~!w zK&J&PL)IRvpmhOTj}3UbKzdI)U_-z@l+lwRWX3?|kfX;TXj5R$P*zVCk#v%x-|pl` zOxY7}TcbIOS;^f-sIFgE2#fdRQ49Q7pVUzs`~dFP_2koeG<|PcPXVo z&IDXc=TisZ5?V>;04@cb1-OhlX*S@ww2I~cmT5K31zb*DG!O7RTGL1KfvBLhv;gpY zx_}k}uA~cT5nv~E(_+9?w2qbluBMA4!hGksl_t zwk5QDuc2q*E}g3>_90<}l*De=w-Ztkdo=AKVypqz)w_6yzAU!Z_%A(K7js*!gfzxl zY$G~ygP*c*Ae~&FvDDBFn%%KmGK|E~3*=9Ed*(LVCZO161b6edGE3}RfuuOR2LQjk zwD_Q7kD(vvf!G6%4nhX_`8gZNAg|1-A%5PKHP0{t!lBrXtP;J=j}x~e801gqI*Y?d z-T@$C^oGMqkS5Z$hx|d8p7rs6@j6}Msuo1sc@dUj?3i~BLQ&el10Kwz@o1#ZD9{}n(!s-CI zbgYX%QoPXcC=8Co&KB<@WSp-nZ6n9{!O~{(ATM82$UiR~ntv#%y%Z2@LNEzH)dP^_ zF_w*-$N6xXn>^2dQ`T<(IWSxXwiEf|{JXN74Nn7kG?UmsgA$UIfN#u{SI7bjA*x(#b!nY+cle$woc}U{S9r9l-EbDjIRBukwF(mz z+#=VEM3N3k5s4Dap52B>+D@YCAmMbojS)RRl9PL|z*4r`6frRaO^cYGke&hwHAHlR zGE<|b=Wom_%`}_p3?|oOHv#)&xoHgZ*KB^(Itf;pfm3{pQ+d|HU@ znz1si<5q$rU}c!$BGrS65{4zkiK%jESd&Bj!sf<}w+U0eP&DaXz#mjxZh2Mf##wWy%ib*E9wE3dwIJRPlftX(mhFK=$~bhmD}b#3>G)EV`8)d4vQ6#!Es zVNeGG%Fcx0XTxxQE(<%2TbhFsThh&BhGSVsm|?2o8D;_DLn1S2&@;{5fJ)ZXG^PYp z3oy`U@re{pbrb6jXI^N`SrpG+e5&ZJwePHZecd_ph2?j~mu5fXI5V|=`A%^^#+5He_vHhejO{U{xpsJjeomf%os^y^%259OV5qg z(}oi|*gAZ)aS*B)E7c6iu_Q&(w2B!zB1H(T!<>)k#`PysT2r%kn}ZF0kQ^oB`cyrr zg&NUFmprLIN_IQ6$|+0Rol_J%6emoUiY5&sF1|?SiBwJ4rlU(~Gi{DIPPUvd@C?^o zWtwYBjoy?jD^tq%6W21{?ye-S^00d~*~edUFS8ARcCvzw@*D1E@>kwaS3@4)TkAHE zIR8VPJ9m}_>WT+w7r$QjsUwKv)I;zd(EQ6q^`w{=E#42M|JdTajR|0O!)?5bidO(6 z(!9e^WIqAYmBw&R!W{Oh>W&bjtPHhY=AQa`;^YVGmyk33T>abzmNhjolrV^-b?Fo4 zsgy{VrxGGznNA1(eSNhbuk~m?RJY+UW+)U7%tKk*6;^JNUAfGN%i-=BmT!iSHp3@1 zVTH$Fm<4?ys-z-=B7A|^0R;HkF??y51HlM?xS>eL-seXf+&)}X_?1jH+&W_b5*Dne z9$Y+Ogx6&_ps{fjwjxNz6e$sG4XHB#5+up*bfKv6HPiW`Tdw3*U2+s%EU7j=cMr}@LJPUG)jJX*$&tjRZf z;dK}aYro(hx7Im6MLpq%9jWuYVAY*Qu?NmWt2)R5{>N3d(6eEeqhGDL;CWA;U^6qiYwVKy-PBlDl< zXV z!)-yXIcW!Jb|-)vR%qSih};nhDops4j~0X(q`*uMsEu0SDc5>IcMG%glkF9xkH_1K zn|x?z2!UwW1*PC{df7x8nuc{T!INy2m#?dgV*ZY1`hyy*E{p>S#&LDAQ=<@!8BO7WF&TToeFS%b+`E!XVXc}*prIci7dJ%sBaprWD?tR_&UeE-w7^ zGQto{pNZ7pjN7?#M@>|0en$NNhULf30dq5X zt>9n#)N3^pe0Rd?@$?VF+opIt4E{@z*t3!}M@s0J!v2KAZxB4ki#n=Fkgx7&%fr1T zGP+o38&p+wGc?Z%V>kFS9R6ka`rs(+G|sz~YmvSK`%u zABR|*nW$Wt^UTZbodso1AmLY1HGo(3gpg09dHL0VM-#Pc3bMHOp+;h+Mu^IN-t1Ruk)Kr%RKY_NByAQ8Bk(S54d!nMD+om~2l7dOh z!Y$mcIY*L%$&$BfuAmELR?QuBBUQANkc*u&8Kk=PsXq7l$E>ji*%PYn9Eo2lq2@wa$dqyk>(*y zLz<5?9cckC z4{0f{=J`lh@)}-%w2ZIf9;D^GmKP$e;H$Y8X(fMv7a{fYHM|&URkG!aUtR}qvTAod z_Z?A!)qAb}2IFHU81op9**X|2G0xh*#vFbOS>Fba+lq{S$DeJi!&vWfF;;K1yT`3; zAIzrqvM$I@3n+bP*=Ka8IhYbeHUz=+A$LD=Vk^Nm_<4GvYX`D}G~5oKrJqt%WQPpM z=wfULexKRPc0*%U6&r?~StZICDm7y=tI*~irYRAE-S9?ExhG2OCIr(Fj>XhbZrXQ8 zBcpzs2*aJ62IT;%#*NC{b5<6G>v=v$H%&MMf6rUNo`L20RmxEm95q_=w=#AJP8ax= zP10&9x+=yagKB6rJfeyh;QfMXZLei*s)Osq!Yw~B6<_N-C>2h+w4&lF=* zu-)6r&cPXPGdlwF-se`nl&}mS5lsXyBj`2+pYNc^Cdn)CY*8&c50{HNQlCYEUlC7| zdy^{Uo;A9P@3Q54NV=9l?r%$&?T65cO644iXN+Sjt})gRn@aa7mr(Enc ztK$)x(glAiYk8oH*eeKTx~>n0bahau@lY%r9~N(uG*zg2R2x@$XgI3J#apngyj;18 znpxwSa>m>S&A&|W7Q9%|p}d3ab+}d0;&LO0VX@C^jFFmKcJ@u!wB}%W#4=(14tw7A z6w|GE(W)mbyzUfA_GiRPaf19Lt{ba|Sl#mE2HwaUCo*Q5;CS73Z^bk(0e}rM{W{1U{{=Vuzuyp_ly(q7AhuP9BXfWMa$Lw&k@fe=w&ZZ*`@1xZgEU_D8eG|d7hsQ8? zA0X?uiw{ZSjD>am$*AC>h^#I{UZ9SpLua6o&BCETajxHLIv;B3+YsvS-DEmjdU`@# zEstz~n}JH!4gU^!2bKv3%_MIGC0Nj95>W)xg~v4}MhBx@U5J!aR3qZ+1XPQHYC&WW z(ESkw2&P-8S~wmVS3~iraKgdn+QDv8lFQ;6K^DOi2xjsi#UI}rQ%wif!1Jm zNaTz-nM|FySp+jljE#oW(Q)w+i56ndTwCM%D~>5wy7pSzT3g`PS)IrG=CV4chtG_@ zSu$IBBdc>N>9?7lmy1pmP3O*JzrOW?XU^AnrGKvP(YeP5=7ysmWX7f(#*ZId#RBir z+VlX3Ul6n-;Bj0>5;=k0IsyoI5LApJg+Ti8ODI`iT1dB8(4`koBlsOcCSZ~TA3}J2 zlkyp|MaHT1cO3sy=Pzw&&-g;&48xrb%>^=dGHs;B1jt*|bOkxoXHO5D3p-?rHy_dN+Ov~{r<{HCoMd*r;= z3nd$4{(q1a>AS>9#Pe414U&9##F=4?xe#t6FVZOa1r;qlbrjRI6N}G5Z)^&If{E=AMy!H}DyLVjzm$`BM^25>TwBQ{NMbg(6%R z|5QkL8|FLwYv_eB?Rs2|naMGs?$RPd!*Owu4E7Tw4uQCT5&Xvjjh)R(3Wi-_h|a&* z4n#?8!?B=Mv8Ln!E=s8n`<;n(h!;HwcU{;teG|T}=%^}WO!!x0VHx6Zna9vM;{xAiRhrJ>$ML@M`D@^^WCW|47a;IO#`&LMnFL&I8NMsXG}Y7h~7ML zUo2oS zP-;Z?ZWrkvTC%9ziu^_cStl|E*JsJAcC+n{N4S&pm+nFm=ElDi18&3Wt%cz$!? zk}y-2?6`Oe^z0?vlr<(De*3 zGL@5LWy@{kLiq1;p%h5#cP*)%#au7WSyFFswYMr!kc;a7H?A+sT9{mvMc^Qx=)e-C zE*+~eU7^sfF??s#P)OjLZV_iJmK2L=6{`4{hOfhTUnLuXZ}+uIx6HI25go$zr#~tp zaaD*rWVMW-n1G&o(MBL!hkVX<&`>srG2&#Gkj+`PX&{+=*5qS$iH5S{yh9wdR3Y0= wib4VGgwZW|o;M1& diff --git a/cli/libs/app_tests.py b/cli/libs/app_tests.py index f361b3e50..d420f3f57 100644 --- a/cli/libs/app_tests.py +++ b/cli/libs/app_tests.py @@ -2,6 +2,7 @@ import json import re +import shlex import subprocess import time from collections.abc import Callable @@ -208,6 +209,28 @@ def _run_remote_script(remote_ctx: dict, script: str) -> subprocess.CompletedPro return remote.run_ssh(remote_ctx["host"], remote_ctx["user"], remote_ctx["secret_path"], script) +def _remote_script_command(remote_ctx: dict, app_name: str, script_name: str, env: dict, base_url: str | None) -> str: + """Build the command that runs a tests/" +escaped_local_app_script=$(printf '%s' "${local_app_script}" | sed 's/[\/&]/\\&/g') +sed "s/__DSH_LOCAL_APP_SCRIPT__/${escaped_local_app_script}/g" /etc/nginx/nginx.conf > /tmp/nginx.conf +mv /tmp/nginx.conf /etc/nginx/nginx.conf + +# Preserve the external browser authority through the reverse proxy. DSH rejects +# non-loopback API requests unless their Host/Origin are listed as trusted. +# shellcheck disable=SC2086 +dsh web --host 127.0.0.1 --port "${DSH_INTERNAL_PORT}" --no-open ${trusted_host_args} & +dsh_pid=$! + +ready=false +for _ in $(seq 1 60); do + status_code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:${DSH_INTERNAL_PORT}/" || true) + if [ "${status_code}" = "200" ] || [ "${status_code}" = "401" ]; then + ready=true + break + fi + if ! kill -0 "${dsh_pid}" >/dev/null 2>&1; then + wait "${dsh_pid}" + exit 1 + fi + sleep 1 +done + +if [ "${ready}" != "true" ]; then + echo "dsh web did not become ready on 127.0.0.1:${DSH_INTERNAL_PORT} within 60 seconds" >&2 + kill "${dsh_pid}" >/dev/null 2>&1 || true + wait "${dsh_pid}" >/dev/null 2>&1 || true + exit 1 +fi + +exec nginx -g 'daemon off;' diff --git a/apps/dsh/src/nginx.conf b/apps/dsh/src/nginx.conf new file mode 100644 index 000000000..e769e8aad --- /dev/null +++ b/apps/dsh/src/nginx.conf @@ -0,0 +1,34 @@ +worker_processes auto; + +events { + worker_connections 1024; +} + +http { + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + server { + listen 3080; + server_name _; + + location / { + sub_filter_once off; + sub_filter '' '__DSH_LOCAL_APP_SCRIPT__'; + proxy_pass http://127.0.0.1:3081; + proxy_http_version 1.1; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_set_header Accept-Encoding ""; + proxy_set_header Host $http_host; + proxy_set_header Origin $http_origin; + proxy_set_header Referer $http_referer; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + } + } +} diff --git a/apps/dsh/tests/cases.yml b/apps/dsh/tests/cases.yml new file mode 100644 index 000000000..2d0b1aad2 --- /dev/null +++ b/apps/dsh/tests/cases.yml @@ -0,0 +1 @@ +optional: [] diff --git a/apps/dsh/variables.json b/apps/dsh/variables.json new file mode 100644 index 000000000..45dcd1814 --- /dev/null +++ b/apps/dsh/variables.json @@ -0,0 +1,50 @@ +{ + "name": "dsh", + "trademark": "DeepSeek Harness", + "release": false, + "upstream": { + "image": "https://www.npmjs.com/package/@deepseek-ai/dsh", + "releases": "https://github.com/deepseek-ai/deepseek-harness/releases", + "docs": [ + "https://github.com/deepseek-ai/deepseek-harness", + "https://deepseek-harness.github.io/deepseek-harness/en/guide/quickstart", + "https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/SAFETY.md" + ] + }, + "edition": [ + { + "dist": "community", + "version": ["0.1.7-rc.2", "latest"] + } + ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3080, + "path": "/" + }, + "admin": { + "port": 3080, + "path": "/?token=xxxx" + } + }, + "requirements": { + "cpu": "2", + "memory": "4", + "disk": "10" + }, + "credentials": { + "token": { + "source": "container-log", + "match": "regex", + "pattern": "dsh web: http://127\\.0\\.0\\.1:3081/\\?token=([^\\s]+)", + "group": 1 + } + }, + "env": { + "first_startup_only": [] + }, + "help": { + "db": "No bundled database is required. DeepSeek Harness stores sessions, attachments, and local state in the dsh_home volume mounted at /var/lib/dsh. The dsh_workspace volume mounted at /workspace is the default project area exposed to the Web UI." + } +} diff --git a/apps/elasticsearch/variables.json b/apps/elasticsearch/variables.json index 96caf591c..6b1d42afd 100644 --- a/apps/elasticsearch/variables.json +++ b/apps/elasticsearch/variables.json @@ -10,6 +10,13 @@ ] } ], + "access": { + "defaultScheme": "https", + "api": { + "port": 9200, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", diff --git a/apps/emqx/variables.json b/apps/emqx/variables.json index 67b893b05..1cae084ac 100644 --- a/apps/emqx/variables.json +++ b/apps/emqx/variables.json @@ -2,6 +2,9 @@ "name": "emqx", "trademark": "EMQX", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/emqx/emqx" + }, "edition": [ { "dist": "community", @@ -18,12 +21,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 18083, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/emqx/emqx" } } diff --git a/apps/flowise/variables.json b/apps/flowise/variables.json index 5bff2426c..22513deec 100644 --- a/apps/flowise/variables.json +++ b/apps/flowise/variables.json @@ -2,6 +2,9 @@ "name": "flowise", "trademark": "Flowise", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/flowiseai/flowise" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/flowiseai/flowise" } } diff --git a/apps/frp/variables.json b/apps/frp/variables.json index 750483177..e501daebb 100644 --- a/apps/frp/variables.json +++ b/apps/frp/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 7500, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/ghost/variables.json b/apps/ghost/variables.json index 1bd944812..a3b6e1f82 100644 --- a/apps/ghost/variables.json +++ b/apps/ghost/variables.json @@ -2,6 +2,9 @@ "name": "ghost", "trademark": "Ghost", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/ghost" + }, "edition": [ { "dist": "community", @@ -11,12 +14,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 2368, + "path": "/" + }, + "admin": { + "port": 2368, + "path": "/ghost" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "4" - }, - "upstream": { - "image": "https://hub.docker.com/_/ghost" } } diff --git a/apps/gitlab/variables.json b/apps/gitlab/variables.json index 64190d0fa..d68545937 100644 --- a/apps/gitlab/variables.json +++ b/apps/gitlab/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "8", diff --git a/apps/grafana/variables.json b/apps/grafana/variables.json index 83580e31a..7a4f5c46f 100644 --- a/apps/grafana/variables.json +++ b/apps/grafana/variables.json @@ -2,6 +2,9 @@ "name": "grafana", "trademark": "Grafana", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/grafana/grafana" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/grafana/grafana" } } diff --git a/apps/haproxy/variables.json b/apps/haproxy/variables.json index eaf709a8f..6af17ebc6 100644 --- a/apps/haproxy/variables.json +++ b/apps/haproxy/variables.json @@ -2,6 +2,9 @@ "name": "haproxy", "trademark": "HAProxy", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/haproxy" + }, "edition": [ { "dist": "community", @@ -20,12 +23,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/stats" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "4" - }, - "upstream": { - "image": "https://hub.docker.com/_/haproxy" } } diff --git a/apps/homeassistant/variables.json b/apps/homeassistant/variables.json index 34c1fe7d0..639e32a22 100644 --- a/apps/homeassistant/variables.json +++ b/apps/homeassistant/variables.json @@ -2,6 +2,9 @@ "name": "homeassistant", "trademark": "Home Assistant", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/homeassistant/home-assistant" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8123, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "4" - }, - "upstream": { - "image": "https://hub.docker.com/r/homeassistant/home-assistant" } } diff --git a/apps/jenkins/README.md b/apps/jenkins/README.md index 1d236d919..a104bb2dd 100644 --- a/apps/jenkins/README.md +++ b/apps/jenkins/README.md @@ -28,7 +28,7 @@ Websoft9 packages this app from the official [Jenkins Docker image](https://hub. Jenkins generates a one-time initial admin password at first startup. The value is stored in `/var/jenkins_home/secrets/initialAdminPassword` inside the container. -Appstore consumers that support `variables.json.credentials.password` can resolve and display this value automatically. If your consumer does not support that metadata yet, read the file manually from the Jenkins container. +Appstore consumers that support `variables.json.credentials` metadata can resolve and display this value automatically. If your consumer does not support that metadata yet, read the file manually from the Jenkins container. Apps run as containers; rebuild after any configuration change. diff --git a/apps/jenkins/variables.json b/apps/jenkins/variables.json index c03a4bc93..8c95143cd 100644 --- a/apps/jenkins/variables.json +++ b/apps/jenkins/variables.json @@ -2,6 +2,15 @@ "name": "jenkins", "trademark": "Jenkins", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/jenkins/jenkins", + "releases": "https://www.jenkins.io/changelog/", + "docs": [ + "https://github.com/jenkinsci/docker", + "https://www.jenkins.io/doc/", + "https://www.jenkins.io/doc/book/installing/docker/" + ] + }, "edition": [ { "dist": "community", @@ -11,6 +20,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", @@ -19,16 +35,8 @@ "credentials": { "password": { "source": "container-file", - "path": "/var/jenkins_home/secrets/initialAdminPassword" + "path": "/var/jenkins_home/secrets/initialAdminPassword", + "format": "text" } - }, - "upstream": { - "image": "https://hub.docker.com/r/jenkins/jenkins", - "releases": "https://www.jenkins.io/changelog/", - "docs": [ - "https://github.com/jenkinsci/docker", - "https://www.jenkins.io/doc/", - "https://www.jenkins.io/doc/book/installing/docker/" - ] } } diff --git a/apps/kasmweb/variables.json b/apps/kasmweb/variables.json index 8daf271db..70868afe2 100644 --- a/apps/kasmweb/variables.json +++ b/apps/kasmweb/variables.json @@ -10,6 +10,13 @@ ] } ], + "access": { + "defaultScheme": "https", + "web": { + "port": 6901, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", diff --git a/apps/keycloak/variables.json b/apps/keycloak/variables.json index 8e00de931..789ebb737 100644 --- a/apps/keycloak/variables.json +++ b/apps/keycloak/variables.json @@ -2,6 +2,9 @@ "name": "keycloak", "trademark": "Keycloak", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/keycloak/keycloak" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "10" - }, - "upstream": { - "image": "https://hub.docker.com/r/keycloak/keycloak" } } diff --git a/apps/kibana/variables.json b/apps/kibana/variables.json index 0d34d51aa..e03cdfba3 100644 --- a/apps/kibana/variables.json +++ b/apps/kibana/variables.json @@ -2,6 +2,9 @@ "name": "kibana", "trademark": "Kibana", "release": true, + "upstream": { + "image": "https://www.docker.elastic.co/r/kibana" + }, "edition": [ { "dist": "community", @@ -10,12 +13,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 5601, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "5" - }, - "upstream": { - "image": "https://www.docker.elastic.co/r/kibana" } } diff --git a/apps/knowage/variables.json b/apps/knowage/variables.json index c2ec0fbec..8355e8fc4 100644 --- a/apps/knowage/variables.json +++ b/apps/knowage/variables.json @@ -2,6 +2,9 @@ "name": "knowage", "trademark": "Knowage", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/knowagelabs/knowage-server-docker" + }, "edition": [ { "dist": "community", @@ -10,12 +13,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + }, + "admin": { + "port": 8080, + "path": "/knowage" + } + }, "requirements": { "cpu": "1", "memory": "4", "disk": "3" - }, - "upstream": { - "image": "https://hub.docker.com/r/knowagelabs/knowage-server-docker" } } diff --git a/apps/linkwarden/variables.json b/apps/linkwarden/variables.json index a747ca4d1..57c34b023 100644 --- a/apps/linkwarden/variables.json +++ b/apps/linkwarden/variables.json @@ -2,6 +2,9 @@ "name": "linkwarden", "trademark": "Linkwarden", "release": true, + "upstream": { + "image": "https://ghcr.io/linkwarden/linkwarden" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "1" - }, - "upstream": { - "image": "https://ghcr.io/linkwarden/linkwarden" } } diff --git a/apps/mattermost/variables.json b/apps/mattermost/variables.json index 93e7de55a..6584f0e0b 100644 --- a/apps/mattermost/variables.json +++ b/apps/mattermost/variables.json @@ -2,6 +2,9 @@ "name": "mattermost", "trademark": "Mattermost", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/mattermost/mattermost-team-edition" + }, "edition": [ { "dist": "community", @@ -10,12 +13,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8065, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "8" - }, - "upstream": { - "image": "https://hub.docker.com/r/mattermost/mattermost-team-edition" } } diff --git a/apps/metabase/variables.json b/apps/metabase/variables.json index 9dc7130ef..6d7716197 100644 --- a/apps/metabase/variables.json +++ b/apps/metabase/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/milvus/variables.json b/apps/milvus/variables.json index 6330ab808..18813e7e3 100644 --- a/apps/milvus/variables.json +++ b/apps/milvus/variables.json @@ -2,6 +2,9 @@ "name": "milvus", "trademark": "Milvus", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/milvusdb/milvus" + }, "edition": [ { "dist": "community", @@ -11,12 +14,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 9091, + "path": "/" + }, + "admin": { + "port": 9091, + "path": "/webui" + } + }, "requirements": { "cpu": "8", "memory": "32", "disk": "4" - }, - "upstream": { - "image": "https://hub.docker.com/r/milvusdb/milvus" } } diff --git a/apps/n8n/variables.json b/apps/n8n/variables.json index ab0f90cdf..4d6b57ed4 100644 --- a/apps/n8n/variables.json +++ b/apps/n8n/variables.json @@ -2,6 +2,15 @@ "name": "n8n", "trademark": "n8n", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/n8nio/n8n", + "releases": "https://github.com/n8n-io/n8n", + "docs": [ + "https://docs.n8n.io/deploy/host-n8n/install-options/install-with-docker.md", + "https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables/deployment.md", + "https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/configure-webhook-urls-with-reverse-proxy.md" + ] + }, "edition": [ { "dist": "community", @@ -11,18 +20,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 5678, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/n8nio/n8n", - "releases": "https://github.com/n8n-io/n8n", - "docs": [ - "https://docs.n8n.io/deploy/host-n8n/install-options/install-with-docker.md", - "https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables/deployment.md", - "https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/configure-webhook-urls-with-reverse-proxy.md" - ] } } diff --git a/apps/nextcloud/variables.json b/apps/nextcloud/variables.json index e11da94fe..d902eeb93 100644 --- a/apps/nextcloud/variables.json +++ b/apps/nextcloud/variables.json @@ -2,6 +2,14 @@ "name": "nextcloud", "trademark": "Nextcloud", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/nextcloud", + "releases": "https://nextcloud.com/changelog/", + "docs": [ + "https://github.com/nextcloud/docker", + "https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html" + ] + }, "edition": [ { "dist": "community", @@ -11,19 +19,18 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "4" }, - "upstream": { - "image": "https://hub.docker.com/_/nextcloud", - "releases": "https://nextcloud.com/changelog/", - "docs": [ - "https://github.com/nextcloud/docker", - "https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html" - ] - }, "env": { "first_startup_only": [ "NEXTCLOUD_ADMIN_USER", diff --git a/apps/nginxproxymanager/variables.json b/apps/nginxproxymanager/variables.json index c287336d6..d0a99d316 100644 --- a/apps/nginxproxymanager/variables.json +++ b/apps/nginxproxymanager/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 81, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/odoo/variables.json b/apps/odoo/variables.json index b9d0b7d84..010613f75 100644 --- a/apps/odoo/variables.json +++ b/apps/odoo/variables.json @@ -23,6 +23,17 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8069, + "path": "/" + }, + "admin": { + "port": 8069, + "path": "/web/login" + } + }, "requirements": { "cpu": "1", "memory": "2", diff --git a/apps/oneapi/variables.json b/apps/oneapi/variables.json index 660d38fca..6c4d9f629 100644 --- a/apps/oneapi/variables.json +++ b/apps/oneapi/variables.json @@ -2,6 +2,9 @@ "name": "oneapi", "trademark": "One API", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/justsong/one-api" + }, "edition": [ { "dist": "community", @@ -11,12 +14,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + }, + "admin": { + "port": 3000, + "path": "/login" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/justsong/one-api" } } diff --git a/apps/onyx/variables.json b/apps/onyx/variables.json index f587ed2a5..5af13c38b 100644 --- a/apps/onyx/variables.json +++ b/apps/onyx/variables.json @@ -2,6 +2,9 @@ "name": "onyx", "trademark": "Onyx", "release": false, + "upstream": { + "image": "https://hub.docker.com/r/onyxdotapp/onyx-backend" + }, "edition": [ { "dist": "community", @@ -11,12 +14,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/admin/indexing/status" + } + }, "requirements": { "cpu": "4", "memory": "10", "disk": "50" - }, - "upstream": { - "image": "https://hub.docker.com/r/onyxdotapp/onyx-backend" } } diff --git a/apps/openclaw/.env b/apps/openclaw/.env index 1e26eccde..ee8e940d1 100644 --- a/apps/openclaw/.env +++ b/apps/openclaw/.env @@ -22,7 +22,6 @@ W9_BRIDGE_PORT_SET=18790 W9_LOGIN_USER=token W9_LOGIN_PASSWORD=${W9_POWER_PASSWORD} W9_URL=openclaw.example.com -W9_URL_REPLACE=true W9_NETWORK=websoft9 @@ -46,6 +45,11 @@ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=true OPENCLAW_PUBLIC_SCHEME= OPENCLAW_PUBLIC_ORIGIN= +# Optional reverse-proxy source allowlist for forwarded-header attribution. +# Space- or comma-separated IPs/CIDRs seen by the Gateway, for example: +# OPENCLAW_TRUSTED_PROXIES=172.17.0.1 127.0.0.1 +OPENCLAW_TRUSTED_PROXIES= + # Not used by default; enable only when needed: # OPENCLAW_GATEWAY_PORT=18789 # OPENCLAW_TZ=UTC diff --git a/apps/openclaw/CHANGELOG.md b/apps/openclaw/CHANGELOG.md index 4e92c3385..2caa6c523 100644 --- a/apps/openclaw/CHANGELOG.md +++ b/apps/openclaw/CHANGELOG.md @@ -10,3 +10,4 @@ - Add `OPENCLAW_PUBLIC_ORIGIN` and patch `gateway.publicOrigin` plus `gateway.controlUi.allowedOrigins` during init so domain settings are package-managed and re-applied on every recreate without changing the upstream gateway entrypoint. - Remove the retired inert `gateway.controlUi.dangerouslyDisableDeviceAuth` seed key from the packaged config. - Let `OPENCLAW_PUBLIC_ORIGIN` override the exact external origin, and otherwise derive it from `OPENCLAW_PUBLIC_SCHEME` plus `W9_URL` so domain access can follow standard Websoft9 host metadata without hardcoding. +- Add `OPENCLAW_TRUSTED_PROXIES` and re-apply `gateway.trustedProxies` on each recreate so reverse-proxy forwarded-header attribution (`proxy_attribution_required`) can be configured without hardcoding proxy IPs. diff --git a/apps/openclaw/README.md b/apps/openclaw/README.md index b00442712..508be89fc 100644 --- a/apps/openclaw/README.md +++ b/apps/openclaw/README.md @@ -27,7 +27,8 @@ Websoft9 packages this app from the official [OpenClaw Docker image](https://ghc - Leave `OPENCLAW_PUBLIC_SCHEME` and `OPENCLAW_PUBLIC_ORIGIN` empty for direct IP/LAN access. For domain access, set `OPENCLAW_PUBLIC_SCHEME=https` to derive the exact browser origin from `W9_URL`, or set `OPENCLAW_PUBLIC_ORIGIN` to an exact override before recreating the app. -- The package re-applies `gateway.publicOrigin` and `gateway.controlUi.allowedOrigins` on every recreate through `openclaw-init`, so domain-related config changes do not get stuck in the persisted volume. +- When a reverse proxy terminates the request, set `OPENCLAW_TRUSTED_PROXIES` to the proxy source IP(s) the Gateway sees (space- or comma-separated). Without it, forwarded headers are rejected with `proxy_attribution_required`; keep the list narrow and make the proxy overwrite `X-Forwarded-*`. +- The package re-applies `gateway.publicOrigin`, `gateway.controlUi.allowedOrigins`, and `gateway.trustedProxies` on every recreate through `openclaw-init`, so domain-related config changes do not get stuck in the persisted volume. Apps run as containers; rebuild after any configuration change. diff --git a/apps/openclaw/docker-compose.yml b/apps/openclaw/docker-compose.yml index ea06a3424..bd3b9a894 100644 --- a/apps/openclaw/docker-compose.yml +++ b/apps/openclaw/docker-compose.yml @@ -12,31 +12,34 @@ services: - OPENCLAW_CONFIG_PATH=/home/node/.openclaw/openclaw.json - OPENCLAW_PUBLIC_SCHEME=${OPENCLAW_PUBLIC_SCHEME:-} - OPENCLAW_PUBLIC_ORIGIN=${OPENCLAW_PUBLIC_ORIGIN:-} + - OPENCLAW_TRUSTED_PROXIES=${OPENCLAW_TRUSTED_PROXIES:-} entrypoint: - "sh" - "-c" - | mkdir -p /home/node/.openclaw [ ! -s /home/node/.openclaw/openclaw.json ] && cp /seed/openclaw.json /home/node/.openclaw/openclaw.json - RESOLVED_PUBLIC_ORIGIN="$${OPENCLAW_PUBLIC_ORIGIN}" - if [ -z "$${RESOLVED_PUBLIC_ORIGIN}" ] && [ -n "$${OPENCLAW_PUBLIC_SCHEME}" ] && [ -n "$${W9_URL}" ]; then - RESOLVED_PUBLIC_ORIGIN="$${OPENCLAW_PUBLIC_SCHEME}://$${W9_URL}" - fi - if [ -n "$${RESOLVED_PUBLIC_ORIGIN}" ]; then - printf '%s\n' \ - '{' \ - ' gateway: {' \ - ' publicOrigin: "'"$${RESOLVED_PUBLIC_ORIGIN}"'" ,' \ - ' controlUi: {' \ - ' allowedOrigins: ["'"$${RESOLVED_PUBLIC_ORIGIN}"'"],' \ - ' },' \ - ' },' \ - '}' > /tmp/websoft9-openclaw.patch.json5 - node dist/index.js config patch --file /tmp/websoft9-openclaw.patch.json5 + rm -f /tmp/w9-origin.json5 /tmp/w9-proxies.json5 + node -e ' + const fs = require("fs"); + const scheme = process.env.OPENCLAW_PUBLIC_SCHEME || ""; + const host = process.env.W9_URL || ""; + const origin = (process.env.OPENCLAW_PUBLIC_ORIGIN || "").trim() || (scheme && host ? scheme + "://" + host : ""); + if (origin) fs.writeFileSync("/tmp/w9-origin.json5", JSON.stringify({ gateway: { publicOrigin: origin, controlUi: { allowedOrigins: [origin] } } })); + const proxies = (process.env.OPENCLAW_TRUSTED_PROXIES || "").split(/[\s,]+/).filter(Boolean); + if (proxies.length) fs.writeFileSync("/tmp/w9-proxies.json5", JSON.stringify({ gateway: { trustedProxies: proxies } })); + ' + if [ -f /tmp/w9-origin.json5 ]; then + node dist/index.js config patch --file /tmp/w9-origin.json5 else node dist/index.js config unset gateway.publicOrigin || true node dist/index.js config unset gateway.controlUi.allowedOrigins || true fi + if [ -f /tmp/w9-proxies.json5 ]; then + node dist/index.js config patch --file /tmp/w9-proxies.json5 + else + node dist/index.js config unset gateway.trustedProxies || true + fi chown -R 1000:1000 /home/node/.openclaw volumes: - openclaw_data:/home/node/.openclaw diff --git a/apps/openclaw/variables.json b/apps/openclaw/variables.json index 4acdafe6b..160223048 100644 --- a/apps/openclaw/variables.json +++ b/apps/openclaw/variables.json @@ -34,6 +34,6 @@ "first_startup_only": [] }, "help": { - "db": "No bundled database. The Gateway token is seeded from W9_LOGIN_PASSWORD, and the package derives the external Control UI origin from OPENCLAW_PUBLIC_ORIGIN or OPENCLAW_PUBLIC_SCHEME plus W9_URL on each recreate." + "db": "No bundled database. The Gateway token is seeded from W9_LOGIN_PASSWORD; the package derives the external Control UI origin from OPENCLAW_PUBLIC_ORIGIN or OPENCLAW_PUBLIC_SCHEME plus W9_URL, and applies OPENCLAW_TRUSTED_PROXIES for reverse-proxy forwarded-header attribution on each recreate." } } diff --git a/apps/openproject/variables.json b/apps/openproject/variables.json index 492181349..a5501e657 100644 --- a/apps/openproject/variables.json +++ b/apps/openproject/variables.json @@ -10,6 +10,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", diff --git a/apps/opensearch/variables.json b/apps/opensearch/variables.json index f302c4c8b..60d8c9a73 100644 --- a/apps/opensearch/variables.json +++ b/apps/opensearch/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "https", + "api": { + "port": 9200, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "8", diff --git a/apps/openwebui/variables.json b/apps/openwebui/variables.json index 754454281..c1a268d6a 100644 --- a/apps/openwebui/variables.json +++ b/apps/openwebui/variables.json @@ -20,6 +20,17 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + }, + "admin": { + "port": 8080, + "path": "/admin" + } + }, "requirements": { "cpu": "2", "memory": "4", diff --git a/apps/oracle/variables.json b/apps/oracle/variables.json index 94d6f3084..20f5741e3 100644 --- a/apps/oracle/variables.json +++ b/apps/oracle/variables.json @@ -2,6 +2,9 @@ "name": "oracle", "trademark": "Oracle Database", "release": true, + "upstream": { + "image": "https://container-registry.oracle.com/ords/ocr/ba/database/express" + }, "edition": [ { "dist": "community", @@ -12,12 +15,20 @@ ] } ], + "access": { + "defaultScheme": "https", + "web": { + "port": 5500, + "path": "/" + }, + "admin": { + "port": 5500, + "path": "/em" + } + }, "requirements": { "cpu": "2", "memory": "8", "disk": "1" - }, - "upstream": { - "image": "https://container-registry.oracle.com/ords/ocr/ba/database/express" } } diff --git a/apps/outline/variables.json b/apps/outline/variables.json index 0e0c5c9ea..79cfee4f4 100644 --- a/apps/outline/variables.json +++ b/apps/outline/variables.json @@ -2,6 +2,9 @@ "name": "outline", "trademark": "Outline", "release": false, + "upstream": { + "image": "https://hub.docker.com/r/outlinewiki/outline" + }, "edition": [ { "dist": "community", @@ -10,12 +13,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/outlinewiki/outline" } } diff --git a/apps/pgadmin/variables.json b/apps/pgadmin/variables.json index 510f5a6bb..2ee3a04ae 100644 --- a/apps/pgadmin/variables.json +++ b/apps/pgadmin/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/pmm/variables.json b/apps/pmm/variables.json index a99047a94..8b846dd07 100644 --- a/apps/pmm/variables.json +++ b/apps/pmm/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "https", + "web": { + "port": 8443, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/pocketbase/CHANGELOG.md b/apps/pocketbase/CHANGELOG.md index 582cf46c5..b6d786358 100644 --- a/apps/pocketbase/CHANGELOG.md +++ b/apps/pocketbase/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG +## 2026-09-28 + +- Add declarative `variables.json.credentials.username` / `password` metadata for PocketBase while keeping legacy `W9_LOGIN_GET_PASSWORD` for compatibility. + ## Release ### Fixes and Enhancements - diff --git a/apps/pocketbase/variables.json b/apps/pocketbase/variables.json index 9a7088ed4..cea5d6be7 100644 --- a/apps/pocketbase/variables.json +++ b/apps/pocketbase/variables.json @@ -2,6 +2,9 @@ "name": "pocketbase", "trademark": "PocketBase", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/spectado/pocketbase" + }, "edition": [ { "dist": "community", @@ -11,12 +14,31 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8090, + "path": "/" + }, + "admin": { + "port": 8090, + "path": "/_/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" }, - "upstream": { - "image": "https://hub.docker.com/r/spectado/pocketbase" + "credentials": { + "username": { + "source": "inline", + "value": "admin@example.com" + }, + "password": { + "source": "container-env", + "name": "W9_LOGIN_PASSWORD", + "format": "text" + } } } diff --git a/apps/portainer/variables.json b/apps/portainer/variables.json index f04a671db..a8b0d1c6c 100644 --- a/apps/portainer/variables.json +++ b/apps/portainer/variables.json @@ -2,6 +2,9 @@ "name": "portainer", "trademark": "Portainer", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/portainer/portainer-ce" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 9000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "4" - }, - "upstream": { - "image": "https://hub.docker.com/r/portainer/portainer-ce" } } diff --git a/apps/portkey/variables.json b/apps/portkey/variables.json index 5bc544df3..55a732cd1 100644 --- a/apps/portkey/variables.json +++ b/apps/portkey/variables.json @@ -2,6 +2,9 @@ "name": "portkey", "trademark": "Portkey", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/portkeyai/gateway" + }, "edition": [ { "dist": "community", @@ -11,12 +14,20 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8787, + "path": "/" + }, + "admin": { + "port": 8787, + "path": "/public" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/portkeyai/gateway" } } diff --git a/apps/prestashop/variables.json b/apps/prestashop/variables.json index 58b64ca71..4abd92762 100644 --- a/apps/prestashop/variables.json +++ b/apps/prestashop/variables.json @@ -24,6 +24,17 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/psadmin" + } + }, "requirements": { "cpu": "1", "memory": "2", diff --git a/apps/prometheus/.env b/apps/prometheus/.env index 1817dadf4..f5db020d8 100644 --- a/apps/prometheus/.env +++ b/apps/prometheus/.env @@ -1,6 +1,6 @@ W9_REPO=prom/prometheus W9_DIST=community -W9_VERSION=v3.14.0 +W9_VERSION=v3.15.0 # Optional password seed: enable only when the package actually controls a DB or built-in login. # See docs/w9-env-spec.md for when W9_POWER_PASSWORD is appropriate. diff --git a/apps/prometheus/CHANGELOG.md b/apps/prometheus/CHANGELOG.md index 456600202..a85e0daa4 100644 --- a/apps/prometheus/CHANGELOG.md +++ b/apps/prometheus/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2026-09-28 + +- Update Prometheus to `v3.15.0` (alias `latest`). + ## 2026-09-15 - Update Prometheus to `v3.14.0` (alias `latest`). diff --git a/apps/prometheus/variables.json b/apps/prometheus/variables.json index b7cf3d31e..a6bea4270 100644 --- a/apps/prometheus/variables.json +++ b/apps/prometheus/variables.json @@ -14,7 +14,7 @@ { "dist": "community", "version": [ - "v3.14.0", + "v3.15.0", "latest" ] } diff --git a/apps/rancher/variables.json b/apps/rancher/variables.json index 5198f430b..cce850643 100644 --- a/apps/rancher/variables.json +++ b/apps/rancher/variables.json @@ -2,6 +2,9 @@ "name": "rancher", "trademark": "Rancher", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/rancher/rancher" + }, "edition": [ { "dist": "community", @@ -10,6 +13,13 @@ ] } ], + "access": { + "defaultScheme": "https", + "web": { + "port": 443, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", @@ -18,10 +28,9 @@ "credentials": { "password": { "source": "container-log", - "pattern": "Bootstrap Password:" + "match": "regex", + "pattern": "Bootstrap Password:\\s*(.+)", + "group": 1 } - }, - "upstream": { - "image": "https://hub.docker.com/r/rancher/rancher" } } diff --git a/apps/redisinsight/variables.json b/apps/redisinsight/variables.json index 5a4a9f4c8..910c347df 100644 --- a/apps/redisinsight/variables.json +++ b/apps/redisinsight/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 5540, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/redmine/variables.json b/apps/redmine/variables.json index 5a7cab16e..6127393e0 100644 --- a/apps/redmine/variables.json +++ b/apps/redmine/variables.json @@ -2,6 +2,9 @@ "name": "redmine", "trademark": "Redmine", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/redmine" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/_/redmine" } } diff --git a/apps/rocketchat/variables.json b/apps/rocketchat/variables.json index c087a0030..5d5275c3e 100644 --- a/apps/rocketchat/variables.json +++ b/apps/rocketchat/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 3000, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", diff --git a/apps/selenium/variables.json b/apps/selenium/variables.json index da4efa4b7..df7097304 100644 --- a/apps/selenium/variables.json +++ b/apps/selenium/variables.json @@ -2,6 +2,9 @@ "name": "selenium", "trademark": "selenium", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/selenium/standalone-chrome" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 4444, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/selenium/standalone-chrome" } } diff --git a/apps/signoz/variables.json b/apps/signoz/variables.json index 8b1b7fbae..3ee689164 100644 --- a/apps/signoz/variables.json +++ b/apps/signoz/variables.json @@ -2,6 +2,9 @@ "name": "signoz", "trademark": "SigNoz", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/signoz/signoz" + }, "edition": [ { "dist": "community", @@ -11,12 +14,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "10" - }, - "upstream": { - "image": "https://hub.docker.com/r/signoz/signoz" } } diff --git a/apps/supabase/variables.json b/apps/supabase/variables.json index 680de32da..18d12fec9 100644 --- a/apps/supabase/variables.json +++ b/apps/supabase/variables.json @@ -2,19 +2,6 @@ "name": "supabase", "trademark": "Supabase", "release": true, - "edition": [ - { - "dist": "community", - "version": [ - "0.8.1" - ] - } - ], - "requirements": { - "cpu": "2", - "memory": "4", - "disk": "40" - }, "upstream": { "image": "https://github.com/supabase/supabase/releases/tag/self-hosted/v0.8.1", "releases": "https://github.com/supabase/supabase/tags", @@ -28,6 +15,30 @@ "https://github.com/supabase/supabase/blob/master/docker/versions.md" ] }, + "edition": [ + { + "dist": "community", + "version": [ + "0.8.1" + ] + } + ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8000, + "path": "/" + }, + "api": { + "port": 8000, + "path": "/" + } + }, + "requirements": { + "cpu": "2", + "memory": "4", + "disk": "40" + }, "env": { "first_startup_only": [ "POSTGRES_PASSWORD" diff --git a/apps/teamcity/variables.json b/apps/teamcity/variables.json index b1df48cfb..a96b80c52 100644 --- a/apps/teamcity/variables.json +++ b/apps/teamcity/variables.json @@ -2,6 +2,15 @@ "name": "teamcity", "trademark": "TeamCity", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/jetbrains/teamcity-server", + "releases": "https://www.jetbrains.com/teamcity/download/", + "docs": [ + "https://www.jetbrains.com/help/teamcity/teamcity-documentation.html", + "https://github.com/JetBrains/teamcity-docker-server", + "https://github.com/JetBrains/teamcity-docker-samples" + ] + }, "edition": [ { "dist": "community", @@ -11,18 +20,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8111, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/r/jetbrains/teamcity-server", - "releases": "https://www.jetbrains.com/teamcity/download/", - "docs": [ - "https://www.jetbrains.com/help/teamcity/teamcity-documentation.html", - "https://github.com/JetBrains/teamcity-docker-server", - "https://github.com/JetBrains/teamcity-docker-samples" - ] } } diff --git a/apps/thingsboard/variables.json b/apps/thingsboard/variables.json index 465a4b4e9..3dd7106e0 100644 --- a/apps/thingsboard/variables.json +++ b/apps/thingsboard/variables.json @@ -2,6 +2,14 @@ "name": "thingsboard", "trademark": "ThingsBoard", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/thingsboard/tb-node", + "releases": "https://github.com/thingsboard/thingsboard/releases", + "docs": [ + "https://thingsboard.io/docs/installation/docker/", + "https://github.com/thingsboard/thingsboard" + ] + }, "edition": [ { "dist": "community", @@ -11,17 +19,16 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "2" - }, - "upstream": { - "image": "https://hub.docker.com/r/thingsboard/tb-node", - "releases": "https://github.com/thingsboard/thingsboard/releases", - "docs": [ - "https://thingsboard.io/docs/installation/docker/", - "https://github.com/thingsboard/thingsboard" - ] } } diff --git a/apps/traefik/variables.json b/apps/traefik/variables.json index db41db8cc..64a03b040 100644 --- a/apps/traefik/variables.json +++ b/apps/traefik/variables.json @@ -2,6 +2,14 @@ "name": "traefik", "trademark": "Traefik", "release": true, + "upstream": { + "image": "https://hub.docker.com/_/traefik", + "releases": "https://github.com/traefik/traefik", + "docs": [ + "https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/", + "https://doc.traefik.io/traefik/migrate/v3/" + ] + }, "edition": [ { "dist": "community", @@ -11,17 +19,24 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" + }, + "admin": { + "port": 8080, + "path": "/dashboard/" + }, + "api": { + "port": 8080, + "path": "/api/" + } + }, "requirements": { "cpu": "1", "memory": "1", "disk": "1" - }, - "upstream": { - "image": "https://hub.docker.com/_/traefik", - "releases": "https://github.com/traefik/traefik", - "docs": [ - "https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/", - "https://doc.traefik.io/traefik/migrate/v3/" - ] } } diff --git a/apps/typesense/variables.json b/apps/typesense/variables.json index 4f454fa0a..7e4dfaf25 100644 --- a/apps/typesense/variables.json +++ b/apps/typesense/variables.json @@ -10,6 +10,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "api": { + "port": 8108, + "path": "/" + } + }, "requirements": { "cpu": "2", "memory": "4", diff --git a/apps/typo3/variables.json b/apps/typo3/variables.json index dfb91e124..a65eb7115 100644 --- a/apps/typo3/variables.json +++ b/apps/typo3/variables.json @@ -2,6 +2,15 @@ "name": "typo3", "trademark": "Typo3", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/martinhelmich/typo3", + "releases": "https://github.com/TYPO3/typo3/releases", + "docs": [ + "https://github.com/martin-helmich/docker-typo3", + "https://github.com/TYPO3/typo3", + "https://docs.typo3.org/" + ] + }, "edition": [ { "dist": "community", @@ -11,6 +20,17 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/typo3" + } + }, "requirements": { "cpu": "2", "memory": "4", @@ -31,14 +51,5 @@ "TYPO3_SETUP_CREATE_SITE", "TYPO3_SERVER_TYPE" ] - }, - "upstream": { - "image": "https://hub.docker.com/r/martinhelmich/typo3", - "releases": "https://github.com/TYPO3/typo3/releases", - "docs": [ - "https://github.com/martin-helmich/docker-typo3", - "https://github.com/TYPO3/typo3", - "https://docs.typo3.org/" - ] } } diff --git a/apps/vault/CHANGELOG.md b/apps/vault/CHANGELOG.md index 8eda9135e..6a6d3e6b5 100644 --- a/apps/vault/CHANGELOG.md +++ b/apps/vault/CHANGELOG.md @@ -5,7 +5,7 @@ - Updated Vault from `1.21` to `2.1`, the latest stable upstream minor (upstream `2.1.1`). - Pinned `W9_VERSION` to `2.1` and declared it in `variables.json`. - Kept the package in Vault dev mode (`server -dev`), matching the current image default. -- Replaced the legacy `W9_LOGIN_GET_TOKEN` hint with a declarative `variables.json.credentials.password` source (`container-log`, pattern `Root Token:`). +- Replaced the legacy `W9_LOGIN_GET_TOKEN` hint with a declarative `variables.json.credentials.token` source (`container-log`, pattern `Root Token:`). - Aligned `.env` and `docker-compose.yml` with current repository policy: braced variable references, inline published-port comment, and the `.env` section banner with a Docs URL. - Removed the obsolete `version:` key and the `# image:` / `# docs:` source comments; moved `VAULT_LOCAL_CONFIG` into `.env`. - Added a healthcheck against `/v1/sys/health`. diff --git a/apps/vault/variables.json b/apps/vault/variables.json index 3ed1d8341..b307d0c72 100644 --- a/apps/vault/variables.json +++ b/apps/vault/variables.json @@ -2,6 +2,14 @@ "name": "vault", "trademark": "Vault", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/hashicorp/vault", + "releases": "https://github.com/hashicorp/vault/releases", + "docs": [ + "https://developer.hashicorp.com/vault/docs", + "https://hub.docker.com/r/hashicorp/vault" + ] + }, "edition": [ { "dist": "community", @@ -11,23 +19,28 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 8200, + "path": "/" + }, + "api": { + "port": 8200, + "path": "/v1/" + } + }, "requirements": { "cpu": "1", "memory": "2", "disk": "1" }, "credentials": { - "password": { + "token": { "source": "container-log", - "pattern": "Root Token:" + "match": "regex", + "pattern": "Root Token:\\s*(.+)", + "group": 1 } - }, - "upstream": { - "image": "https://hub.docker.com/r/hashicorp/vault", - "releases": "https://github.com/hashicorp/vault/releases", - "docs": [ - "https://developer.hashicorp.com/vault/docs", - "https://hub.docker.com/r/hashicorp/vault" - ] } } diff --git a/apps/weaviate/variables.json b/apps/weaviate/variables.json index 5f4d2bfdf..e4228dc18 100644 --- a/apps/weaviate/variables.json +++ b/apps/weaviate/variables.json @@ -11,6 +11,13 @@ ] } ], + "access": { + "defaultScheme": "http", + "api": { + "port": 8080, + "path": "/v1/" + } + }, "requirements": { "cpu": "2", "memory": "4", diff --git a/apps/wordpress/variables.json b/apps/wordpress/variables.json index 790af82bf..4e8611eb0 100644 --- a/apps/wordpress/variables.json +++ b/apps/wordpress/variables.json @@ -21,6 +21,17 @@ ] } ], + "access": { + "defaultScheme": "http", + "web": { + "port": 80, + "path": "/" + }, + "admin": { + "port": 80, + "path": "/wp-admin" + } + }, "requirements": { "cpu": "1", "memory": "1", diff --git a/apps/youtrack/CHANGELOG.md b/apps/youtrack/CHANGELOG.md index 89903e80a..f9ba63698 100644 --- a/apps/youtrack/CHANGELOG.md +++ b/apps/youtrack/CHANGELOG.md @@ -6,5 +6,5 @@ - Remove dead `W9_URL_REPLACE` and `W9_URL_WITH_PORT` helpers that failed the policy gate. - Normalize `.env` and `docker-compose.yml` to current repository policy, including braced variable references and port purpose comments. - Raise the documented memory requirement to 1.5 GB to match upstream. -- Declare the first-run Configuration Wizard token as a `container-log` credential source. +- Declare the first-run Configuration Wizard token as a `container-file` credential source. - Regenerate README. diff --git a/apps/youtrack/variables.json b/apps/youtrack/variables.json index b9c76dee8..df78b69ae 100644 --- a/apps/youtrack/variables.json +++ b/apps/youtrack/variables.json @@ -2,6 +2,12 @@ "name": "youtrack", "trademark": "YouTrack", "release": true, + "upstream": { + "image": "https://hub.docker.com/r/jetbrains/youtrack", + "docs": [ + "https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html" + ] + }, "edition": [ { "dist": "community", @@ -10,10 +16,11 @@ ] } ], - "credentials": { - "password": { - "source": "container-file", - "pattern": "/opt/youtrack/conf/internal/services/configurationWizard/wizard_token.txt" + "access": { + "defaultScheme": "http", + "web": { + "port": 8080, + "path": "/" } }, "requirements": { @@ -21,13 +28,14 @@ "memory": "1.5", "disk": "10" }, + "credentials": { + "token": { + "source": "container-file", + "path": "/opt/youtrack/conf/internal/services/configurationWizard/wizard_token.txt", + "format": "text" + } + }, "env": { "first_startup_only": [] - }, - "upstream": { - "image": "https://hub.docker.com/r/jetbrains/youtrack", - "docs": [ - "https://www.jetbrains.com/help/youtrack/server/youtrack-docker-installation.html" - ] } } diff --git a/cli/libs/__pycache__/app.cpython-312.pyc b/cli/libs/__pycache__/app.cpython-312.pyc index 0969265483fc512448cca0808f0e139c88220cc8..9fc6ce212c8af838c7c2762e4f1df9e3a65e36b9 100644 GIT binary patch delta 2789 zcmc&$|7%;<6@O29-y~VGWJ}f;%aZ&hww|5XsWW?V;)bMk)CN|!S--5wk}0z8ODsp< zdf#)XJvk~R1D9pQ?j6jfUb`P!D7b}yx)n+pY=drPzch75&6~>xTYuRGbDS(}g~85w zitIN11NK0lbKW`Uo_o%@=booGPW}Ci=l5>6lY=KY^!`k;d)X7>KV0tn^~o;Md__1N zCf&pfG(vjF9@26}Jl#%uNh?UAq>uQ4c90|q0F4oqv;mEielkFUV3{C$$vzSSu2T!m z9B0P@tdM#DU=0#BtqKXj;`6iqIoXh_md71`-%Gb*T0?{*G-ldv7aCB>=(({OyN;`3Yz2=qlXvJZP7stIE zjaC~$KFO)JNz*w|%3jpy`CML8ZKg0)D&=Veyr{e>85w#(Gj#NppdmohpPtU>X<8~7 zX|l79_JFD#etH7n1#Z*Tyd-_%J-m|o*_j*B2R>~dyU{+j-hO!9HU63~$0lB@hf>_a&|;-(6uLNOnTb*FegXfHw^ zLK47qrZeSoTF;isnsvI&tAqm%)0-=1^Rq-tXJ~dhcS$2uWk-}~gm&X1rJxxZl7X%n zf>#}#Cs&&+L)S;ykCmjyEk+lGO(Y8;^p;!pIv0go zUiNP#mTU*hTQN=vZTe%Yb8m&Or`F?#*Zt$$oZFFDoY)LRSDs>L7gKD+0WM<0*GJc5 zhros3vp4}R@{X9)4whRnw&ZB@Ji>GCQC);Me$+ZKT&p>3Jg>4}I1h?e`0TE;-#H9g zijb=HyDr;Y6tC-21pC(Tp@Eoqu*lV zp8Y3|qW%~HUetQ!v6=5HKb=I`!vLyCCy>Yb(&GpzgoEtoo;~8XfjwFKljmJtx)+rA#@X0A!hgiJJNH$0?Q`e@ zBYKKHhxRS?RB=91%-n2F!Q4Fld`>s?a_z=auNfLxNnJ*+itqx0RgDGUOn2jM;uXfa{iAElg{MpF z*|7cHL*g}ff2T&ePuZP#{q6YeXk)+a?FttSc&R_cM(ji&B9Vwpl$ZPBKF1o)4tN%X zihY7RJIMC;ji}BrNGf1@=y5btL(L8PxW_aKAtYz)GvLiFWk@%{8jKSwXFpIAavrlj}57vRlz#NdDjyeV>(euOqPloU4;;7 z7l3L@O+p)6x#7E2hA*5jP`+$B@61EreV632##vRUw~N(4mLKH4CN%}L25~lsZUqF( z`Zctc=&m%4DwJA!Q@{Y>-0~nvLGi7jWNl{MXN$8M)C#xmk_GzX)m=lYlPyC$$$G2b zzXTNE{HuA>pQ8CI2)oW-N6S6%nM$9ZHD;-nPE%}KdK6&}z?92S*}OM5Wn~HdFbAJ9 zn1H|OuU4nfDu&R5Fos}Few?WFD|`;Q7ZLEPm~ysMC}fJnlpZe|IcTz1QH3+a`Zr_! zIr>5wFOLA`!g=YJ{UOGbi0Y?AH1XV@GZ9| vnXq?eG{ARlv_-b;K>zFtY>7a(B+k>aB_pSBl5^Vu+-*B&3*52+XYKqyngMH4 delta 2598 zcmcgtU2GIp6rS0gnf>i{>2B$EDebmhV5hX85D@v*P>O5hFJOc!Znt};9L;McT;9|FUchlXlXvWEpY$g!_M_mn5d-S!Y6FuzSL#_SNRa zd#>0(&b6S$!3%#_R=gW$H|2w#qk?cowq*9G67eWixvCTtc2bGBm4X+?v{K+1x^Dsy zt%mx0Rj%)jh?nFuo~lTFRS$8s0--97JtPQJJoi{tDm21u(f&GF6wVDwLRFxKwX8ms z6{$!twt#)Ta8v3!zGdW;{8T@^*?cd)(XWqDeO8ot)eXQJa{f7lfJU z%*Z=!F9@^)URBX{k7*NXfMUjJGXm0RCqfrOH-PQWWDO%@=5#}|6%e&Dq3vkJ9NUtq zQ%m*Q;e0t)tPm}erMc1kxJGCaIW0mDkmPcd0)_p`Sgk7#JRA3SdR+m#kD%*9_0EN|E)G;B6ySmUKgzqghQxI{6@ z)&#eBZ3V(Pp;?Rsd;GmXpa?r_7lY5c0-Nz2>P4ViSZCvbt^Y_g zxJGH7XeLdw!>t>epTHThR4CsahD3L;H(L`*AByYP7p<#%F#@S1rEW)z@_F`V0 zqoG4A65rar7$?<`IfBe!{RALxla4VXzE(a1hGK0de%Z5i$z=n&3?t+KRD~W#8CP?X zyIA{97-Q`Du1nz*Qr&bp47Ql1J6TWnCh-e4)ZI6^#3wihMs$Q0k>BL(JdSEigxxrh zFKT;EjSOV194SPGeUOo%&@7BIc9&`JBAoBh! zu}!J27R`be=OHd^j6^01@h~IRvCKQ`aV%$~sv8>QYBrPF)av8C7g#vX$_@j%Sc?6b zI&{RpUv<&-pmZ7tZ%d0;;&emBfS#cx4854oO>m0sCi#-;vV)^qmS{9%S`$T$;JNh7yY}v{Vg9GnE3Mdl%p)s`iif~d+US5IsU*T__WxYMg zL)Qm0H?`3pHT{OB-wYem@~vomUJ!k{INg6Mxa|DC7x!IEUL1d;_o{UD=zF10q)(20 z+W2+!YyC$0+3Q2(x>md~RQe%Uo^zwmFWyF0>)A2s8rTozh>_aTu&x*R2LJFw`$$w1 z`}ML$Uj^G~KZf-*$>*%&s^pwIuLLymn)sL2mXHnV7NkfBoO}@^SaCr+3F<+#p_&tH z30H*^IRTmh$R%$456LZwg+l ze2Q-L9*6Z8uKqt}^Ews5wMitsj_@+V67vmsRg>_&%8XR33e_?hils?;r^P#Cdmxh0 zVt$w>01ke_fL?V7EfUS=MkzbnTN&ZapdB6YQ^daj6x*wFRq?J->?#zmgzd@crBb#` zY~_Gq<)NgoOeppW{*C2T^oMtQNhg(}wv%1~g9v}~9xL=c>A4q7h)s6~*tdO!%^|Vt o*7EqA8^)gk&2uu0bBYjZn)9IK6%_xx52U+p!PR`n1rlH2FS~I^hX4Qo diff --git a/cli/libs/__pycache__/main.cpython-312.pyc b/cli/libs/__pycache__/main.cpython-312.pyc index 6f4b5f8f40ef196440de2340ce83b1e350f59040..a5ff0f37c8da3f58f851253ab39f3b951b9c6ea8 100644 GIT binary patch delta 7248 zcmb6e3v?9Kb!IoS+1-4BgnT%V@C$5&B>a&=FpvZh2+B7IVYAtpkdV!8e6tCL1OxcP z9t3ipfF3@6ibpX;!4bsLo~rn-wgR5YnO3W|m1_M2v9>7I-g{@6S++^U9Juek_wN7Q z_uhT8M}A{J@Vq_wcS%WZ0scl8{%Y;St|Q4A`PrUA*%$T|%1TS29P&3PY;2&uhWS`) zp_Z3l=y0b1xm^&%3$Jl+(Nu9dFS&jQKUUN>TtY$kCp+#C+T%)vE~6`Si3YO4$&1E~ z6HdTeE&@@?i`<# zlp?3f{p9}g069%g=egr^G6r@zBjVld_EMp$KoB-0w7dEFX=$bH335sn%tD(QY5PUm zj7U2m(xzd{Y;tB$l(V`VhDcG^TEzE{_oTQl`ydLf+2M5l;k6m|Y|kLxJ^q+@kUusd z%u6N=^o{|pd4eeH7TSfXM6g3_7i9qjtKwFdEbbC_iy$&NPH43qvab_8cHM2nxVAo^ zuo*yD!HNO0naE=Ukeka!;=n8b{PhV##~`9+0QiUSpN+!l%+mYAhtm!R&WxMAx8JGE z(u*oaW3xL=yf;3LXHT4%I40Lp!RJkEv$0Zsa^e`5Zf{jo-4+b-FDKq4Ci0R=(}uEA zVAh>J=C7-7QDj{T`)a_j64VL)2)TUMq#XCnfK9?7mH&3qs+9Ch(ECu!JDH_t(o46v zdG_SeksID-w{;6bhdAE`%>vs}Z06gRVYAG(3Y(-V9-KVN|Jtgz>l)utTi#VSoKiQu zlX}}0>1)`KIgA#1g*ix_s$2 z7*MpAulp)aT*Hr-4H)WoV`#E7C9JC9`8j&j&O1Q7?)=Z}ycg#Mqk;`t6+dO1$bHHi#Gd_~2S!G;>9 zsNoP(V%6|N75Ui|$QS{a<61FNdK9GoQyaAKoFlU2+_|hIu?$A_IJv&ImbLSx%N}fe z9F-qL@JpPsE5Vj;mx5G#So>Q6acp3@(d}d{ic!YZ^bNiDeZht6P zTffHJ%mVBX@2ngsw)4j;E2X!9`g=`U@l9Mp9Wolr%N?r=Cm+Cm8vxx^cB@}$3fG5% zl-X8N!|MM5sf<7&*3KJN=SgP(U8(I_on*U0>XX&8GfpFwxk*UWK@r%TzI^Fjm}}6c z_6^uMetWUm&16nG#TZs-iOTPQyvuLPHR7g_%!Q8hxyR?ZSQ>Q<_4sDj~nh zj=%tAOvMsL{+Y?=akDg}cB+0~ZEYwZvr|Y*mK_k_79iKzFK`gIbClElNiTa3M^Pht zpFdq&T=Y7kZz8Z#^e@oW?F~w+F17dq&5B`=ABoy|?wWDZIY3*qWouO1vMcSY@HkmI zawjn*C@aj%6rbFvL~ReU3fCvfw8$opUXPr$dD5pq^{{rs+HY+RO3o$xhQ`7GD*VKT z0;&L@!bE8G6sFX|PO01auoB4%1g+yBm<5MS z73>0%e~q9A(+2WiwYl zlR0QZWC0zPRNYZquLNY(5NAop{P!fYXOZbR0Nq`$(k=`76+FrOVRni)wB%mz=4`#0JY*rN(^ffBFv&pBb>q1Osm>9Y}#MZ!Az!$C!u}0ld zQy;8t46|F&7qrU{|II&s;+f(;7h~)8(c%OA==PzhS8hi*7e3ye+ffaYdOsJe(4`rn z8-alsiJ({K$j&?@8HE5{VxtlBAjn5RJC{P75VdRhjvbSv0+_!=dt=9tILP!*?i6?8rJs66oDK;%NWa=RKCY^VZxY%xE- zw@{h}=s9iVzSVJkhG+l7g#!}pSA+*nh5`J8hjR*hN+?rJ?BYZA7Ce%5)!CcO=)9YO z1{WikiJ(u7|F`iWB5E>#TcD(3u^C(1q5AUSGz+OX?GfGMpzOM_||)y1dzq^{hCQq#NH zh(!s*E-KIygCV%SgJCca&iFYvem#OR1my_kB0!rX_X6Arp#Z#7y%DD($Kmr`oQm`D!cbw`Kh_K~FM(l&nZ zXiiPv-qF>ZWNh!1s5({=j;%sag}~H!g`fGy3M4N~Z_uiaW!n~&fN~YO=DiH~`gC|w z7EBL>{Jwx%42Z9>M)nmy3NEW|Al)b&9`UT_h-ahWyhmhzhtbQ+#$P|4S5OTDvCH&F z7>iP++wtKlgT4cN@N>LF0gM$Q-w6b@*j)nv-b46hV|`M%wMk(GvmqUoTfwLm=dBqwaH$=%U6yTf>t z_GUdiu&o8lHr{fi;5}0Tl}*I|K8_Lgp^1IeVWNLEvA>xZy#tBmO>?YogOiOX6d6J9 zD8fER?h^>;MMYy&U7sNRJq0dO4ic%wbOB}17`=Z;q#+0=dXm%MSbyT7`kpXTPKQwg zk&oY_48kmt-{Bak=}CBMlyC>;T8tX1uX>e@5<3jx)+$D7ITAz%4Y5-}P8e3@hLLm^ z(U|FtarltA^l0eS@XX7GL;Q`KjRf_rkjikPvyzI+BT_@>-c_j1yQ~^Cj}TEmF^xpO z2&0SkCz(T4EpgJih_(vm4r_)Ra*)z z*;h*JFb>=3RsZS!Tjut?>d|{4veX#=o|4O2>#G%>X_Rpk0jlLFDpb%BMpYfnMmvc5 zX|N|Fu2yxfQEmg9}ub{FtvmXDurgEOp{k7^Jd=JiOX~!?nZDzA#*TmS1{d7W|$t z^Tm@h@V5{qBfwAo>>dQ22=*X&5W%Ae-a>E&0e&N8_|=eo%4eROBc2DCKC^W0BKl^< ze!!V{j5L1QVEC-pQ%lRrOP0=G;=OTx$&&Jgixx0^lQMh&GJHX?Y5Y$ohrv$||2>&E z1uI{M#U;bai)tR@Hzlkc>BppRozojba&tf_W*dM6{t4Q(E10o+jH~;_u delta 7222 zcmb7J3vg7`8Q#0Qce9)45)vLkNP;0@BP3u9FL{SY0TWO`0t(A!_Xburdw0&=JR4jP zP-v+_&J+&>C(V)X~}*WMEV%w04Sj>Z1>a>NpzFT4fxk|Nr03y_+@J!VY}* zzvrC)asKn4|NQ6X=pSs|Z`iWm$;wK%z;EZd-#&cjOWoPS%LTVOr@YdUkp-=ISj-Q=AUi5+) zE7a@LpAa3Zfn@dSV{ddk9}gA)xcvGK8f7o;0=&>FEe{2Ax(JLi{+?a;MOojU)zU1;Fti^r5af($W)tq9u?9z`ITx;P$MTq5p( zMy~#H@p3_^=U0DHJRI%u*!6L{kRv{L^paw654?A)HA}og#!l>d9AP&&+lJ0R@@IQzgIh!{Z6Rgh@EKx*6wA^ zVWxWzn(90bwivrI;z8i`MIy{}sNqJBsN0Tm*a3w21Rk#~5f8%qd+N)z4r_i5b}cdn z>5BL}%cqXk)i6nk%1laD!wXlG7myQ^1jM!YSBw*nfYg7gzgTh25np}Yv3g$Sd}!6| zfnZ}JYvt!wAC5f_4eThw3)p3o!Ywz_n6Gsr5bQ;4H%4@1DsnUPOA~4J*)i$uOB!$tGe~q2E{l*~SiZWk7YVxtoY#+YcnG}&>8D+o1PCx^NSlvoSb2OsW zlg{#Rv{Ui>8XM(MfW3=j$+E+i3LtU%>^TI(Qpy6JT++*~U>9m+@AHyPv!}g<FUnnkC&JOcSgXK*<%FeH3}jrQCSN$f{sBE<_53Eq`aplf z>pV_|Ia+pS_ypk^LV_9asyG3P;y*Ud z!XK7z6F-Bt7u2826}A{fJThw;an0U9kdY+KC5J=p@Y>jzyufwrQ(*D9blWo{&7qKC zxS5oa45t{tR1b6RQ(IzTpF<1#0%0)oc~RR)H)@D4-O?9X8XPdB&uW21ET;r{+ zrR3R!jM--*9{Ui9?O}gXG8@#qbltqK(I&F)zes(HKqh{JRFa$1Exch%F0W`CCftqAM+Lt7>}tWa%z zC#YRp%0tFwU*PT$J@$+Q8)?oozBPYm@E#1QN`+mJ1*EpiOAqU zNCQydzoiJ`9^PHNW|t>+KkPl0j{RlOSO=+!V(|?ek&Dek5%K`EjDQpg$#4?Oax|X) z0&;7Z8HV+nC<>SbaLmPA@X#DOcT(=~>8Xzv&zyYrphmEfXbxq<0;CMxdI08Qo0;;m zK{0oC^Tg56SEIJ?KIx!cuGu5a$|fzFPB7Nwh3t<-cIkK}Dhm1v_t#CV1B?#jXXup%YGZOYX zw{}&F9_SCMGrPvvVIw@ae{76cks&!Ej6{iPSW9nc4u%3=@LD)2EE1x^Op`8sIup>E zi3k-4hAuM&S>cTTVSlAK3HlGKcRux@HKn)jI9L{YyZ<&=3Aub7m^iq{lABQ2eiXs{>cI(O74*EW4tr+3)qy%XI`}<@O8JpPkI$@!SC1{JzUezl z`fpu5f)n-%_di!DR)gq|)Tf?XZ%^qS_w$uLyAAB?mnASSX|||uqm~BXZSgTUG4ng?b-DPwqXptj z5cjpZ;pn2YktkHN1|yUUJx<7Ca+5C@W`E~z9-lvT@DQie2bT{rimXjo5kBq2B2UUq ztT`8im4$e>V;A@fCyHWoj2X^GDnS^&(qd$WWhe>4Q7{iqgZbEAgD@9i9>M|y+Q<3{ z0;iN5f(x`aJ}EPCVy&3qnIb--E0f=MGFPkx5u?@M$?t5sNv3d8XlO`YVSVH4Udd!R z=v6onLJ}NYi_of+-e5~`h?&=14BSy_(dTGdU??{;0VRUre9WmlaRta&rA|F{SL$eg ztY`d$CotN}P^W$-x-@44+z9BFljQl4T#X9O-6{ zpehnYA4rI2E4C5OHlQ?HFdWstm-x}zb=W(Yaj`z-BytxHC2An$@mh=eHyR@kAVtat z;)$Op#8YGN(3cBpi=}3F=ojh1tcNcM=7JRHcTW;~#ZX1H7xBN3ZAAUepgu62_@UAG zkwMYDl~~RhZK(~;)^B!X1YPflqB~v>ilB=gwOzy49@0OM z1!2cPa&qBSSx@mrKZlqvEXvT`ut?gZfi##)jZD$E9zjpoLZw_Usl zBteu@h@Gl>qNwyBijFCwjM?aUrU%WXn+H(?d1XYO;oyJ$&?7;q6;drvB%;6)o*?Nrn#wCJ#jC@mm2*)P;i-ReY3(x z=v5whfJ!~`3e|f=QPD@ei4#Qq)Hsk4H>mMxQf=ZFra>cB zC^W;HbvPV5eCe6ng;P9yWinz!nnmEINi%v-r=`F*tv zFS879j;soyn%ABkE6n7Pv!zq9_+?l}GAy#F=3%o@Xc+>2RJq}t*(3*=L(**421DSV zMX6ISoZT!`4Hw2<8CKA1gXhN?`Mn}MdmWbS+}<>#oEAs=S1zEwvRSP8y;f|yOAVcS HWyJpghbAE+ diff --git a/cli/libs/app.py b/cli/libs/app.py index 485fdad6c..b2ce9036a 100644 --- a/cli/libs/app.py +++ b/cli/libs/app.py @@ -24,11 +24,16 @@ def _app_names(root: Path) -> list[str]: return sorted(path.name for path in root.iterdir() if path.is_dir()) +def _has_root_dockerfile(target: Path | None) -> bool: + return bool(target and (target / "Dockerfile").exists()) + + def collect_apps(include_archived: bool = False, scope: str | None = None) -> list[dict]: names = [path.name for path in active_app_dirs()] output = [] for name in names: + target = app_dir(name) metadata = resolve_app_metadata(name) item = { "name": name, @@ -36,6 +41,7 @@ def collect_apps(include_archived: bool = False, scope: str | None = None) -> li "cadence": metadata.cadence, "update_policy": metadata.update_policy, "scope": _app_scope(name), + "dockerfile": _has_root_dockerfile(target), } if scope and item["scope"] != scope: continue @@ -43,6 +49,7 @@ def collect_apps(include_archived: bool = False, scope: str | None = None) -> li if include_archived: for name in _app_names(repo_path("archive", "apps")): + target = app_dir(name) metadata = resolve_app_metadata(name) item = { "name": name, @@ -50,6 +57,7 @@ def collect_apps(include_archived: bool = False, scope: str | None = None) -> li "cadence": metadata.cadence, "update_policy": metadata.update_policy, "scope": _app_scope(name), + "dockerfile": _has_root_dockerfile(target), } if scope and item["scope"] != scope: continue @@ -237,9 +245,16 @@ def list_apps( output = collect_apps(include_archived=include_archived, scope=scope) if not as_json: - table = Table("name", "status", "cadence", "update policy", "scope", header_style="dim", box=box.SIMPLE) + table = Table("name", "status", "cadence", "update policy", "scope", "dockerfile", header_style="dim", box=box.SIMPLE) for item in output: - table.add_row(item["name"], item["status"], item["cadence"], item["update_policy"], item["scope"]) + table.add_row( + item["name"], + item["status"], + item["cadence"], + item["update_policy"], + item["scope"], + "yes" if item["dockerfile"] else "no", + ) Console().print(table) return diff --git a/cli/libs/main.py b/cli/libs/main.py index c97b74c38..630579189 100644 --- a/cli/libs/main.py +++ b/cli/libs/main.py @@ -63,15 +63,23 @@ def list_command( all_apps = app_ops.collect_apps(include_archived=True) internal = sum(1 for item in active if item["scope"] == "internal") public = len(active) - internal + dockerfile = sum(1 for item in active if item["dockerfile"]) archived = len(all_apps) - len(active) Console().print( - f"[dim]total {len(all_apps)} | active {len(active)} (public {public}, internal {internal}) | archived {archived}[/dim]" + f"[dim]total {len(all_apps)} | active {len(active)} (public {public}, internal {internal}, dockerfile {dockerfile}) | archived {archived}[/dim]" ) if not include_archived: Console().print("[dim]use --include-archived to show archived apps[/dim]") - table = Table("name", "status", "cadence", "update policy", "scope", header_style="dim", box=box.SIMPLE) + table = Table("name", "status", "cadence", "update policy", "scope", "dockerfile", header_style="dim", box=box.SIMPLE) for item in output: - table.add_row(item["name"], item["status"], item["cadence"], item["update_policy"], item["scope"]) + table.add_row( + item["name"], + item["status"], + item["cadence"], + item["update_policy"], + item["scope"], + "yes" if item["dockerfile"] else "no", + ) Console().print(table) return print_output(output, as_json) diff --git a/cli/tests/test_app.py b/cli/tests/test_app.py index f75b5e262..41e68b1e9 100644 --- a/cli/tests/test_app.py +++ b/cli/tests/test_app.py @@ -7,7 +7,7 @@ def test_collect_apps_includes_archived_and_filters_scope(repo_fixture, app_factory): app_factory("public-app") - app_factory("internal-app", variables={ + internal_app = app_factory("internal-app", variables={ "name": "internal-app", "scope": "internal", "release": True, @@ -15,6 +15,7 @@ def test_collect_apps_includes_archived_and_filters_scope(repo_fixture, app_fact "edition": [{"dist": "community", "version": ["1.0"]}], }) app_factory("archived-app", archived=True) + (internal_app / "Dockerfile").write_text("FROM busybox\n", encoding="utf-8") archive_path = repo_fixture / "metadata" / "archive.yaml" archive = yaml.safe_load(archive_path.read_text(encoding="utf-8")) @@ -23,9 +24,12 @@ def test_collect_apps_includes_archived_and_filters_scope(repo_fixture, app_fact all_names = [item["name"] for item in app_module.collect_apps(include_archived=True)] internal_names = [item["name"] for item in app_module.collect_apps(scope="internal")] + apps = {item["name"]: item for item in app_module.collect_apps(include_archived=True)} assert all_names == ["archived-app", "internal-app", "public-app"] assert internal_names == ["internal-app"] + assert apps["internal-app"]["dockerfile"] is True + assert apps["public-app"]["dockerfile"] is False def test_collect_app_info_returns_relative_path(app_factory): diff --git a/cli/tests/test_main.py b/cli/tests/test_main.py index 46a61ca99..4689f26dd 100644 --- a/cli/tests/test_main.py +++ b/cli/tests/test_main.py @@ -61,3 +61,33 @@ def test_run_returns_exit_code_4_on_repository_error(monkeypatch): assert main.run() == 4 assert output == [("repo missing", True)] + + +def test_list_command_shows_dockerfile_summary_and_column(monkeypatch): + printed = [] + + class FakeConsole: + def print(self, value): + printed.append(value) + + monkeypatch.setattr(main, "Console", FakeConsole) + monkeypatch.setattr( + main.app_ops, + "collect_apps", + lambda include_archived=False, scope=None: [ + {"name": "alpha", "status": "active", "cadence": "monthly", "update_policy": "patch-minor", "scope": "public", "dockerfile": True}, + {"name": "beta", "status": "active", "cadence": "monthly", "update_policy": "patch-minor", "scope": "internal", "dockerfile": False}, + {"name": "gamma", "status": "archived", "cadence": "none", "update_policy": "none", "scope": "public", "dockerfile": True}, + ] if include_archived else [ + {"name": "alpha", "status": "active", "cadence": "monthly", "update_policy": "patch-minor", "scope": "public", "dockerfile": True}, + {"name": "beta", "status": "active", "cadence": "monthly", "update_policy": "patch-minor", "scope": "internal", "dockerfile": False}, + ], + ) + + main.list_command(include_archived=False, scope=None, as_json=False) + + assert printed[0] == "[dim]total 3 | active 2 (public 1, internal 1, dockerfile 1) | archived 1[/dim]" + assert printed[1] == "[dim]use --include-archived to show archived apps[/dim]" + headers = [column.header for column in printed[2].columns] + assert headers == ["name", "status", "cadence", "update policy", "scope", "dockerfile"] + assert printed[2].columns[5]._cells == ["yes", "no"] diff --git a/docs/appstore-release-spec.md b/docs/appstore-release-spec.md index 00952fe1e..dbc2ed4df 100644 --- a/docs/appstore-release-spec.md +++ b/docs/appstore-release-spec.md @@ -40,14 +40,20 @@ - `credentials` 不替代 `.env` 中已有的字面值凭据;若 `W9_LOGIN_PASSWORD` 本身就是固定值,仍直接使用 `.env` - `credentials` 仅描述“凭据从哪里取”,不允许在仓库中写任意宿主级 shell -当前约定先只支持 `password` 槽位,结构如下: +当前约定支持 3 个固定槽位:`username`、`password`、`token`。每个槽位都声明“值从哪里来”的 source。 ```json { "credentials": { "password": { - "source": "container-file", - "path": "/var/jenkins_home/secrets/initialAdminPassword" + "username": { + "source": "inline", + "value": "admin" + }, + "password": { + "source": "container-file", + "path": "/var/jenkins_home/secrets/initialAdminPassword" + } } } } @@ -55,15 +61,75 @@ 支持的 `source`: -- `container-file`: 密码存在目标应用容器内的某个文件,由消费端在 `websoft9` 容器中执行固定 `docker exec cat ` 读取 -- `container-log`: 密码存在目标应用容器日志中,由消费端在 `websoft9` 容器中执行固定 `docker logs ` 并按 `pattern` 提取 +- `inline`: 值直接内联写在 `variables.json` 中,适合固定且非敏感的登录标识,例如 `username` +- `container-env`: 值存在目标应用容器的运行环境变量中,消费端读取声明的 `name` +- `container-file`: 密码存在目标应用容器内的某个文本或结构化文件,消费端以只读方式读取 `path`;实现不应假设容器内存在 `cat`、`sh` 或其他特定用户空间工具 +- `container-log`: 密码存在目标应用容器日志中,消费端读取 `docker logs ` 的输出并按 `pattern` 提取 +- `container-cli`: 密码需要通过目标应用容器内的官方只读 CLI 获取,消费端按 `argv` 直接执行,不通过 shell 拼接 字段规则: -- `credentials.password.source=container-file` 时必须声明 `path` -- `credentials.password.source=container-log` 时必须声明 `pattern` +- `credentials..source=inline` 时必须声明 `value` +- `credentials..source=container-env` 时必须声明 `name` +- `credentials..source=container-file` 时必须声明 `path` +- `credentials..source=container-log` 时必须声明 `pattern` +- `credentials..source=container-cli` 时必须声明 `argv` +- `slot` 当前只允许 `username`、`password`、`token` +- `format` 可选,当前支持 `text`、`json`、`env` +- `format=json` 时必须声明 `jsonPath` +- `format=env` 时必须声明 `key` +- `match` 可选,当前支持 `substring`、`regex` +- `match=regex` 时必须声明 `group` +- `trim` 可选,默认由消费端按 `true` 处理 - `credentials` 为 machine-readable 结构,不做自由文本,不在其中嵌入完整 shell 命令 -- 旧的 `W9_LOGIN_GET_PASSWORD` 可作为兼容兜底,但新 app 或被触达的 app 不再推荐新增使用 +- `inline` 主要用于固定、非敏感值;不要把动态生成的 secret 明文内联到 `variables.json` +- `container-cli.argv` 必须是参数数组,不接受完整 shell 字符串,也不应用于重置密码、创建密码等带副作用动作 +- 旧的 `W9_LOGIN_GET_PASSWORD` / `W9_LOGIN_GET_TOKEN` 可作为兼容兜底,但新 app 或被触达的 app 不再推荐新增使用 + +示例: + +```json +{ + "credentials": { + "username": { + "source": "inline", + "value": "admin@example.com" + }, + "password": { + "source": "container-env", + "name": "BOOTSTRAP_INFO", + "format": "json", + "jsonPath": "$.admin.password" + } + } +} +``` + +```json +{ + "credentials": { + "token": { + "source": "container-log", + "match": "regex", + "pattern": "Root Token:\\s*(.+)", + "group": 1 + } + } +} +``` + +```json +{ + "credentials": { + "token": { + "source": "container-cli", + "argv": ["myapp", "admin", "show-bootstrap", "--json"], + "format": "json", + "jsonPath": "$.token" + } + } +} +``` ### 职责边界 diff --git a/docs/upstream-spec.md b/docs/upstream-spec.md index ff4a65dd7..4a8c47861 100644 --- a/docs/upstream-spec.md +++ b/docs/upstream-spec.md @@ -248,14 +248,18 @@ Do not ask AI to perform routine deterministic scanning when a stable source typ ## Runtime Credential Metadata -Some apps generate an initial password or token at first startup instead of taking it from `.env`. +Some apps generate an initial username, password, or token at first startup instead of taking it from `.env`. For these cases, app packages may declare machine-readable credential sources in `variables.json`. -Current minimal shape: +Current shape: ```json { "credentials": { + "username": { + "source": "inline", + "value": "admin" + }, "password": { "source": "container-file", "path": "/var/jenkins_home/secrets/initialAdminPassword" @@ -266,12 +270,20 @@ Current minimal shape: Rules: -- `container-file` means the consumer executes a fixed `docker exec cat ` flow in the - `websoft9` container and captures stdout as the password value -- `container-log` means the consumer executes a fixed `docker logs ` flow in the `websoft9` - container and extracts the password by `pattern` +- fixed slots are `username`, `password`, and `token` +- `inline` means the credential value is stored directly in `variables.json`; use it mainly for fixed, + non-sensitive values such as `username` +- `container-env` means the credential value is read from the target container runtime environment by `name` +- `container-file` means the password is stored in a file inside the target container; consumers should + read `path` in a read-only way and must not assume `cat`, `sh`, or other specific user-space tools exist +- `container-log` means the consumer reads `docker logs ` output and extracts the password by + `pattern`; `match=regex` with a capture `group` is allowed when a plain substring is not enough +- `container-cli` means the consumer runs a read-only in-container CLI command expressed as `argv` + without shell wrapping; use this only when the app exposes an official credential-printing command +- `format=json` with `jsonPath` and `format=env` with `key` may be used for structured outputs - keep this metadata declarative; do not store full shell commands in `variables.json` -- prefer `credentials.password` over the legacy `W9_LOGIN_GET_PASSWORD` when a touched app needs this behavior +- prefer `credentials.` over the legacy `W9_LOGIN_GET_PASSWORD` or `W9_LOGIN_GET_TOKEN` when a touched + app needs this behavior ## Access Metadata diff --git a/docs/w9-env-spec.md b/docs/w9-env-spec.md index f6ded3f59..146cc4ece 100644 --- a/docs/w9-env-spec.md +++ b/docs/w9-env-spec.md @@ -351,9 +351,10 @@ Use: Do not add login pair unless the package really controls it. -If the consumer can resolve the generated password after deployment, prefer declaring a machine-readable -`variables.json.credentials.password` source such as `container-file` or `container-log` instead of -adding `W9_LOGIN_USER` / `W9_LOGIN_PASSWORD` or the legacy `W9_LOGIN_GET_PASSWORD` command string. +If the consumer can resolve a generated username, password, or token after deployment, prefer declaring a +machine-readable `variables.json.credentials.` source such as `inline`, `container-env`, +`container-file`, `container-log`, or `container-cli` instead of adding `W9_LOGIN_USER` / +`W9_LOGIN_PASSWORD` or the legacy `W9_LOGIN_GET_PASSWORD` / `W9_LOGIN_GET_TOKEN` command string. ### Web App With Bundled PostgreSQL Or MySQL diff --git a/i18n/translation.json b/i18n/translation.json index 70dd392b0..944f0a061 100644 --- a/i18n/translation.json +++ b/i18n/translation.json @@ -147,6 +147,10 @@ "OPENAI API KEY", "OPENAI API 密钥" ], + "W9_DEEPSEEK_API_KEY_SET": [ + "DEEPSEEK API KEY", + "DEEPSEEK API 密钥" + ], "W9_LOGIN_OPENAI_API_KEY": [ "OPENAI LOGIN KEY", "OPENAI 登陆密码" diff --git a/metadata/catalog/dsh.json b/metadata/catalog/dsh.json new file mode 100644 index 000000000..ce37b0482 --- /dev/null +++ b/metadata/catalog/dsh.json @@ -0,0 +1,21 @@ +{ + "trademark": "DeepSeek Harness", + "catalogBindings": [ + { + "parentKey": "ai", + "childKey": "ai-agent-chat" + }, + { + "parentKey": "itdeveloper", + "childKey": "ide" + } + ], + "summary": "Open-source AI agent harness.", + "overview": "Self-hosted web UI for building, running, and inspecting AI coding agents with pluggable tools and models.", + "description": "DeepSeek Harness is an open-source agent harness from DeepSeek AI for building, running, and inspecting AI agents in real-world environments. It provides a browser-based UI, plugin-based runtime composition, session traces, and tooling for models, skills, sandboxes, storage, and workflows.\n\nThis Websoft9 package deploys DeepSeek Harness as a single container built from the official Node.js 22 image and the upstream `@deepseek-ai/dsh` npm distribution. Persistent state such as sessions and attachments is stored in the `dsh_home` volume, and a dedicated `/workspace` volume is provided as the default working directory for projects opened in the UI.\n\nTypical use cases include evaluating coding agents, experimenting with agent tooling and workflows, creating plugin-based harness presets, and running a self-hosted agent workbench for development teams.", + "websiteurl": "https://www.deepseek.com/harness/en/", + "screenshots": [ + "https://www.deepseek.com/harness/images/harness/feat-plugin.en.png", + "https://www.deepseek.com/harness/images/harness/trajectory-real-view.en.png" + ] +} diff --git a/metadata/maintenance.yaml b/metadata/maintenance.yaml index 01d4f2ed5..ba3597b6c 100644 --- a/metadata/maintenance.yaml +++ b/metadata/maintenance.yaml @@ -33,6 +33,7 @@ cadence: - woocommerce - wordpress - wordpresspro + - dsh update_policy: {} lifecycle: frozen: [] diff --git a/metadata/variables.schema.json b/metadata/variables.schema.json index 9df025241..d5c35f91d 100644 --- a/metadata/variables.schema.json +++ b/metadata/variables.schema.json @@ -91,6 +91,13 @@ "type": "object", "additionalProperties": false, "properties": { + "defaultScheme": { + "type": "string", + "enum": [ + "http", + "https" + ] + }, "web": { "$ref": "#/$defs/accessEntry" }, @@ -201,38 +208,379 @@ "type": "object", "additionalProperties": false, "properties": { + "username": { + "$ref": "#/$defs/credentialSource" + }, "password": { + "$ref": "#/$defs/credentialSource" + }, + "token": { + "$ref": "#/$defs/credentialSource" + } + } + }, + "help": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "$defs": { + "credentialSource": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": [ + "source", + "value" + ], + "properties": { + "source": { + "const": "inline" + }, + "value": { + "type": "string" + } + } + }, + { "type": "object", "additionalProperties": false, "required": [ - "source" + "source", + "name" ], "properties": { "source": { + "const": "container-env" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "format": { + "type": "string", + "enum": [ + "text", + "json", + "env" + ] + }, + "jsonPath": { + "type": "string", + "minLength": 1 + }, + "key": { + "type": "string", + "minLength": 1 + }, + "match": { "type": "string", "enum": [ - "container-file", - "container-log" + "substring", + "regex" ] }, + "pattern": { + "type": "string", + "minLength": 1 + }, + "group": { + "type": "integer", + "minimum": 0 + }, + "trim": { + "type": "boolean" + } + }, + "allOf": [ + { + "if": { + "properties": { + "format": { + "const": "json" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "jsonPath" + ] + } + }, + { + "if": { + "properties": { + "format": { + "const": "env" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "key" + ] + } + }, + { + "if": { + "properties": { + "match": { + "const": "regex" + } + }, + "required": [ + "match" + ] + }, + "then": { + "required": [ + "group" + ] + } + } + ] + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "source", + "path" + ], + "properties": { + "source": { + "const": "container-file" + }, "path": { - "type": "string" + "type": "string", + "minLength": 1 + }, + "format": { + "type": "string", + "enum": [ + "text", + "json", + "env" + ] + }, + "jsonPath": { + "type": "string", + "minLength": 1 + }, + "key": { + "type": "string", + "minLength": 1 + }, + "trim": { + "type": "boolean" + } + }, + "allOf": [ + { + "if": { + "properties": { + "format": { + "const": "json" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "jsonPath" + ] + } + }, + { + "if": { + "properties": { + "format": { + "const": "env" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "key" + ] + } + } + ] + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "source", + "pattern" + ], + "properties": { + "source": { + "const": "container-log" + }, + "match": { + "type": "string", + "enum": [ + "substring", + "regex" + ] }, "pattern": { - "type": "string" + "type": "string", + "minLength": 1 + }, + "group": { + "type": "integer", + "minimum": 0 + }, + "trim": { + "type": "boolean" } - } + }, + "allOf": [ + { + "if": { + "properties": { + "match": { + "const": "regex" + } + }, + "required": [ + "match" + ] + }, + "then": { + "required": [ + "group" + ] + } + } + ] + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "source", + "argv" + ], + "properties": { + "source": { + "const": "container-cli" + }, + "argv": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "format": { + "type": "string", + "enum": [ + "text", + "json", + "env" + ] + }, + "jsonPath": { + "type": "string", + "minLength": 1 + }, + "key": { + "type": "string", + "minLength": 1 + }, + "match": { + "type": "string", + "enum": [ + "substring", + "regex" + ] + }, + "pattern": { + "type": "string", + "minLength": 1 + }, + "group": { + "type": "integer", + "minimum": 0 + }, + "trim": { + "type": "boolean" + } + }, + "allOf": [ + { + "if": { + "properties": { + "format": { + "const": "json" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "jsonPath" + ] + } + }, + { + "if": { + "properties": { + "format": { + "const": "env" + } + }, + "required": [ + "format" + ] + }, + "then": { + "required": [ + "key" + ] + } + }, + { + "if": { + "properties": { + "match": { + "const": "regex" + } + }, + "required": [ + "match" + ] + }, + "then": { + "required": [ + "group" + ] + } + } + ] } - } + ] }, - "help": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "$defs": { "accessEntry": { "type": "object", "additionalProperties": false, diff --git a/skills/app-update/SKILL.md b/skills/app-update/SKILL.md index a9d71e77c..01259229e 100644 --- a/skills/app-update/SKILL.md +++ b/skills/app-update/SKILL.md @@ -37,7 +37,7 @@ Supporting files in this skill: 10. When `.env` is touched, keep the "image environment variables" section intact and mirror the template layout in `metadata/templates/new-app/.env.tmpl`: keep the section banner, the Docs URL, the "Used by docker-compose.yml" group, and the commented "Not used by default" group. Refresh the single Docs URL if the upstream changed, keep only the variables required by the current package shape plus any user-facing essentials, keep the used vars aligned with `docker-compose.yml`, and keep commented unused vars at no more than 5. Follow the decision rules in `docs/w9-env-spec.md` for `W9_URL`, `W9_URL_REPLACE`, login pairs, `_SET` ports, and dependency helpers. Use a domain-style `W9_URL` placeholder (for example `appname.example.com` or `example.youdomain.com`); do not use `internet_ip:${W9_HTTP_PORT_SET}`. Whenever `.env` is touched at all, convert every environment-variable reference in the whole file to the braced form `${VAR}`; do not leave bare `$VAR` in the file even on lines that were already present. 11. When `docker-compose.yml` is touched, ensure every published port line carries an inline `# purpose` comment and that no `# image:` / `# docs:` source comments remain — image and documentation sources live only in `variables.json` `upstream`. Convert every environment-variable reference in the whole file to the braced form `${VAR}` (for example `${W9_REPO}`, `${W9_HTTP_PORT_SET}`), not just the lines being changed. 12. When a credential or config env var only takes effect on first container startup (the image's entrypoint uses a marker file, e.g. `webconsole.security.enabled`), record that fact in `variables.json` as `env.first_startup_only` (a list of such env names). The README generator then auto-renders the warning; keep the "how to rotate" solution in the hand-written README Change Password section or Notes instead of in metadata. -13. When an app does not control a built-in admin password in `.env` but the password or token can be resolved after startup from inside the application container or from its logs, declare a declarative source in `variables.json.credentials.password` using `container-file` or `container-log`. Prefer this over adding the legacy `W9_LOGIN_GET_PASSWORD` command string. +13. When an app does not control a built-in admin username, password, or token in `.env` but the value can be resolved after startup, declare declarative metadata in `variables.json.credentials.` where `` is `username`, `password`, or `token`. Supported sources are `inline`, `container-env`, `container-file`, `container-log`, and `container-cli`. Prefer this over adding legacy `W9_LOGIN_GET_PASSWORD` or `W9_LOGIN_GET_TOKEN` command strings. 14. Healthchecks should default to the main app container only. Add healthchecks to sidecar or dependency containers only when the official upstream compose explicitly defines them or the task explicitly requires them. 15. If the target app has app-local drift against the current repository rules (for example template, metadata, env policy, or generated README expectations), fix the minimum blocking or directly relevant items as part of the same update. 16. Keep `apps//CHANGELOG.md` as the single source of app change history. Use a pure-date heading `## YYYY-MM-DD` as the first-level heading for each change batch; list all changes for that date below it. Do not duplicate changelog content into `README.md`. diff --git a/skills/app-update/checklist.md b/skills/app-update/checklist.md index fa89182f6..28b1ae5b4 100644 --- a/skills/app-update/checklist.md +++ b/skills/app-update/checklist.md @@ -13,6 +13,6 @@ - [ ] Keep changes app-local - [ ] Update `apps//CHANGELOG.md` with a pure-date heading `## YYYY-MM-DD` for this change batch - [ ] Register new translatable env keys in `i18n/translation.json` if needed -- [ ] When an interactive app exposes its initial password or token from a container file or logs, prefer `variables.json.credentials.password` over the legacy `W9_LOGIN_GET_PASSWORD` +- [ ] When an interactive app exposes an initial username, password, or token after deployment, prefer declarative `variables.json.credentials.` metadata (`inline`, `container-env`, `container-file`, `container-log`, `container-cli`) over legacy `W9_LOGIN_GET_PASSWORD` / `W9_LOGIN_GET_TOKEN` - [ ] Run structure, policy, deploy, and reachability checks when applicable - [ ] Produce a short test report diff --git a/skills/new-app/SKILL.md b/skills/new-app/SKILL.md index 45fe29054..e2295d8d9 100644 --- a/skills/new-app/SKILL.md +++ b/skills/new-app/SKILL.md @@ -36,7 +36,7 @@ If any required input is missing, stop and ask the user for it before researchin 9. Register any new translatable env key in `i18n/translation.json`. 10. Healthchecks should default to the main app container only. Add healthchecks to sidecar or dependency containers only when the official upstream compose explicitly defines them or the task explicitly requires them. 11. If a credential or config env var only takes effect on first container startup (the image entrypoint uses a marker file, e.g. `webconsole.security.enabled`), record it in `variables.json` as `env.first_startup_only` (list of env names) so the README auto-renders the warning. -12. If the app creates its initial password or token interactively and the consumer can resolve it after deployment from a container file or from logs, declare `variables.json.credentials.password` with `source=container-file` plus `path`, or `source=container-log` plus `pattern`. Prefer this declarative metadata over the legacy `W9_LOGIN_GET_PASSWORD` command string. +12. If the app creates its initial username, password, or token interactively and the consumer can resolve it after deployment, declare `variables.json.credentials.` metadata where `` is `username`, `password`, or `token`. Supported sources are `inline`, `container-env`, `container-file`, `container-log`, and `container-cli`. Prefer this declarative metadata over legacy `W9_LOGIN_GET_PASSWORD` / `W9_LOGIN_GET_TOKEN` command strings. 13. Keep `apps//CHANGELOG.md` as the single source of app change history. Use a pure-date heading `## YYYY-MM-DD` as the first-level heading for the initial change batch (the template already injects today's date); list the initial package changes below it. Do not duplicate changelog content into `README.md`. 14. Run `.venv/bin/libs app-gen-readme --app --json` after metadata or README marker content changes so generated sections stay current. 15. For dependency images such as PostgreSQL, MySQL, MariaDB, Redis, or pgvector, prefer `x.x` tags even when upstream examples show `x.x.x`, unless exact patch pinning is demonstrably required. Hard-coded dependency `x.x.x` tags in `docker-compose.yml` are policy drift and should be normalized before handoff. diff --git a/skills/new-app/prompt-fragments.md b/skills/new-app/prompt-fragments.md index f45c8b243..70f82b8f5 100644 --- a/skills/new-app/prompt-fragments.md +++ b/skills/new-app/prompt-fragments.md @@ -22,4 +22,4 @@ Prove the app can be deployed, not just generated. ## Credential Source Rule -When an app does not control a fixed first admin password in `.env` but the consumer can resolve it after deployment, declare a declarative `variables.json.credentials.password` source such as `container-file` or `container-log`. Prefer this over embedding a full `docker exec` or `docker logs` command in `W9_LOGIN_GET_PASSWORD`. +When an app does not control a fixed first admin username, password, or token in `.env` but the consumer can resolve it after deployment, declare declarative `variables.json.credentials.` metadata where `` is `username`, `password`, or `token`. Supported sources are `inline`, `container-env`, `container-file`, `container-log`, and `container-cli`. Prefer this over embedding full commands in `W9_LOGIN_GET_PASSWORD` or `W9_LOGIN_GET_TOKEN`.