diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index 2d82d2a28..438d1f397 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -42,7 +42,7 @@ jobs: RENOVATE_DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} run: node .github/scripts/check_dockerhub_credentials.mjs - name: Run Renovate - uses: renovatebot/github-action@f3a31a786096ba6b40d0f0ffe11a494ef73bfa7c # v46.3.4 + uses: renovatebot/github-action@a63d9a7bc606f7336fd20b47f2f32259a35e6213 # v46.3.9 with: configurationFile: .github/renovate-global.js docker-cmd-file: .github/scripts/renovate-entrypoint.sh diff --git a/apps/2fauth/8.0.2/docker-compose.yml b/apps/2fauth/8.0.2/docker-compose.yml index 426ca38d2..69aae4e6b 100644 --- a/apps/2fauth/8.0.2/docker-compose.yml +++ b/apps/2fauth/8.0.2/docker-compose.yml @@ -1,6 +1,6 @@ services: 2fauth: - image: "2fauth/2fauth:8.0.2" + image: "2fauth/2fauth:8.0.3" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml b/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml index 101d5fdc0..8721f8f28 100644 --- a/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml +++ b/apps/adguardhome-sync-linuxserver/0.9.3/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: adguardhome-sync: - image: "linuxserver/adguardhome-sync:0.9.3" + image: "linuxserver/adguardhome-sync:0.9.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/adguardhome-sync-linuxserver/0.9.4/.env.sample b/apps/adguardhome-sync-linuxserver/0.9.4/.env.sample new file mode 100644 index 000000000..2347cb9d8 --- /dev/null +++ b/apps/adguardhome-sync-linuxserver/0.9.4/.env.sample @@ -0,0 +1,5 @@ +CONTAINER_NAME= +PANEL_APP_PORT_HTTP=8080 +CONFIG_PATH=./data/config +CONFIGFILE=/config/adguardhome-sync.yaml +TIME_ZONE=Asia/Shanghai diff --git a/apps/adguardhome-sync-linuxserver/0.9.4/data.yml b/apps/adguardhome-sync-linuxserver/0.9.4/data.yml new file mode 100644 index 000000000..e0e7db5b6 --- /dev/null +++ b/apps/adguardhome-sync-linuxserver/0.9.4/data.yml @@ -0,0 +1,67 @@ +additionalProperties: + formFields: + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: /config/adguardhome-sync.yaml + edit: true + envKey: CONFIGFILE + labelEn: Config file + labelZh: 配置文件 + label: + en: Config file + zh: 配置文件 + zh-Hant: 設定檔 + ja: 設定ファイル + ko: 설정 파일 + ru: Файл конфигурации + ms: Fail konfigurasi + pt-br: Arquivo de configuracao + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/libredb-studio/0.16.1/data/.gitkeep b/apps/adguardhome-sync-linuxserver/0.9.4/data/.gitkeep similarity index 100% rename from apps/libredb-studio/0.16.1/data/.gitkeep rename to apps/adguardhome-sync-linuxserver/0.9.4/data/.gitkeep diff --git a/apps/adguardhome-sync-linuxserver/0.9.4/docker-compose.yml b/apps/adguardhome-sync-linuxserver/0.9.4/docker-compose.yml new file mode 100644 index 000000000..8721f8f28 --- /dev/null +++ b/apps/adguardhome-sync-linuxserver/0.9.4/docker-compose.yml @@ -0,0 +1,21 @@ +networks: + 1panel-network: + external: true +services: + adguardhome-sync: + image: "linuxserver/adguardhome-sync:0.9.4" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + - CONFIGFILE=${CONFIGFILE} + labels: + createdBy: "Apps" diff --git a/apps/modrinth-linuxserver/0.21.9/scripts/init.sh b/apps/adguardhome-sync-linuxserver/0.9.4/scripts/init.sh similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/scripts/init.sh rename to apps/adguardhome-sync-linuxserver/0.9.4/scripts/init.sh diff --git a/apps/bentopdf/2.8.8/scripts/uninstall.sh b/apps/adguardhome-sync-linuxserver/0.9.4/scripts/uninstall.sh similarity index 100% rename from apps/bentopdf/2.8.8/scripts/uninstall.sh rename to apps/adguardhome-sync-linuxserver/0.9.4/scripts/uninstall.sh diff --git a/apps/adguardhome-sync-linuxserver/0.9.4/scripts/upgrade.sh b/apps/adguardhome-sync-linuxserver/0.9.4/scripts/upgrade.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/adguardhome-sync-linuxserver/0.9.4/scripts/upgrade.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/anqicms/3.6.4/docker-compose.yml b/apps/anqicms/3.6.4/docker-compose.yml index 8b39150da..2e01acbce 100644 --- a/apps/anqicms/3.6.4/docker-compose.yml +++ b/apps/anqicms/3.6.4/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:8001" volumes: - anqicms-data:/app - image: "anqicms/anqicms:v3.6.4" + image: "anqicms/anqicms:v3.6.7" labels: createdBy: "Apps" diff --git a/apps/anqicms/3.6.6/docker-compose.yml b/apps/anqicms/3.6.6/docker-compose.yml index 9a4df0041..2e01acbce 100644 --- a/apps/anqicms/3.6.6/docker-compose.yml +++ b/apps/anqicms/3.6.6/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:8001" volumes: - anqicms-data:/app - image: "anqicms/anqicms:v3.6.6" + image: "anqicms/anqicms:v3.6.7" labels: createdBy: "Apps" diff --git a/apps/anubis/1.28.0/docker-compose.yml b/apps/anubis/1.28.0/docker-compose.yml index e3c3337fb..7fffcbf51 100644 --- a/apps/anubis/1.28.0/docker-compose.yml +++ b/apps/anubis/1.28.0/docker-compose.yml @@ -1,6 +1,6 @@ services: anubis: - image: "ghcr.io/techarohq/anubis:v1.28.0@sha256:48deacf791149e27c2cf6f5d8b4243e1a3db7a5816a168de0af6e58d9a519e2e" + image: "ghcr.io/techarohq/anubis:v1.28.1@sha256:1744c03a918160a28b0d2e8c1656de6585f4071b30b4c3b0d51d02282c9b7c25" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/asciinema-server/20260626-postgres17-digest-b0f9560/.env.sample b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/.env.sample new file mode 100644 index 000000000..8cdde8aa2 --- /dev/null +++ b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/.env.sample @@ -0,0 +1,12 @@ +CONTAINER_NAME=asciinema-server +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=4000 +ASCIINEMA_URL_SCHEME=http +ASCIINEMA_URL_HOST=asciinema.local +ASCIINEMA_URL_PORT=4000 +ASCIINEMA_SECRET_KEY_BASE=generate +ASCIINEMA_POSTGRES_PASSWORD=generate +ASCIINEMA_SIGN_UP_DISABLED=false +ASCIINEMA_UPLOAD_AUTH_REQUIRED=true +ASCIINEMA_TIMEZONE=UTC +APP_DATA_DIR=./data diff --git a/apps/asciinema-server/20260626-postgres17-digest-b0f9560/data.yml b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/data.yml new file mode 100644 index 000000000..52526127b --- /dev/null +++ b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/data.yml @@ -0,0 +1,195 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 4000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http + edit: true + envKey: ASCIINEMA_URL_SCHEME + labelEn: Public URL Scheme + labelZh: 公开 URL 协议 + label: + en: Public URL Scheme + zh: 公开 URL 协议 + zh-Hant: 公開 URL 協定 + ja: 公開 URL スキーム + ko: 공개 URL 스킴 + ru: Схема публичного URL + ms: Skema URL awam + pt-br: Esquema da URL publica + required: true + type: select + values: + - label: HTTP + value: http + - label: HTTPS + value: https + - default: asciinema.local + edit: true + envKey: ASCIINEMA_URL_HOST + labelEn: Public URL Host + labelZh: 公开 URL 主机名 + label: + en: Public URL Host + zh: 公开 URL 主机名 + zh-Hant: 公開 URL 主機名稱 + ja: 公開 URL ホスト + ko: 공개 URL 호스트 + ru: Хост публичного URL + ms: Hos URL awam + pt-br: Host da URL publica + required: true + type: text + - default: 4000 + edit: true + envKey: ASCIINEMA_URL_PORT + labelEn: Public URL Port + labelZh: 公开 URL 端口 + label: + en: Public URL Port + zh: 公开 URL 端口 + zh-Hant: 公開 URL 連接埠 + ja: 公開 URL ポート + ko: 공개 URL 포트 + ru: Порт публичного URL + ms: Port URL awam + pt-br: Porta da URL publica + required: true + rule: paramPort + type: number + - default: generate + edit: true + envKey: ASCIINEMA_SECRET_KEY_BASE + labelEn: Session Secret + labelZh: 会话密钥 + label: + en: Session Secret + zh: 会话密钥 + zh-Hant: 工作階段密鑰 + ja: セッションシークレット + ko: 세션 비밀 키 + ru: Секрет сеанса + ms: Rahsia sesi + pt-br: Segredo de sessao + required: true + type: password + - default: generate + edit: true + envKey: ASCIINEMA_POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: "false" + edit: true + envKey: ASCIINEMA_SIGN_UP_DISABLED + labelEn: Disable Public Signup + labelZh: 禁用公开注册 + label: + en: Disable Public Signup + zh: 禁用公开注册 + zh-Hant: 停用公開註冊 + ja: 公開登録を無効化 + ko: 공개 가입 비활성화 + ru: Отключить публичную регистрацию + ms: Lumpuhkan pendaftaran awam + pt-br: Desativar cadastro publico + required: true + type: select + values: + - label: "False" + value: "false" + - label: "True" + value: "true" + - default: "true" + edit: true + envKey: ASCIINEMA_UPLOAD_AUTH_REQUIRED + labelEn: Require Upload Authentication + labelZh: 要求上传认证 + label: + en: Require Upload Authentication + zh: 要求上传认证 + zh-Hant: 要求上傳驗證 + ja: アップロード認証を必須化 + ko: 업로드 인증 필요 + ru: Требовать аутентификацию загрузки + ms: Wajibkan pengesahan muat naik + pt-br: Exigir autenticacao para envio + required: true + type: select + values: + - label: "True" + value: "true" + - label: "False" + value: "false" + - default: UTC + edit: true + envKey: ASCIINEMA_TIMEZONE + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horario + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/asciinema-server/20260626-postgres17-digest-b0f9560/docker-compose.yml b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/docker-compose.yml new file mode 100644 index 000000000..52aa49d9b --- /dev/null +++ b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/docker-compose.yml @@ -0,0 +1,82 @@ +services: + asciinema-server: + image: "ghcr.io/asciinema/asciinema-server:20260626@sha256:0474b2186be46b23f5e16dbd4319727dbb68f27a303705fbe632821bf524b313" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + depends_on: + asciinema-postgres: + condition: service_healthy + user: "1000:0" + networks: + - 1panel-network + - asciinema-internal + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:4000" + environment: + - DATABASE_URL=postgresql://asciinema:${ASCIINEMA_POSTGRES_PASSWORD}@asciinema-postgres/asciinema + - SECRET_KEY_BASE=${ASCIINEMA_SECRET_KEY_BASE} + - URL_SCHEME=${ASCIINEMA_URL_SCHEME} + - URL_HOST=${ASCIINEMA_URL_HOST} + - URL_PORT=${ASCIINEMA_URL_PORT} + - TZ=${ASCIINEMA_TIMEZONE} + - BIND_ALL=1 + - ADMIN_BIND_ALL=0 + - ADMIN_PANEL_ON_MAIN_ENDPOINT=1 + - SIGN_UP_DISABLED=${ASCIINEMA_SIGN_UP_DISABLED} + - UPLOAD_AUTH_REQUIRED=${ASCIINEMA_UPLOAD_AUTH_REQUIRED} + - DATA_DIR=/var/lib/asciinema + - CACHE_PATH=/var/cache/asciinema + read_only: true + tmpfs: + - /tmp:uid=1000,gid=0,mode=1770 + - /var/cache/asciinema:uid=1000,gid=0,mode=0770 + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + volumes: + - "${APP_DATA_DIR}/asciinema:/var/lib/asciinema" + healthcheck: + test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:4000/ || exit 1"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 60s + labels: + createdBy: "Apps" + + asciinema-postgres: + image: "postgres:17-alpine@sha256:1bcc18d81671b02906296aad470ec9778a6b71016ce953092024b680bad007b0" + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - asciinema-internal + environment: + - POSTGRES_USER=asciinema + - POSTGRES_PASSWORD=${ASCIINEMA_POSTGRES_PASSWORD} + - POSTGRES_DB=asciinema + read_only: true + tmpfs: + - /run/postgresql:uid=70,gid=70,mode=0770 + - /tmp:uid=70,gid=70,mode=1770 + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U asciinema -d asciinema"] + interval: 5s + timeout: 5s + retries: 12 + start_period: 10s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true + asciinema-internal: + internal: true diff --git a/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/init.sh b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/init.sh new file mode 100755 index 000000000..cc88da466 --- /dev/null +++ b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/init.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ROOT_DIR}/.asciinema-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +validate_port() { + local key="$1" + local value="$2" + [[ "$value" =~ ^[0-9]+$ ]] || fail "$key must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "$key must be between 1 and 65535" +} + +validate_boolean() { + local key="$1" + local value="$2" + [[ "$value" == "true" || "$value" == "false" ]] || fail "$key must be true or false" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" + +bind_address="$(read_env_value PANEL_APP_BIND_ADDRESS)" +[[ "$bind_address" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "PANEL_APP_BIND_ADDRESS must be an IPv4 address" +IFS=. read -r octet1 octet2 octet3 octet4 <<< "$bind_address" +for octet in "$octet1" "$octet2" "$octet3" "$octet4"; do + ((10#$octet <= 255)) || fail "PANEL_APP_BIND_ADDRESS contains an invalid IPv4 octet" +done + +validate_port PANEL_APP_PORT_HTTP "$(read_env_value PANEL_APP_PORT_HTTP)" +validate_port ASCIINEMA_URL_PORT "$(read_env_value ASCIINEMA_URL_PORT)" + +url_scheme="$(read_env_value ASCIINEMA_URL_SCHEME)" +[[ "$url_scheme" == "http" || "$url_scheme" == "https" ]] || fail "ASCIINEMA_URL_SCHEME must be http or https" + +url_host="$(read_env_value ASCIINEMA_URL_HOST)" +[[ ${#url_host} -ge 1 && ${#url_host} -le 253 ]] || fail "ASCIINEMA_URL_HOST has an invalid length" +[[ "$url_host" != *..* ]] || fail "ASCIINEMA_URL_HOST contains an empty label" +IFS=. read -ra host_labels <<< "$url_host" +for host_label in "${host_labels[@]}"; do + [[ ${#host_label} -le 63 ]] || fail "ASCIINEMA_URL_HOST contains an oversized label" + [[ "$host_label" =~ ^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?$ ]] || fail "ASCIINEMA_URL_HOST must be a hostname or IPv4 address" +done + +validate_boolean ASCIINEMA_SIGN_UP_DISABLED "$(read_env_value ASCIINEMA_SIGN_UP_DISABLED)" +validate_boolean ASCIINEMA_UPLOAD_AUTH_REQUIRED "$(read_env_value ASCIINEMA_UPLOAD_AUTH_REQUIRED)" + +timezone="$(read_env_value ASCIINEMA_TIMEZONE)" +[[ "$timezone" =~ ^[A-Za-z0-9_+/-]{1,64}$ && "$timezone" != *..* ]] || fail "ASCIINEMA_TIMEZONE contains unsupported characters" + +secret="$(read_env_value ASCIINEMA_SECRET_KEY_BASE)" +if [[ -z "$secret" || "$secret" == "generate" ]]; then + command -v base64 >/dev/null 2>&1 || fail "base64 is required to generate ASCIINEMA_SECRET_KEY_BASE" + random_material="$(head -c 96 /dev/urandom | base64 | tr -dc A-Za-z0-9)" + secret="${random_material:0:64}" +fi +[[ "$secret" =~ ^[A-Za-z0-9]{64,128}$ ]] || fail "ASCIINEMA_SECRET_KEY_BASE must contain 64 to 128 alphanumeric characters" + +postgres_password="$(read_env_value ASCIINEMA_POSTGRES_PASSWORD)" +if [[ -z "$postgres_password" || "$postgres_password" == "generate" ]]; then + command -v base64 >/dev/null 2>&1 || fail "base64 is required to generate ASCIINEMA_POSTGRES_PASSWORD" + random_material="$(head -c 48 /dev/urandom | base64 | tr -dc A-Za-z0-9)" + postgres_password="${random_material:0:32}" +fi +[[ "$postgres_password" =~ ^[A-Za-z0-9]{16,128}$ ]] || fail "ASCIINEMA_POSTGRES_PASSWORD must contain 16 to 128 alphanumeric characters" + +data_dir_raw="$(read_env_value APP_DATA_DIR)" +[[ -n "$data_dir_raw" ]] || data_dir_raw="./data" +case "$data_dir_raw" in + /*) fail "APP_DATA_DIR must be relative to the application version directory" ;; + *$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'") fail "APP_DATA_DIR contains unsupported characters" ;; +esac +data_dir="$(realpath -m -- "${ROOT_DIR}/${data_dir_raw#./}")" +case "$data_dir" in + "${ROOT_DIR}"/*) ;; + *) fail "APP_DATA_DIR must stay inside the application version directory" ;; +esac +[[ ! -L "$data_dir" ]] || fail "APP_DATA_DIR must not be a symbolic link" + +install -d -m 0750 -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "${ROOT_DIR}"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +[[ "$(id -u)" -eq 0 ]] || fail "asciinema server init must run as root to prepare data ownership" + +for child_name in asciinema postgres; do + [[ ! -L "$data_dir/$child_name" ]] || fail "APP_DATA_DIR/$child_name must not be a symbolic link" +done +install -d -m 0770 -- "$data_dir/asciinema" +install -d -m 0700 -- "$data_dir/postgres" +for child_name in asciinema postgres; do + resolved_child="$(realpath -e -- "$data_dir/$child_name")" + case "$resolved_child" in + "${resolved_data_dir}"/*) ;; + *) fail "APP_DATA_DIR/$child_name resolves outside APP_DATA_DIR" ;; + esac +done +chown -R --no-dereference 1000:0 "$data_dir/asciinema" +chown -R --no-dereference 70:70 "$data_dir/postgres" +chmod 0770 "$data_dir/asciinema" +chmod 0700 "$data_dir/postgres" + +set_env_value ASCIINEMA_SECRET_KEY_BASE "$secret" +set_env_value ASCIINEMA_POSTGRES_PASSWORD "$postgres_password" +chmod 600 "$ENV_FILE" diff --git a/apps/godoxy/0.31.3/scripts/uninstall.sh b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/uninstall.sh similarity index 100% rename from apps/godoxy/0.31.3/scripts/uninstall.sh rename to apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/uninstall.sh diff --git a/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/upgrade.sh b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/upgrade.sh new file mode 100755 index 000000000..138b520cb --- /dev/null +++ b/apps/asciinema-server/20260626-postgres17-digest-b0f9560/scripts/upgrade.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" 'index($0, key "=") == 1 { value = substr($0, length(key) + 2) } END { print value }' "$file")" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ENV_FILE}.asciinema-upgrade.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0; prefix = key "=" } + index($0, prefix) == 1 { + if (!written) { + print prefix value + written = 1 + } + next + } + { print } + END { if (!written) print prefix value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +credentials_need_recovery() { + local key value + for key in ASCIINEMA_SECRET_KEY_BASE ASCIINEMA_POSTGRES_PASSWORD; do + value="$(read_env_value "$ENV_FILE" "$key")" + [[ -z "$value" || "$value" == generate ]] && return 0 + done + return 1 +} + +restore_credentials_from_env() { + local candidate="$1" + local current_container previous_container key current_value previous_value + local needs_recovery=0 + + [[ -f "$candidate" && ! -L "$candidate" ]] || return 1 + current_container="$(read_env_value "$ENV_FILE" CONTAINER_NAME)" + previous_container="$(read_env_value "$candidate" CONTAINER_NAME)" + [[ -n "$current_container" && "$previous_container" == "$current_container" ]] || return 1 + + for key in ASCIINEMA_SECRET_KEY_BASE ASCIINEMA_POSTGRES_PASSWORD; do + current_value="$(read_env_value "$ENV_FILE" "$key")" + [[ -n "$current_value" && "$current_value" != generate ]] && continue + previous_value="$(read_env_value "$candidate" "$key")" + if [[ "$key" == ASCIINEMA_SECRET_KEY_BASE ]]; then + [[ "$previous_value" =~ ^[A-Za-z0-9]{64,128}$ ]] || return 1 + else + [[ "$previous_value" =~ ^[A-Za-z0-9]{16,128}$ ]] || return 1 + fi + set_env_value "$key" "$previous_value" + needs_recovery=1 + done + + (( needs_recovery == 1 )) || ! credentials_need_recovery +} + +restore_credentials_from_backup() ( + local panel_root app_key install_name backup_dir archive archive_entry + local latest_mtime=0 archive_mtime temp_dir old_env + + panel_root="${ROOT_DIR%%/apps/*}" + [[ "$panel_root" != "$ROOT_DIR" ]] || exit 1 + app_key="$(basename "$(dirname "$ROOT_DIR")")" + install_name="$(basename "$ROOT_DIR")" + [[ "$app_key" =~ ^[A-Za-z0-9._-]+$ && "$install_name" =~ ^[A-Za-z0-9._-]+$ ]] || exit 1 + backup_dir="$panel_root/backup/app/local${app_key}/${install_name}" + [[ -d "$backup_dir" && ! -L "$backup_dir" ]] || exit 1 + + archive="" + while IFS= read -r -d '' candidate; do + archive_mtime="$(stat -c '%Y' "$candidate")" + if [[ -z "$archive" ]] || (( archive_mtime > latest_mtime )); then + archive="$candidate" + latest_mtime="$archive_mtime" + fi + done < <(find "$backup_dir" -maxdepth 1 -type f -name 'upgrade_backup_*.tar.gz' -print0) + [[ -n "$archive" ]] || exit 1 + + archive_entry="$(tar -tzf "$archive" | awk '$0 ~ /(^|\/)app\.tar\.gz$/ { print; exit }')" + [[ -n "$archive_entry" ]] || exit 1 + if ! tar -xOf "$archive" -- "$archive_entry" | tar -tzf - | grep -Fqx "$install_name/.env"; then + exit 1 + fi + + temp_dir="$(mktemp -d "$ROOT_DIR/.asciinema-upgrade-backup.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + old_env="$temp_dir/old.env" + tar -xOf "$archive" -- "$archive_entry" | tar -xOzf - -- "$install_name/.env" > "$old_env" + chmod 600 "$old_env" + restore_credentials_from_env "$old_env" +) + +[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE not found or is a symbolic link" + +if credentials_need_recovery; then + if ! restore_credentials_from_backup && credentials_need_recovery; then + fail "Could not recover generated Asciinema credentials from the 1Panel upgrade backup; refusing to continue with newly generated credentials" + fi +fi + +exec "$SCRIPT_DIR/init.sh" diff --git a/apps/asciinema-server/20260626/docker-compose.yml b/apps/asciinema-server/20260626/docker-compose.yml index 79cd2544f..52aa49d9b 100644 --- a/apps/asciinema-server/20260626/docker-compose.yml +++ b/apps/asciinema-server/20260626/docker-compose.yml @@ -46,7 +46,7 @@ services: createdBy: "Apps" asciinema-postgres: - image: "postgres:17-alpine@sha256:742f40ea20b9ff2ff31db5458d127452988a2164df9e17441e191f3b72252193" + image: "postgres:17-alpine@sha256:1bcc18d81671b02906296aad470ec9778a6b71016ce953092024b680bad007b0" container_name: ${CONTAINER_NAME}-postgres restart: unless-stopped user: "70:70" diff --git a/apps/asciinema-server/20260626/scripts/upgrade.sh b/apps/asciinema-server/20260626/scripts/upgrade.sh index cd089ba98..138b520cb 100755 --- a/apps/asciinema-server/20260626/scripts/upgrade.sh +++ b/apps/asciinema-server/20260626/scripts/upgrade.sh @@ -1,4 +1,126 @@ #!/usr/bin/env bash set -euo pipefail -"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)/init.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" 'index($0, key "=") == 1 { value = substr($0, length(key) + 2) } END { print value }' "$file")" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ENV_FILE}.asciinema-upgrade.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0; prefix = key "=" } + index($0, prefix) == 1 { + if (!written) { + print prefix value + written = 1 + } + next + } + { print } + END { if (!written) print prefix value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +credentials_need_recovery() { + local key value + for key in ASCIINEMA_SECRET_KEY_BASE ASCIINEMA_POSTGRES_PASSWORD; do + value="$(read_env_value "$ENV_FILE" "$key")" + [[ -z "$value" || "$value" == generate ]] && return 0 + done + return 1 +} + +restore_credentials_from_env() { + local candidate="$1" + local current_container previous_container key current_value previous_value + local needs_recovery=0 + + [[ -f "$candidate" && ! -L "$candidate" ]] || return 1 + current_container="$(read_env_value "$ENV_FILE" CONTAINER_NAME)" + previous_container="$(read_env_value "$candidate" CONTAINER_NAME)" + [[ -n "$current_container" && "$previous_container" == "$current_container" ]] || return 1 + + for key in ASCIINEMA_SECRET_KEY_BASE ASCIINEMA_POSTGRES_PASSWORD; do + current_value="$(read_env_value "$ENV_FILE" "$key")" + [[ -n "$current_value" && "$current_value" != generate ]] && continue + previous_value="$(read_env_value "$candidate" "$key")" + if [[ "$key" == ASCIINEMA_SECRET_KEY_BASE ]]; then + [[ "$previous_value" =~ ^[A-Za-z0-9]{64,128}$ ]] || return 1 + else + [[ "$previous_value" =~ ^[A-Za-z0-9]{16,128}$ ]] || return 1 + fi + set_env_value "$key" "$previous_value" + needs_recovery=1 + done + + (( needs_recovery == 1 )) || ! credentials_need_recovery +} + +restore_credentials_from_backup() ( + local panel_root app_key install_name backup_dir archive archive_entry + local latest_mtime=0 archive_mtime temp_dir old_env + + panel_root="${ROOT_DIR%%/apps/*}" + [[ "$panel_root" != "$ROOT_DIR" ]] || exit 1 + app_key="$(basename "$(dirname "$ROOT_DIR")")" + install_name="$(basename "$ROOT_DIR")" + [[ "$app_key" =~ ^[A-Za-z0-9._-]+$ && "$install_name" =~ ^[A-Za-z0-9._-]+$ ]] || exit 1 + backup_dir="$panel_root/backup/app/local${app_key}/${install_name}" + [[ -d "$backup_dir" && ! -L "$backup_dir" ]] || exit 1 + + archive="" + while IFS= read -r -d '' candidate; do + archive_mtime="$(stat -c '%Y' "$candidate")" + if [[ -z "$archive" ]] || (( archive_mtime > latest_mtime )); then + archive="$candidate" + latest_mtime="$archive_mtime" + fi + done < <(find "$backup_dir" -maxdepth 1 -type f -name 'upgrade_backup_*.tar.gz' -print0) + [[ -n "$archive" ]] || exit 1 + + archive_entry="$(tar -tzf "$archive" | awk '$0 ~ /(^|\/)app\.tar\.gz$/ { print; exit }')" + [[ -n "$archive_entry" ]] || exit 1 + if ! tar -xOf "$archive" -- "$archive_entry" | tar -tzf - | grep -Fqx "$install_name/.env"; then + exit 1 + fi + + temp_dir="$(mktemp -d "$ROOT_DIR/.asciinema-upgrade-backup.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + old_env="$temp_dir/old.env" + tar -xOf "$archive" -- "$archive_entry" | tar -xOzf - -- "$install_name/.env" > "$old_env" + chmod 600 "$old_env" + restore_credentials_from_env "$old_env" +) + +[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE not found or is a symbolic link" + +if credentials_need_recovery; then + if ! restore_credentials_from_backup && credentials_need_recovery; then + fail "Could not recover generated Asciinema credentials from the 1Panel upgrade backup; refusing to continue with newly generated credentials" + fi +fi + +exec "$SCRIPT_DIR/init.sh" diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/.env.sample b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/.env.sample new file mode 100644 index 000000000..524b1e86d --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41001 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/data.yml b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/data.yml new file mode 100644 index 000000000..d3bdf1eca --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41001 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/docker-compose.yml b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/docker-compose.yml new file mode 100644 index 000000000..7331c7967 --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/docker-compose.yml @@ -0,0 +1,35 @@ +services: + audacity-kasm: + image: "kasmweb/audacity:1.19.0-rolling-weekly@sha256:8a5c6dad4f16e0cad022aee7beb2fa0d664c5331d6eb60b15790b2b482a0bf92" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/init.sh b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/uninstall.sh b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/upgrade.sh b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/audacity-kasm/1.19.0-rolling-weekly-digest-69ac302/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/audacity-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/audacity-kasm/1.19.0-rolling-weekly/docker-compose.yml index fcaea28d2..7331c7967 100644 --- a/apps/audacity-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/audacity-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: audacity-kasm: - image: "kasmweb/audacity:1.19.0-rolling-weekly@sha256:b3470505d3734a6a3322e19ae8fdf3f6d792d856126ede3af58e4af1f3aa7be4" + image: "kasmweb/audacity:1.19.0-rolling-weekly@sha256:8a5c6dad4f16e0cad022aee7beb2fa0d664c5331d6eb60b15790b2b482a0bf92" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/audacity-linuxserver/3.7.8/docker-compose.yml b/apps/audacity-linuxserver/3.7.8/docker-compose.yml index 4fdfce65e..4258504d5 100644 --- a/apps/audacity-linuxserver/3.7.8/docker-compose.yml +++ b/apps/audacity-linuxserver/3.7.8/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: audacity: - image: "linuxserver/audacity:3.7.8" + image: "linuxserver/audacity:3.7.9" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/bark/2.3.7/docker-compose.yml b/apps/bark/2.3.7/docker-compose.yml index 156f030ce..fa73fda2e 100644 --- a/apps/bark/2.3.7/docker-compose.yml +++ b/apps/bark/2.3.7/docker-compose.yml @@ -1,6 +1,6 @@ services: bark: - image: "finab/bark-server:v2.3.7" + image: "finab/bark-server:v2.3.9" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/bark/2.3.8/.env.sample b/apps/bark/2.3.8/.env.sample new file mode 100644 index 000000000..959c1804b --- /dev/null +++ b/apps/bark/2.3.8/.env.sample @@ -0,0 +1,20 @@ +BARK_SERVER_ADDRESS="0.0.0.0" +BARK_SERVER_BASIC_AUTH_PASSWORD="" +BARK_SERVER_BASIC_AUTH_USER="" +BARK_SERVER_CASE_SENSITIVE="false" +BARK_SERVER_CERT="" +BARK_SERVER_CONCURRENCY=262144 +BARK_SERVER_DATA_DIR="/data" +BARK_SERVER_DSN="" +BARK_SERVER_IDLE_TIMEOUT="10s" +BARK_SERVER_KEY="" +BARK_SERVER_PROXY_HEADER="" +BARK_SERVER_READ_TIMEOUT="3s" +BARK_SERVER_REDUCE_MEMORY_USAGE="false" +BARK_SERVER_SERVERLESS="false" +BARK_SERVER_STRICT_ROUTING="false" +BARK_SERVER_URL_PREFIX="/" +BARK_SERVER_WRITE_TIMEOUT="3s" +CONTAINER_NAME="bark" +DATA_PATH="./data" +PANEL_APP_PORT_HTTP=40280 diff --git a/apps/bark/2.3.8/data.yml b/apps/bark/2.3.8/data.yml new file mode 100644 index 000000000..ff0028592 --- /dev/null +++ b/apps/bark/2.3.8/data.yml @@ -0,0 +1,327 @@ +additionalProperties: + formFields: + - default: "40280" + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: 'Port' + zh: '端口' + zh-Hant: '埠' + ja: 'ポート' + ko: '포트' + ru: 'Порт' + ms: 'Port' + pt-br: 'Porta' + required: true + rule: paramPort + type: number + - default: "./data" + disabled: true + envKey: DATA_PATH + labelEn: Data Path + labelZh: 数据路径 + label: + en: 'Data Path' + zh: '数据路径' + zh-Hant: '資料路徑' + ja: 'データパス' + ko: '데이터 경로' + ru: 'Путь к данным' + ms: 'Laluan data' + pt-br: 'Caminho de dados' + required: true + type: text + - default: "0.0.0.0" + disabled: true + envKey: BARK_SERVER_ADDRESS + labelEn: Bark Server Address + labelZh: Bark 服务器地址 + label: + en: 'Bark Server Address' + zh: 'Bark 服务器地址' + zh-Hant: 'Bark 伺服器位址' + ja: 'Bark サーバーアドレス' + ko: 'Bark 서버 주소' + ru: 'Адрес сервера Bark' + ms: 'Alamat pelayan Bark' + pt-br: 'Endereço do servidor Bark' + required: true + type: text + - default: "/" + edit: true + envKey: BARK_SERVER_URL_PREFIX + labelEn: Bark Server URL Prefix + labelZh: Bark 服务器 URL 前缀 + label: + en: 'Bark Server URL Prefix' + zh: 'Bark 服务器 URL 前缀' + zh-Hant: 'Bark 伺服器 URL 前綴' + ja: 'Bark サーバー URL プレフィックス' + ko: 'Bark 서버 URL 접두사' + ru: 'Префикс URL сервера Bark' + ms: 'Awalan URL pelayan Bark' + pt-br: 'Prefixo de URL do servidor Bark' + required: true + type: text + - default: "/data" + disabled: true + envKey: BARK_SERVER_DATA_DIR + labelEn: Bark Server Data Directory + labelZh: Bark 服务器数据目录 + label: + en: 'Bark Server Data Directory' + zh: 'Bark 服务器数据目录' + zh-Hant: 'Bark 伺服器資料目錄' + ja: 'Bark サーバーデータディレクトリ' + ko: 'Bark 서버 데이터 디렉터리' + ru: 'Каталог данных сервера Bark' + ms: 'Direktori data pelayan Bark' + pt-br: 'Diretório de dados do servidor Bark' + required: true + type: text + - default: "" + edit: true + envKey: BARK_SERVER_DSN + labelEn: Bark Server DSN + labelZh: Bark 服务器 DSN + label: + en: 'Bark Server DSN' + zh: 'Bark 服务器 DSN' + zh-Hant: 'Bark 伺服器 DSN' + ja: 'Bark サーバー DSN' + ko: 'Bark 서버 DSN' + ru: 'DSN сервера Bark' + ms: 'DSN pelayan Bark' + pt-br: 'DSN do servidor Bark' + required: false + type: text + - default: "false" + edit: true + envKey: BARK_SERVER_SERVERLESS + labelEn: Bark Server Serverless + labelZh: Bark 服务器无服务器 + label: + en: 'Bark Server Serverless' + zh: 'Bark 服务器无服务器' + zh-Hant: 'Bark 伺服器無伺服器' + ja: 'Bark サーバー Serverless' + ko: 'Bark 서버 서버리스' + ru: 'Bark Serverless' + ms: 'Bark Serverless' + pt-br: 'Bark Serverless' + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "" + edit: true + envKey: BARK_SERVER_CERT + labelEn: Bark Server Certificate + labelZh: Bark 服务器证书 + label: + en: 'Bark Server Certificate' + zh: 'Bark 服务器证书' + zh-Hant: 'Bark 伺服器憑證' + ja: 'Bark サーバー証明書' + ko: 'Bark 서버 인증서' + ru: 'Сертификат сервера Bark' + ms: 'Sijil pelayan Bark' + pt-br: 'Certificado do servidor Bark' + required: false + type: text + - default: "" + edit: true + envKey: BARK_SERVER_KEY + labelEn: Bark Server Key + labelZh: Bark 服务器密钥 + label: + en: 'Bark Server Key' + zh: 'Bark 服务器密钥' + zh-Hant: 'Bark 伺服器金鑰' + ja: 'Bark サーバーキー' + ko: 'Bark 서버 키' + ru: 'Ключ сервера Bark' + ms: 'Kunci pelayan Bark' + pt-br: 'Chave do servidor Bark' + required: false + type: text + - default: "false" + edit: true + envKey: BARK_SERVER_CASE_SENSITIVE + labelEn: Bark Server Case Sensitive + labelZh: Bark 服务器区分大小写 + label: + en: 'Bark Server Case Sensitive' + zh: 'Bark 服务器区分大小写' + zh-Hant: 'Bark 伺服器區分大小寫' + ja: 'Bark サーバー大文字小文字を区別' + ko: 'Bark 서버 대/소문자 구분' + ru: 'Bark с учетом регистра' + ms: 'Bark peka huruf besar/kecil' + pt-br: 'Bark sensível a maiúsculas/minúsculas' + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "false" + edit: true + envKey: BARK_SERVER_STRICT_ROUTING + labelEn: Bark Server Strict Routing + labelZh: Bark 服务器严格路由 + label: + en: 'Bark Server Strict Routing' + zh: 'Bark 服务器严格路由' + zh-Hant: 'Bark 伺服器嚴格路由' + ja: 'Bark サーバー厳格ルーティング' + ko: 'Bark 서버 엄격 라우팅' + ru: 'Строгая маршрутизация Bark' + ms: 'Perutean ketat Bark' + pt-br: 'Roteamento estrito do Bark' + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "false" + edit: true + envKey: BARK_SERVER_REDUCE_MEMORY_USAGE + labelEn: Bark Server Reduce Memory Usage + labelZh: Bark 服务器减少内存使用 + label: + en: 'Bark Server Reduce Memory Usage' + zh: 'Bark 服务器减少内存使用' + zh-Hant: 'Bark 伺服器減少記憶體使用' + ja: 'Bark サーバーメモリ使用量を削減' + ko: 'Bark 서버 메모리 사용량 감소' + ru: 'Снижение использования памяти Bark' + ms: 'Kurangkan penggunaan memori Bark' + pt-br: 'Reduzir uso de memória do Bark' + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "" + edit: true + envKey: BARK_SERVER_BASIC_AUTH_USER + labelEn: Bark Server Basic Auth User + labelZh: Bark 服务器基本身份验证用户 + label: + en: 'Bark Server Basic Auth User' + zh: 'Bark 服务器基本身份验证用户' + zh-Hant: 'Bark 伺服器基本驗證使用者' + ja: 'Bark サーバー基本認証ユーザー' + ko: 'Bark 서버 기본 인증 사용자' + ru: 'Пользователь базовой аутентификации Bark' + ms: 'Pengguna pengesahan asas Bark' + pt-br: 'Usuário de autenticação básica do Bark' + required: false + type: text + - default: "" + edit: true + envKey: BARK_SERVER_BASIC_AUTH_PASSWORD + labelEn: Bark Server Basic Auth Password + labelZh: Bark 服务器基本身份验证密码 + label: + en: 'Bark Server Basic Auth Password' + zh: 'Bark 服务器基本身份验证密码' + zh-Hant: 'Bark 伺服器基本驗證密碼' + ja: 'Bark サーバー基本認証パスワード' + ko: 'Bark 서버 기본 인증 비밀번호' + ru: 'Пароль базовой аутентификации Bark' + ms: 'Kata laluan pengesahan asas Bark' + pt-br: 'Senha de autenticação básica do Bark' + required: false + type: password + - default: "" + edit: true + envKey: BARK_SERVER_PROXY_HEADER + labelEn: Bark Server Proxy Header + labelZh: Bark 服务器代理头 + label: + en: 'Bark Server Proxy Header' + zh: 'Bark 服务器代理头' + zh-Hant: 'Bark 伺服器代理標頭' + ja: 'Bark サーバー プロキシヘッダー' + ko: 'Bark 서버 프록시 헤더' + ru: 'Прокси-заголовок Bark' + ms: 'Pengepala proksi Bark' + pt-br: 'Cabeçalho de proxy do Bark' + required: false + type: text + - default: "262144" + edit: true + envKey: BARK_SERVER_CONCURRENCY + labelEn: Bark Server Concurrency + labelZh: Bark 服务器并发 + label: + en: 'Bark Server Concurrency' + zh: 'Bark 服务器并发' + zh-Hant: 'Bark 伺服器並發' + ja: 'Bark サーバー同時実行数' + ko: 'Bark 서버 동시성' + ru: 'Параллельность Bark' + ms: 'Kebersamaan Bark' + pt-br: 'Concorrência do Bark' + required: true + type: number + - default: "3s" + edit: true + envKey: BARK_SERVER_READ_TIMEOUT + labelEn: Bark Server Read Timeout + labelZh: Bark 服务器读取超时 + label: + en: 'Bark Server Read Timeout' + zh: 'Bark 服务器读取超时' + zh-Hant: 'Bark 伺服器讀取逾時' + ja: 'Bark サーバー読み取りタイムアウト' + ko: 'Bark 서버 읽기 시간 초과' + ru: 'Тайм-аут чтения Bark' + ms: 'Had masa baca Bark' + pt-br: 'Tempo limite de leitura do Bark' + required: true + type: text + - default: "3s" + edit: true + envKey: BARK_SERVER_WRITE_TIMEOUT + labelEn: Bark Server Write Timeout + labelZh: Bark 服务器写入超时 + label: + en: 'Bark Server Write Timeout' + zh: 'Bark 服务器写入超时' + zh-Hant: 'Bark 伺服器寫入逾時' + ja: 'Bark サーバー書き込みタイムアウト' + ko: 'Bark 서버 쓰기 시간 초과' + ru: 'Тайм-аут записи Bark' + ms: 'Had masa tulis Bark' + pt-br: 'Tempo limite de gravação do Bark' + required: true + type: text + - default: "10s" + edit: true + envKey: BARK_SERVER_IDLE_TIMEOUT + labelEn: Bark Server Idle Timeout + labelZh: Bark 服务器空闲超时 + label: + en: 'Bark Server Idle Timeout' + zh: 'Bark 服务器空闲超时' + zh-Hant: 'Bark 伺服器閒置逾時' + ja: 'Bark サーバーアイドルタイムアウト' + ko: 'Bark 서버 유휴 시간 초과' + ru: 'Тайм-аут простоя Bark' + ms: 'Had masa melahu Bark' + pt-br: 'Tempo limite de ociosidade do Bark' + required: true + type: text diff --git a/apps/bark/2.3.8/docker-compose.yml b/apps/bark/2.3.8/docker-compose.yml new file mode 100644 index 000000000..fa73fda2e --- /dev/null +++ b/apps/bark/2.3.8/docker-compose.yml @@ -0,0 +1,35 @@ +services: + bark: + image: "finab/bark-server:v2.3.9" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}" + volumes: + - ${DATA_PATH}:/data + environment: + - BARK_SERVER_ADDRESS=${BARK_SERVER_ADDRESS}:${PANEL_APP_PORT_HTTP} + - BARK_SERVER_URL_PREFIX=${BARK_SERVER_URL_PREFIX} + - BARK_SERVER_DATA_DIR=${BARK_SERVER_DATA_DIR} + - BARK_SERVER_DSN=${BARK_SERVER_DSN} + - BARK_SERVER_SERVERLESS=${BARK_SERVER_SERVERLESS} + - BARK_SERVER_CERT=${BARK_SERVER_CERT} + - BARK_SERVER_KEY=${BARK_SERVER_KEY} + - BARK_SERVER_CASE_SENSITIVE=${BARK_SERVER_CASE_SENSITIVE} + - BARK_SERVER_STRICT_ROUTING=${BARK_SERVER_STRICT_ROUTING} + - BARK_SERVER_REDUCE_MEMORY_USAGE=${BARK_SERVER_REDUCE_MEMORY_USAGE} + - BARK_SERVER_BASIC_AUTH_USER=${BARK_SERVER_BASIC_AUTH_USER} + - BARK_SERVER_BASIC_AUTH_PASSWORD=${BARK_SERVER_BASIC_AUTH_PASSWORD} + - BARK_SERVER_PROXY_HEADER=${BARK_SERVER_PROXY_HEADER} + - BARK_SERVER_CONCURRENCY=${BARK_SERVER_CONCURRENCY} + - BARK_SERVER_READ_TIMEOUT=${BARK_SERVER_READ_TIMEOUT} + - BARK_SERVER_WRITE_TIMEOUT=${BARK_SERVER_WRITE_TIMEOUT} + - BARK_SERVER_IDLE_TIMEOUT=${BARK_SERVER_IDLE_TIMEOUT} + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/bentopdf/2.8.8/.env.sample b/apps/bentopdf/2.8.10/.env.sample similarity index 100% rename from apps/bentopdf/2.8.8/.env.sample rename to apps/bentopdf/2.8.10/.env.sample diff --git a/apps/bentopdf/2.8.8/data.yml b/apps/bentopdf/2.8.10/data.yml similarity index 100% rename from apps/bentopdf/2.8.8/data.yml rename to apps/bentopdf/2.8.10/data.yml diff --git a/apps/bentopdf/2.8.8/docker-compose.yml b/apps/bentopdf/2.8.10/docker-compose.yml similarity index 82% rename from apps/bentopdf/2.8.8/docker-compose.yml rename to apps/bentopdf/2.8.10/docker-compose.yml index b57dcc162..62ae50b32 100644 --- a/apps/bentopdf/2.8.8/docker-compose.yml +++ b/apps/bentopdf/2.8.10/docker-compose.yml @@ -1,6 +1,6 @@ services: bentopdf: - image: "ghcr.io/alam00000/bentopdf-simple:2.8.8" + image: "ghcr.io/alam00000/bentopdf-simple:2.8.10" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/bentopdf/2.8.8/scripts/init.sh b/apps/bentopdf/2.8.10/scripts/init.sh similarity index 100% rename from apps/bentopdf/2.8.8/scripts/init.sh rename to apps/bentopdf/2.8.10/scripts/init.sh diff --git a/apps/copyparty/1.20.24/scripts/uninstall.sh b/apps/bentopdf/2.8.10/scripts/uninstall.sh old mode 100644 new mode 100755 similarity index 100% rename from apps/copyparty/1.20.24/scripts/uninstall.sh rename to apps/bentopdf/2.8.10/scripts/uninstall.sh diff --git a/apps/bentopdf/2.8.8/scripts/upgrade.sh b/apps/bentopdf/2.8.10/scripts/upgrade.sh similarity index 100% rename from apps/bentopdf/2.8.8/scripts/upgrade.sh rename to apps/bentopdf/2.8.10/scripts/upgrade.sh diff --git a/apps/bisq2-node/2.1.11.2/docker-compose.yml b/apps/bisq2-node/2.1.11.2/docker-compose.yml index 6652e08a8..7f900e957 100644 --- a/apps/bisq2-node/2.1.11.2/docker-compose.yml +++ b/apps/bisq2-node/2.1.11.2/docker-compose.yml @@ -1,6 +1,6 @@ services: server: - image: "ghcr.io/bisq-network/bisq2-api:2.1.12.1@sha256:79fb71d64d23863c8f48d484a9d84754dae40e12cb395bca348f17dcec46b297" + image: "ghcr.io/bisq-network/bisq2-api:2.1.13.0@sha256:caeebd10b6e232ea8567941e4c6594b4000b5771c9e53cef25cf9b9766d5570d" container_name: ${CONTAINER_NAME} restart: unless-stopped stop_grace_period: 1m @@ -38,7 +38,7 @@ services: createdBy: "Apps" web: - image: "ghcr.io/bisq-network/bisq2-api-web-ui:2.1.12.1@sha256:3819d97d98b12fc4ea7253c1da69ca201b85d500e36c992b8bb71173500036a5" + image: "ghcr.io/bisq-network/bisq2-api-web-ui:2.1.13.0@sha256:9f818767031aeef0ae6018dfd1c02dd0fad392207013e4a6b6a12f6161f6fd27" container_name: ${CONTAINER_NAME}-web restart: unless-stopped network_mode: "service:server" diff --git a/apps/blade-of-agony-linuxserver/3.1.20261008/docker-compose.yml b/apps/blade-of-agony-linuxserver/3.1.20261008/docker-compose.yml index a10d53504..388f55a55 100644 --- a/apps/blade-of-agony-linuxserver/3.1.20261008/docker-compose.yml +++ b/apps/blade-of-agony-linuxserver/3.1.20261008/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: blade-of-agony: - image: "linuxserver/blade-of-agony:3.1.20261008" + image: "linuxserver/blade-of-agony:3.1.20261015" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/.env.sample b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/.env.sample new file mode 100644 index 000000000..aef22eb21 --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41002 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/data.yml b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/data.yml new file mode 100644 index 000000000..60c196866 --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41002 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/docker-compose.yml b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/docker-compose.yml new file mode 100644 index 000000000..9a628b17a --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/docker-compose.yml @@ -0,0 +1,35 @@ +services: + blender-kasm: + image: "kasmweb/blender:1.19.0-rolling-weekly@sha256:798d98be65e7d3d1a663174b4d129983ad7124637e873c284e004685505c924a" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/init.sh b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/uninstall.sh b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/upgrade.sh b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/blender-kasm/1.19.0-rolling-weekly-digest-3b85963/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/blender-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/blender-kasm/1.19.0-rolling-weekly/docker-compose.yml index b047ff4e2..9a628b17a 100644 --- a/apps/blender-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/blender-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: blender-kasm: - image: "kasmweb/blender:1.19.0-rolling-weekly@sha256:c81407b3d67751fba307181294967a850ade0280faef0ebafaa60535fb5773ec" + image: "kasmweb/blender:1.19.0-rolling-weekly@sha256:798d98be65e7d3d1a663174b4d129983ad7124637e873c284e004685505c924a" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/.env.sample b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/.env.sample new file mode 100644 index 000000000..2356c0e71 --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41003 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/data.yml b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/data.yml new file mode 100644 index 000000000..3a254709b --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41003 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/docker-compose.yml b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/docker-compose.yml new file mode 100644 index 000000000..42cb0eba2 --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/docker-compose.yml @@ -0,0 +1,35 @@ +services: + brave-kasm: + image: "kasmweb/brave:1.19.0-rolling-weekly@sha256:7572468fa4752180aa800bc49b4c9aa3858a0a8de1c4dcbfafeb499e090bf7d1" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/init.sh b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/uninstall.sh b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/upgrade.sh b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/brave-kasm/1.19.0-rolling-weekly-digest-d209484/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/brave-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/brave-kasm/1.19.0-rolling-weekly/docker-compose.yml index b32d30d82..42cb0eba2 100644 --- a/apps/brave-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/brave-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: brave-kasm: - image: "kasmweb/brave:1.19.0-rolling-weekly@sha256:bff2fa3b2ff73ed0fc70a7f61661918090165fc528a68e7ffd509bbf05ae91b3" + image: "kasmweb/brave:1.19.0-rolling-weekly@sha256:7572468fa4752180aa800bc49b4c9aa3858a0a8de1c4dcbfafeb499e090bf7d1" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/brave-linuxserver/1.97.56/docker-compose.yml b/apps/brave-linuxserver/1.97.56/docker-compose.yml index 2a17701bb..fad9f3c2c 100644 --- a/apps/brave-linuxserver/1.97.56/docker-compose.yml +++ b/apps/brave-linuxserver/1.97.56/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: brave: - image: "linuxserver/brave:1.97.56" + image: "linuxserver/brave:1.97.59" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/bytebase/3.23.1/docker-compose.yml b/apps/bytebase/3.23.1/docker-compose.yml index f073e8376..2a28f4ba5 100644 --- a/apps/bytebase/3.23.1/docker-compose.yml +++ b/apps/bytebase/3.23.1/docker-compose.yml @@ -9,6 +9,8 @@ services: volumes: - "${DATA_PATH}:/var/opt/bytebase" init: true + environment: + PG_URL: "postgresql://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${PANEL_DB_NAME}" healthcheck: test: ["CMD", "curl", "--fail", "http://localhost:5678/healthz", "||", "exit", "1"] interval: 5m @@ -18,9 +20,7 @@ services: - "/var/opt/bytebase" - "--port" - "8080" - - "--pg" - - "postgresql://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${PANEL_DB_NAME}" - image: bytebase/bytebase:3.23.1 + image: bytebase/bytebase:3.24.0 labels: createdBy: "Apps" diff --git a/apps/bytebase/3.24.0/.env.sample b/apps/bytebase/3.24.0/.env.sample new file mode 100644 index 000000000..e1c1621c3 --- /dev/null +++ b/apps/bytebase/3.24.0/.env.sample @@ -0,0 +1,8 @@ +CONTAINER_NAME="bytebase" +PANEL_APP_PORT_HTTP="40171" +PANEL_DB_NAME="bytebase" +POSTGRES_HOST="postgres" +PANEL_DB_USER_PASSWORD="password_xxJdZD" +POSTGRES_PORT="5432" +PANEL_DB_USER="postgres" +DATA_PATH="./data" diff --git a/apps/bytebase/3.24.0/data.yml b/apps/bytebase/3.24.0/data.yml new file mode 100644 index 000000000..8d4281596 --- /dev/null +++ b/apps/bytebase/3.24.0/data.yml @@ -0,0 +1,120 @@ +additionalProperties: + formFields: + - default: "" + edit: true + envKey: POSTGRES_HOST + key: postgresql + labelEn: Postgres database Service + labelZh: Postgres数据库服务 + label: + en: 'Postgres database Service' + zh: 'Postgres数据库服务' + zh-Hant: 'Postgres 資料庫服務' + ja: 'Postgres データベースサービス' + ko: 'Postgres 데이터베이스 서비스' + ru: 'Сервис базы данных Postgres' + ms: 'Perkhidmatan Pangkalan Data Postgres' + pt-br: 'Serviço de Banco de Dados Postgres' + required: true + type: service + - default: "5432" + edit: true + envKey: POSTGRES_PORT + labelEn: Postgres database Service Port + labelZh: Postgres数据库服务端口 + label: + en: 'Postgres database Service Port' + zh: 'Postgres数据库服务端口' + zh-Hant: 'Postgres 資料庫服務埠' + ja: 'Postgres データベースサービスポート' + ko: 'Postgres 데이터베이스 서비스 포트' + ru: 'Порт сервиса базы данных Postgres' + ms: 'Port Perkhidmatan Pangkalan Data Postgres' + pt-br: 'Porta do Serviço de Banco de Dados Postgres' + required: true + rule: paramPort + type: number + - default: bytebase + edit: true + envKey: PANEL_DB_NAME + labelEn: Database + labelZh: 数据库名 + label: + en: 'Database' + zh: '数据库名' + zh-Hant: '數據庫名' + ja: 'データベース' + ko: '데이터베이스' + ru: 'База данных' + ms: 'Pangkalan Data' + pt-br: 'Banco de Dados' + required: true + rule: paramCommon + type: text + - default: "" + edit: true + envKey: PANEL_DB_USER + labelEn: User + labelZh: 数据库用户 + label: + en: 'User' + zh: '数据库用户' + zh-Hant: '數據庫用戶' + ja: 'ユーザー' + ko: '사용자' + ru: 'Пользователь' + ms: 'Pengguna' + pt-br: 'Usuário' + required: true + rule: paramCommon + type: text + - default: "" + edit: true + envKey: PANEL_DB_USER_PASSWORD + labelEn: Password + labelZh: 数据库用户密码 + label: + en: 'Password' + zh: '数据库用户密码' + zh-Hant: '數據庫用戶密碼' + ja: 'パスワード' + ko: '비밀번호' + ru: 'Пароль' + ms: 'Kata laluan' + pt-br: 'Senha' + required: true + rule: paramCommon + type: password + - default: 40171 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: 'Port' + zh: '端口' + zh-Hant: '埠' + ja: 'ポート' + ko: '포트' + ru: 'Порт' + ms: 'Port' + pt-br: 'Porta' + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: DATA_PATH + labelEn: Data storage folder + labelZh: 数据存放文件夹 + label: + en: 'Data storage folder' + zh: '数据存放文件夹' + zh-Hant: '資料存放資料夾' + ja: 'データ保存フォルダー' + ko: '데이터 저장 폴더' + ru: 'Папка хранения данных' + ms: 'Folder storan data' + pt-br: 'Pasta de armazenamento de dados' + required: true + type: text diff --git a/apps/bytebase/3.24.0/docker-compose.yml b/apps/bytebase/3.24.0/docker-compose.yml new file mode 100644 index 000000000..f821c30fd --- /dev/null +++ b/apps/bytebase/3.24.0/docker-compose.yml @@ -0,0 +1,29 @@ +services: + bytebase: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - "${DATA_PATH}:/var/opt/bytebase" + init: true + environment: + PG_URL: "postgresql://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${PANEL_DB_NAME}" + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:5678/healthz", "||", "exit", "1"] + interval: 5m + timeout: 60s + command: + - "--data" + - "/var/opt/bytebase" + - "--port" + - "8080" + image: bytebase/bytebase:3.24.0 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/calibre-linuxserver/9.15.0/docker-compose.yml b/apps/calibre-linuxserver/9.15.0/docker-compose.yml index 6a7635f83..771f200c6 100644 --- a/apps/calibre-linuxserver/9.15.0/docker-compose.yml +++ b/apps/calibre-linuxserver/9.15.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: calibre: - image: "linuxserver/calibre:9.15.0" + image: "linuxserver/calibre:9.16.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/centrifugo/6.9.7/docker-compose.yml b/apps/centrifugo/6.9.7/docker-compose.yml index ee1dab80e..e1615bb5e 100644 --- a/apps/centrifugo/6.9.7/docker-compose.yml +++ b/apps/centrifugo/6.9.7/docker-compose.yml @@ -1,6 +1,6 @@ services: centrifugo: - image: "centrifugo/centrifugo:v6.9.7" + image: "centrifugo/centrifugo:v6.10.1" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/certd/1.45.0/.env.sample b/apps/certd/1.45.3/.env.sample similarity index 100% rename from apps/certd/1.45.0/.env.sample rename to apps/certd/1.45.3/.env.sample diff --git a/apps/certd/1.45.0/data.yml b/apps/certd/1.45.3/data.yml similarity index 100% rename from apps/certd/1.45.0/data.yml rename to apps/certd/1.45.3/data.yml diff --git a/apps/certd/1.45.0/docker-compose.yml b/apps/certd/1.45.3/docker-compose.yml similarity index 94% rename from apps/certd/1.45.0/docker-compose.yml rename to apps/certd/1.45.3/docker-compose.yml index d122be3b9..a91766090 100644 --- a/apps/certd/1.45.0/docker-compose.yml +++ b/apps/certd/1.45.3/docker-compose.yml @@ -1,6 +1,6 @@ services: certd: - image: "greper/certd:1.45.0" + image: "greper/certd:1.45.5" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/chatgpt-on-wechat/2.2.0/docker-compose.yml b/apps/chatgpt-on-wechat/2.2.0/docker-compose.yml index f556d79d7..b8fb50437 100644 --- a/apps/chatgpt-on-wechat/2.2.0/docker-compose.yml +++ b/apps/chatgpt-on-wechat/2.2.0/docker-compose.yml @@ -1,6 +1,6 @@ services: chatgpt-on-wechat: - image: "zhayujie/chatgpt-on-wechat:2.2.0" + image: "zhayujie/chatgpt-on-wechat:2.2.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/chatwoot/latest/docker-compose.yml b/apps/chatwoot/latest/docker-compose.yml index 9dbae2af0..628ab6eeb 100644 --- a/apps/chatwoot/latest/docker-compose.yml +++ b/apps/chatwoot/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: chatwoot: - image: "chatwoot/chatwoot:v4.18.0-ce" + image: "chatwoot/chatwoot:v4.19.0-ce" container_name: ${CONTAINER_NAME} restart: always depends_on: @@ -49,7 +49,7 @@ services: createdBy: "Apps" chatwoot-sidekiq: - image: "chatwoot/chatwoot:v4.18.0-ce" + image: "chatwoot/chatwoot:v4.19.0-ce" container_name: ${CONTAINER_NAME}-sidekiq restart: always depends_on: diff --git a/apps/chrome-linuxserver/155.0.8059/docker-compose.yml b/apps/chrome-linuxserver/155.0.8059/docker-compose.yml index 5c31ce758..62564acbb 100644 --- a/apps/chrome-linuxserver/155.0.8059/docker-compose.yml +++ b/apps/chrome-linuxserver/155.0.8059/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: chrome: - image: "linuxserver/chrome:155.0.8059" + image: "linuxserver/chrome:156.0.8078" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/.env.sample b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/.env.sample new file mode 100644 index 000000000..67b377a18 --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41004 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/data.yml b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/data.yml new file mode 100644 index 000000000..fabe03407 --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41004 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/docker-compose.yml b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/docker-compose.yml new file mode 100644 index 000000000..02072a0b8 --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/docker-compose.yml @@ -0,0 +1,35 @@ +services: + chromium-kasm: + image: "kasmweb/chromium:1.19.0-rolling-weekly@sha256:2f3826c2714850f2fd58523c384f7fa6811492f087e29131a328b29dcc2a244e" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/init.sh b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/uninstall.sh b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/upgrade.sh b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/chromium-kasm/1.19.0-rolling-weekly-digest-3afc823/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/chromium-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/chromium-kasm/1.19.0-rolling-weekly/docker-compose.yml index c07cdb8ad..02072a0b8 100644 --- a/apps/chromium-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/chromium-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: chromium-kasm: - image: "kasmweb/chromium:1.19.0-rolling-weekly@sha256:f76e81f05197ec4684e4f3062a5af148bd50816cfee8f47b9abfe55bd044a399" + image: "kasmweb/chromium:1.19.0-rolling-weekly@sha256:2f3826c2714850f2fd58523c384f7fa6811492f087e29131a328b29dcc2a244e" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/claper/3.1.0/docker-compose.yml b/apps/claper/3.1.0/docker-compose.yml index 6c9fb5cc3..4ab450826 100644 --- a/apps/claper/3.1.0/docker-compose.yml +++ b/apps/claper/3.1.0/docker-compose.yml @@ -1,6 +1,6 @@ services: claper: - image: "ghcr.io/claperco/claper:3.1.0@sha256:26afc860d5f6518a1445a2a1099954f5853ffcad45e73d167cd92be29d4299a7" + image: "ghcr.io/claperco/claper:3.1.2@sha256:7df61dd04ffecfed17b7d7004e12504e52e340a33334a4ef9f34f4bf88dbb7f9" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/cloudbeaver/26.2.2/docker-compose.yml b/apps/cloudbeaver/26.2.2/docker-compose.yml index cb4a59880..73ea7669b 100644 --- a/apps/cloudbeaver/26.2.2/docker-compose.yml +++ b/apps/cloudbeaver/26.2.2/docker-compose.yml @@ -9,7 +9,7 @@ services: volumes: - "${DATA_PATH}:/opt/cloudbeaver/workspace" tty: true - image: dbeaver/cloudbeaver:26.2.2 + image: dbeaver/cloudbeaver:26.2.3 labels: createdBy: "Apps" diff --git a/apps/codex2api/3.0.7/docker-compose.yml b/apps/codex2api/3.0.7/docker-compose.yml index 0f9a4996a..dd8b2928c 100644 --- a/apps/codex2api/3.0.7/docker-compose.yml +++ b/apps/codex2api/3.0.7/docker-compose.yml @@ -1,6 +1,6 @@ services: codex2api: - image: "ghcr.io/james-6-23/codex2api:3.0.7@sha256:18020db0336b6d2cb082f8fe9190cf6cad3ba3f5c020630e0bfc6c7cd8f3716d" + image: "ghcr.io/james-6-23/codex2api:3.0.8@sha256:7a8bb243071abb3718c825c091fcca5eeacda8e4b697e4116ca36240a1e4a49a" container_name: ${CONTAINER_NAME} restart: always security_opt: diff --git a/apps/commafeed/7.3.2-h2/docker-compose.yml b/apps/commafeed/7.3.2-h2/docker-compose.yml index 2e62d3a79..b24877d3d 100644 --- a/apps/commafeed/7.3.2-h2/docker-compose.yml +++ b/apps/commafeed/7.3.2-h2/docker-compose.yml @@ -1,6 +1,6 @@ services: commafeed: - image: athou/commafeed:7.3.2-h2 + image: athou/commafeed:7.4.0-h2 container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/copyparty/1.20.24/.env.sample b/apps/copyparty/1.20.25/.env.sample similarity index 100% rename from apps/copyparty/1.20.24/.env.sample rename to apps/copyparty/1.20.25/.env.sample diff --git a/apps/copyparty/1.20.24/data.yml b/apps/copyparty/1.20.25/data.yml similarity index 100% rename from apps/copyparty/1.20.24/data.yml rename to apps/copyparty/1.20.25/data.yml diff --git a/apps/copyparty/1.20.24/docker-compose.yml b/apps/copyparty/1.20.25/docker-compose.yml similarity index 94% rename from apps/copyparty/1.20.24/docker-compose.yml rename to apps/copyparty/1.20.25/docker-compose.yml index feada5c9e..4d6f676d0 100644 --- a/apps/copyparty/1.20.24/docker-compose.yml +++ b/apps/copyparty/1.20.25/docker-compose.yml @@ -1,6 +1,6 @@ services: copyparty: - image: "ghcr.io/9001/copyparty-ac:1.20.24" + image: "ghcr.io/9001/copyparty-ac:1.20.26" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/copyparty/1.20.24/scripts/init.sh b/apps/copyparty/1.20.25/scripts/init.sh similarity index 100% rename from apps/copyparty/1.20.24/scripts/init.sh rename to apps/copyparty/1.20.25/scripts/init.sh diff --git a/apps/modrinth-linuxserver/0.21.9/scripts/uninstall.sh b/apps/copyparty/1.20.25/scripts/uninstall.sh old mode 100755 new mode 100644 similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/scripts/uninstall.sh rename to apps/copyparty/1.20.25/scripts/uninstall.sh diff --git a/apps/copyparty/1.20.24/scripts/upgrade.sh b/apps/copyparty/1.20.25/scripts/upgrade.sh similarity index 100% rename from apps/copyparty/1.20.24/scripts/upgrade.sh rename to apps/copyparty/1.20.25/scripts/upgrade.sh diff --git a/apps/cpa-manager-plus/1.14.4/docker-compose.yml b/apps/cpa-manager-plus/1.14.4/docker-compose.yml index 1f853eac0..d9404d577 100644 --- a/apps/cpa-manager-plus/1.14.4/docker-compose.yml +++ b/apps/cpa-manager-plus/1.14.4/docker-compose.yml @@ -1,6 +1,6 @@ services: cpa-manager-plus: - image: "seakee/cpa-manager-plus:v1.14.4" + image: "seakee/cpa-manager-plus:v1.14.9" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:18317" diff --git a/apps/cpa-manager-plus/1.14.5/.env.sample b/apps/cpa-manager-plus/1.14.5/.env.sample new file mode 100644 index 000000000..056aaf63e --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_HTTP=18317 +APP_DATA_DIR=./data +CPA_MANAGER_ADMIN_KEY= +USAGE_COLLECTOR_MODE=auto +USAGE_CORS_ORIGINS=* +CONTAINER_NAME=cpa-manager-plus diff --git a/apps/cpa-manager-plus/1.14.5/data.yml b/apps/cpa-manager-plus/1.14.5/data.yml new file mode 100644 index 000000000..abd1ff895 --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/data.yml @@ -0,0 +1,92 @@ +additionalProperties: + formFields: + - default: 18317 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 访问端口 + label: + en: Web Port + zh: Web 访问端口 + zh-Hant: Web 存取埠 + ja: Web ポート + ko: Web 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text + - default: '' + edit: true + envKey: CPA_MANAGER_ADMIN_KEY + labelEn: Admin Key + labelZh: 管理员密钥 + label: + en: Admin Key + zh: 管理员密钥 + zh-Hant: 管理員密鑰 + ja: 管理者キー + ko: 관리자 키 + ru: Ключ администратора + ms: Kunci Pentadbir + pt-br: Chave de Admin + required: false + type: password + - default: auto + edit: true + envKey: USAGE_COLLECTOR_MODE + labelEn: Collector Mode + labelZh: 采集模式 + label: + en: Collector Mode + zh: 采集模式 + zh-Hant: 採集模式 + ja: 収集モード + ko: 수집 모드 + ru: Режим сбора + ms: Mod Pengumpulan + pt-br: Modo de Coleta + required: true + type: select + values: + - label: auto + value: auto + - label: subscribe + value: subscribe + - label: http + value: http + - label: resp + value: resp + - default: '*' + edit: true + envKey: USAGE_CORS_ORIGINS + labelEn: CORS Origins + labelZh: CORS 来源 + label: + en: CORS Origins + zh: CORS 来源 + zh-Hant: CORS 來源 + ja: CORS オリジン + ko: CORS Origin + ru: Источники CORS + ms: Origin CORS + pt-br: Origens CORS + required: false + type: text diff --git a/apps/libredb-studio/0.16.1/scripts/.gitkeep b/apps/cpa-manager-plus/1.14.5/data/.gitkeep similarity index 100% rename from apps/libredb-studio/0.16.1/scripts/.gitkeep rename to apps/cpa-manager-plus/1.14.5/data/.gitkeep diff --git a/apps/cpa-manager-plus/1.14.5/docker-compose.yml b/apps/cpa-manager-plus/1.14.5/docker-compose.yml new file mode 100644 index 000000000..d9404d577 --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/docker-compose.yml @@ -0,0 +1,38 @@ +services: + cpa-manager-plus: + image: "seakee/cpa-manager-plus:v1.14.9" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTP}:18317" + environment: + - HTTP_ADDR=0.0.0.0:18317 + - USAGE_DB_PATH=/data/usage.sqlite + - CPA_MANAGER_DATA_KEY_PATH=/data/data.key + - CPA_MANAGER_ADMIN_KEY=${CPA_MANAGER_ADMIN_KEY:-} + - USAGE_COLLECTOR_MODE=${USAGE_COLLECTOR_MODE} + - USAGE_RESP_QUEUE=usage + - USAGE_RESP_POP_SIDE=right + - USAGE_BATCH_SIZE=100 + - USAGE_POLL_INTERVAL_MS=500 + - USAGE_QUERY_LIMIT=50000 + - USAGE_CORS_ORIGINS=${USAGE_CORS_ORIGINS} + volumes: + - "${APP_DATA_DIR}:/data" + healthcheck: + test: + - CMD + - wget + - -qO- + - http://127.0.0.1:18317/health + interval: 10s + timeout: 3s + retries: 3 + labels: + createdBy: Apps + networks: + - 1panel-network + restart: unless-stopped + +networks: + 1panel-network: + external: true diff --git a/apps/modrinth-linuxserver/0.21.9/data/.gitkeep b/apps/cpa-manager-plus/1.14.5/scripts/.gitkeep similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/data/.gitkeep rename to apps/cpa-manager-plus/1.14.5/scripts/.gitkeep diff --git a/apps/cpa-manager-plus/1.14.5/scripts/init.sh b/apps/cpa-manager-plus/1.14.5/scripts/init.sh new file mode 100755 index 000000000..8e10f4a59 --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/scripts/init.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +DATA_DIR="${APP_DATA_DIR:-$(read_env_value APP_DATA_DIR)}" +DATA_DIR="${DATA_DIR:-./data}" +if [[ "$DATA_DIR" != /* ]]; then + DATA_DIR="$ROOT_DIR/${DATA_DIR#./}" +fi +mkdir -p "$DATA_DIR" diff --git a/apps/cpa-manager-plus/1.14.5/scripts/uninstall.sh b/apps/cpa-manager-plus/1.14.5/scripts/uninstall.sh new file mode 100755 index 000000000..024242bb3 --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -euo pipefail + +docker-compose down --volumes diff --git a/apps/cpa-manager-plus/1.14.5/scripts/upgrade.sh b/apps/cpa-manager-plus/1.14.5/scripts/upgrade.sh new file mode 100755 index 000000000..8e10f4a59 --- /dev/null +++ b/apps/cpa-manager-plus/1.14.5/scripts/upgrade.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +DATA_DIR="${APP_DATA_DIR:-$(read_env_value APP_DATA_DIR)}" +DATA_DIR="${DATA_DIR:-./data}" +if [[ "$DATA_DIR" != /* ]]; then + DATA_DIR="$ROOT_DIR/${DATA_DIR#./}" +fi +mkdir -p "$DATA_DIR" diff --git a/apps/cpa-usage-keeper/1.15.10/.env.sample b/apps/cpa-usage-keeper/1.15.11/.env.sample similarity index 100% rename from apps/cpa-usage-keeper/1.15.10/.env.sample rename to apps/cpa-usage-keeper/1.15.11/.env.sample diff --git a/apps/cpa-usage-keeper/1.15.10/data.yml b/apps/cpa-usage-keeper/1.15.11/data.yml similarity index 100% rename from apps/cpa-usage-keeper/1.15.10/data.yml rename to apps/cpa-usage-keeper/1.15.11/data.yml diff --git a/apps/cpa-usage-keeper/1.15.10/docker-compose.yml b/apps/cpa-usage-keeper/1.15.11/docker-compose.yml similarity index 96% rename from apps/cpa-usage-keeper/1.15.10/docker-compose.yml rename to apps/cpa-usage-keeper/1.15.11/docker-compose.yml index 349d7be55..3e64954b9 100644 --- a/apps/cpa-usage-keeper/1.15.10/docker-compose.yml +++ b/apps/cpa-usage-keeper/1.15.11/docker-compose.yml @@ -1,6 +1,6 @@ services: cpa-usage-keeper: - image: ghcr.io/willxup/cpa-usage-keeper:v1.15.10 + image: ghcr.io/willxup/cpa-usage-keeper:v2.0.0 container_name: ${CONTAINER_NAME} ports: - ${PANEL_APP_PORT_HTTP}:8080 diff --git a/apps/dagu/2.18.2/docker-compose.yml b/apps/dagu/2.18.2/docker-compose.yml index a513ff9b5..ace4bfff2 100644 --- a/apps/dagu/2.18.2/docker-compose.yml +++ b/apps/dagu/2.18.2/docker-compose.yml @@ -1,6 +1,6 @@ services: dagu: - image: "ghcr.io/dagucloud/dagu:2.18.2" + image: "ghcr.io/dagucloud/dagu:2.22.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/daily-stock-analysis/3.31.0/docker-compose.yml b/apps/daily-stock-analysis/3.31.0/docker-compose.yml index e559481cb..1bfa84806 100644 --- a/apps/daily-stock-analysis/3.31.0/docker-compose.yml +++ b/apps/daily-stock-analysis/3.31.0/docker-compose.yml @@ -1,6 +1,6 @@ services: daily-stock-analysis: - image: "zhulinsen/daily_stock_analysis:v3.31.0" + image: "zhulinsen/daily_stock_analysis:v3.33.0" container_name: ${CONTAINER_NAME} labels: createdBy: "Apps" diff --git a/apps/dailytxt/2.6.3/docker-compose.yml b/apps/dailytxt/2.6.3/docker-compose.yml index cbf14dd6e..33b65735b 100644 --- a/apps/dailytxt/2.6.3/docker-compose.yml +++ b/apps/dailytxt/2.6.3/docker-compose.yml @@ -1,6 +1,6 @@ services: dailytxt: - image: "phitux/dailytxt:2.6.3" + image: "phitux/dailytxt:2.6.4" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/darktable-linuxserver/25.6.1/docker-compose.yml b/apps/darktable-linuxserver/25.6.1/docker-compose.yml index 13305b3fe..a6ee6c3a1 100644 --- a/apps/darktable-linuxserver/25.6.1/docker-compose.yml +++ b/apps/darktable-linuxserver/25.6.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: darktable: - image: "linuxserver/darktable:25.6.1" + image: "linuxserver/darktable:25.6.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/dbx/0.6.16/docker-compose.yml b/apps/dbx/0.6.16/docker-compose.yml index d781c66d2..e15edabc0 100644 --- a/apps/dbx/0.6.16/docker-compose.yml +++ b/apps/dbx/0.6.16/docker-compose.yml @@ -1,6 +1,6 @@ services: dbx: - image: "t8y2/dbx@sha256:115593a8b1df524a148a3e45d02f2c3e04c0b43a0bfee0a7dab589f6aa9d1f21" + image: "t8y2/dbx@sha256:5c292e10ac6faea7bd4f368cbec28b20ae2f122f94aeeb88d660531dbd39e4b3" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:4224" diff --git a/apps/dbx/0.6.38/docker-compose.yml b/apps/dbx/0.6.38/docker-compose.yml index 7bd93ad3e..a32b74bd9 100644 --- a/apps/dbx/0.6.38/docker-compose.yml +++ b/apps/dbx/0.6.38/docker-compose.yml @@ -1,6 +1,6 @@ services: dbx: - image: "t8y2/dbx:0.6.38@sha256:d1b03b7e3e232ca9c67c524b53d56e9f240d68e179b1dca9cdd69fed7d51ed8f" + image: "t8y2/dbx:0.6.51@sha256:5c292e10ac6faea7bd4f368cbec28b20ae2f122f94aeeb88d660531dbd39e4b3" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:4224" diff --git a/apps/dbx/0.6.47/.env.sample b/apps/dbx/0.6.47/.env.sample new file mode 100644 index 000000000..e12b20094 --- /dev/null +++ b/apps/dbx/0.6.47/.env.sample @@ -0,0 +1,3 @@ +CONTAINER_NAME=dbx-compose-check +PANEL_APP_PORT_HTTP=4224 +PANEL_DB_PASSWORD=dbx_password diff --git a/apps/dbx/0.6.47/data.yml b/apps/dbx/0.6.47/data.yml new file mode 100644 index 000000000..522f4c7ab --- /dev/null +++ b/apps/dbx/0.6.47/data.yml @@ -0,0 +1,65 @@ +additionalProperties: + formFields: + - default: 4224 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelZh: 端口 + labelEn: Port + label: + zh: 端口 + en: Port + zh-hant: 連接埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + tr: Bağlantı noktası + es-es: Puerto + fa: درگاه + lo: ພອດ + required: true + rule: paramPort + type: number + - default: dbx_password + edit: true + envKey: PANEL_DB_PASSWORD + labelZh: 访问密码 + labelEn: Access Password + label: + zh: 访问密码 + en: Access Password + zh-hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de acesso + tr: Erişim parolası + es-es: Contraseña de acceso + fa: گذرواژه دسترسی + lo: ລະຫັດຜ່ານເຂົ້າໃຊ້ງານ + required: true + rule: paramComplexity + type: password + random: true + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + tr: Saat dilimi + es-es: Zona horaria + fa: منطقه زمانی + lo: ເຂດເວລາ + required: true + type: text diff --git a/apps/dbx/0.6.47/docker-compose.yml b/apps/dbx/0.6.47/docker-compose.yml new file mode 100644 index 000000000..a32b74bd9 --- /dev/null +++ b/apps/dbx/0.6.47/docker-compose.yml @@ -0,0 +1,19 @@ +services: + dbx: + image: "t8y2/dbx:0.6.51@sha256:5c292e10ac6faea7bd4f368cbec28b20ae2f122f94aeeb88d660531dbd39e4b3" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTP}:4224" + environment: + - DBX_PASSWORD=${PANEL_DB_PASSWORD} + volumes: + - ./data:/app/data + restart: unless-stopped + networks: + - 1panel-network + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/dbx/0.6.47/scripts/init.sh b/apps/dbx/0.6.47/scripts/init.sh new file mode 100755 index 000000000..2de7e6c39 --- /dev/null +++ b/apps/dbx/0.6.47/scripts/init.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +mkdir -p ./data diff --git a/apps/socket-proxy-linuxserver/3.4.6/scripts/uninstall.sh b/apps/dbx/0.6.47/scripts/uninstall.sh similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/scripts/uninstall.sh rename to apps/dbx/0.6.47/scripts/uninstall.sh diff --git a/apps/docker-hub-proxy/4.0.0/.env.sample b/apps/docker-hub-proxy/4.0.0/.env.sample new file mode 100644 index 000000000..9607e5b01 --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/.env.sample @@ -0,0 +1,11 @@ +PANEL_APP_PORT_HTTP=8000 +PANEL_APP_PORT_HTTPS=8443 +APP_DATA_DIR=./data +PROXY_DOMAIN=localhost +PROXY_TIMEOUT=10.0 +ADMIN_USER= +ADMIN_PASS= +IP_WHITELIST= +IMAGE_WHITELIST_REGEX= +IMAGE_BLACKLIST_REGEX= +CONTAINER_NAME=docker-hub-proxy diff --git a/apps/docker-hub-proxy/4.0.0/data.yml b/apps/docker-hub-proxy/4.0.0/data.yml new file mode 100644 index 000000000..03f58915d --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/data.yml @@ -0,0 +1,230 @@ +additionalProperties: + description: + en: Docker Hub image proxy and acceleration service. + zh: Docker Hub 镜像代理与加速服务。 + zh-Hant: Docker Hub 映像代理與加速服務。 + ja: Docker Hub イメージのプロキシおよび高速化サービス。 + ko: Docker Hub 이미지 프록시 및 가속 서비스입니다. + ru: Сервис проксирования и ускорения образов Docker Hub. + ms: Perkhidmatan proksi dan pecutan imej Docker Hub. + pt-br: Serviço de proxy e aceleração de imagens do Docker Hub. + tr: Docker Hub imajları için proxy ve hızlandırma hizmeti. + es-es: Servicio de proxy y aceleración de imágenes de Docker Hub. + fa: سرویس پراکسی و شتاب‌دهی تصویرهای Docker Hub. + lo: ບໍລິການພຣັອກຊີແລະເລັ່ງຄວາມໄວຮູບພາບ Docker Hub. + formFields: + - default: 8000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 访问端口 + label: + en: Web Port + zh: Web 访问端口 + zh-Hant: Web 存取埠 + ja: Web ポート + ko: Web 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + tr: Web Portu + es-es: Puerto web + fa: درگاه وب + lo: ພອດເວັບ + required: true + rule: paramPort + type: number + - default: 8443 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: Registry Proxy HTTPS Port + labelZh: 镜像代理 HTTPS 端口 + label: + en: Registry Proxy HTTPS Port + zh: 镜像代理 HTTPS 端口 + zh-Hant: 映像代理 HTTPS 連接埠 + ja: レジストリプロキシ HTTPS ポート + ko: 레지스트리 프록시 HTTPS 포트 + ru: HTTPS-порт прокси реестра + ms: Port HTTPS Proksi Registry + pt-br: Porta HTTPS do Proxy de Registro + tr: Kayıt Defteri Proxy HTTPS Portu + es-es: Puerto HTTPS del proxy del registro + fa: درگاه HTTPS پراکسی رجیستری + lo: ພອດ HTTPS ຂອງພຣັອກຊີ Registry + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + tr: Veri Dizini + es-es: Directorio de datos + fa: مسیر داده‌ها + lo: ໄດເຣັກທໍຣີຂໍ້ມູນ + required: true + type: text + - default: localhost + edit: true + envKey: PROXY_DOMAIN + labelEn: Registry Proxy Domain (certificate name) + labelZh: 镜像代理域名(证书名称) + label: + en: Registry Proxy Domain (certificate name) + zh: 镜像代理域名(证书名称) + zh-Hant: 映像代理網域(憑證名稱) + ja: レジストリプロキシドメイン(証明書名) + ko: 레지스트리 프록시 도메인(인증서 이름) + ru: Домен прокси реестра (имя сертификата) + ms: Domain Proksi Registry (nama sijil) + pt-br: Domínio do Proxy de Registro (nome do certificado) + tr: Kayıt Defteri Proxy Etki Alanı (sertifika adı) + es-es: Dominio del proxy del registro (nombre del certificado) + fa: دامنه پراکسی رجیستری (نام گواهی) + lo: ໂດເມນພຣັອກຊີ Registry (ຊື່ໃບຮັບຮອງ) + description: + en: A self-signed certificate is generated under the app data directory. Docker clients must trust it. If you change this domain later, replace both files under certs/. + zh: 应用会在数据目录生成自签名证书,Docker 客户端需信任该证书。以后更改域名时,请同时替换 certs/ 下的证书和密钥。 + zh-Hant: 應用會在資料目錄產生自簽名憑證,Docker 用戶端必須信任該憑證。之後若變更網域,請同時替換 certs/ 下的憑證與金鑰。 + ja: アプリのデータディレクトリに自己署名証明書が生成されます。Docker クライアントでこの証明書を信頼する必要があります。後でドメインを変更する場合は、certs/ 内の証明書と鍵を両方置き換えてください。 + ko: 앱 데이터 디렉터리에 자체 서명 인증서가 생성됩니다. Docker 클라이언트가 이 인증서를 신뢰해야 합니다. 나중에 도메인을 변경하면 certs/의 인증서와 키를 모두 교체하세요. + ru: В каталоге данных приложения создаётся самоподписанный сертификат. Клиенты Docker должны ему доверять. При последующем изменении домена замените оба файла в каталоге certs/. + ms: Sijil ditandatangani sendiri dijana dalam direktori data aplikasi. Klien Docker perlu mempercayai sijil ini. Jika domain diubah kemudian, gantikan kedua-dua fail dalam certs/. + pt-br: Um certificado autoassinado é gerado no diretório de dados do app. Os clientes Docker precisam confiar nele. Se você alterar o domínio depois, substitua os dois arquivos em certs/. + tr: Uygulama veri dizininde kendinden imzalı bir sertifika oluşturulur. Docker istemcileri bu sertifikaya güvenmelidir. Etki alanını sonradan değiştirirseniz certs/ altındaki iki dosyayı da değiştirin. + es-es: Se genera un certificado autofirmado en el directorio de datos de la aplicación. Los clientes Docker deben confiar en él. Si cambia el dominio más adelante, sustituya los dos archivos de certs/. + fa: یک گواهی خودامضا در مسیر داده‌های برنامه ساخته می‌شود. کلاینت‌های Docker باید به این گواهی اعتماد کنند. اگر بعداً دامنه را تغییر دادید، هر دو فایل داخل certs/ را جایگزین کنید. + lo: ໃບຮັບຮອງແບບເຊັນຕົນເອງຈະຖືກສ້າງໃນໄດເຣັກທໍຣີຂໍ້ມູນຂອງແອັບ. ລູກຂ່າຍ Docker ຕ້ອງໄວ້ໃຈໃບຮັບຮອງນີ້. ຖ້າປ່ຽນໂດເມນພາຍຫຼັງ ໃຫ້ປ່ຽນທັງສອງໄຟລ໌ໃນ certs/. + required: true + type: text + - default: '10.0' + edit: true + envKey: PROXY_TIMEOUT + labelEn: Proxy Timeout + labelZh: 代理超时秒数 + label: + en: Proxy Timeout + zh: 代理超时秒数 + zh-Hant: 代理逾時秒數 + ja: プロキシタイムアウト + ko: 프록시 타임아웃 + ru: Таймаут прокси + ms: Tamat Masa Proksi + pt-br: Tempo Limite do Proxy + tr: Proxy Zaman Aşımı + es-es: Tiempo de espera del proxy + fa: مهلت‌زمان پراکسی + lo: ເວລາໝົດກຳນົດຂອງພຣັອກຊີ + required: true + type: text + - default: '' + edit: true + envKey: ADMIN_USER + labelEn: Admin Username + labelZh: 管理员用户名 + label: + en: Admin Username + zh: 管理员用户名 + zh-Hant: 管理員使用者名稱 + ja: 管理者ユーザー名 + ko: 관리자 사용자 이름 + ru: Имя администратора + ms: Nama Pengguna Admin + pt-br: Nome de Usuario do Admin + tr: Yönetici Kullanıcı Adı + es-es: Nombre de usuario del administrador + fa: نام کاربری مدیر + lo: ຊື່ຜູ້ໃຊ້ຜູ້ດູແລ + required: false + type: text + - default: '' + edit: true + envKey: ADMIN_PASS + labelEn: Admin Password + labelZh: 管理员密码 + label: + en: Admin Password + zh: 管理员密码 + zh-Hant: 管理員密碼 + ja: 管理者パスワード + ko: 관리자 비밀번호 + ru: Пароль администратора + ms: Kata Laluan Admin + pt-br: Senha do Admin + tr: Yönetici Parolası + es-es: Contraseña del administrador + fa: رمز عبور مدیر + lo: ລະຫັດຜ່ານຜູ້ດູແລ + required: false + type: password + - default: '' + edit: true + envKey: IP_WHITELIST + labelEn: IP Whitelist + labelZh: IP 白名单 + label: + en: IP Whitelist + zh: IP 白名单 + zh-Hant: IP 白名單 + ja: IP ホワイトリスト + ko: IP 허용 목록 + ru: Белый список IP + ms: Senarai Putih IP + pt-br: Lista de Permissao de IP + tr: IP İzin Listesi + es-es: Lista de IP permitidas + fa: فهرست مجاز IP + lo: ລາຍຊື່ IP ທີ່ອະນຸຍາດ + required: false + type: text + - default: '' + edit: true + envKey: IMAGE_WHITELIST_REGEX + labelEn: Image Whitelist Regex + labelZh: 镜像白名单正则 + label: + en: Image Whitelist Regex + zh: 镜像白名单正则 + zh-Hant: 映像白名單正則 + ja: イメージ許可正規表現 + ko: 이미지 화이트리스트 정규식 + ru: Regex белого списка образов + ms: Regex Senarai Putih Imej + pt-br: Regex da Lista de Permissao de Imagens + tr: İmaj İzin Listesi Regex + es-es: Regex de imágenes permitidas + fa: عبارت منظم تصاویر مجاز + lo: Regex ຂອງຮູບພາບທີ່ອະນຸຍາດ + required: false + type: text + - default: '' + edit: true + envKey: IMAGE_BLACKLIST_REGEX + labelEn: Image Blacklist Regex + labelZh: 镜像黑名单正则 + label: + en: Image Blacklist Regex + zh: 镜像黑名单正则 + zh-Hant: 映像黑名單正則 + ja: イメージ拒否正規表現 + ko: 이미지 블랙리스트 정규식 + ru: Regex черного списка образов + ms: Regex Senarai Hitam Imej + pt-br: Regex da Lista Negra de Imagens + tr: İmaj Engelleme Listesi Regex + es-es: Regex de imágenes bloqueadas + fa: عبارت منظم تصاویر مسدود + lo: Regex ຂອງຮູບພາບທີ່ຖືກບລັອກ + required: false + type: text diff --git a/apps/socket-proxy-linuxserver/3.4.6/data/.gitkeep b/apps/docker-hub-proxy/4.0.0/data/.gitkeep similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/data/.gitkeep rename to apps/docker-hub-proxy/4.0.0/data/.gitkeep diff --git a/apps/docker-hub-proxy/4.0.0/docker-compose.yml b/apps/docker-hub-proxy/4.0.0/docker-compose.yml new file mode 100644 index 000000000..7686042f3 --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/docker-compose.yml @@ -0,0 +1,181 @@ +services: + docker-hub-proxy: + image: "qq510023514/docker-hub:v4.0.0" + container_name: ${CONTAINER_NAME} + command: + - python + - -c + - | + import ipaddress + import os + import re + import signal + import subprocess + import sys + import tempfile + from pathlib import Path + + domain = os.environ.get("PROXY_DOMAIN", "localhost").strip() + ipv4_pattern = re.fullmatch(r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}", domain) + domain_pattern = re.fullmatch( + r"[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*", + domain, + ) + if ipv4_pattern: + address = ipaddress.ip_address(domain) + if address.version != 4: + raise SystemExit("PROXY_DOMAIN must be a valid IPv4 address or DNS name") + subject_alt_name = f"IP:{address},IP:127.0.0.1" + elif domain_pattern: + subject_alt_name = f"DNS:{domain},IP:127.0.0.1" + else: + raise SystemExit("PROXY_DOMAIN must be a DNS name or IPv4 address without a scheme or port") + + cert_dir = Path("/app/data/certs") + if cert_dir.is_symlink(): + raise SystemExit("/app/data/certs must not be a symbolic link") + cert_dir.mkdir(mode=0o700, parents=True, exist_ok=True) + cert_file = cert_dir / "fullchain.pem" + key_file = cert_dir / "privkey.pem" + if cert_file.is_symlink() or key_file.is_symlink(): + raise SystemExit("TLS certificate files must not be symbolic links") + + if cert_file.exists() or key_file.exists(): + if ( + not cert_file.is_file() + or not key_file.is_file() + or not cert_file.stat().st_size + or not key_file.stat().st_size + ): + raise SystemExit("Both TLS certificate files must exist and be non-empty; refusing to replace either file") + cert_public_key = subprocess.run( + ["openssl", "x509", "-in", str(cert_file), "-pubkey", "-noout"], + check=True, + capture_output=True, + ).stdout + cert_public_key = subprocess.run( + ["openssl", "pkey", "-pubin", "-outform", "DER"], + input=cert_public_key, + check=True, + capture_output=True, + ).stdout + key_public_key = subprocess.run( + ["openssl", "pkey", "-in", str(key_file), "-pubout", "-outform", "DER"], + check=True, + capture_output=True, + ).stdout + if cert_public_key != key_public_key: + raise SystemExit("TLS certificate and private key do not match") + check_name = "-checkip" if ipv4_pattern else "-checkhost" + certificate_name = subprocess.run( + ["openssl", "x509", "-in", str(cert_file), "-noout", check_name, domain], + capture_output=True, + text=True, + ) + if certificate_name.returncode != 0: + raise SystemExit("existing TLS certificate does not match PROXY_DOMAIN") + else: + temporary_paths = [] + try: + for prefix in (".fullchain.", ".privkey."): + descriptor, temporary_path = tempfile.mkstemp(prefix=prefix, dir=cert_dir) + os.close(descriptor) + temporary_paths.append(Path(temporary_path)) + temporary_cert, temporary_key = temporary_paths + subprocess.run( + [ + "openssl", "req", "-x509", "-newkey", "rsa:2048", "-sha256", "-nodes", + "-days", "825", "-keyout", str(temporary_key), "-out", str(temporary_cert), + "-subj", f"/CN={domain}", "-addext", f"subjectAltName={subject_alt_name}", + "-addext", "basicConstraints=critical,CA:TRUE", "-addext", + "keyUsage=critical,digitalSignature,keyEncipherment,keyCertSign", + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + os.chmod(temporary_key, 0o600) + os.chmod(temporary_cert, 0o644) + os.replace(temporary_key, key_file) + os.replace(temporary_cert, cert_file) + finally: + for temporary_path in temporary_paths: + temporary_path.unlink(missing_ok=True) + + children = [] + + def stop_children(signum, _frame): + for child in children: + if child.poll() is None: + child.terminate() + for child in children: + try: + child.wait(timeout=20) + except subprocess.TimeoutExpired: + child.kill() + child.wait() + raise SystemExit(128 + signum) + + signal.signal(signal.SIGTERM, stop_children) + signal.signal(signal.SIGINT, stop_children) + for service in ("web", "proxy"): + children.append( + subprocess.Popen( + [sys.executable, "-m", "app.main", "--service", service] + ) + ) + try: + pid, status = os.wait() + except ChildProcessError: + raise SystemExit(1) + for child in children: + if child.pid != pid and child.poll() is None: + child.terminate() + for child in children: + if child.pid != pid: + try: + child.wait(timeout=20) + except subprocess.TimeoutExpired: + child.kill() + child.wait() + if os.WIFEXITED(status): + raise SystemExit(os.WEXITSTATUS(status)) + raise SystemExit(128 + os.WTERMSIG(status)) + ports: + - "${PANEL_APP_PORT_HTTP}:8000" + - "${PANEL_APP_PORT_HTTPS}:8443" + environment: + - HOST=0.0.0.0 + - PORT=8000 + - PROXY_PORT=8443 + - PROXY_URL=https://${PROXY_DOMAIN}:${PANEL_APP_PORT_HTTPS} + - PROXY_TIMEOUT=${PROXY_TIMEOUT} + - ADMIN_USER=${ADMIN_USER} + - ADMIN_PASS=${ADMIN_PASS} + - IP_WHITELIST=${IP_WHITELIST} + - IMAGE_WHITELIST_REGEX=${IMAGE_WHITELIST_REGEX} + - IMAGE_BLACKLIST_REGEX=${IMAGE_BLACKLIST_REGEX} + - SSL_CERTFILE=/app/data/certs/fullchain.pem + - SSL_KEYFILE=/app/data/certs/privkey.pem + volumes: + - "${APP_DATA_DIR}:/app/data" + healthcheck: + test: + - CMD + - python + - -c + - import ssl, urllib.request; urllib.request.urlopen("http://127.0.0.1:8000/openapi.json", timeout=10).close(); urllib.request.urlopen("https://127.0.0.1:8443/openapi.json", context=ssl._create_unverified_context(), timeout=10).close() + interval: 10s + timeout: 5s + start_period: 10s + retries: 3 + labels: + createdBy: Apps + networks: + - 1panel-network + stop_grace_period: 30s + restart: unless-stopped + +networks: + 1panel-network: + external: true diff --git a/apps/docker-hub-proxy/4.0.0/scripts/.gitkeep b/apps/docker-hub-proxy/4.0.0/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/docker-hub-proxy/4.0.0/scripts/init.sh b/apps/docker-hub-proxy/4.0.0/scripts/init.sh new file mode 100755 index 000000000..121679082 --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/scripts/init.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -euo pipefail + +APP_DATA_DIR="${APP_DATA_DIR:-./data}" +if test -z "$APP_DATA_DIR" || test "$APP_DATA_DIR" = "/"; then + echo "APP_DATA_DIR must be a non-root directory" >&2 + exit 1 +fi + +PROXY_DOMAIN="${PROXY_DOMAIN:-localhost}" +CERT_DOMAIN_RE='^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(\.([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*$' +IPV4_RE='^([0-9]{1,3}\.){3}[0-9]{1,3}$' +if [[ "$PROXY_DOMAIN" =~ $IPV4_RE ]]; then + IFS='.' read -r -a octets <<< "$PROXY_DOMAIN" + for octet in "${octets[@]}"; do + if [[ ${#octet} -gt 1 && "$octet" == 0* ]]; then + echo "PROXY_DOMAIN is not a canonical IPv4 address" >&2 + exit 1 + fi + if (( 10#$octet > 255 )); then + echo "PROXY_DOMAIN is not a valid IPv4 address" >&2 + exit 1 + fi + done +elif [[ ! "$PROXY_DOMAIN" =~ $CERT_DOMAIN_RE ]]; then + echo "PROXY_DOMAIN must be a DNS name or IPv4 address without a scheme or port" >&2 + exit 1 +fi diff --git a/apps/docker-hub-proxy/4.0.0/scripts/uninstall.sh b/apps/docker-hub-proxy/4.0.0/scripts/uninstall.sh new file mode 100755 index 000000000..024242bb3 --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -euo pipefail + +docker-compose down --volumes diff --git a/apps/docker-hub-proxy/4.0.0/scripts/upgrade.sh b/apps/docker-hub-proxy/4.0.0/scripts/upgrade.sh new file mode 100755 index 000000000..0e3964343 --- /dev/null +++ b/apps/docker-hub-proxy/4.0.0/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +bash "$SCRIPT_DIR/init.sh" diff --git a/apps/documenso/2.19.0/.env.sample b/apps/documenso/2.19.0/.env.sample new file mode 100644 index 000000000..4b0f4bf3b --- /dev/null +++ b/apps/documenso/2.19.0/.env.sample @@ -0,0 +1,23 @@ +CONTAINER_NAME=documenso-compose-check +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=3000 +NEXT_PUBLIC_WEBAPP_URL=http://127.0.0.1:3000 +PANEL_DB_TYPE=postgresql +PANEL_DB_HOST=postgresql +PANEL_DB_PORT=5432 +PANEL_DB_NAME=documenso +PANEL_DB_USER=documenso +PANEL_DB_USER_PASSWORD=change-me +SMTP_HOST=smtp.example.com +SMTP_PORT=587 +SMTP_SECURE=false +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_FROM_NAME=Documenso +SMTP_FROM_ADDRESS=documenso@example.com +DISABLE_SIGNUP=false +NEXTAUTH_SECRET=generate +NEXT_PRIVATE_ENCRYPTION_KEY=generate +NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=generate +SIGNING_PASSPHRASE=generate +NEXT_PRIVATE_DATABASE_URL=generate diff --git a/apps/documenso/2.19.0/data.yml b/apps/documenso/2.19.0/data.yml new file mode 100644 index 000000000..a630418fe --- /dev/null +++ b/apps/documenso/2.19.0/data.yml @@ -0,0 +1,377 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://127.0.0.1:3000 + edit: true + envKey: NEXT_PUBLIC_WEBAPP_URL + labelEn: Public URL + labelZh: 公开访问地址 + label: + en: Public URL + zh: 公开访问地址 + zh-Hant: 公開存取網址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publica + required: true + type: text + - default: postgresql + envKey: PANEL_DB_TYPE + labelEn: Database Runtime + labelZh: 数据库运行环境 + label: + en: Database Runtime + zh: 数据库运行环境 + zh-Hant: 資料庫執行環境 + ja: データベース実行環境 + ko: 데이터베이스 런타임 + ru: Среда базы данных + ms: Masa jalan pangkalan data + pt-br: Ambiente de banco de dados + required: true + type: apps + values: + - label: PostgreSQL + value: postgresql + child: + default: '' + envKey: PANEL_DB_HOST + labelEn: PostgreSQL Service + labelZh: PostgreSQL 服务 + label: + en: PostgreSQL Service + zh: PostgreSQL 服务 + zh-Hant: PostgreSQL 服務 + ja: PostgreSQL サービス + ko: PostgreSQL 서비스 + ru: Сервис PostgreSQL + ms: Perkhidmatan PostgreSQL + pt-br: Servico PostgreSQL + required: true + type: service + - default: 5432 + edit: false + envKey: PANEL_DB_PORT + labelEn: Database Port + labelZh: 数据库端口 + label: + en: Database Port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco de dados + required: true + type: number + - default: documenso + edit: false + envKey: PANEL_DB_NAME + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: documenso + edit: false + envKey: PANEL_DB_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: change-me + edit: false + envKey: PANEL_DB_USER_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + rule: paramComplexity + type: password + - default: smtp.example.com + edit: true + envKey: SMTP_HOST + labelEn: SMTP Host + labelZh: SMTP 主机 + label: + en: SMTP Host + zh: SMTP 主机 + zh-Hant: SMTP 主機 + ja: SMTP ホスト + ko: SMTP 호스트 + ru: SMTP-сервер + ms: Hos SMTP + pt-br: Servidor SMTP + required: true + type: text + - default: 587 + edit: true + envKey: SMTP_PORT + labelEn: SMTP Port + labelZh: SMTP 端口 + label: + en: SMTP Port + zh: SMTP 端口 + zh-Hant: SMTP 連接埠 + ja: SMTP ポート + ko: SMTP 포트 + ru: SMTP-порт + ms: Port SMTP + pt-br: Porta SMTP + required: true + type: number + - default: 'false' + edit: true + envKey: SMTP_SECURE + labelEn: SMTP Implicit TLS + labelZh: SMTP 隐式 TLS + label: + en: SMTP Implicit TLS + zh: SMTP 隐式 TLS + zh-Hant: SMTP 隱式 TLS + ja: SMTP 暗黙 TLS + ko: SMTP 암시적 TLS + ru: Неявный TLS SMTP + ms: TLS tersirat SMTP + pt-br: TLS implicito SMTP + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: '' + edit: true + envKey: SMTP_USERNAME + labelEn: SMTP Username + labelZh: SMTP 用户名 + label: + en: SMTP Username + zh: SMTP 用户名 + zh-Hant: SMTP 使用者名稱 + ja: SMTP ユーザー名 + ko: SMTP 사용자 이름 + ru: Имя пользователя SMTP + ms: Nama pengguna SMTP + pt-br: Usuario SMTP + required: false + type: text + - default: '' + edit: true + envKey: SMTP_PASSWORD + labelEn: SMTP Password + labelZh: SMTP 密码 + label: + en: SMTP Password + zh: SMTP 密码 + zh-Hant: SMTP 密碼 + ja: SMTP パスワード + ko: SMTP 비밀번호 + ru: Пароль SMTP + ms: Kata laluan SMTP + pt-br: Senha SMTP + required: false + type: password + - default: Documenso + edit: true + envKey: SMTP_FROM_NAME + labelEn: Email Sender Name + labelZh: 邮件发件人名称 + label: + en: Email Sender Name + zh: 邮件发件人名称 + zh-Hant: 郵件寄件者名稱 + ja: メール送信者名 + ko: 이메일 발신자 이름 + ru: Имя отправителя + ms: Nama penghantar e-mel + pt-br: Nome do remetente + required: true + type: text + - default: documenso@example.com + edit: true + envKey: SMTP_FROM_ADDRESS + labelEn: Email Sender Address + labelZh: 邮件发件人地址 + label: + en: Email Sender Address + zh: 邮件发件人地址 + zh-Hant: 郵件寄件者地址 + ja: メール送信元アドレス + ko: 이메일 발신자 주소 + ru: Адрес отправителя + ms: Alamat penghantar e-mel + pt-br: Endereco do remetente + required: true + type: text + - default: 'false' + edit: true + envKey: DISABLE_SIGNUP + labelEn: Disable New Signups + labelZh: 禁止新用户注册 + label: + en: Disable New Signups + zh: 禁止新用户注册 + zh-Hant: 停用新使用者註冊 + ja: 新規登録を無効化 + ko: 신규 가입 비활성화 + ru: Запретить новые регистрации + ms: Nyahdayakan pendaftaran baharu + pt-br: Desativar novos cadastros + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: generate + disabled: true + edit: true + envKey: NEXTAUTH_SECRET + labelEn: Authentication Secret + labelZh: 认证密钥 + label: + en: Authentication Secret + zh: 认证密钥 + zh-Hant: 驗證密鑰 + ja: 認証シークレット + ko: 인증 비밀 키 + ru: Секрет аутентификации + ms: Rahsia pengesahan + pt-br: Segredo de autenticacao + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_ENCRYPTION_KEY + labelEn: Primary Encryption Key + labelZh: 主加密密钥 + label: + en: Primary Encryption Key + zh: 主加密密钥 + zh-Hant: 主要加密金鑰 + ja: プライマリ暗号化キー + ko: 기본 암호화 키 + ru: Основной ключ шифрования + ms: Kunci penyulitan utama + pt-br: Chave de criptografia primaria + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY + labelEn: Secondary Encryption Key + labelZh: 备用加密密钥 + label: + en: Secondary Encryption Key + zh: 备用加密密钥 + zh-Hant: 備用加密金鑰 + ja: セカンダリ暗号化キー + ko: 보조 암호화 키 + ru: Резервный ключ шифрования + ms: Kunci penyulitan sekunder + pt-br: Chave de criptografia secundaria + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: SIGNING_PASSPHRASE + labelEn: Signing Certificate Passphrase + labelZh: 签名证书密码 + label: + en: Signing Certificate Passphrase + zh: 签名证书密码 + zh-Hant: 簽署憑證密碼 + ja: 署名証明書パスフレーズ + ko: 서명 인증서 암호 + ru: Пароль сертификата подписи + ms: Frasa laluan sijil tandatangan + pt-br: Senha do certificado de assinatura + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_DATABASE_URL + labelEn: Generated Database URL + labelZh: 生成的数据库地址 + label: + en: Generated Database URL + zh: 生成的数据库地址 + zh-Hant: 產生的資料庫網址 + ja: 生成されたデータベース URL + ko: 생성된 데이터베이스 URL + ru: Сформированный URL базы данных + ms: URL pangkalan data dijana + pt-br: URL de banco de dados gerada + required: true + type: password diff --git a/apps/documenso/2.19.0/data/.gitkeep b/apps/documenso/2.19.0/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/documenso/2.19.0/docker-compose.yml b/apps/documenso/2.19.0/docker-compose.yml new file mode 100644 index 000000000..4ac63e83a --- /dev/null +++ b/apps/documenso/2.19.0/docker-compose.yml @@ -0,0 +1,58 @@ +services: + documenso: + image: "documenso/documenso:v2.19.0@sha256:a0592ef8edbc8d74e5e236d9c2dffb3fc4f0104a3ea1c52baaae4673799d8d91" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + command: + - sh + - -c + - ../../node_modules/.bin/prisma migrate deploy --schema ../../packages/prisma/schema.prisma && exec env HOSTNAME=0.0.0.0 node build/server/main.js + networks: + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:3000" + environment: + PORT: "3000" + NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} + NEXT_PRIVATE_ENCRYPTION_KEY: ${NEXT_PRIVATE_ENCRYPTION_KEY} + NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: ${NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY} + NEXT_PUBLIC_WEBAPP_URL: ${NEXT_PUBLIC_WEBAPP_URL} + NEXT_PRIVATE_INTERNAL_WEBAPP_URL: http://127.0.0.1:3000 + NEXT_PRIVATE_DATABASE_URL: ${NEXT_PRIVATE_DATABASE_URL} + NEXT_PRIVATE_DIRECT_DATABASE_URL: ${NEXT_PRIVATE_DATABASE_URL} + NEXT_PUBLIC_UPLOAD_TRANSPORT: database + NEXT_PRIVATE_SMTP_TRANSPORT: smtp-auth + NEXT_PRIVATE_SMTP_HOST: ${SMTP_HOST} + NEXT_PRIVATE_SMTP_PORT: ${SMTP_PORT} + NEXT_PRIVATE_SMTP_SECURE: ${SMTP_SECURE} + NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS: "false" + NEXT_PRIVATE_SMTP_USERNAME: ${SMTP_USERNAME} + NEXT_PRIVATE_SMTP_PASSWORD: ${SMTP_PASSWORD} + NEXT_PRIVATE_SMTP_FROM_NAME: ${SMTP_FROM_NAME} + NEXT_PRIVATE_SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS} + NEXT_PRIVATE_SIGNING_TRANSPORT: local + NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH: /opt/documenso/cert.p12 + NEXT_PRIVATE_SIGNING_PASSPHRASE: ${SIGNING_PASSPHRASE} + NEXT_PUBLIC_DISABLE_SIGNUP: ${DISABLE_SIGNUP} + DOCUMENSO_DISABLE_TELEMETRY: "true" + read_only: true + tmpfs: + - /tmp:size=256m,mode=1777 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + volumes: + - ./data/cert.p12:/opt/documenso/cert.p12:ro + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/documenso/2.19.0/scripts/init.sh b/apps/documenso/2.19.0/scripts/init.sh new file mode 100755 index 000000000..0647a5749 --- /dev/null +++ b/apps/documenso/2.19.0/scripts/init.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +DATA_DIR="${ROOT_DIR}/data" +CERT_FILE="${DATA_DIR}/cert.p12" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value="" + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ROOT_DIR}/.documenso-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +generate_secret() { + local key="$1" + local value + value="$(read_env_value "$key")" + if [[ -z "$value" || "$value" == generate ]]; then + value="$(openssl rand -hex 32)" + set_env_value "$key" "$value" + fi + [[ ${#value} -ge 32 ]] || fail "$key must contain at least 32 characters" +} + +urlencode() { + local LC_ALL=C + local input="$1" + local output="" + local character encoded + local index + for ((index = 0; index < ${#input}; index++)); do + character="${input:index:1}" + case "$character" in + [A-Za-z0-9.~_-]) output+="$character" ;; + *) printf -v encoded '%%%02X' "'$character"; output+="$encoded" ;; + esac + done + printf '%s' "$output" +} + +secure_certificate_file() { + local current_uid owner_uid + current_uid="$(id -u)" + if [[ "$current_uid" == 0 ]]; then + chown 1001:65533 -- "$CERT_FILE" + elif [[ "$current_uid" == 1001 ]]; then + owner_uid="$(stat -c %u -- "$CERT_FILE")" + [[ "$owner_uid" == 1001 ]] || fail "the signing certificate must be owned by UID 1001" + [[ -r "$CERT_FILE" ]] || fail "the signing certificate is not readable by UID 1001" + else + fail "init.sh must run as root or UID 1001" + fi + chmod 0400 -- "$CERT_FILE" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +command -v openssl >/dev/null 2>&1 || fail "openssl is required" + +db_host="$(read_env_value PANEL_DB_HOST)" +db_port="$(read_env_value PANEL_DB_PORT)" +db_name="$(read_env_value PANEL_DB_NAME)" +db_user="$(read_env_value PANEL_DB_USER)" +db_password="$(read_env_value PANEL_DB_USER_PASSWORD)" +[[ "$db_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || fail "PANEL_DB_HOST is invalid" +if [[ ! "$db_port" =~ ^[0-9]+$ ]] || ((db_port < 1 || db_port > 65535)); then + fail "PANEL_DB_PORT is invalid" +fi +[[ "$db_name" =~ ^[A-Za-z0-9_][A-Za-z0-9_-]*$ ]] || fail "PANEL_DB_NAME is invalid" +[[ "$db_user" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]] || fail "PANEL_DB_USER is invalid" +[[ -n "$db_password" && "$db_password" != change-me ]] || fail "PANEL_DB_USER_PASSWORD must be configured" + +public_url="$(read_env_value NEXT_PUBLIC_WEBAPP_URL)" +public_url_pattern='^https?://[A-Za-z0-9._~:/?&=%+-]+$' +[[ "$public_url" =~ $public_url_pattern ]] || fail "NEXT_PUBLIC_WEBAPP_URL must be a complete HTTP(S) URL" +smtp_host="$(read_env_value SMTP_HOST)" +smtp_port="$(read_env_value SMTP_PORT)" +smtp_secure="$(read_env_value SMTP_SECURE)" +smtp_from_address="$(read_env_value SMTP_FROM_ADDRESS)" +[[ "$smtp_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ && "$smtp_host" != smtp.example.com ]] || fail "SMTP_HOST must name a working SMTP service" +if [[ ! "$smtp_port" =~ ^[0-9]+$ ]] || ((smtp_port < 1 || smtp_port > 65535)); then + fail "SMTP_PORT is invalid" +fi +[[ "$smtp_secure" == true || "$smtp_secure" == false ]] || fail "SMTP_SECURE must be true or false" +[[ "$smtp_from_address" =~ ^[^[:space:]@]+@[^[:space:]@]+$ ]] || fail "SMTP_FROM_ADDRESS is invalid" + +chmod 600 "$ENV_FILE" +generate_secret NEXTAUTH_SECRET +generate_secret NEXT_PRIVATE_ENCRYPTION_KEY +generate_secret NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY + +encoded_user="$(urlencode "$db_user")" +encoded_password="$(urlencode "$db_password")" +set_env_value NEXT_PRIVATE_DATABASE_URL "postgresql://${encoded_user}:${encoded_password}@${db_host}:${db_port}/${db_name}" + +install -d -m 0700 "$DATA_DIR" +passphrase="$(read_env_value SIGNING_PASSPHRASE)" +[[ ! -L "$CERT_FILE" ]] || fail "$CERT_FILE must not be a symbolic link" +if [[ -s "$CERT_FILE" ]]; then + [[ -n "$passphrase" && "$passphrase" != generate ]] || fail "SIGNING_PASSPHRASE is missing for the existing certificate" + CERT_PASS="$passphrase" openssl pkcs12 -in "$CERT_FILE" -passin env:CERT_PASS -noout >/dev/null 2>&1 || fail "the existing signing certificate or passphrase is invalid" + secure_certificate_file +else + if [[ -z "$passphrase" || "$passphrase" == generate ]]; then + passphrase="$(openssl rand -hex 32)" + set_env_value SIGNING_PASSPHRASE "$passphrase" + fi + temp_dir="$(mktemp -d "${ROOT_DIR}/.documenso-cert.tmp.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + openssl req -new -newkey rsa:3072 -x509 -sha256 -nodes -days 3650 \ + -subj "/CN=Documenso 1Panel Signing" \ + -keyout "${temp_dir}/private.key" -out "${temp_dir}/certificate.crt" >/dev/null 2>&1 + CERT_PASS="$passphrase" openssl pkcs12 -export \ + -inkey "${temp_dir}/private.key" -in "${temp_dir}/certificate.crt" \ + -out "${temp_dir}/cert.p12" -passout env:CERT_PASS + install -m 0400 "${temp_dir}/cert.p12" "$CERT_FILE" + secure_certificate_file + rm -rf -- "$temp_dir" + trap - EXIT +fi diff --git a/apps/documenso/2.19.0/scripts/uninstall.sh b/apps/documenso/2.19.0/scripts/uninstall.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/documenso/2.19.0/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/documenso/2.19.0/scripts/upgrade.sh b/apps/documenso/2.19.0/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/documenso/2.19.0/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/dpanel/1.11.0-lite/docker-compose.yml b/apps/dpanel/1.11.0-lite/docker-compose.yml index 294267d0f..0de863e0b 100644 --- a/apps/dpanel/1.11.0-lite/docker-compose.yml +++ b/apps/dpanel/1.11.0-lite/docker-compose.yml @@ -1,6 +1,6 @@ services: dpanel: - image: "dpanel/dpanel:1.11.0-lite" + image: "dpanel/dpanel:1.11.1-lite" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/dpanel/README.md b/apps/dpanel/README.md index 881d436de..62d86bb5b 100644 --- a/apps/dpanel/README.md +++ b/apps/dpanel/README.md @@ -30,6 +30,10 @@ Docker 可视化面板系统。 | --- | --- | --- | --- | | APP_NAME | 应用名称 | dpanel | 是 | +## 安全风险 + +- 本应用将宿主机 `/var/run/docker.sock` 挂载到容器。容器可通过 Docker API 控制宿主机上的其他容器,通常等同于获得宿主机 root 权限。仅在可信环境部署,并限制对面板和应用的访问。 + ## 使用说明 - 账户密码 ``` diff --git a/apps/easynode/3.8.2/docker-compose.yml b/apps/easynode/3.8.2/docker-compose.yml index 421195229..fbd5a3815 100644 --- a/apps/easynode/3.8.2/docker-compose.yml +++ b/apps/easynode/3.8.2/docker-compose.yml @@ -1,6 +1,6 @@ services: easynode: - image: "chaoszhu/easynode:v3.8.2" + image: "chaoszhu/easynode:v3.8.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/ech0/5.8.0/docker-compose.yml b/apps/ech0/5.8.0/docker-compose.yml index 29e45bbda..b05b7fe4d 100644 --- a/apps/ech0/5.8.0/docker-compose.yml +++ b/apps/ech0/5.8.0/docker-compose.yml @@ -1,6 +1,6 @@ services: ech0: - image: "sn0wl1n/ech0:v5.8.0" + image: "sn0wl1n/ech0:v5.8.2" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/element-web/1.12.30/.env.sample b/apps/element-web/1.12.32/.env.sample similarity index 100% rename from apps/element-web/1.12.30/.env.sample rename to apps/element-web/1.12.32/.env.sample diff --git a/apps/element-web/1.12.30/data.yml b/apps/element-web/1.12.32/data.yml similarity index 100% rename from apps/element-web/1.12.30/data.yml rename to apps/element-web/1.12.32/data.yml diff --git a/apps/element-web/1.12.30/docker-compose.yml b/apps/element-web/1.12.32/docker-compose.yml similarity index 86% rename from apps/element-web/1.12.30/docker-compose.yml rename to apps/element-web/1.12.32/docker-compose.yml index 719279004..7e5a70862 100644 --- a/apps/element-web/1.12.30/docker-compose.yml +++ b/apps/element-web/1.12.32/docker-compose.yml @@ -6,7 +6,7 @@ services: - 1panel-network ports: - "${PANEL_APP_PORT_HTTP}:80" - image: vectorim/element-web:v1.12.30 + image: vectorim/element-web:v1.12.32 labels: createdBy: "Apps" diff --git a/apps/emby-linuxserver/4.10.1/docker-compose.yml b/apps/emby-linuxserver/4.10.1/docker-compose.yml index 83ecaa424..b19bbc24d 100644 --- a/apps/emby-linuxserver/4.10.1/docker-compose.yml +++ b/apps/emby-linuxserver/4.10.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: emby: - image: "linuxserver/emby:4.10.1" + image: "linuxserver/emby:4.10.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/fast-note-sync-service/3.6.0/docker-compose.yml b/apps/fast-note-sync-service/3.6.0/docker-compose.yml index 6ef32ef08..a87a3c032 100644 --- a/apps/fast-note-sync-service/3.6.0/docker-compose.yml +++ b/apps/fast-note-sync-service/3.6.0/docker-compose.yml @@ -1,6 +1,6 @@ services: fast-note-sync-service: - image: "haierkeys/fast-note-sync-service:3.6.0" + image: "haierkeys/fast-note-sync-service:3.6.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/.env.sample b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/.env.sample new file mode 100644 index 000000000..09664e294 --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41006 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/data.yml b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/data.yml new file mode 100644 index 000000000..020cf4264 --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41006 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/docker-compose.yml b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/docker-compose.yml new file mode 100644 index 000000000..90ccc0acd --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/docker-compose.yml @@ -0,0 +1,35 @@ +services: + filezilla-kasm: + image: "kasmweb/filezilla:1.19.0-rolling-weekly@sha256:7cdc7fcc0d7b7abce2a748fd9bdbb75f9c0f22589cf76a2f31a2e9bd9049f726" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/init.sh b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/uninstall.sh b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/upgrade.sh b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly-digest-c1a3a07/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/filezilla-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/filezilla-kasm/1.19.0-rolling-weekly/docker-compose.yml index 1af7770bb..90ccc0acd 100644 --- a/apps/filezilla-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/filezilla-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: filezilla-kasm: - image: "kasmweb/filezilla:1.19.0-rolling-weekly@sha256:ab9b51d455bd6e43fa4f8c7605946513d54b811b1cd61a5e49a2d9156bb525e7" + image: "kasmweb/filezilla:1.19.0-rolling-weekly@sha256:7cdc7fcc0d7b7abce2a748fd9bdbb75f9c0f22589cf76a2f31a2e9bd9049f726" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/firecrawl/2.11.359/docker-compose.yml b/apps/firecrawl/2.11.359/docker-compose.yml index dd12f4a77..f064d1431 100644 --- a/apps/firecrawl/2.11.359/docker-compose.yml +++ b/apps/firecrawl/2.11.359/docker-compose.yml @@ -59,7 +59,7 @@ services: createdBy: "Apps" firecrawl-playwright: - image: "ghcr.io/firecrawl/playwright-service@sha256:df1a393ce8bfc3801570a826a9b0dcc400ae48789adb4a0ad9d6cbf0229b059b" + image: "ghcr.io/firecrawl/playwright-service@sha256:c9789ffbf5571eb5b8456891ae688bdee095a6f9662e0bed50d9e86eb022bd6f" container_name: ${CONTAINER_NAME}-playwright restart: always networks: @@ -127,7 +127,7 @@ services: createdBy: "Apps" firecrawl-postgres: - image: "ghcr.io/firecrawl/nuq-postgres@sha256:9b638af78d99873bc0ba2b57c9cbcd01df6ce96efeaa72d36cfbe0f76521e8fc" + image: "ghcr.io/firecrawl/nuq-postgres@sha256:d719b36160eac2fcbe16dc7e7360ddb9c82c40ea5da183d07bf4a755c6a0b2ae" container_name: ${CONTAINER_NAME}-postgres restart: always networks: diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/.env.sample b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/.env.sample new file mode 100644 index 000000000..b164dbaad --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41007 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/data.yml b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/data.yml new file mode 100644 index 000000000..9d73a4245 --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41007 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/docker-compose.yml b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/docker-compose.yml new file mode 100644 index 000000000..b275c3f2a --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/docker-compose.yml @@ -0,0 +1,35 @@ +services: + firefox-kasm: + image: "kasmweb/firefox:1.19.0-rolling-weekly@sha256:be19faa1478c3627392edc964674e4043f21a65f961e06a3ad6915628992f687" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/init.sh b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/uninstall.sh b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/upgrade.sh b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/firefox-kasm/1.19.0-rolling-weekly-digest-6013048/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/firefox-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/firefox-kasm/1.19.0-rolling-weekly/docker-compose.yml index b849e65c4..b275c3f2a 100644 --- a/apps/firefox-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/firefox-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: firefox-kasm: - image: "kasmweb/firefox:1.19.0-rolling-weekly@sha256:c1b299774de2216e8454f8608388695a3325d21538b0ba2932c0fd04e75bd1f7" + image: "kasmweb/firefox:1.19.0-rolling-weekly@sha256:be19faa1478c3627392edc964674e4043f21a65f961e06a3ad6915628992f687" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/flarum/1.8.19/docker-compose.yml b/apps/flarum/1.8.19/docker-compose.yml index a3fd2966e..776cedb88 100644 --- a/apps/flarum/1.8.19/docker-compose.yml +++ b/apps/flarum/1.8.19/docker-compose.yml @@ -27,7 +27,7 @@ services: - DB_NOPREFIX=false - DB_TIMEOUT=60 - FLARUM_BASE_URL=${FLARUM_EXTERNAL_URL} - image: crazymax/flarum:1.8.19 + image: crazymax/flarum:1.8.20 labels: createdBy: Apps networks: diff --git a/apps/flexget-linuxserver/3.22.1/docker-compose.yml b/apps/flexget-linuxserver/3.22.1/docker-compose.yml index f26b6ba8f..da2c5b952 100644 --- a/apps/flexget-linuxserver/3.22.1/docker-compose.yml +++ b/apps/flexget-linuxserver/3.22.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: flexget: - image: "linuxserver/flexget:3.22.1" + image: "linuxserver/flexget:3.22.6" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/flexget/3.21.3/docker-compose.yml b/apps/flexget/3.21.3/docker-compose.yml index 80f14968c..4068ef331 100644 --- a/apps/flexget/3.21.3/docker-compose.yml +++ b/apps/flexget/3.21.3/docker-compose.yml @@ -14,7 +14,7 @@ services: - PGID=1000 - TZ=${TIME_ZONE} - FG_WEBUI_PASSWD=${WEB_PASSWORD} - image: wiserain/flexget:3.21.3 + image: wiserain/flexget:3.22.2 labels: createdBy: "Apps" diff --git a/apps/flipt/2.13.1/docker-compose.yml b/apps/flipt/2.13.1/docker-compose.yml index 2cb4d19c6..84e37dcf0 100644 --- a/apps/flipt/2.13.1/docker-compose.yml +++ b/apps/flipt/2.13.1/docker-compose.yml @@ -1,6 +1,6 @@ services: flipt: - image: "docker.flipt.io/flipt/flipt:v2.13.1" + image: "docker.flipt.io/flipt/flipt:v2.14.0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/flycast-linuxserver/2.7.20261004/docker-compose.yml b/apps/flycast-linuxserver/2.7.20261004/docker-compose.yml index 5f5708b89..64968aa8a 100644 --- a/apps/flycast-linuxserver/2.7.20261004/docker-compose.yml +++ b/apps/flycast-linuxserver/2.7.20261004/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: flycast: - image: "linuxserver/flycast:2.7.20261004" + image: "linuxserver/flycast:2.7.20261018" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/flycast-linuxserver/2.7.20261018/.env.sample b/apps/flycast-linuxserver/2.7.20261018/.env.sample new file mode 100644 index 000000000..606e99f17 --- /dev/null +++ b/apps/flycast-linuxserver/2.7.20261018/.env.sample @@ -0,0 +1,11 @@ +CONTAINER_NAME=flycast-linuxserver +PANEL_APP_PORT_HTTP=3000 +PANEL_APP_PORT_HTTPS=3001 +CONFIG_PATH=./data/config +TIME_ZONE=Asia/Shanghai +CUSTOM_USER=admin +PASSWORD=linuxserver-change-me +TITLE=Flycast +SELKIES_UI_TITLE=Selkies +DASHBOARD=selkies-dashboard +LC_ALL=zh_CN.UTF-8 diff --git a/apps/flycast-linuxserver/2.7.20261018/data.yml b/apps/flycast-linuxserver/2.7.20261018/data.yml new file mode 100644 index 000000000..502817537 --- /dev/null +++ b/apps/flycast-linuxserver/2.7.20261018/data.yml @@ -0,0 +1,174 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: 3001 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: Https Port + labelZh: Https 端口 + label: + en: Https Port + zh: Https 端口 + zh-Hant: Https 連接埠 + ja: Https ポート + ko: Https 포트 + ru: Порт Https + ms: Port Https + pt-br: Porta Https + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text + + - default: admin + edit: true + envKey: CUSTOM_USER + labelEn: Basic Auth Username + labelZh: 访问用户名(Basic Auth) + label: + en: Basic Auth Username + zh: 访问用户名(Basic Auth) + zh-Hant: 存取使用者名稱(Basic Auth) + ja: ベーシック認証ユーザー名 + ko: 기본 인증 사용자명 + ru: Имя пользователя (Basic Auth) + ms: Nama pengguna (Basic Auth) + pt-br: Usuario Basic Auth + required: true + type: text + - default: linuxserver-change-me + edit: true + envKey: PASSWORD + labelEn: Basic Auth Password + labelZh: 访问密码(Basic Auth) + label: + en: Basic Auth Password + zh: 访问密码(Basic Auth) + zh-Hant: 存取密碼(Basic Auth) + ja: ベーシック認証パスワード + ko: 기본 인증 비밀번호 + ru: Пароль (Basic Auth) + ms: Kata laluan Basic Auth + pt-br: Senha Basic Auth + random: true + required: true + rule: paramComplexity + type: password + - default: Flycast + edit: true + envKey: TITLE + labelEn: Browser page title + labelZh: 浏览器页面标题 + label: + en: Browser page title + zh: 浏览器页面标题 + zh-Hant: 瀏覽器頁面標題 + ja: ブラウザーのページタイトル + ko: 브라우저 페이지 제목 + ru: Заголовок страницы браузера + ms: Tajuk halaman pelayar + pt-br: Titulo da pagina do navegador + required: false + type: text + - default: Selkies + edit: true + envKey: SELKIES_UI_TITLE + labelEn: Sidebar title + labelZh: 侧边栏标题 + label: + en: Sidebar title + zh: 侧边栏标题 + zh-Hant: 側邊欄標題 + ja: サイドバータイトル + ko: 사이드바 제목 + ru: Заголовок боковой панели + ms: Tajuk bar sisi + pt-br: Titulo da barra lateral + required: false + type: text + - default: selkies-dashboard + edit: true + envKey: DASHBOARD + labelEn: Dashboard UI + labelZh: 仪表盘界面 + label: + en: Dashboard UI + zh: 仪表盘界面 + zh-Hant: 儀表板介面 + ja: ダッシュボード UI + ko: 대시보드 UI + ru: Интерфейс панели + ms: UI papan pemuka + pt-br: Interface do painel + required: false + type: select + values: + - label: selkies-dashboard + value: selkies-dashboard + - label: selkies-dashboard-zinc + value: selkies-dashboard-zinc + - label: selkies-dashboard-wish + value: selkies-dashboard-wish + - default: zh_CN.UTF-8 + edit: true + envKey: LC_ALL + labelEn: Desktop language locale + labelZh: 桌面语言区域 + label: + en: Desktop language locale + zh: 桌面语言区域 + zh-Hant: 桌面語言區域 + ja: デスクトップ言語ロケール + ko: 데스크톱 언어 로케일 + ru: Локаль языка рабочего стола + ms: Lokal bahasa desktop + pt-br: Localidade do idioma da area de trabalho + required: false + type: text diff --git a/apps/flycast-linuxserver/2.7.20261018/data/.gitkeep b/apps/flycast-linuxserver/2.7.20261018/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/flycast-linuxserver/2.7.20261018/docker-compose.yml b/apps/flycast-linuxserver/2.7.20261018/docker-compose.yml new file mode 100644 index 000000000..64968aa8a --- /dev/null +++ b/apps/flycast-linuxserver/2.7.20261018/docker-compose.yml @@ -0,0 +1,27 @@ +networks: + 1panel-network: + external: true +services: + flycast: + image: "linuxserver/flycast:2.7.20261018" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + - "${PANEL_APP_PORT_HTTPS}:3001" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + - CUSTOM_USER=${CUSTOM_USER} + - PASSWORD=${PASSWORD} + - TITLE=${TITLE:-Flycast} + - SELKIES_UI_TITLE=${SELKIES_UI_TITLE:-Selkies} + - DASHBOARD=${DASHBOARD:-selkies-dashboard} + - LC_ALL=${LC_ALL} + labels: + createdBy: "Apps" diff --git a/apps/flycast-linuxserver/2.7.20261018/scripts/init.sh b/apps/flycast-linuxserver/2.7.20261018/scripts/init.sh new file mode 100755 index 000000000..e5658f096 --- /dev/null +++ b/apps/flycast-linuxserver/2.7.20261018/scripts/init.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +paths=( + "${CONFIG_PATH:-./data/config}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/tianji/1.33.9/scripts/uninstall.sh b/apps/flycast-linuxserver/2.7.20261018/scripts/uninstall.sh similarity index 100% rename from apps/tianji/1.33.9/scripts/uninstall.sh rename to apps/flycast-linuxserver/2.7.20261018/scripts/uninstall.sh diff --git a/apps/modrinth-linuxserver/0.21.9/scripts/upgrade.sh b/apps/flycast-linuxserver/2.7.20261018/scripts/upgrade.sh similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/scripts/upgrade.sh rename to apps/flycast-linuxserver/2.7.20261018/scripts/upgrade.sh diff --git a/apps/forgejo/16/docker-compose.yml b/apps/forgejo/16/docker-compose.yml index b9f686196..f37dafa82 100644 --- a/apps/forgejo/16/docker-compose.yml +++ b/apps/forgejo/16/docker-compose.yml @@ -1,6 +1,6 @@ services: forgejo: - image: codeberg.org/forgejo/forgejo:16 + image: codeberg.org/forgejo/forgejo:17 container_name: ${CONTAINER_NAME} environment: - USER_UID=1000 diff --git a/apps/freshrss-linuxserver/1.30.1/docker-compose.yml b/apps/freshrss-linuxserver/1.30.1/docker-compose.yml index 44f5df26f..39bc6c4dc 100644 --- a/apps/freshrss-linuxserver/1.30.1/docker-compose.yml +++ b/apps/freshrss-linuxserver/1.30.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: freshrss: - image: "linuxserver/freshrss:1.30.1" + image: "linuxserver/freshrss:1.30.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/freshrss/1.30.1/docker-compose.yml b/apps/freshrss/1.30.1/docker-compose.yml index 1529c8ae5..cda8af18c 100644 --- a/apps/freshrss/1.30.1/docker-compose.yml +++ b/apps/freshrss/1.30.1/docker-compose.yml @@ -1,6 +1,6 @@ services: freshrss: - image: "freshrss/freshrss:1.30.1" + image: "freshrss/freshrss:1.30.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/.env.sample b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/.env.sample new file mode 100644 index 000000000..7eb61c2be --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41008 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/data.yml b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/data.yml new file mode 100644 index 000000000..385109d3a --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41008 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/docker-compose.yml b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/docker-compose.yml new file mode 100644 index 000000000..10662c9e1 --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/docker-compose.yml @@ -0,0 +1,35 @@ +services: + gimp-kasm: + image: "kasmweb/gimp:1.19.0-rolling-weekly@sha256:40d1ae6d0f3ba9eb601eeaf2e0f5853a96d0089ef8c5acc763285f3deeacf9c6" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/init.sh b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/uninstall.sh b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/upgrade.sh b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/gimp-kasm/1.19.0-rolling-weekly-digest-5f34d2d/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/gimp-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/gimp-kasm/1.19.0-rolling-weekly/docker-compose.yml index 6696329df..10662c9e1 100644 --- a/apps/gimp-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/gimp-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: gimp-kasm: - image: "kasmweb/gimp:1.19.0-rolling-weekly@sha256:b974cbed31eaf75b04d278b4ccc4b9ad3c30804f7dd022bf4458808a91fe9dea" + image: "kasmweb/gimp:1.19.0-rolling-weekly@sha256:40d1ae6d0f3ba9eb601eeaf2e0f5853a96d0089ef8c5acc763285f3deeacf9c6" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/glance/0.8.6/docker-compose.yml b/apps/glance/0.8.6/docker-compose.yml index 7b57399c8..cdd44caaa 100644 --- a/apps/glance/0.8.6/docker-compose.yml +++ b/apps/glance/0.8.6/docker-compose.yml @@ -1,6 +1,6 @@ services: glance: - image: "glanceapp/glance:v0.8.6" + image: "glanceapp/glance:v0.8.7" container_name: ${CONTAINER_NAME} restart: always networks: @@ -8,7 +8,7 @@ services: ports: - "${PANEL_APP_PORT_HTTP}:8080" volumes: - - ${GLANCE_CONFIG_PATH}:/app/glance.yml + - ${GLANCE_CONFIG_PATH}:/app/config/glance.yml - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro labels: diff --git a/apps/glance/0.8.7/.env.sample b/apps/glance/0.8.7/.env.sample new file mode 100644 index 000000000..e8331432c --- /dev/null +++ b/apps/glance/0.8.7/.env.sample @@ -0,0 +1,3 @@ +CONTAINER_NAME="glance" +GLANCE_CONFIG_PATH="./data/glance.yml" +PANEL_APP_PORT_HTTP=40281 diff --git a/apps/glance/0.8.7/data.yml b/apps/glance/0.8.7/data.yml new file mode 100644 index 000000000..66473f40f --- /dev/null +++ b/apps/glance/0.8.7/data.yml @@ -0,0 +1,35 @@ +additionalProperties: + formFields: + - default: "40281" + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: 'Port' + zh: '端口' + zh-Hant: '埠' + ja: 'ポート' + ko: '포트' + ru: 'Порт' + ms: 'Port' + pt-br: 'Porta' + required: true + rule: paramPort + type: number + - default: "./data/glance.yml" + disabled: true + envKey: GLANCE_CONFIG_PATH + labelEn: Glance Configuration Path + labelZh: Glance 配置文件路径 + label: + en: 'Glance Configuration Path' + zh: 'Glance 配置文件路径' + zh-Hant: 'Glance 設定檔路徑' + ja: 'Glance 設定パス' + ko: 'Glance 구성 경로' + ru: 'Путь к конфигурации Glance' + ms: 'Laluan konfigurasi Glance' + pt-br: 'Caminho de configuração do Glance' + required: true + type: text diff --git a/apps/glance/0.8.7/data/glance.yml b/apps/glance/0.8.7/data/glance.yml new file mode 100644 index 000000000..7cde96e97 --- /dev/null +++ b/apps/glance/0.8.7/data/glance.yml @@ -0,0 +1,106 @@ +theme: + background-color: 225 14 15 + primary-color: 157 47 65 + contrast-multiplier: 1.1 + +pages: + - name: Home + columns: + - size: small + widgets: + - type: clock + hour-format: 24h + timezones: + - timezone: Etc/UTC + label: MapleStory Game Time + - timezone: Asia/Shanghai + label: Shanghai + - timezone: America/New_York + label: New York + + - type: calendar + + - type: twitch-channels + channels: + - theprimeagen + - cohhcarnage + - christitustech + - blurbs + - asmongold + - jembawls + + - size: full + widgets: + - type: hacker-news + + - type: rss + limit: 10 + collapse-after: 3 + cache: 3h + style: detailed-list + feeds: + - url: https://supertechfans.com/cn/index.xml + title: HackerNews + + - type: videos + channels: + - UCR-DXc1voovS8nhAvccRZhg # Jeff Geerling + - UCv6J_jJa8GJqFwQNgNrMuww # ServeTheHome + - UCOk-gHyjcWZNj3Br4oxwh0A # Techno Tim + limit: 25 + collapse-after-rows: 2 + style: grid-cards + + - type: reddit + subreddit: selfhosted + + - size: small + widgets: + - type: weather + location: Beijing, China + + - type: bookmarks + groups: + - links: + - title: Google + url: https://google.com/ + - title: Github + url: https://github.com/ + - title: Hostloc + url: https://hostloc.com/ + - title: Nodeseek + url: https://www.nodeseek.com/ + - title: Linuxdo + url: https://linux.do/ + - title: V2ex + url: https://www.v2ex.com/ + - title: Entertainment + color: 10 70 50 + links: + - title: Bilibli + url: https://www.bilibili.com/ + - title: YouTube + url: https://www.youtube.com/ + - title: Netflix + url: https://www.netflix.com/ + - title: Disney+ + url: https://www.disneyplus.com/ + + - type: markets + markets: + - symbol: SPY + name: S&P 500 + - symbol: BTC-USD + name: Bitcoin + - symbol: NVDA + name: NVIDIA + - symbol: AAPL + name: Apple + - symbol: MSFT + name: Microsoft + - symbol: GOOGL + name: Google + - symbol: AMD + name: AMD + - symbol: RDDT + name: Reddit \ No newline at end of file diff --git a/apps/glance/0.8.7/docker-compose.yml b/apps/glance/0.8.7/docker-compose.yml new file mode 100644 index 000000000..cdd44caaa --- /dev/null +++ b/apps/glance/0.8.7/docker-compose.yml @@ -0,0 +1,19 @@ +services: + glance: + image: "glanceapp/glance:v0.8.7" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - ${GLANCE_CONFIG_PATH}:/app/config/glance.yml + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/glance/latest/docker-compose.yml b/apps/glance/latest/docker-compose.yml index 1dceda5b1..7404db598 100644 --- a/apps/glance/latest/docker-compose.yml +++ b/apps/glance/latest/docker-compose.yml @@ -8,7 +8,7 @@ services: ports: - "${PANEL_APP_PORT_HTTP}:8080" volumes: - - ${GLANCE_CONFIG_PATH}:/app/glance.yml + - ${GLANCE_CONFIG_PATH}:/app/config/glance.yml - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro labels: diff --git a/apps/godoxy/0.31.3/.env.sample b/apps/godoxy/0.31.4/.env.sample similarity index 100% rename from apps/godoxy/0.31.3/.env.sample rename to apps/godoxy/0.31.4/.env.sample diff --git a/apps/godoxy/0.31.3/config.yml b/apps/godoxy/0.31.4/config.yml similarity index 100% rename from apps/godoxy/0.31.3/config.yml rename to apps/godoxy/0.31.4/config.yml diff --git a/apps/godoxy/0.31.3/data.yml b/apps/godoxy/0.31.4/data.yml similarity index 100% rename from apps/godoxy/0.31.3/data.yml rename to apps/godoxy/0.31.4/data.yml diff --git a/apps/godoxy/0.31.3/docker-compose.yml b/apps/godoxy/0.31.4/docker-compose.yml similarity index 93% rename from apps/godoxy/0.31.3/docker-compose.yml rename to apps/godoxy/0.31.4/docker-compose.yml index 44c7c951f..0333933e3 100644 --- a/apps/godoxy/0.31.3/docker-compose.yml +++ b/apps/godoxy/0.31.4/docker-compose.yml @@ -25,7 +25,7 @@ services: createdBy: "Apps" godoxy: - image: "ghcr.io/yusing/godoxy:v0.31.3@sha256:3edf8b84879482160371a36b468a6be65f482bfcb579082e61d9f2b494cd6fab" + image: "ghcr.io/yusing/godoxy:v0.31.4@sha256:b74195a84eb6957d172ab985148c1a02f63ba37f35f7d5e80c063a0402b5aa16" container_name: ${CONTAINER_NAME} restart: unless-stopped depends_on: diff --git a/apps/godoxy/0.31.3/scripts/init.sh b/apps/godoxy/0.31.4/scripts/init.sh similarity index 100% rename from apps/godoxy/0.31.3/scripts/init.sh rename to apps/godoxy/0.31.4/scripts/init.sh diff --git a/apps/tianji/1.33.9/scripts/init.sh b/apps/godoxy/0.31.4/scripts/uninstall.sh similarity index 100% rename from apps/tianji/1.33.9/scripts/init.sh rename to apps/godoxy/0.31.4/scripts/uninstall.sh diff --git a/apps/godoxy/0.31.3/scripts/upgrade.sh b/apps/godoxy/0.31.4/scripts/upgrade.sh similarity index 100% rename from apps/godoxy/0.31.3/scripts/upgrade.sh rename to apps/godoxy/0.31.4/scripts/upgrade.sh diff --git a/apps/gotosocial/0.22.1/docker-compose.yml b/apps/gotosocial/0.22.1/docker-compose.yml index af79aea9a..8172113c8 100644 --- a/apps/gotosocial/0.22.1/docker-compose.yml +++ b/apps/gotosocial/0.22.1/docker-compose.yml @@ -1,6 +1,6 @@ services: gotosocial: - image: "docker.io/superseriousbusiness/gotosocial:0.22.1" + image: "docker.io/superseriousbusiness/gotosocial:0.22.3" container_name: ${CONTAINER_NAME} user: "1000:1000" restart: always diff --git a/apps/gotosocial/0.22.2/.env.sample b/apps/gotosocial/0.22.2/.env.sample new file mode 100644 index 000000000..314e6efb5 --- /dev/null +++ b/apps/gotosocial/0.22.2/.env.sample @@ -0,0 +1,7 @@ +PANEL_APP_PORT_HTTP=18084 +TZ=Asia/Shanghai +APP_DATA_DIR=./data +GTS_HOST=gotosocial.example.com +GTS_PROTOCOL=https +GTS_TRUSTED_PROXIES=127.0.0.1/32,::1,172.16.0.0/12 +CONTAINER_NAME=gotosocial diff --git a/apps/gotosocial/0.22.2/data.yml b/apps/gotosocial/0.22.2/data.yml new file mode 100644 index 000000000..b66d01940 --- /dev/null +++ b/apps/gotosocial/0.22.2/data.yml @@ -0,0 +1,104 @@ +additionalProperties: + formFields: + - default: 18084 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horario + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text + - default: gotosocial.example.com + edit: true + envKey: GTS_HOST + labelEn: Instance Domain + labelZh: 实例域名 + label: + en: Instance Domain + zh: 实例域名 + zh-Hant: 實例網域 + ja: インスタンスドメイン + ko: 인스턴스 도메인 + ru: Домен экземпляра + ms: Domain instans + pt-br: Dominio da instancia + required: true + type: text + - default: https + edit: true + envKey: GTS_PROTOCOL + labelEn: Public Protocol + labelZh: 公开访问协议 + label: + en: Public Protocol + zh: 公开访问协议 + zh-Hant: 公開存取協定 + ja: 公開プロトコル + ko: 공개 프로토콜 + ru: Публичный протокол + ms: Protokol awam + pt-br: Protocolo publico + required: true + type: select + values: + - label: https + value: https + - label: http + value: http + - default: 127.0.0.1/32,::1,172.16.0.0/12 + edit: true + envKey: GTS_TRUSTED_PROXIES + labelEn: Trusted Proxies + labelZh: 可信代理 + label: + en: Trusted Proxies + zh: 可信代理 + zh-Hant: 可信代理 + ja: 信頼済みプロキシ + ko: 신뢰할 수 있는 프록시 + ru: Доверенные прокси + ms: Proksi dipercayai + pt-br: Proxies confiaveis + required: true + type: text diff --git a/apps/gotosocial/0.22.2/data/.gitkeep b/apps/gotosocial/0.22.2/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/gotosocial/0.22.2/docker-compose.yml b/apps/gotosocial/0.22.2/docker-compose.yml new file mode 100644 index 000000000..8172113c8 --- /dev/null +++ b/apps/gotosocial/0.22.2/docker-compose.yml @@ -0,0 +1,30 @@ +services: + gotosocial: + image: "docker.io/superseriousbusiness/gotosocial:0.22.3" + container_name: ${CONTAINER_NAME} + user: "1000:1000" + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + environment: + - TZ=${TZ} + - GTS_HOST=${GTS_HOST} + - GTS_PROTOCOL=${GTS_PROTOCOL} + - GTS_BIND_ADDRESS=0.0.0.0 + - GTS_PORT=8080 + - GTS_DB_TYPE=sqlite + - GTS_DB_ADDRESS=/gotosocial/storage/sqlite.db + - GTS_LETSENCRYPT_ENABLED=false + - GTS_TRUSTED_PROXIES=${GTS_TRUSTED_PROXIES} + - GTS_WAZERO_COMPILATION_CACHE=/gotosocial/.cache + volumes: + - "${APP_DATA_DIR}/storage:/gotosocial/storage" + - "${APP_DATA_DIR}/cache:/gotosocial/.cache" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/gotosocial/0.22.2/scripts/.gitkeep b/apps/gotosocial/0.22.2/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/gotosocial/0.22.2/scripts/init.sh b/apps/gotosocial/0.22.2/scripts/init.sh new file mode 100644 index 000000000..783b47c7e --- /dev/null +++ b/apps/gotosocial/0.22.2/scripts/init.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +DATA_DIR="${APP_DATA_DIR:-./data}" +mkdir -p "${DATA_DIR}/storage" "${DATA_DIR}/cache" +chown -R 1000:1000 "${DATA_DIR}" 2>/dev/null || true diff --git a/apps/gotosocial/0.22.2/scripts/uninstall.sh b/apps/gotosocial/0.22.2/scripts/uninstall.sh new file mode 100644 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/gotosocial/0.22.2/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/unleash/8.1.0/scripts/upgrade.sh b/apps/gotosocial/0.22.2/scripts/upgrade.sh old mode 100755 new mode 100644 similarity index 100% rename from apps/unleash/8.1.0/scripts/upgrade.sh rename to apps/gotosocial/0.22.2/scripts/upgrade.sh diff --git a/apps/grafana/13.2.3/docker-compose.yml b/apps/grafana/13.2.3/docker-compose.yml index f02276c55..a2a03071a 100644 --- a/apps/grafana/13.2.3/docker-compose.yml +++ b/apps/grafana/13.2.3/docker-compose.yml @@ -9,7 +9,7 @@ services: volumes: - ./data:/var/lib/grafana user: '0' - image: grafana/grafana:13.2.3 + image: grafana/grafana:13.2.4 labels: createdBy: "Apps" diff --git a/apps/grav-linuxserver/2.2.5/docker-compose.yml b/apps/grav-linuxserver/2.2.5/docker-compose.yml index 6d5d023fd..c6b1d3636 100644 --- a/apps/grav-linuxserver/2.2.5/docker-compose.yml +++ b/apps/grav-linuxserver/2.2.5/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: grav: - image: "linuxserver/grav:2.2.5" + image: "linuxserver/grav:2.2.11" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/grav-linuxserver/2.2.6/.env.sample b/apps/grav-linuxserver/2.2.6/.env.sample new file mode 100644 index 000000000..48c7ba572 --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME= +PANEL_APP_PORT_HTTP=80 +CONFIG_PATH=./data/config +TIME_ZONE=Asia/Shanghai diff --git a/apps/grav-linuxserver/2.2.6/data.yml b/apps/grav-linuxserver/2.2.6/data.yml new file mode 100644 index 000000000..35bcc4eeb --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/data.yml @@ -0,0 +1,51 @@ +additionalProperties: + formFields: + - default: 80 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/grav-linuxserver/2.2.6/data/.gitkeep b/apps/grav-linuxserver/2.2.6/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/grav-linuxserver/2.2.6/docker-compose.yml b/apps/grav-linuxserver/2.2.6/docker-compose.yml new file mode 100644 index 000000000..c6b1d3636 --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/docker-compose.yml @@ -0,0 +1,20 @@ +networks: + 1panel-network: + external: true +services: + grav: + image: "linuxserver/grav:2.2.11" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + labels: + createdBy: "Apps" diff --git a/apps/grav-linuxserver/2.2.6/scripts/init.sh b/apps/grav-linuxserver/2.2.6/scripts/init.sh new file mode 100755 index 000000000..e5658f096 --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/scripts/init.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +paths=( + "${CONFIG_PATH:-./data/config}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/grav-linuxserver/2.2.6/scripts/uninstall.sh b/apps/grav-linuxserver/2.2.6/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/grav-linuxserver/2.2.6/scripts/upgrade.sh b/apps/grav-linuxserver/2.2.6/scripts/upgrade.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/grav-linuxserver/2.2.6/scripts/upgrade.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/headscale/0.29.4/docker-compose.yml b/apps/headscale/0.29.4/docker-compose.yml index f113c993a..43a1e439e 100644 --- a/apps/headscale/0.29.4/docker-compose.yml +++ b/apps/headscale/0.29.4/docker-compose.yml @@ -19,7 +19,7 @@ services: timeout: 5s retries: 5 command: ['serve'] - image: headscale/headscale:v0.29.4 + image: headscale/headscale:v0.29.5 labels: createdBy: "Apps" diff --git a/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml b/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml index 0caad2886..ccd16a46b 100644 --- a/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml +++ b/apps/healthchecks-linuxserver/4.4.20261005/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: healthchecks: - image: "linuxserver/healthchecks:4.4.20261005" + image: "linuxserver/healthchecks:5.0.20261020" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/hitkeep/2.14.1/docker-compose.yml b/apps/hitkeep/2.14.1/docker-compose.yml index d20f3f19d..40e6b3528 100644 --- a/apps/hitkeep/2.14.1/docker-compose.yml +++ b/apps/hitkeep/2.14.1/docker-compose.yml @@ -1,6 +1,6 @@ services: hitkeep: - image: "docker.io/pascalebeier/hitkeep:2.14.1@sha256:c1f1f770244e64331201c737df0d190fcca0af059f2a6d07b941409e3937be41" + image: "docker.io/pascalebeier/hitkeep:2.14.2@sha256:e13b3807ede2521cf4bdf3232fa12cbc8833bae2d85cde336bb4fc5eee347f5d" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:8080" diff --git a/apps/imgproxy/4.0.17/docker-compose.yml b/apps/imgproxy/4.0.17/docker-compose.yml index 884f4a5f1..7bbb46973 100644 --- a/apps/imgproxy/4.0.17/docker-compose.yml +++ b/apps/imgproxy/4.0.17/docker-compose.yml @@ -1,6 +1,6 @@ services: imgproxy: - image: "ghcr.io/imgproxy/imgproxy:v4.0.17" + image: "ghcr.io/imgproxy/imgproxy:v4.0.19" container_name: ${CONTAINER_NAME} restart: unless-stopped ports: diff --git a/apps/imgproxy/4.0.18/.env.sample b/apps/imgproxy/4.0.18/.env.sample new file mode 100644 index 000000000..e5ac72e13 --- /dev/null +++ b/apps/imgproxy/4.0.18/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=imgproxy-compose-check +PANEL_APP_PORT_HTTP=8080 +IMGPROXY_KEY= +IMGPROXY_SALT= +IMGPROXY_ALLOWED_SOURCES= +IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES=false diff --git a/apps/imgproxy/4.0.18/data.yml b/apps/imgproxy/4.0.18/data.yml new file mode 100644 index 000000000..5b14b111e --- /dev/null +++ b/apps/imgproxy/4.0.18/data.yml @@ -0,0 +1,88 @@ +additionalProperties: + formFields: + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: IMGPROXY_KEY + labelEn: Signature Key + labelZh: 签名 Key + label: + en: Signature Key + zh: 签名 Key + zh-Hant: 簽章金鑰 + ja: 署名キー + ko: 서명 키 + ru: Ключ подписи + ms: Kunci Tandatangan + pt-br: Chave de assinatura + required: false + type: password + - default: "" + edit: true + envKey: IMGPROXY_SALT + labelEn: Signature Salt + labelZh: 签名 Salt + label: + en: Signature Salt + zh: 签名 Salt + zh-Hant: 簽章鹽值 + ja: 署名ソルト + ko: 서명 솔트 + ru: Соль подписи + ms: Garam Tandatangan + pt-br: Salt de assinatura + required: false + type: password + - default: "" + edit: true + envKey: IMGPROXY_ALLOWED_SOURCES + labelEn: Allowed Sources + labelZh: 来源白名单 + label: + en: Allowed Sources + zh: 来源白名单 + zh-Hant: 來源允許清單 + ja: 許可する取得元 + ko: 허용된 원본 + ru: Разрешенные источники + ms: Sumber Dibenarkan + pt-br: Origens permitidas + required: false + type: text + - default: "false" + edit: true + envKey: IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES + labelEn: Allow Private Sources + labelZh: 允许私有网络来源 + label: + en: Allow Private Sources + zh: 允许私有网络来源 + zh-Hant: 允許私有網路來源 + ja: プライベートネットワークを許可 + ko: 사설 네트워크 원본 허용 + ru: Разрешить частные сети + ms: Benarkan Sumber Rangkaian Peribadi + pt-br: Permitir origens de rede privada + required: true + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" diff --git a/apps/imgproxy/4.0.18/docker-compose.yml b/apps/imgproxy/4.0.18/docker-compose.yml new file mode 100644 index 000000000..7bbb46973 --- /dev/null +++ b/apps/imgproxy/4.0.18/docker-compose.yml @@ -0,0 +1,36 @@ +services: + imgproxy: + image: "ghcr.io/imgproxy/imgproxy:v4.0.19" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + environment: + - IMGPROXY_KEY=${IMGPROXY_KEY} + - IMGPROXY_SALT=${IMGPROXY_SALT} + - IMGPROXY_ALLOWED_SOURCES=${IMGPROXY_ALLOWED_SOURCES} + - IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES=${IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES} + - IMGPROXY_ALLOW_LOOPBACK_SOURCE_ADDRESSES=false + - IMGPROXY_ALLOW_LINK_LOCAL_SOURCE_ADDRESSES=false + - IMGPROXY_IGNORE_SSL_VERIFICATION=false + healthcheck: + test: + - CMD + - imgproxy + - health + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + networks: + - 1panel-network + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/imgproxy/4.0.18/scripts/init.sh b/apps/imgproxy/4.0.18/scripts/init.sh new file mode 100755 index 000000000..2a53d75f3 --- /dev/null +++ b/apps/imgproxy/4.0.18/scripts/init.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/apps/imgproxy/4.0.18/scripts/uninstall.sh b/apps/imgproxy/4.0.18/scripts/uninstall.sh new file mode 100755 index 000000000..5a3f6bd6b --- /dev/null +++ b/apps/imgproxy/4.0.18/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --volumes --remove-orphans +else + docker compose down --volumes --remove-orphans +fi diff --git a/apps/imgproxy/4.0.18/scripts/upgrade.sh b/apps/imgproxy/4.0.18/scripts/upgrade.sh new file mode 100755 index 000000000..2a53d75f3 --- /dev/null +++ b/apps/imgproxy/4.0.18/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/apps/immich-kiosk/0.44.0/docker-compose.yml b/apps/immich-kiosk/0.44.0/docker-compose.yml index 4bc3f8b54..490755de6 100644 --- a/apps/immich-kiosk/0.44.0/docker-compose.yml +++ b/apps/immich-kiosk/0.44.0/docker-compose.yml @@ -1,6 +1,6 @@ services: immich-kiosk: - image: "ghcr.io/damongolding/immich-kiosk:0.44.0@sha256:b8d3f19dba36bfe7004f231cf238eb4e1ac21ebae9f57800e455196ed4a5c899" + image: "ghcr.io/damongolding/immich-kiosk:0.45.2@sha256:6d97f523c2b6e764284608a77e4a2b097ca335d10c70aa95b6bcc64831717360" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/.env.sample b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/.env.sample new file mode 100644 index 000000000..3c0334a41 --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTPS=41009 +HTTP_PWD=replace-with-a-random-value +SHM_SIZE=1024m +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/data.yml b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/data.yml new file mode 100644 index 000000000..8f3edf366 --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 41009 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: Порт HTTPS + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PWD + labelEn: Access Password + labelZh: 访问密码 + label: + en: Access Password + zh: 访问密码 + zh-Hant: 存取密碼 + ja: アクセスパスワード + ko: 접속 비밀번호 + ru: Пароль доступа + ms: Kata Laluan Akses + pt-br: Senha de Acesso + random: true + required: true + rule: paramComplexity + type: password + - default: 1024m + edit: true + envKey: SHM_SIZE + labelEn: Shared Memory Size + labelZh: 共享内存大小 + label: + en: Shared Memory Size + zh: 共享内存大小 + zh-Hant: 共享記憶體大小 + ja: 共有メモリサイズ + ko: 공유 메모리 크기 + ru: Размер общей памяти + ms: Saiz Memori Dikongsi + pt-br: Tamanho da Memória Compartilhada + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/docker-compose.yml b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/docker-compose.yml new file mode 100644 index 000000000..eb8ebf18b --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/docker-compose.yml @@ -0,0 +1,35 @@ +services: + inkscape-kasm: + image: "kasmweb/inkscape:1.19.0-rolling-weekly@sha256:90d159222ad53d5e9013d03fe35eed3304a84ed9ba183b0ef9b8c3c03618dee0" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTPS}:6901" + environment: + - VNC_PW=${HTTP_PWD} + shm_size: ${SHM_SIZE} + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD-SHELL + - >- + curl --insecure --fail --silent --show-error + --user "kasm_user:$$VNC_PW" + https://127.0.0.1:6901/ + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + volumes: + - "${APP_DATA_DIR}:/home/kasm-user" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/init.sh b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/init.sh new file mode 100755 index 000000000..338044ef8 --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/init.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +data_raw="${APP_DATA_DIR:-}" +if [[ -z "$data_raw" ]]; then + data_raw="$(read_env_value APP_DATA_DIR)" +fi +data_raw="${data_raw:-./data}" + +[[ "$data_raw" != /* ]] || { + printf '%s\n' 'APP_DATA_DIR must be a relative path' >&2 + exit 1 +} + +data_dir="$(realpath -m -- "$ROOT_DIR/${data_raw#./}")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; +esac + +mkdir -p -- "$data_dir" +resolved_data_dir="$(realpath -e -- "$data_dir")" +case "$resolved_data_dir" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; +esac + +chown 1000:1000 -- "$resolved_data_dir" +chmod 0700 -- "$resolved_data_dir" diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/uninstall.sh b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/uninstall.sh new file mode 100755 index 000000000..e043feffb --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Preserve APP_DATA_DIR so uninstall does not destroy user files. diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/upgrade.sh b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly-digest-c605d17/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/inkscape-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/inkscape-kasm/1.19.0-rolling-weekly/docker-compose.yml index 9ab892c57..eb8ebf18b 100644 --- a/apps/inkscape-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/inkscape-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: inkscape-kasm: - image: "kasmweb/inkscape:1.19.0-rolling-weekly@sha256:d920b6903269fbe45cac222f0c5bee04193e862eb037c6d51ffa613fda8b935d" + image: "kasmweb/inkscape:1.19.0-rolling-weekly@sha256:90d159222ad53d5e9013d03fe35eed3304a84ed9ba183b0ef9b8c3c03618dee0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/insomnia-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/insomnia-kasm/1.19.0-rolling-weekly/docker-compose.yml index 11719ee82..96d250b50 100644 --- a/apps/insomnia-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/insomnia-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: insomnia-kasm: - image: "kasmweb/insomnia:1.19.0-rolling-weekly@sha256:523abab3afbb94114c761f0acadb9e3be8ed691cc4d1599feb5ee8e3287074bc" + image: "kasmweb/insomnia:1.19.0-rolling-weekly@sha256:09fad0138a405f0e16804614d39f51f3249b12f066e033c51012688d0aae7e16" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/iptv-api/3.0.0/docker-compose.yml b/apps/iptv-api/3.0.0/docker-compose.yml index e59674dee..d06d56442 100644 --- a/apps/iptv-api/3.0.0/docker-compose.yml +++ b/apps/iptv-api/3.0.0/docker-compose.yml @@ -1,6 +1,6 @@ services: iptv-api: - image: "guovern/iptv-api:3.0.0" + image: "guovern/iptv-api:3.1.0" container_name: ${CONTAINER_NAME} restart: always tty: true diff --git a/apps/isrvd/slim/docker-compose.yml b/apps/isrvd/slim/docker-compose.yml index 592ff2649..6c8d2789c 100644 --- a/apps/isrvd/slim/docker-compose.yml +++ b/apps/isrvd/slim/docker-compose.yml @@ -1,6 +1,6 @@ services: isrvd: - image: "rehiy/isrvd:slim@sha256:e1af9f5ad2acfe0c60aacec81abba819fc3b9cf7662b808ab95540f7fba6f337" + image: "rehiy/isrvd:slim@sha256:f2b4e558f08699b5691eb2690f04e3ee3026dde7825313423f4f9f475bc07c2e" container_name: ${CONTAINER_NAME} entrypoint: - /data/conf/app-entrypoint.sh diff --git a/apps/kanboard/1.2.54/docker-compose.yml b/apps/kanboard/1.2.54/docker-compose.yml index 48b2ac23f..2b316eca3 100644 --- a/apps/kanboard/1.2.54/docker-compose.yml +++ b/apps/kanboard/1.2.54/docker-compose.yml @@ -1,6 +1,6 @@ services: kanboard: - image: "kanboard/kanboard:v1.2.54" + image: "kanboard/kanboard:v1.2.56" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/.env.sample b/apps/kaneo/2.26.0-postgres16-digest-721873c/.env.sample new file mode 100644 index 000000000..a92b6d019 --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/.env.sample @@ -0,0 +1,10 @@ +CONTAINER_NAME=kaneo +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=5173 +KANEO_CLIENT_URL=http://localhost:5173 +DISABLE_REGISTRATION=false +POSTGRES_DB=kaneo +POSTGRES_USER=kaneo +POSTGRES_PASSWORD=generate +AUTH_SECRET=generate +APP_DATA_DIR=./data diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/data.yml b/apps/kaneo/2.26.0-postgres16-digest-721873c/data.yml new file mode 100644 index 000000000..0fc03fec2 --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/data.yml @@ -0,0 +1,156 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 5173 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://localhost:5173 + edit: true + envKey: KANEO_CLIENT_URL + labelEn: Public URL + labelZh: 公开访问地址 + label: + en: Public URL + zh: 公开访问地址 + zh-Hant: 公開存取網址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publica + required: true + type: text + - default: "false" + edit: true + envKey: DISABLE_REGISTRATION + labelEn: Disable Registration + labelZh: 禁止新用户注册 + label: + en: Disable Registration + zh: 禁止新用户注册 + zh-Hant: 停用新使用者註冊 + ja: 新規登録を無効化 + ko: 신규 등록 비활성화 + ru: Отключить регистрацию + ms: Nyahdayakan pendaftaran + pt-br: Desativar cadastro + required: true + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: kaneo + edit: true + envKey: POSTGRES_DB + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: kaneo + edit: true + envKey: POSTGRES_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: generate + edit: true + envKey: POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: generate + edit: true + envKey: AUTH_SECRET + labelEn: Authentication Secret + labelZh: 认证密钥 + label: + en: Authentication Secret + zh: 认证密钥 + zh-Hant: 驗證密鑰 + ja: 認証シークレット + ko: 인증 비밀 키 + ru: Секрет аутентификации + ms: Rahsia pengesahan + pt-br: Segredo de autenticacao + required: true + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/docker-compose.yml b/apps/kaneo/2.26.0-postgres16-digest-721873c/docker-compose.yml new file mode 100644 index 000000000..83b0ea689 --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/docker-compose.yml @@ -0,0 +1,73 @@ +services: + kaneo-postgres: + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - kaneo-internal + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + read_only: true + tmpfs: + - /tmp:mode=1777,size=64m + - /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + labels: + createdBy: "Apps" + + kaneo: + image: ghcr.io/usekaneo/kaneo:2.26.0@sha256:549af9f6faf74825171badeda63db124461125bf9151425519b580513d4e4f5b + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1001:1001" + depends_on: + kaneo-postgres: + condition: service_healthy + networks: + - kaneo-internal + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:5173" + environment: + KANEO_CLIENT_URL: ${KANEO_CLIENT_URL} + POSTGRES_HOST: kaneo-postgres + POSTGRES_PORT: "5432" + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + AUTH_SECRET: ${AUTH_SECRET} + DISABLE_REGISTRATION: ${DISABLE_REGISTRATION} + tmpfs: + - /tmp:mode=1777,size=64m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5173/api/health"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + kaneo-internal: + internal: true + 1panel-network: + external: true diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/init.sh b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/init.sh new file mode 100755 index 000000000..0881dba08 --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/init.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +configured_value() { + local key="$1" + local default_value="$2" + local value + value="${!key:-}" + if [[ -z "$value" ]]; then + value="$(read_env_value "$key")" + fi + printf '%s\n' "${value:-$default_value}" +} + +resolve_app_path() { + local key="$1" + local raw="$2" + local clean candidate resolved current part + local -a parts=() + case "$raw" in + ""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;; + esac + if [[ "$raw" =~ [[:cntrl:]] ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + clean="${raw#./}" + [[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; } + command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; } + case "$resolved" in + "$ROOT_DIR"/*) ;; + *) echo "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in "${parts[@]}"; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + done + printf '%s\n' "$resolved" +} + +set_env_value() { + local key="$1" + local value + local temp_file + case "$key" in + POSTGRES_PASSWORD|AUTH_SECRET) value="${!key}" ;; + *) fail "unsupported environment key: $key" ;; + esac + temp_file="$(mktemp "${ROOT_DIR}/.kaneo-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +validate_ipv4() { + local key="$1" + local value="$2" + local octet + local -a octets + [[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address" + IFS=. read -r -a octets <<< "$value" + for octet in "${octets[@]}"; do + ((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet" + done +} + +validate_port() { + local key="$1" + local value="$2" + [[ "$value" =~ ^[0-9]+$ ]] || fail "$key must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "$key must be between 1 and 65535" +} + +generate_alphanumeric() { + local length="$1" + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge $length ]] || fail "unable to generate sufficient random material" + printf '%s\n' "${material:0:length}" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Kaneo init must run as root" +command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" + +validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" +validate_port PANEL_APP_PORT_HTTP "$(read_env_value PANEL_APP_PORT_HTTP)" + +client_url="$(read_env_value KANEO_CLIENT_URL)" +[[ "$client_url" =~ ^https?://([A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?|localhost)(:([0-9]{1,5}))?$ ]] || + fail "KANEO_CLIENT_URL must be an HTTP(S) origin without a path" +[[ "$client_url" != *..* ]] || fail "KANEO_CLIENT_URL contains an invalid hostname" +if [[ -n "${BASH_REMATCH[4]:-}" ]]; then + validate_port KANEO_CLIENT_URL_PORT "${BASH_REMATCH[4]}" +fi + +disable_registration="$(read_env_value DISABLE_REGISTRATION)" +[[ "$disable_registration" == true || "$disable_registration" == false ]] || + fail "DISABLE_REGISTRATION must be true or false" + +postgres_db="$(read_env_value POSTGRES_DB)" +postgres_user="$(read_env_value POSTGRES_USER)" +[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" +[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" + +POSTGRES_PASSWORD="$(read_env_value POSTGRES_PASSWORD)" +if [[ -z "$POSTGRES_PASSWORD" || "$POSTGRES_PASSWORD" == generate ]]; then + POSTGRES_PASSWORD="$(generate_alphanumeric 48)" +fi +[[ "$POSTGRES_PASSWORD" =~ ^[A-Za-z0-9]{32,128}$ ]] || + fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + +AUTH_SECRET="$(read_env_value AUTH_SECRET)" +if [[ -z "$AUTH_SECRET" || "$AUTH_SECRET" == generate ]]; then + AUTH_SECRET="$(openssl rand -hex 32)" +fi +[[ "$AUTH_SECRET" =~ ^[A-Fa-f0-9]{64,128}$ ]] || + fail "AUTH_SECRET must contain 64 to 128 hexadecimal characters" + +data_raw="$(configured_value APP_DATA_DIR ./data)" +data_dir="$(resolve_app_path APP_DATA_DIR "$data_raw")" + +install -d -m 0700 -- "$data_dir/postgres" +resolved_data="$(realpath -e -- "$data_dir")" +case "$resolved_data" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +chown -R 70:70 -- "$resolved_data/postgres" +chmod 0700 -- "$resolved_data/postgres" + +set_env_value POSTGRES_PASSWORD +set_env_value AUTH_SECRET +chmod 600 "$ENV_FILE" diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/uninstall.sh b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/uninstall.sh new file mode 100755 index 000000000..5c199242b --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --remove-orphans +else + docker compose down --remove-orphans +fi diff --git a/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/upgrade.sh b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/upgrade.sh new file mode 100755 index 000000000..fc9c84487 --- /dev/null +++ b/apps/kaneo/2.26.0-postgres16-digest-721873c/scripts/upgrade.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +read_env_value() { + local file="$1" + local key="$2" + local value + value="$(sed -n "s/^${key}=//p" "$file" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +resolve_app_path() { + local key="$1" + local raw="$2" + local clean candidate resolved current part + local -a parts=() + case "$raw" in + ""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;; + esac + if [[ "$raw" =~ [[:cntrl:]] ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + clean="${raw#./}" + [[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; } + command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; } + case "$resolved" in + "$ROOT_DIR"/*) ;; + *) echo "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in "${parts[@]}"; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + done + printf '%s\n' "$resolved" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ENV_FILE}.kaneo-upgrade.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +restore_credentials_from_env() { + local candidate="$1" + local current_container current_db current_user current_password current_auth + local candidate_container candidate_db candidate_user + local candidate_password candidate_auth + + [[ -f "$candidate" && ! -L "$candidate" ]] || return 1 + current_password="$(read_env_value "$ENV_FILE" POSTGRES_PASSWORD)" + current_auth="$(read_env_value "$ENV_FILE" AUTH_SECRET)" + current_container="$(read_env_value "$ENV_FILE" CONTAINER_NAME)" + current_db="$(read_env_value "$ENV_FILE" POSTGRES_DB)" + current_user="$(read_env_value "$ENV_FILE" POSTGRES_USER)" + candidate_container="$(read_env_value "$candidate" CONTAINER_NAME)" + candidate_db="$(read_env_value "$candidate" POSTGRES_DB)" + candidate_user="$(read_env_value "$candidate" POSTGRES_USER)" + [[ "$candidate_container" == "$current_container" && + "$candidate_db" == "$current_db" && + "$candidate_user" == "$current_user" ]] || return 1 + + candidate_password="$(read_env_value "$candidate" POSTGRES_PASSWORD)" + candidate_auth="$(read_env_value "$candidate" AUTH_SECRET)" + [[ "$candidate_password" =~ ^[A-Za-z0-9]{32,128}$ ]] || return 1 + [[ "$candidate_auth" =~ ^[A-Fa-f0-9]{64,128}$ ]] || return 1 + if [[ "$current_password" == "" || "$current_password" == generate ]]; then + set_env_value POSTGRES_PASSWORD "$candidate_password" + fi + if [[ "$current_auth" == "" || "$current_auth" == generate ]]; then + set_env_value AUTH_SECRET "$candidate_auth" + fi + return 0 +} + +restore_credentials_from_backup() ( + local panel_root app_key install_name backup_dir archive archive_entry + local temp_dir inner_archive env_entry latest_mtime archive_mtime + + panel_root="${ROOT_DIR%%/apps/*}" + [[ "$panel_root" != "$ROOT_DIR" ]] || exit 1 + app_key="$(basename "$(dirname "$ROOT_DIR")")" + install_name="$(basename "$ROOT_DIR")" + [[ "$app_key" =~ ^[A-Za-z0-9._-]+$ && "$install_name" =~ ^[A-Za-z0-9._-]+$ ]] || exit 1 + backup_dir="$panel_root/backup/app/local${app_key}/${install_name}" + [[ -d "$backup_dir" && ! -L "$backup_dir" ]] || exit 1 + + archive="" + latest_mtime=0 + while IFS= read -r -d '' candidate; do + archive_mtime="$(stat -c '%Y' "$candidate")" + if [[ -z "$archive" ]] || (( archive_mtime > latest_mtime )); then + archive="$candidate" + latest_mtime="$archive_mtime" + fi + done < <(find "$backup_dir" -maxdepth 1 -type f -name 'upgrade_backup_*.tar.gz' -print0) + [[ -n "$archive" ]] || exit 1 + + temp_dir="$(mktemp -d "$ROOT_DIR/.kaneo-upgrade-backup.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + inner_archive="$temp_dir/app.tar.gz" + archive_entry="$(tar -tzf "$archive" | awk '$0 ~ /\/app\.tar\.gz$/ { print; exit }')" + [[ -n "$archive_entry" ]] || exit 1 + tar -xOf "$archive" "$archive_entry" > "$inner_archive" + env_entry="$install_name/.env" + tar -tzf "$inner_archive" | grep -Fqx "$env_entry" || exit 1 + tar -xOf "$inner_archive" "$env_entry" > "$temp_dir/old.env" + restore_credentials_from_env "$temp_dir/old.env" +) + +restore_previous_credentials() { + local current_password current_auth candidate + local -a snapshots + + current_password="$(read_env_value "$ENV_FILE" POSTGRES_PASSWORD)" + current_auth="$(read_env_value "$ENV_FILE" AUTH_SECRET)" + if [[ "$current_password" != "" && "$current_password" != generate && + "$current_auth" != "" && "$current_auth" != generate ]]; then + return 0 + fi + + shopt -s nullglob + snapshots=(/tmp/1panel-app-upgrade-*/.env) + shopt -u nullglob + for candidate in "${snapshots[@]}"; do + restore_credentials_from_env "$candidate" && return 0 + done + restore_credentials_from_backup +} + +[[ -f "$ENV_FILE" ]] || { + printf '%s\n' "$ENV_FILE not found" >&2 + exit 1 +} +if ! restore_previous_credentials; then + data_raw="$(read_env_value "$ENV_FILE" APP_DATA_DIR)" + data_dir="$(resolve_app_path APP_DATA_DIR "$data_raw")" + if [[ -f "$data_dir/postgres/PG_VERSION" ]]; then + printf '%s\n' 'Kaneo upgrade could not recover existing database credentials' >&2 + exit 1 + fi +fi +exec "$SCRIPT_DIR/init.sh" diff --git a/apps/kaneo/2.26.0/docker-compose.yml b/apps/kaneo/2.26.0/docker-compose.yml index df19bb406..83b0ea689 100644 --- a/apps/kaneo/2.26.0/docker-compose.yml +++ b/apps/kaneo/2.26.0/docker-compose.yml @@ -1,6 +1,6 @@ services: kaneo-postgres: - image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777 + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 container_name: ${CONTAINER_NAME}-postgres restart: unless-stopped user: "70:70" diff --git a/apps/kaneo/2.27.0/.env.sample b/apps/kaneo/2.27.0/.env.sample new file mode 100644 index 000000000..a92b6d019 --- /dev/null +++ b/apps/kaneo/2.27.0/.env.sample @@ -0,0 +1,10 @@ +CONTAINER_NAME=kaneo +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=5173 +KANEO_CLIENT_URL=http://localhost:5173 +DISABLE_REGISTRATION=false +POSTGRES_DB=kaneo +POSTGRES_USER=kaneo +POSTGRES_PASSWORD=generate +AUTH_SECRET=generate +APP_DATA_DIR=./data diff --git a/apps/kaneo/2.27.0/data.yml b/apps/kaneo/2.27.0/data.yml new file mode 100644 index 000000000..0fc03fec2 --- /dev/null +++ b/apps/kaneo/2.27.0/data.yml @@ -0,0 +1,156 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 5173 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://localhost:5173 + edit: true + envKey: KANEO_CLIENT_URL + labelEn: Public URL + labelZh: 公开访问地址 + label: + en: Public URL + zh: 公开访问地址 + zh-Hant: 公開存取網址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publica + required: true + type: text + - default: "false" + edit: true + envKey: DISABLE_REGISTRATION + labelEn: Disable Registration + labelZh: 禁止新用户注册 + label: + en: Disable Registration + zh: 禁止新用户注册 + zh-Hant: 停用新使用者註冊 + ja: 新規登録を無効化 + ko: 신규 등록 비활성화 + ru: Отключить регистрацию + ms: Nyahdayakan pendaftaran + pt-br: Desativar cadastro + required: true + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: kaneo + edit: true + envKey: POSTGRES_DB + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: kaneo + edit: true + envKey: POSTGRES_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: generate + edit: true + envKey: POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: generate + edit: true + envKey: AUTH_SECRET + labelEn: Authentication Secret + labelZh: 认证密钥 + label: + en: Authentication Secret + zh: 认证密钥 + zh-Hant: 驗證密鑰 + ja: 認証シークレット + ko: 인증 비밀 키 + ru: Секрет аутентификации + ms: Rahsia pengesahan + pt-br: Segredo de autenticacao + required: true + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/kaneo/2.27.0/docker-compose.yml b/apps/kaneo/2.27.0/docker-compose.yml new file mode 100644 index 000000000..e8d83cbc6 --- /dev/null +++ b/apps/kaneo/2.27.0/docker-compose.yml @@ -0,0 +1,73 @@ +services: + kaneo-postgres: + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - kaneo-internal + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + read_only: true + tmpfs: + - /tmp:mode=1777,size=64m + - /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + labels: + createdBy: "Apps" + + kaneo: + image: ghcr.io/usekaneo/kaneo:2.27.0@sha256:51178ddb9010f5beb2e32e93f84234ecf408a751764b9d3f85582ed87355901e + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1001:1001" + depends_on: + kaneo-postgres: + condition: service_healthy + networks: + - kaneo-internal + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:5173" + environment: + KANEO_CLIENT_URL: ${KANEO_CLIENT_URL} + POSTGRES_HOST: kaneo-postgres + POSTGRES_PORT: "5432" + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + AUTH_SECRET: ${AUTH_SECRET} + DISABLE_REGISTRATION: ${DISABLE_REGISTRATION} + tmpfs: + - /tmp:mode=1777,size=64m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5173/api/health"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + kaneo-internal: + internal: true + 1panel-network: + external: true diff --git a/apps/kaneo/2.27.0/scripts/init.sh b/apps/kaneo/2.27.0/scripts/init.sh new file mode 100755 index 000000000..0881dba08 --- /dev/null +++ b/apps/kaneo/2.27.0/scripts/init.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +configured_value() { + local key="$1" + local default_value="$2" + local value + value="${!key:-}" + if [[ -z "$value" ]]; then + value="$(read_env_value "$key")" + fi + printf '%s\n' "${value:-$default_value}" +} + +resolve_app_path() { + local key="$1" + local raw="$2" + local clean candidate resolved current part + local -a parts=() + case "$raw" in + ""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;; + esac + if [[ "$raw" =~ [[:cntrl:]] ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + clean="${raw#./}" + [[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; } + command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; } + case "$resolved" in + "$ROOT_DIR"/*) ;; + *) echo "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in "${parts[@]}"; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + done + printf '%s\n' "$resolved" +} + +set_env_value() { + local key="$1" + local value + local temp_file + case "$key" in + POSTGRES_PASSWORD|AUTH_SECRET) value="${!key}" ;; + *) fail "unsupported environment key: $key" ;; + esac + temp_file="$(mktemp "${ROOT_DIR}/.kaneo-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +validate_ipv4() { + local key="$1" + local value="$2" + local octet + local -a octets + [[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address" + IFS=. read -r -a octets <<< "$value" + for octet in "${octets[@]}"; do + ((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet" + done +} + +validate_port() { + local key="$1" + local value="$2" + [[ "$value" =~ ^[0-9]+$ ]] || fail "$key must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "$key must be between 1 and 65535" +} + +generate_alphanumeric() { + local length="$1" + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge $length ]] || fail "unable to generate sufficient random material" + printf '%s\n' "${material:0:length}" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Kaneo init must run as root" +command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" + +validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" +validate_port PANEL_APP_PORT_HTTP "$(read_env_value PANEL_APP_PORT_HTTP)" + +client_url="$(read_env_value KANEO_CLIENT_URL)" +[[ "$client_url" =~ ^https?://([A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?|localhost)(:([0-9]{1,5}))?$ ]] || + fail "KANEO_CLIENT_URL must be an HTTP(S) origin without a path" +[[ "$client_url" != *..* ]] || fail "KANEO_CLIENT_URL contains an invalid hostname" +if [[ -n "${BASH_REMATCH[4]:-}" ]]; then + validate_port KANEO_CLIENT_URL_PORT "${BASH_REMATCH[4]}" +fi + +disable_registration="$(read_env_value DISABLE_REGISTRATION)" +[[ "$disable_registration" == true || "$disable_registration" == false ]] || + fail "DISABLE_REGISTRATION must be true or false" + +postgres_db="$(read_env_value POSTGRES_DB)" +postgres_user="$(read_env_value POSTGRES_USER)" +[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" +[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" + +POSTGRES_PASSWORD="$(read_env_value POSTGRES_PASSWORD)" +if [[ -z "$POSTGRES_PASSWORD" || "$POSTGRES_PASSWORD" == generate ]]; then + POSTGRES_PASSWORD="$(generate_alphanumeric 48)" +fi +[[ "$POSTGRES_PASSWORD" =~ ^[A-Za-z0-9]{32,128}$ ]] || + fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + +AUTH_SECRET="$(read_env_value AUTH_SECRET)" +if [[ -z "$AUTH_SECRET" || "$AUTH_SECRET" == generate ]]; then + AUTH_SECRET="$(openssl rand -hex 32)" +fi +[[ "$AUTH_SECRET" =~ ^[A-Fa-f0-9]{64,128}$ ]] || + fail "AUTH_SECRET must contain 64 to 128 hexadecimal characters" + +data_raw="$(configured_value APP_DATA_DIR ./data)" +data_dir="$(resolve_app_path APP_DATA_DIR "$data_raw")" + +install -d -m 0700 -- "$data_dir/postgres" +resolved_data="$(realpath -e -- "$data_dir")" +case "$resolved_data" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +chown -R 70:70 -- "$resolved_data/postgres" +chmod 0700 -- "$resolved_data/postgres" + +set_env_value POSTGRES_PASSWORD +set_env_value AUTH_SECRET +chmod 600 "$ENV_FILE" diff --git a/apps/kaneo/2.27.0/scripts/uninstall.sh b/apps/kaneo/2.27.0/scripts/uninstall.sh new file mode 100755 index 000000000..5c199242b --- /dev/null +++ b/apps/kaneo/2.27.0/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --remove-orphans +else + docker compose down --remove-orphans +fi diff --git a/apps/kaneo/2.27.0/scripts/upgrade.sh b/apps/kaneo/2.27.0/scripts/upgrade.sh new file mode 100755 index 000000000..fc9c84487 --- /dev/null +++ b/apps/kaneo/2.27.0/scripts/upgrade.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +read_env_value() { + local file="$1" + local key="$2" + local value + value="$(sed -n "s/^${key}=//p" "$file" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +resolve_app_path() { + local key="$1" + local raw="$2" + local clean candidate resolved current part + local -a parts=() + case "$raw" in + ""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;; + esac + if [[ "$raw" =~ [[:cntrl:]] ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + clean="${raw#./}" + [[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; } + command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; } + case "$resolved" in + "$ROOT_DIR"/*) ;; + *) echo "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in "${parts[@]}"; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + echo "unsafe ${key} path" >&2 + return 1 + fi + done + printf '%s\n' "$resolved" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ENV_FILE}.kaneo-upgrade.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +restore_credentials_from_env() { + local candidate="$1" + local current_container current_db current_user current_password current_auth + local candidate_container candidate_db candidate_user + local candidate_password candidate_auth + + [[ -f "$candidate" && ! -L "$candidate" ]] || return 1 + current_password="$(read_env_value "$ENV_FILE" POSTGRES_PASSWORD)" + current_auth="$(read_env_value "$ENV_FILE" AUTH_SECRET)" + current_container="$(read_env_value "$ENV_FILE" CONTAINER_NAME)" + current_db="$(read_env_value "$ENV_FILE" POSTGRES_DB)" + current_user="$(read_env_value "$ENV_FILE" POSTGRES_USER)" + candidate_container="$(read_env_value "$candidate" CONTAINER_NAME)" + candidate_db="$(read_env_value "$candidate" POSTGRES_DB)" + candidate_user="$(read_env_value "$candidate" POSTGRES_USER)" + [[ "$candidate_container" == "$current_container" && + "$candidate_db" == "$current_db" && + "$candidate_user" == "$current_user" ]] || return 1 + + candidate_password="$(read_env_value "$candidate" POSTGRES_PASSWORD)" + candidate_auth="$(read_env_value "$candidate" AUTH_SECRET)" + [[ "$candidate_password" =~ ^[A-Za-z0-9]{32,128}$ ]] || return 1 + [[ "$candidate_auth" =~ ^[A-Fa-f0-9]{64,128}$ ]] || return 1 + if [[ "$current_password" == "" || "$current_password" == generate ]]; then + set_env_value POSTGRES_PASSWORD "$candidate_password" + fi + if [[ "$current_auth" == "" || "$current_auth" == generate ]]; then + set_env_value AUTH_SECRET "$candidate_auth" + fi + return 0 +} + +restore_credentials_from_backup() ( + local panel_root app_key install_name backup_dir archive archive_entry + local temp_dir inner_archive env_entry latest_mtime archive_mtime + + panel_root="${ROOT_DIR%%/apps/*}" + [[ "$panel_root" != "$ROOT_DIR" ]] || exit 1 + app_key="$(basename "$(dirname "$ROOT_DIR")")" + install_name="$(basename "$ROOT_DIR")" + [[ "$app_key" =~ ^[A-Za-z0-9._-]+$ && "$install_name" =~ ^[A-Za-z0-9._-]+$ ]] || exit 1 + backup_dir="$panel_root/backup/app/local${app_key}/${install_name}" + [[ -d "$backup_dir" && ! -L "$backup_dir" ]] || exit 1 + + archive="" + latest_mtime=0 + while IFS= read -r -d '' candidate; do + archive_mtime="$(stat -c '%Y' "$candidate")" + if [[ -z "$archive" ]] || (( archive_mtime > latest_mtime )); then + archive="$candidate" + latest_mtime="$archive_mtime" + fi + done < <(find "$backup_dir" -maxdepth 1 -type f -name 'upgrade_backup_*.tar.gz' -print0) + [[ -n "$archive" ]] || exit 1 + + temp_dir="$(mktemp -d "$ROOT_DIR/.kaneo-upgrade-backup.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + inner_archive="$temp_dir/app.tar.gz" + archive_entry="$(tar -tzf "$archive" | awk '$0 ~ /\/app\.tar\.gz$/ { print; exit }')" + [[ -n "$archive_entry" ]] || exit 1 + tar -xOf "$archive" "$archive_entry" > "$inner_archive" + env_entry="$install_name/.env" + tar -tzf "$inner_archive" | grep -Fqx "$env_entry" || exit 1 + tar -xOf "$inner_archive" "$env_entry" > "$temp_dir/old.env" + restore_credentials_from_env "$temp_dir/old.env" +) + +restore_previous_credentials() { + local current_password current_auth candidate + local -a snapshots + + current_password="$(read_env_value "$ENV_FILE" POSTGRES_PASSWORD)" + current_auth="$(read_env_value "$ENV_FILE" AUTH_SECRET)" + if [[ "$current_password" != "" && "$current_password" != generate && + "$current_auth" != "" && "$current_auth" != generate ]]; then + return 0 + fi + + shopt -s nullglob + snapshots=(/tmp/1panel-app-upgrade-*/.env) + shopt -u nullglob + for candidate in "${snapshots[@]}"; do + restore_credentials_from_env "$candidate" && return 0 + done + restore_credentials_from_backup +} + +[[ -f "$ENV_FILE" ]] || { + printf '%s\n' "$ENV_FILE not found" >&2 + exit 1 +} +if ! restore_previous_credentials; then + data_raw="$(read_env_value "$ENV_FILE" APP_DATA_DIR)" + data_dir="$(resolve_app_path APP_DATA_DIR "$data_raw")" + if [[ -f "$data_dir/postgres/PG_VERSION" ]]; then + printf '%s\n' 'Kaneo upgrade could not recover existing database credentials' >&2 + exit 1 + fi +fi +exec "$SCRIPT_DIR/init.sh" diff --git a/apps/keila/0.30.3/.env.sample b/apps/keila/0.30.3/.env.sample new file mode 100644 index 000000000..4a20393b2 --- /dev/null +++ b/apps/keila/0.30.3/.env.sample @@ -0,0 +1,23 @@ +PANEL_APP_PORT_HTTP=4000 +PANEL_DB_HOST= +PANEL_DB_PORT=5432 +PANEL_DB_NAME=keila +PANEL_DB_USER=keila +PANEL_DB_USER_PASSWORD= +KEILA_USER=root@localhost +KEILA_PASSWORD= +SECRET_KEY_BASE= +URL_HOST=localhost +URL_SCHEMA=http +URL_PORT=4000 +APP_DATA_DIR=./data +MAILER_SMTP_HOST= +MAILER_SMTP_PORT=587 +MAILER_SMTP_FROM_EMAIL= +MAILER_SMTP_USER= +MAILER_SMTP_PASSWORD= +MAILER_ENABLE_SSL=false +MAILER_ENABLE_STARTTLS=false +LOG_LEVEL=info +TZ=Asia/Shanghai +CONTAINER_NAME= diff --git a/apps/keila/0.30.3/data.yml b/apps/keila/0.30.3/data.yml new file mode 100644 index 000000000..034f34e2c --- /dev/null +++ b/apps/keila/0.30.3/data.yml @@ -0,0 +1,401 @@ +additionalProperties: + formFields: + - default: 4000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: postgresql + envKey: PANEL_DB_TYPE + labelEn: Database Runtime + labelZh: 数据库 Runtime + label: + en: Database Runtime + zh: 数据库 Runtime + zh-Hant: 資料庫 Runtime + ja: データベース Runtime + ko: 데이터베이스 Runtime + ru: Среда базы данных + ms: Runtime Pangkalan Data + pt-br: Runtime de banco de dados + required: true + type: apps + values: + - label: PostgreSQL + value: postgresql + child: + default: '' + envKey: PANEL_DB_HOST + labelEn: PostgreSQL Service + labelZh: PostgreSQL 服务 + label: + en: PostgreSQL Service + zh: PostgreSQL 服务 + zh-Hant: PostgreSQL 服務 + ja: PostgreSQL サービス + ko: PostgreSQL 서비스 + ru: Сервис PostgreSQL + ms: Perkhidmatan PostgreSQL + pt-br: Serviço PostgreSQL + required: true + type: service + - default: 5432 + edit: true + envKey: PANEL_DB_PORT + labelEn: Database Port + labelZh: 数据库端口 + label: + en: Database Port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port Pangkalan Data + pt-br: Porta do banco de dados + required: true + rule: paramPort + type: number + - default: keila + edit: true + envKey: PANEL_DB_NAME + labelEn: Database Name + labelZh: 数据库名 + label: + en: Database Name + zh: 数据库名 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama Pangkalan Data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: keila + edit: true + envKey: PANEL_DB_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna Pangkalan Data + pt-br: Usuário do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: '' + edit: true + envKey: PANEL_DB_USER_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata Laluan Pangkalan Data + pt-br: Senha do banco de dados + required: true + rule: paramComplexity + type: password + - default: root@localhost + edit: true + envKey: KEILA_USER + labelEn: Initial Administrator Email + labelZh: 初始管理员邮箱 + label: + en: Initial Administrator Email + zh: 初始管理员邮箱 + zh-Hant: 初始管理員電子郵件 + ja: 初期管理者メールアドレス + ko: 초기 관리자 이메일 + ru: Email начального администратора + ms: E-mel Pentadbir Awal + pt-br: E-mail do administrador inicial + required: true + type: text + - default: '' + edit: true + envKey: KEILA_PASSWORD + labelEn: Initial Administrator Password (10+ characters) + labelZh: 初始管理员密码(至少 10 个字符) + label: + en: Initial Administrator Password (10+ characters) + zh: 初始管理员密码(至少 10 个字符) + zh-Hant: 初始管理員密碼(至少 10 個字元) + ja: 初期管理者パスワード(10 文字以上) + ko: 초기 관리자 비밀번호(10자 이상) + ru: Пароль начального администратора (не менее 10 символов) + ms: Kata Laluan Pentadbir Awal (10+ aksara) + pt-br: Senha do administrador inicial (10+ caracteres) + required: true + type: password + - default: '' + edit: true + envKey: SECRET_KEY_BASE + labelEn: Session Secret (64+ characters; shorter values are replaced) + labelZh: 会话密钥(至少 64 字符;不足会被替换) + label: + en: Session Secret (64+ characters; shorter values are replaced) + zh: 会话密钥(至少 64 字符;不足会被替换) + zh-Hant: 工作階段密鑰(至少 64 字元;不足會被替換) + ja: セッションシークレット(64文字以上、短い値は置換) + ko: 세션 비밀 키(64자 이상, 짧은 값은 교체됨) + ru: Секрет сеанса (64+ символа; короткие значения заменяются) + ms: Rahsia sesi (64+ aksara; nilai pendek akan diganti) + pt-br: Segredo da sessão (64+ caracteres; valores curtos são substituídos) + required: true + type: password + - default: '' + edit: true + envKey: URL_HOST + labelEn: Public Host (domain or IP) + labelZh: 公开访问主机(域名或 IP) + label: + en: Public Host (domain or IP) + zh: 公开访问主机(域名或 IP) + zh-Hant: 公開存取主機(網域或 IP) + ja: 公開ホスト(ドメインまたは IP) + ko: 공개 호스트(도메인 또는 IP) + ru: Публичный хост (домен или IP) + ms: Hos Awam (domain atau IP) + pt-br: Host público (domínio ou IP) + required: true + type: text + - default: http + edit: true + envKey: URL_SCHEMA + labelEn: Public URL Scheme + labelZh: 公开 URL 协议 + label: + en: Public URL Scheme + zh: 公开 URL 协议 + zh-Hant: 公開 URL 通訊協定 + ja: 公開 URL スキーム + ko: 공개 URL 스키마 + ru: Схема публичного URL + ms: Skema URL Awam + pt-br: Esquema da URL pública + required: true + type: select + values: + - label: http + value: http + - label: https + value: https + - default: 4000 + edit: true + envKey: URL_PORT + labelEn: Public URL Port + labelZh: 公开 URL 端口 + label: + en: Public URL Port + zh: 公开 URL 端口 + zh-Hant: 公開 URL 連接埠 + ja: 公開 URL ポート + ko: 공개 URL 포트 + ru: Порт публичного URL + ms: Port URL Awam + pt-br: Porta da URL pública + required: true + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de dados + required: true + type: text + - default: '' + edit: true + envKey: MAILER_SMTP_HOST + labelEn: SMTP Host + labelZh: SMTP 主机 + label: + en: SMTP Host + zh: SMTP 主机 + zh-Hant: SMTP 主機 + ja: SMTP ホスト + ko: SMTP 호스트 + ru: SMTP-хост + ms: Hos SMTP + pt-br: Host SMTP + required: true + type: text + - default: 587 + edit: true + envKey: MAILER_SMTP_PORT + labelEn: SMTP Port + labelZh: SMTP 端口 + label: + en: SMTP Port + zh: SMTP 端口 + zh-Hant: SMTP 連接埠 + ja: SMTP ポート + ko: SMTP 포트 + ru: SMTP-порт + ms: Port SMTP + pt-br: Porta SMTP + required: true + type: number + - default: '' + edit: true + envKey: MAILER_SMTP_FROM_EMAIL + labelEn: SMTP From Email + labelZh: SMTP 发件邮箱 + label: + en: SMTP From Email + zh: SMTP 发件邮箱 + zh-Hant: SMTP 寄件者電子郵件 + ja: SMTP 送信元メールアドレス + ko: SMTP 발신 이메일 + ru: Email отправителя SMTP + ms: E-mel Pengirim SMTP + pt-br: E-mail do remetente SMTP + required: true + type: text + - default: '' + edit: true + envKey: MAILER_SMTP_USER + labelEn: SMTP Username + labelZh: SMTP 用户名 + label: + en: SMTP Username + zh: SMTP 用户名 + zh-Hant: SMTP 使用者名稱 + ja: SMTP ユーザー名 + ko: SMTP 사용자 이름 + ru: Имя пользователя SMTP + ms: Nama Pengguna SMTP + pt-br: Usuário SMTP + required: true + type: text + - default: '' + edit: true + envKey: MAILER_SMTP_PASSWORD + labelEn: SMTP Password + labelZh: SMTP 密码 + label: + en: SMTP Password + zh: SMTP 密码 + zh-Hant: SMTP 密碼 + ja: SMTP パスワード + ko: SMTP 비밀번호 + ru: Пароль SMTP + ms: Kata Laluan SMTP + pt-br: Senha SMTP + required: true + type: password + - default: 'false' + edit: true + envKey: MAILER_ENABLE_SSL + labelEn: Enable SMTP SSL/TLS + labelZh: 启用 SMTP SSL/TLS + label: + en: Enable SMTP SSL/TLS + zh: 启用 SMTP SSL/TLS + zh-Hant: 啟用 SMTP SSL/TLS + ja: SMTP SSL/TLS を有効化 + ko: SMTP SSL/TLS 활성화 + ru: Включить SMTP SSL/TLS + ms: Dayakan SMTP SSL/TLS + pt-br: Ativar SMTP SSL/TLS + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: 'false' + edit: true + envKey: MAILER_ENABLE_STARTTLS + labelEn: Enable SMTP STARTTLS + labelZh: 启用 SMTP STARTTLS + label: + en: Enable SMTP STARTTLS + zh: 启用 SMTP STARTTLS + zh-Hant: 啟用 SMTP STARTTLS + ja: SMTP STARTTLS を有効化 + ko: SMTP STARTTLS 활성화 + ru: Включить SMTP STARTTLS + ms: Dayakan SMTP STARTTLS + pt-br: Ativar SMTP STARTTLS + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: info + edit: true + envKey: LOG_LEVEL + labelEn: Log Level + labelZh: 日志级别 + label: + en: Log Level + zh: 日志级别 + zh-Hant: 日誌層級 + ja: ログレベル + ko: 로그 수준 + ru: Уровень журналирования + ms: Tahap Log + pt-br: Nível de log + required: true + type: select + values: + - label: debug + value: debug + - label: info + value: info + - label: error + value: error + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon Waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/keila/0.30.3/docker-compose.yml b/apps/keila/0.30.3/docker-compose.yml new file mode 100644 index 000000000..7236b211c --- /dev/null +++ b/apps/keila/0.30.3/docker-compose.yml @@ -0,0 +1,44 @@ +services: + keila: + image: "pentacent/keila:0.30.3" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:4000" + environment: + - DB_URL=postgres://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${PANEL_DB_HOST}:${PANEL_DB_PORT}/${PANEL_DB_NAME} + - DB_ENABLE_SSL=false + - KEILA_USER=${KEILA_USER} + - KEILA_PASSWORD=${KEILA_PASSWORD} + - SECRET_KEY_BASE=${SECRET_KEY_BASE} + - URL_HOST=${URL_HOST} + - URL_SCHEMA=${URL_SCHEMA} + - URL_PORT=${URL_PORT} + - USER_CONTENT_DIR=/opt/app/uploads + - MAILER_SMTP_HOST=${MAILER_SMTP_HOST} + - MAILER_SMTP_PORT=${MAILER_SMTP_PORT} + - MAILER_SMTP_FROM_EMAIL=${MAILER_SMTP_FROM_EMAIL} + - MAILER_SMTP_USER=${MAILER_SMTP_USER} + - MAILER_SMTP_PASSWORD=${MAILER_SMTP_PASSWORD} + - MAILER_ENABLE_SSL=${MAILER_ENABLE_SSL} + - MAILER_ENABLE_STARTTLS=${MAILER_ENABLE_STARTTLS} + - LOG_LEVEL=${LOG_LEVEL} + - TZ=${TZ} + volumes: + - "${APP_DATA_DIR}/uploads:/opt/app/uploads" + healthcheck: + test: + - CMD-SHELL + - wget --quiet --tries=1 --spider http://127.0.0.1:4000/auth/login || exit 1 + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/keila/0.30.3/scripts/init.sh b/apps/keila/0.30.3/scripts/init.sh new file mode 100755 index 000000000..39d1340bc --- /dev/null +++ b/apps/keila/0.30.3/scripts/init.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +strip_matching_quotes() { + local value="$1" + + if [[ ${#value} -ge 2 ]]; then + if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then + value="${value:1:${#value}-2}" + elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then + value="${value:1:${#value}-2}" + fi + fi + printf '%s\n' "$value" +} + +read_env_value() { + local key="$1" + local value="" + + if [[ -f "$ENV_FILE" ]]; then + value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)" + fi + strip_matching_quotes "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + + temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ ("^" key "=") { + if (!updated) { + print key "=" value + updated = 1 + } + next + } + { print } + END { + if (!updated) print key "=" value + } + ' "$ENV_FILE" > "$temp_file" + chmod --reference="$ENV_FILE" "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +ensure_secret_key_base() { + local secret_key_base + + secret_key_base="$(read_env_value SECRET_KEY_BASE)" + if [[ ${#secret_key_base} -lt 64 ]]; then + printf '%s\n' "SECRET_KEY_BASE is shorter than 64 characters; replacing it with a generated 128-character hexadecimal secret." >&2 + secret_key_base="$(od -An -N64 -tx1 /dev/urandom | tr -d ' \n')" + set_env_value SECRET_KEY_BASE "$secret_key_base" + fi +} + +path_is_dotenv_safe() { + local value="$1" + + case "$value" in + *$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*) return 1 ;; + *) return 0 ;; + esac +} + +[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}" +ensure_secret_key_base + +if [[ ${APP_DATA_DIR+x} ]]; then + APP_DATA_DIR_RAW="$APP_DATA_DIR" +else + APP_DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)" +fi +APP_DATA_DIR_RAW="$(strip_matching_quotes "${APP_DATA_DIR_RAW:-./data}")" + +[[ -n "$APP_DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty" +path_is_dotenv_safe "$APP_DATA_DIR_RAW" || fail "APP_DATA_DIR contains unsupported dotenv characters" + +case "$APP_DATA_DIR_RAW" in + /*) + APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")" + ;; + *) + APP_DATA_DIR_ABS="$(realpath -m -- "${ROOT_DIR}/${APP_DATA_DIR_RAW#./}")" + case "$APP_DATA_DIR_ABS" in + "${ROOT_DIR}" | "${ROOT_DIR}"/*) ;; + *) fail "Relative APP_DATA_DIR must stay inside the application directory" ;; + esac + ;; +esac + +[[ "$APP_DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root" +[[ ! -L "$APP_DATA_DIR_ABS" ]] || fail "APP_DATA_DIR must not be a symbolic link" +if [[ -e "$APP_DATA_DIR_ABS" && ! -d "$APP_DATA_DIR_ABS" ]]; then + fail "APP_DATA_DIR must be a directory" +fi + +mkdir -p -- "${APP_DATA_DIR_ABS}/uploads" +chmod 750 -- "$APP_DATA_DIR_ABS" "${APP_DATA_DIR_ABS}/uploads" diff --git a/apps/keila/0.30.3/scripts/uninstall.sh b/apps/keila/0.30.3/scripts/uninstall.sh new file mode 100755 index 000000000..5a3f6bd6b --- /dev/null +++ b/apps/keila/0.30.3/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --volumes --remove-orphans +else + docker compose down --volumes --remove-orphans +fi diff --git a/apps/keila/0.30.3/scripts/upgrade.sh b/apps/keila/0.30.3/scripts/upgrade.sh new file mode 100755 index 000000000..593b31667 --- /dev/null +++ b/apps/keila/0.30.3/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +exec bash "${SCRIPT_DIR}/init.sh" diff --git a/apps/kimai-linuxserver/2.69.0/docker-compose.yml b/apps/kimai-linuxserver/2.69.0/docker-compose.yml index e26531348..8940fa838 100644 --- a/apps/kimai-linuxserver/2.69.0/docker-compose.yml +++ b/apps/kimai-linuxserver/2.69.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: kimai: - image: "linuxserver/kimai:2.69.0" + image: "linuxserver/kimai:2.70.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/leafwiki/0.13.0/docker-compose.yml b/apps/leafwiki/0.13.0/docker-compose.yml index 18fb89345..47c51d685 100644 --- a/apps/leafwiki/0.13.0/docker-compose.yml +++ b/apps/leafwiki/0.13.0/docker-compose.yml @@ -1,6 +1,6 @@ services: leafwiki: - image: "ghcr.io/perber/leafwiki:v0.13.0@sha256:c40904dafd9db81ca2c9334e8c67fb168d175876111fc1fd9a00e3715b66775b" + image: "ghcr.io/perber/leafwiki:v0.14.2@sha256:bc07b7d0f3de916233a570f6d4b54fe14de0e2cb77e179e6a07bf417fb109585" container_name: ${CONTAINER_NAME} restart: unless-stopped init: true diff --git a/apps/libredb-studio/0.16.1/scripts/init.sh b/apps/libredb-studio/0.16.1/scripts/init.sh deleted file mode 100755 index e766a4995..000000000 --- a/apps/libredb-studio/0.16.1/scripts/init.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" -ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" - -read_env_value() { - local key="$1" - [[ -f "$ENV_FILE" ]] || return 0 - local value - value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" - case "$value" in - \"*\") value="${value#\"}"; value="${value%\"}" ;; - \'*\') value="${value#\'}"; value="${value%\'}" ;; - esac - printf '%s\n' "$value" -} - -configured_value() { - local key="$1" - local default_value="$2" - local value - value="${!key:-}" - if [[ -z "$value" ]]; then - value="$(read_env_value "$key")" - fi - printf '%s\n' "${value:-$default_value}" -} - -resolve_app_path() { - local key="$1" - local raw="$2" - local clean candidate resolved current part - local -a parts=() - case "$raw" in - ""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;; - esac - if [[ "$raw" =~ [[:cntrl:]] ]]; then - echo "unsafe ${key} path" >&2 - return 1 - fi - clean="${raw#./}" - [[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; } - command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; } - candidate="$ROOT_DIR/$clean" - resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; } - case "$resolved" in - "$ROOT_DIR"/*) ;; - *) echo "unsafe ${key} path" >&2; return 1 ;; - esac - current="$ROOT_DIR" - IFS='/' read -r -a parts <<< "$clean" - for part in "${parts[@]}"; do - [[ -z "$part" || "$part" == "." ]] && continue - current="$current/$part" - if [[ -L "$current" ]]; then - echo "unsafe ${key} path" >&2 - return 1 - fi - done - printf '%s\n' "$resolved" -} - -APP_DATA_DIR_RAW="$(configured_value APP_DATA_DIR_1 "./data")" -APP_DATA_DIR_PATH="$(resolve_app_path APP_DATA_DIR_1 "$APP_DATA_DIR_RAW")" -mkdir -p -- "$APP_DATA_DIR_PATH" diff --git a/apps/libredb-studio/0.16.1/.env.sample b/apps/libredb-studio/0.18.2/.env.sample similarity index 100% rename from apps/libredb-studio/0.16.1/.env.sample rename to apps/libredb-studio/0.18.2/.env.sample diff --git a/apps/libredb-studio/0.16.1/data.yml b/apps/libredb-studio/0.18.2/data.yml similarity index 100% rename from apps/libredb-studio/0.16.1/data.yml rename to apps/libredb-studio/0.18.2/data.yml diff --git a/apps/libredb-studio/0.18.2/data/.gitkeep b/apps/libredb-studio/0.18.2/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/0.16.1/docker-compose.yml b/apps/libredb-studio/0.18.2/docker-compose.yml similarity index 89% rename from apps/libredb-studio/0.16.1/docker-compose.yml rename to apps/libredb-studio/0.18.2/docker-compose.yml index 27747acff..103f65d5a 100644 --- a/apps/libredb-studio/0.16.1/docker-compose.yml +++ b/apps/libredb-studio/0.18.2/docker-compose.yml @@ -1,6 +1,6 @@ services: libredb-studio: - image: "libredb/libredb-studio:0.16.1" + image: "libredb/libredb-studio:1.0.0" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:3000" diff --git a/apps/libredb-studio/0.18.2/scripts/.gitkeep b/apps/libredb-studio/0.18.2/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/0.18.2/scripts/init.sh b/apps/libredb-studio/0.18.2/scripts/init.sh new file mode 100755 index 000000000..7d76e9388 --- /dev/null +++ b/apps/libredb-studio/0.18.2/scripts/init.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +trim() { + local value="$1" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s\n' "$value" +} + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + # Read as the compose file reads it, trimmed before quotes come off. + value="$(sed -n \ + -e "s/^[[:space:]]*export[[:space:]]\{1,\}${key}[[:space:]]*=//p;t" \ + -e "s/^[[:space:]]*${key}[[:space:]]*=//p" \ + "$ENV_FILE" | tail -n 1)" + value="$(trim "$value")" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +env_has_key() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 1 + grep -qE "^[[:space:]]*(export[[:space:]]+)?${key}[[:space:]]*=" "$ENV_FILE" +} + +# A value that is set but empty is not defaulted. Compose refuses the project on +# one, so saying why here beats letting it fail with an opaque mount spec. +configured_value() { + local key="$1" + local default_value="$2" + local value + if [[ -n "${!key+x}" ]]; then + value="$(trim "${!key}")" + if [[ -z "$value" ]]; then + printf '%s\n' "unsafe ${key} path: ${key} is set to an empty value" >&2 + return 1 + fi + printf '%s\n' "$value" + return 0 + fi + value="$(read_env_value "$key")" + if [[ -z "$value" ]] && env_has_key "$key"; then + printf '%s\n' "unsafe ${key} path: ${key} in .env has an empty value" >&2 + return 1 + fi + printf '%s\n' "${value:-$default_value}" +} + +# Resolves . and .. textually, as the daemon cleans a bind source. realpath +# resolves symlinks first and lands elsewhere when one precedes a .. . +lexclean() { + local path="$1" + local part out="" absolute=0 n=0 i + local -a parts=() stack=() + [[ "$path" == /* ]] && absolute=1 + IFS='/' read -r -a parts <<< "$path" + for part in ${parts[@]+"${parts[@]}"}; do + case "$part" in + ''|.) continue ;; + ..) + if (( n > 0 )) && [[ "${stack[n-1]}" != ".." ]]; then + n=$(( n - 1 )) + elif (( absolute == 0 )); then + stack[n]=".."; n=$(( n + 1 )) + fi + ;; + *) stack[n]="$part"; n=$(( n + 1 )) ;; + esac + done + for (( i = 0; i < n; i++ )); do + out="$out/${stack[i]}" + done + if (( absolute )); then + printf '%s\n' "${out:-/}" + else + printf '%s\n' "${out#/}" + fi +} + +# An absolute value is kept, so an upgrade finds the data where the installation +# keeps it. A relative value stays under the app directory. +resolve_app_path() { + local key="$1" + local raw="$2" + local rest clean candidate resolved current part + local -a parts=() + + [[ -n "$raw" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + if [[ "$raw" =~ [[:cntrl:]] ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + # The compose file parses the same value and reads these differently. + case "$raw" in + *'$'*|*:*|'#'*|*[[:space:]]'#'*|*\\*|*\"*|*\'*) + printf '%s\n' "unsafe ${key} path: remove the \$ : \\ \" ' or word-opening # from ${key} in .env, because the compose file reads those and would mount a different directory than this script creates" >&2 + return 1 ;; + esac + + if [[ "$raw" == '~'* ]]; then + if [[ -z "${HOME:-}" ]]; then + printf '%s\n' "unsafe ${key} path: ~ cannot be resolved because HOME is not set" >&2 + return 1 + fi + rest="${raw#\~}"; rest="${rest#/}" + raw="${HOME%/}${rest:+/$rest}" + fi + + if [[ "$raw" == /* ]]; then + resolved="$(lexclean "$raw")" + if [[ "$resolved" == "/" ]]; then + printf '%s\n' "unsafe ${key} path: the root directory cannot hold the app data" >&2 + return 1 + fi + current="" + IFS='/' read -r -a parts <<< "$resolved" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" ]] && continue + current="$current/$part" + if [[ -L "$current" && ! -e "$current" ]]; then + printf '%s\n' "unsafe ${key} path: ${current} is a link that does not resolve to an existing path" >&2 + return 1 + fi + done + if [[ -e "$resolved" && ! -d "$resolved" ]]; then + printf '%s\n' "unsafe ${key} path: ${resolved} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$resolved" + return 0 + fi + + # Compose reads a bare name as a named volume and refuses the whole project. + if [[ "$raw" != .* ]]; then + printf '%s\n' "unsafe ${key} path: a relative value has to begin with a dot, as ./data does" >&2 + return 1 + fi + case "$raw" in + .|..|../*|*/../*|*/..) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + clean="$(lexclean "$raw")" + [[ -n "$clean" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + case "$candidate" in + "$ROOT_DIR"/*) ;; + *) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + done + if [[ -e "$candidate" && ! -d "$candidate" ]]; then + printf '%s\n' "unsafe ${key} path: ${candidate} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$candidate" +} + +ensure_dir() { + local key="$1" + local raw path + raw="$(configured_value "$key" "$2")" + path="$(resolve_app_path "$key" "$raw")" + mkdir -p -- "$path" +} + +ensure_dir "APP_DATA_DIR_1" "./data" diff --git a/apps/libredb-studio/0.16.1/scripts/uninstall.sh b/apps/libredb-studio/0.18.2/scripts/uninstall.sh similarity index 100% rename from apps/libredb-studio/0.16.1/scripts/uninstall.sh rename to apps/libredb-studio/0.18.2/scripts/uninstall.sh diff --git a/apps/libredb-studio/0.16.1/scripts/upgrade.sh b/apps/libredb-studio/0.18.2/scripts/upgrade.sh similarity index 100% rename from apps/libredb-studio/0.16.1/scripts/upgrade.sh rename to apps/libredb-studio/0.18.2/scripts/upgrade.sh diff --git a/apps/libredb-studio/0.18.3/.env.sample b/apps/libredb-studio/0.18.3/.env.sample new file mode 100644 index 000000000..4021823c1 --- /dev/null +++ b/apps/libredb-studio/0.18.3/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME=libredb-studio-compose-check +PANEL_APP_PORT_HTTP=3000 +TZ=Asia/Shanghai +APP_DATA_DIR_1=./data diff --git a/apps/libredb-studio/0.18.3/data.yml b/apps/libredb-studio/0.18.3/data.yml new file mode 100644 index 000000000..a0de60952 --- /dev/null +++ b/apps/libredb-studio/0.18.3/data.yml @@ -0,0 +1,51 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR_1 + labelEn: Data Directory 1 + labelZh: 数据目录 1 + label: + en: Data Directory 1 + zh: 数据目录 1 + zh-Hant: 資料目錄 1 + ja: データディレクトリ 1 + ko: 데이터 디렉터리 1 + ru: Каталог данных 1 + ms: Direktori data 1 + pt-br: Diretório de dados 1 + required: true + type: text diff --git a/apps/libredb-studio/0.18.3/data/.gitkeep b/apps/libredb-studio/0.18.3/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/0.18.3/docker-compose.yml b/apps/libredb-studio/0.18.3/docker-compose.yml new file mode 100644 index 000000000..103f65d5a --- /dev/null +++ b/apps/libredb-studio/0.18.3/docker-compose.yml @@ -0,0 +1,16 @@ +services: + libredb-studio: + image: "libredb/libredb-studio:1.0.0" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + environment: + - TZ=${TZ} + - STORAGE_PROVIDER=sqlite + - STORAGE_SQLITE_PATH=/app/data/libredb-storage.db + labels: + createdBy: "Apps" + restart: on-failure:3 + volumes: + - "${APP_DATA_DIR_1}:/app/data" +networks: {} diff --git a/apps/libredb-studio/0.18.3/scripts/.gitkeep b/apps/libredb-studio/0.18.3/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/0.18.3/scripts/init.sh b/apps/libredb-studio/0.18.3/scripts/init.sh new file mode 100755 index 000000000..7d76e9388 --- /dev/null +++ b/apps/libredb-studio/0.18.3/scripts/init.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +trim() { + local value="$1" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s\n' "$value" +} + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + # Read as the compose file reads it, trimmed before quotes come off. + value="$(sed -n \ + -e "s/^[[:space:]]*export[[:space:]]\{1,\}${key}[[:space:]]*=//p;t" \ + -e "s/^[[:space:]]*${key}[[:space:]]*=//p" \ + "$ENV_FILE" | tail -n 1)" + value="$(trim "$value")" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +env_has_key() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 1 + grep -qE "^[[:space:]]*(export[[:space:]]+)?${key}[[:space:]]*=" "$ENV_FILE" +} + +# A value that is set but empty is not defaulted. Compose refuses the project on +# one, so saying why here beats letting it fail with an opaque mount spec. +configured_value() { + local key="$1" + local default_value="$2" + local value + if [[ -n "${!key+x}" ]]; then + value="$(trim "${!key}")" + if [[ -z "$value" ]]; then + printf '%s\n' "unsafe ${key} path: ${key} is set to an empty value" >&2 + return 1 + fi + printf '%s\n' "$value" + return 0 + fi + value="$(read_env_value "$key")" + if [[ -z "$value" ]] && env_has_key "$key"; then + printf '%s\n' "unsafe ${key} path: ${key} in .env has an empty value" >&2 + return 1 + fi + printf '%s\n' "${value:-$default_value}" +} + +# Resolves . and .. textually, as the daemon cleans a bind source. realpath +# resolves symlinks first and lands elsewhere when one precedes a .. . +lexclean() { + local path="$1" + local part out="" absolute=0 n=0 i + local -a parts=() stack=() + [[ "$path" == /* ]] && absolute=1 + IFS='/' read -r -a parts <<< "$path" + for part in ${parts[@]+"${parts[@]}"}; do + case "$part" in + ''|.) continue ;; + ..) + if (( n > 0 )) && [[ "${stack[n-1]}" != ".." ]]; then + n=$(( n - 1 )) + elif (( absolute == 0 )); then + stack[n]=".."; n=$(( n + 1 )) + fi + ;; + *) stack[n]="$part"; n=$(( n + 1 )) ;; + esac + done + for (( i = 0; i < n; i++ )); do + out="$out/${stack[i]}" + done + if (( absolute )); then + printf '%s\n' "${out:-/}" + else + printf '%s\n' "${out#/}" + fi +} + +# An absolute value is kept, so an upgrade finds the data where the installation +# keeps it. A relative value stays under the app directory. +resolve_app_path() { + local key="$1" + local raw="$2" + local rest clean candidate resolved current part + local -a parts=() + + [[ -n "$raw" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + if [[ "$raw" =~ [[:cntrl:]] ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + # The compose file parses the same value and reads these differently. + case "$raw" in + *'$'*|*:*|'#'*|*[[:space:]]'#'*|*\\*|*\"*|*\'*) + printf '%s\n' "unsafe ${key} path: remove the \$ : \\ \" ' or word-opening # from ${key} in .env, because the compose file reads those and would mount a different directory than this script creates" >&2 + return 1 ;; + esac + + if [[ "$raw" == '~'* ]]; then + if [[ -z "${HOME:-}" ]]; then + printf '%s\n' "unsafe ${key} path: ~ cannot be resolved because HOME is not set" >&2 + return 1 + fi + rest="${raw#\~}"; rest="${rest#/}" + raw="${HOME%/}${rest:+/$rest}" + fi + + if [[ "$raw" == /* ]]; then + resolved="$(lexclean "$raw")" + if [[ "$resolved" == "/" ]]; then + printf '%s\n' "unsafe ${key} path: the root directory cannot hold the app data" >&2 + return 1 + fi + current="" + IFS='/' read -r -a parts <<< "$resolved" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" ]] && continue + current="$current/$part" + if [[ -L "$current" && ! -e "$current" ]]; then + printf '%s\n' "unsafe ${key} path: ${current} is a link that does not resolve to an existing path" >&2 + return 1 + fi + done + if [[ -e "$resolved" && ! -d "$resolved" ]]; then + printf '%s\n' "unsafe ${key} path: ${resolved} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$resolved" + return 0 + fi + + # Compose reads a bare name as a named volume and refuses the whole project. + if [[ "$raw" != .* ]]; then + printf '%s\n' "unsafe ${key} path: a relative value has to begin with a dot, as ./data does" >&2 + return 1 + fi + case "$raw" in + .|..|../*|*/../*|*/..) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + clean="$(lexclean "$raw")" + [[ -n "$clean" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + case "$candidate" in + "$ROOT_DIR"/*) ;; + *) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + done + if [[ -e "$candidate" && ! -d "$candidate" ]]; then + printf '%s\n' "unsafe ${key} path: ${candidate} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$candidate" +} + +ensure_dir() { + local key="$1" + local raw path + raw="$(configured_value "$key" "$2")" + path="$(resolve_app_path "$key" "$raw")" + mkdir -p -- "$path" +} + +ensure_dir "APP_DATA_DIR_1" "./data" diff --git a/apps/libredb-studio/0.18.3/scripts/uninstall.sh b/apps/libredb-studio/0.18.3/scripts/uninstall.sh new file mode 100755 index 000000000..bf82cc8bf --- /dev/null +++ b/apps/libredb-studio/0.18.3/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +true diff --git a/apps/libredb-studio/0.18.3/scripts/upgrade.sh b/apps/libredb-studio/0.18.3/scripts/upgrade.sh new file mode 100755 index 000000000..980ffd27c --- /dev/null +++ b/apps/libredb-studio/0.18.3/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(CDPATH="" cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +exec "${SCRIPT_DIR}/init.sh" diff --git a/apps/libredb-studio/1.0.0/.env.sample b/apps/libredb-studio/1.0.0/.env.sample new file mode 100644 index 000000000..4021823c1 --- /dev/null +++ b/apps/libredb-studio/1.0.0/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME=libredb-studio-compose-check +PANEL_APP_PORT_HTTP=3000 +TZ=Asia/Shanghai +APP_DATA_DIR_1=./data diff --git a/apps/libredb-studio/1.0.0/data.yml b/apps/libredb-studio/1.0.0/data.yml new file mode 100644 index 000000000..a0de60952 --- /dev/null +++ b/apps/libredb-studio/1.0.0/data.yml @@ -0,0 +1,51 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR_1 + labelEn: Data Directory 1 + labelZh: 数据目录 1 + label: + en: Data Directory 1 + zh: 数据目录 1 + zh-Hant: 資料目錄 1 + ja: データディレクトリ 1 + ko: 데이터 디렉터리 1 + ru: Каталог данных 1 + ms: Direktori data 1 + pt-br: Diretório de dados 1 + required: true + type: text diff --git a/apps/libredb-studio/1.0.0/data/.gitkeep b/apps/libredb-studio/1.0.0/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/1.0.0/docker-compose.yml b/apps/libredb-studio/1.0.0/docker-compose.yml new file mode 100644 index 000000000..103f65d5a --- /dev/null +++ b/apps/libredb-studio/1.0.0/docker-compose.yml @@ -0,0 +1,16 @@ +services: + libredb-studio: + image: "libredb/libredb-studio:1.0.0" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + environment: + - TZ=${TZ} + - STORAGE_PROVIDER=sqlite + - STORAGE_SQLITE_PATH=/app/data/libredb-storage.db + labels: + createdBy: "Apps" + restart: on-failure:3 + volumes: + - "${APP_DATA_DIR_1}:/app/data" +networks: {} diff --git a/apps/libredb-studio/1.0.0/scripts/.gitkeep b/apps/libredb-studio/1.0.0/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/libredb-studio/1.0.0/scripts/init.sh b/apps/libredb-studio/1.0.0/scripts/init.sh new file mode 100755 index 000000000..7d76e9388 --- /dev/null +++ b/apps/libredb-studio/1.0.0/scripts/init.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +trim() { + local value="$1" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s\n' "$value" +} + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + # Read as the compose file reads it, trimmed before quotes come off. + value="$(sed -n \ + -e "s/^[[:space:]]*export[[:space:]]\{1,\}${key}[[:space:]]*=//p;t" \ + -e "s/^[[:space:]]*${key}[[:space:]]*=//p" \ + "$ENV_FILE" | tail -n 1)" + value="$(trim "$value")" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +env_has_key() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 1 + grep -qE "^[[:space:]]*(export[[:space:]]+)?${key}[[:space:]]*=" "$ENV_FILE" +} + +# A value that is set but empty is not defaulted. Compose refuses the project on +# one, so saying why here beats letting it fail with an opaque mount spec. +configured_value() { + local key="$1" + local default_value="$2" + local value + if [[ -n "${!key+x}" ]]; then + value="$(trim "${!key}")" + if [[ -z "$value" ]]; then + printf '%s\n' "unsafe ${key} path: ${key} is set to an empty value" >&2 + return 1 + fi + printf '%s\n' "$value" + return 0 + fi + value="$(read_env_value "$key")" + if [[ -z "$value" ]] && env_has_key "$key"; then + printf '%s\n' "unsafe ${key} path: ${key} in .env has an empty value" >&2 + return 1 + fi + printf '%s\n' "${value:-$default_value}" +} + +# Resolves . and .. textually, as the daemon cleans a bind source. realpath +# resolves symlinks first and lands elsewhere when one precedes a .. . +lexclean() { + local path="$1" + local part out="" absolute=0 n=0 i + local -a parts=() stack=() + [[ "$path" == /* ]] && absolute=1 + IFS='/' read -r -a parts <<< "$path" + for part in ${parts[@]+"${parts[@]}"}; do + case "$part" in + ''|.) continue ;; + ..) + if (( n > 0 )) && [[ "${stack[n-1]}" != ".." ]]; then + n=$(( n - 1 )) + elif (( absolute == 0 )); then + stack[n]=".."; n=$(( n + 1 )) + fi + ;; + *) stack[n]="$part"; n=$(( n + 1 )) ;; + esac + done + for (( i = 0; i < n; i++ )); do + out="$out/${stack[i]}" + done + if (( absolute )); then + printf '%s\n' "${out:-/}" + else + printf '%s\n' "${out#/}" + fi +} + +# An absolute value is kept, so an upgrade finds the data where the installation +# keeps it. A relative value stays under the app directory. +resolve_app_path() { + local key="$1" + local raw="$2" + local rest clean candidate resolved current part + local -a parts=() + + [[ -n "$raw" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + if [[ "$raw" =~ [[:cntrl:]] ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + # The compose file parses the same value and reads these differently. + case "$raw" in + *'$'*|*:*|'#'*|*[[:space:]]'#'*|*\\*|*\"*|*\'*) + printf '%s\n' "unsafe ${key} path: remove the \$ : \\ \" ' or word-opening # from ${key} in .env, because the compose file reads those and would mount a different directory than this script creates" >&2 + return 1 ;; + esac + + if [[ "$raw" == '~'* ]]; then + if [[ -z "${HOME:-}" ]]; then + printf '%s\n' "unsafe ${key} path: ~ cannot be resolved because HOME is not set" >&2 + return 1 + fi + rest="${raw#\~}"; rest="${rest#/}" + raw="${HOME%/}${rest:+/$rest}" + fi + + if [[ "$raw" == /* ]]; then + resolved="$(lexclean "$raw")" + if [[ "$resolved" == "/" ]]; then + printf '%s\n' "unsafe ${key} path: the root directory cannot hold the app data" >&2 + return 1 + fi + current="" + IFS='/' read -r -a parts <<< "$resolved" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" ]] && continue + current="$current/$part" + if [[ -L "$current" && ! -e "$current" ]]; then + printf '%s\n' "unsafe ${key} path: ${current} is a link that does not resolve to an existing path" >&2 + return 1 + fi + done + if [[ -e "$resolved" && ! -d "$resolved" ]]; then + printf '%s\n' "unsafe ${key} path: ${resolved} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$resolved" + return 0 + fi + + # Compose reads a bare name as a named volume and refuses the whole project. + if [[ "$raw" != .* ]]; then + printf '%s\n' "unsafe ${key} path: a relative value has to begin with a dot, as ./data does" >&2 + return 1 + fi + case "$raw" in + .|..|../*|*/../*|*/..) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + clean="$(lexclean "$raw")" + [[ -n "$clean" ]] || { printf '%s\n' "unsafe ${key} path" >&2; return 1; } + candidate="$ROOT_DIR/$clean" + case "$candidate" in + "$ROOT_DIR"/*) ;; + *) printf '%s\n' "unsafe ${key} path" >&2; return 1 ;; + esac + current="$ROOT_DIR" + IFS='/' read -r -a parts <<< "$clean" + for part in ${parts[@]+"${parts[@]}"}; do + [[ -z "$part" || "$part" == "." ]] && continue + current="$current/$part" + if [[ -L "$current" ]]; then + printf '%s\n' "unsafe ${key} path" >&2 + return 1 + fi + done + if [[ -e "$candidate" && ! -d "$candidate" ]]; then + printf '%s\n' "unsafe ${key} path: ${candidate} exists and is not a directory" >&2 + return 1 + fi + printf '%s\n' "$candidate" +} + +ensure_dir() { + local key="$1" + local raw path + raw="$(configured_value "$key" "$2")" + path="$(resolve_app_path "$key" "$raw")" + mkdir -p -- "$path" +} + +ensure_dir "APP_DATA_DIR_1" "./data" diff --git a/apps/libredb-studio/1.0.0/scripts/uninstall.sh b/apps/libredb-studio/1.0.0/scripts/uninstall.sh new file mode 100755 index 000000000..bf82cc8bf --- /dev/null +++ b/apps/libredb-studio/1.0.0/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +true diff --git a/apps/libredb-studio/1.0.0/scripts/upgrade.sh b/apps/libredb-studio/1.0.0/scripts/upgrade.sh new file mode 100755 index 000000000..980ffd27c --- /dev/null +++ b/apps/libredb-studio/1.0.0/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(CDPATH="" cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +exec "${SCRIPT_DIR}/init.sh" diff --git a/apps/libredb-studio/README.md b/apps/libredb-studio/README.md index 6b2b063dd..965addd20 100644 --- a/apps/libredb-studio/README.md +++ b/apps/libredb-studio/README.md @@ -2,15 +2,15 @@ ## 产品介绍 -LibreDB Studio 是一个 MIT 许可、自托管、基于浏览器的数据库 IDE,支持 PostgreSQL、MySQL、Oracle、SQL Server、SQLite、MongoDB、Redis、ClickHouse、Elasticsearch 等 16 种引擎。首次启动时管理员密码会打印到容器日志(零配置)。 +LibreDB Studio 是一个 MIT 许可、自托管、基于浏览器的数据库 IDE,27 种驱动覆盖 PostgreSQL、MySQL、Oracle、Db2 LUW、SQL Server、SQLite、MongoDB、Redis、ClickHouse 等 54 种引擎(另有 27 种引擎使用相同协议,通过已有驱动连接)。按上游 `docs/DATABASE_PROVIDERS.md` 的驱动表,27 种驱动中有 10 种只发送读取请求;其中 Druid、Elasticsearch 和 OpenSearch 的语法里完全没有 `UPDATE` 和 `CREATE TABLE`,只能查询和浏览。首次启动时管理员密码会打印到容器日志(零配置)。 这里列出的数据库引擎是应用连接目标,应用商店包本身只运行 LibreDB Studio,不会自动安装 PostgreSQL、Redis 或其他数据库服务。 ## 主要功能 -- 一个浏览器界面管理 16 种数据库引擎 +- 一个浏览器界面接入 27 种数据库驱动,27 种中有 10 种为只读 - SSO (OIDC)、RBAC 与查询审计日志(免费版内置) -- ER 图、EXPLAIN 可视化、模式对比 +- ER 图、EXPLAIN 可视化、模式对比,在支持这些功能的引擎上可用 ## 访问说明 @@ -18,17 +18,30 @@ LibreDB Studio 是一个 MIT 许可、自托管、基于浏览器的数据库 ID - 安装后访问:http://: - 首次登录密码见容器日志 +## 数据与升级 + +- `APP_DATA_DIR_1` 可以是相对路径,也可以是绝对路径。绝对路径会保留在应用目录之外,因此已设置绝对路径的安装保留原目录,不需要迁移数据;0.16.1 的脚本会拒绝所有绝对路径并中断升级。相对路径须以点开头,如 `./data`。被拒绝的值会说明原因,`scripts/init.sh` 里列出了全部规则。 +- 本包使用 `STORAGE_PROVIDER=sqlite`,因此处于 0.18.1 所修复问题的影响范围内(共享浏览器配置文件时工作区在账户之间泄露,含已保存的连接凭据和查询历史,影响 0.8.0 到 0.18.0)。本包不设置 `ADMIN_PASSWORD`,首次启动写入 `auth-bootstrap.json` 的密码升级后继续有效;升级会让已登录用户退出一次,之前签发的 MCP 令牌需要重新创建。SQLite 连接现在只对管理员开放。 + ## Introduction -LibreDB Studio is an MIT-licensed, self-hosted, browser-based database IDE and client for 16 engines: PostgreSQL, MySQL, Oracle, SQL Server, SQLite, libSQL, DuckDB, MongoDB, Redis, Couchbase, ClickHouse, Druid, Elasticsearch, OpenSearch, Apache Trino and Apache Cassandra. +LibreDB Studio is an MIT-licensed, self-hosted, browser-based database IDE and client. 27 drivers reach 54 named engines, because 27 further engines speak one of the same wire protocols. Per the provider table in upstream `docs/DATABASE_PROVIDERS.md`, 10 of the 27 drivers send only read requests; on Druid, Elasticsearch and OpenSearch that is the engine's own doing, since no `UPDATE` and no `CREATE TABLE` is in their grammar at all, so you query and browse there rather than manage. The listed database engines are connection targets. This AppStore package runs LibreDB Studio as a single service and does not provision PostgreSQL, Redis, or other database servers. ## Features -- One browser interface for 16 database engines +- One browser interface for 27 database drivers, 10 of the 27 read-only - SSO (OIDC), RBAC and query audit logs in the free build -- ER diagrams, EXPLAIN visualization and schema comparison -- Website: https://libredb.org -- Repository: https://github.com/libredb/libredb-studio +- ER diagrams, EXPLAIN visualization and schema comparison, on the engines that support them - On first run the admin password is printed to the container log (zero-config). Default port 3000. + +## Data and upgrades + +- `APP_DATA_DIR_1` takes a relative path or an absolute one. An absolute path is kept outside the app directory, so an installation that already holds one keeps its directory and nothing is migrated; the 0.16.1 script refused every absolute value and stopped the upgrade. A relative path has to begin with a dot, as `./data` does. A refused value says why, and `scripts/init.sh` carries the full set of rules. +- This package runs `STORAGE_PROVIDER=sqlite`, so it is in range of the workspace leak between accounts sharing a browser profile that 0.18.1 fixed, affecting 0.8.0 through 0.18.0. It sets no `ADMIN_PASSWORD`, so the password written to `auth-bootstrap.json` on first run keeps working after the upgrade; the upgrade signs local users out once and invalidates MCP tokens issued before it. A SQLite connection now opens for an admin only. + +## 参考资料 + +- 官网: +- 源码: diff --git a/apps/libredb-studio/data.yml b/apps/libredb-studio/data.yml index 3eba13acb..e3e6b1b3d 100644 --- a/apps/libredb-studio/data.yml +++ b/apps/libredb-studio/data.yml @@ -2,25 +2,25 @@ name: LibreDB Studio tags: - Database - Tool -title: 自托管、基于浏览器的数据库 IDE,支持 16 种引擎 -description: 自托管、基于浏览器的数据库 IDE,支持 16 种引擎 +title: 自托管、基于浏览器的数据库 IDE,41 种驱动 +description: 自托管、基于浏览器的数据库 IDE,41 种驱动 additionalProperties: key: libredb-studio name: LibreDB Studio tags: - Database - Tool - shortDescZh: 自托管、基于浏览器的数据库 IDE,支持 16 种引擎 - shortDescEn: Self-hosted, browser-based database IDE for 16 engines + shortDescZh: 自托管、基于浏览器的数据库 IDE,41 种驱动 + shortDescEn: Self-hosted, browser-based database IDE, 41 drivers description: - en: LibreDB Studio is an MIT-licensed, self-hosted, browser-based database IDE for 16 engines including PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB, Redis, ClickHouse, Elasticsearch and more. SSO (OIDC), RBAC and query audit logs ship in the free build. On first run the admin password is printed to the container log. - zh: LibreDB Studio 是一个 MIT 许可、自托管、基于浏览器的数据库 IDE,支持 PostgreSQL、MySQL、Oracle、SQL Server、SQLite、MongoDB、Redis、ClickHouse、Elasticsearch 等 16 种引擎。免费版包含 SSO (OIDC)、RBAC 和查询审计日志。首次启动时管理员密码会打印到容器日志。 - zh-Hant: LibreDB Studio 是一個 MIT 授權、自託管、基於瀏覽器的資料庫 IDE,支援 16 種引擎。免費版包含 SSO (OIDC)、RBAC 和查詢稽核日誌。 - ja: LibreDB Studio は、16 種類のデータベースエンジンに対応した MIT ライセンスのセルフホスト型・ブラウザベースのデータベース IDE です。無料版に SSO (OIDC)、RBAC、監査ログを搭載しています。 - ko: LibreDB Studio는 16개 데이터베이스 엔진을 지원하는 MIT 라이선스의 셀프 호스팅 브라우저 기반 데이터베이스 IDE입니다. 무료 버전에 SSO(OIDC), RBAC, 감사 로그가 포함됩니다. - ru: LibreDB Studio представляет собой самостоятельно размещаемую браузерную IDE для баз данных с поддержкой 16 движков, под лицензией MIT. SSO (OIDC), RBAC и журналы аудита включены в бесплатную версию. - ms: LibreDB Studio ialah IDE pangkalan data berlesen MIT yang berasaskan pelayar dan dihoskan sendiri untuk 16 enjin. SSO (OIDC), RBAC dan log audit disertakan dalam versi percuma. - pt-br: LibreDB Studio é uma IDE baseada em navegador e auto-hospedada para 16 motores de banco de dados, licenciada sob MIT. SSO (OIDC), RBAC e logs de auditoria estão incluídos na versão gratuita. + en: Self-hosted, browser-based database IDE. 41 drivers reach 68 named engines. + zh: 自托管、基于浏览器的数据库 IDE。41 种驱动覆盖 68 种引擎。 + zh-Hant: 自託管、基於瀏覽器的資料庫 IDE。41 種驅動程式涵蓋 68 種引擎。 + ja: セルフホスト型・ブラウザベースのデータベース IDE。41 種類のドライバーで 68 種類のエンジンに接続できます。 + ko: 셀프 호스팅 방식의 브라우저 기반 데이터베이스 IDE입니다. 드라이버 41개로 엔진 68개에 연결합니다. + ru: Браузерная IDE для баз данных, которую вы разворачиваете у себя. 41 драйвер обеспечивает доступ к 68 движкам. + ms: IDE pangkalan data berasaskan pelayar yang anda hoskan sendiri. 41 pemacu memberi akses kepada 68 enjin. + pt-br: IDE de banco de dados auto-hospedada e executada no navegador. 41 drivers dão acesso a 68 motores de banco de dados. type: tool crossVersionUpdate: true limit: 0 diff --git a/apps/librespeed-linuxserver/6.3.0/docker-compose.yml b/apps/librespeed-linuxserver/6.3.0/docker-compose.yml index 8c8ea3fb5..57818f16c 100644 --- a/apps/librespeed-linuxserver/6.3.0/docker-compose.yml +++ b/apps/librespeed-linuxserver/6.3.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: librespeed: - image: "linuxserver/librespeed:6.3.0" + image: "linuxserver/librespeed:6.3.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/librespeed/6.3.0/docker-compose.yml b/apps/librespeed/6.3.0/docker-compose.yml index bf7e5e8f2..001a40e06 100644 --- a/apps/librespeed/6.3.0/docker-compose.yml +++ b/apps/librespeed/6.3.0/docker-compose.yml @@ -1,6 +1,6 @@ services: librespeed: - image: "ghcr.io/librespeed/speedtest:6.3.0" + image: "ghcr.io/librespeed/speedtest:6.3.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/librewolf-linuxserver/157.0.1/docker-compose.yml b/apps/librewolf-linuxserver/157.0.1/docker-compose.yml index 4a9f39ce3..76e0b8e17 100644 --- a/apps/librewolf-linuxserver/157.0.1/docker-compose.yml +++ b/apps/librewolf-linuxserver/157.0.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: librewolf: - image: "linuxserver/librewolf:157.0.1" + image: "linuxserver/librewolf:158.0.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/luanti-linuxserver/5.17.0/docker-compose.yml b/apps/luanti-linuxserver/5.17.0/docker-compose.yml index 395f17dbe..421f47e95 100644 --- a/apps/luanti-linuxserver/5.17.0/docker-compose.yml +++ b/apps/luanti-linuxserver/5.17.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: luanti: - image: "linuxserver/luanti:5.17.0" + image: "linuxserver/luanti:5.18.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/lubelogger/1.7.3/docker-compose.yml b/apps/lubelogger/1.7.3/docker-compose.yml index 0df2824ec..3d52b5e7c 100644 --- a/apps/lubelogger/1.7.3/docker-compose.yml +++ b/apps/lubelogger/1.7.3/docker-compose.yml @@ -1,6 +1,6 @@ services: lubelogger: - image: "ghcr.io/hargata/lubelogger:v1.7.3@sha256:c9d2bbb48c7f84d90e54496e2cd60422e56a5f2345c8a539ebe09f77e629d321" + image: "ghcr.io/hargata/lubelogger:v1.7.4@sha256:483a9d7582c4e6258615fd822b3de4193b4fcc4230d0a92432c6d407faf75af2" container_name: ${CONTAINER_NAME} restart: unless-stopped init: true diff --git a/apps/lubelogger/1.7.4/.env.sample b/apps/lubelogger/1.7.4/.env.sample new file mode 100644 index 000000000..30ea2f982 --- /dev/null +++ b/apps/lubelogger/1.7.4/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_HTTP=8080 +LUBELOGGER_ROOT_USERNAME=admin +LUBELOGGER_ROOT_PASSWORD=LubeLogger_Admin_2026! +TIME_ZONE=Etc/UTC +APP_DATA_DIR=./data +CONTAINER_NAME= diff --git a/apps/lubelogger/1.7.4/data.yml b/apps/lubelogger/1.7.4/data.yml new file mode 100644 index 000000000..53a507e42 --- /dev/null +++ b/apps/lubelogger/1.7.4/data.yml @@ -0,0 +1,86 @@ +additionalProperties: + formFields: + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: admin + edit: true + envKey: LUBELOGGER_ROOT_USERNAME + labelEn: Root Username + labelZh: 根管理员用户名 + label: + en: Root Username + zh: 根管理员用户名 + zh-Hant: 根管理員使用者名稱 + ja: ルート管理者ユーザー名 + ko: 루트 관리자 사용자 이름 + ru: Имя корневого администратора + ms: Nama Pengguna Pentadbir Root + pt-br: Nome do Administrador Raiz + required: true + rule: paramCommon + type: text + - default: LubeLogger_Admin_2026! + edit: true + envKey: LUBELOGGER_ROOT_PASSWORD + labelEn: Root Password + labelZh: 根管理员密码 + label: + en: Root Password + zh: 根管理员密码 + zh-Hant: 根管理員密碼 + ja: ルート管理者パスワード + ko: 루트 관리자 비밀번호 + ru: Пароль корневого администратора + ms: Kata Laluan Pentadbir Root + pt-br: Senha do Administrador Raiz + random: true + required: true + rule: paramComplexity + type: password + - default: Etc/UTC + edit: true + envKey: TIME_ZONE + labelEn: Time Zone + labelZh: 时区 + label: + en: Time Zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon Masa + pt-br: Fuso Horario + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretorio de Dados + required: true + type: text diff --git a/apps/lubelogger/1.7.4/data/.gitkeep b/apps/lubelogger/1.7.4/data/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/lubelogger/1.7.4/data/.gitkeep @@ -0,0 +1 @@ + diff --git a/apps/lubelogger/1.7.4/docker-compose.yml b/apps/lubelogger/1.7.4/docker-compose.yml new file mode 100644 index 000000000..3d52b5e7c --- /dev/null +++ b/apps/lubelogger/1.7.4/docker-compose.yml @@ -0,0 +1,46 @@ +services: + lubelogger: + image: "ghcr.io/hargata/lubelogger:v1.7.4@sha256:483a9d7582c4e6258615fd822b3de4193b4fcc4230d0a92432c6d407faf75af2" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + init: true + user: "1000:1000" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + env_file: + - path: "${APP_DATA_DIR}/.lubelogger-auth.env" + required: false + environment: + - HOME=/App/data + - TZ=${TIME_ZONE} + - ASPNETCORE_HTTP_PORTS=8080 + - Kestrel__EndpointDefaults__Protocols=Http1 + - DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2SUPPORT=false + - LUBELOGGER_LOCALE_OVERRIDE=en_US + - LUBELOGGER_LOCALE_DT_OVERRIDE=en_US + read_only: true + tmpfs: + - /tmp:size=64m,mode=1777 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD-SHELL + - >- + bash -lc 'exec 3<>/dev/tcp/127.0.0.1/8080; exec 3>&-; exec 3<&-' + interval: 30s + timeout: 5s + start_period: 15s + retries: 3 + volumes: + - "${APP_DATA_DIR}:/App/data" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/lubelogger/1.7.4/scripts/init.sh b/apps/lubelogger/1.7.4/scripts/init.sh new file mode 100755 index 000000000..144ab4632 --- /dev/null +++ b/apps/lubelogger/1.7.4/scripts/init.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +configured_value() { + local key="$1" + local default_value="$2" + local value="${!key:-}" + if [[ -z "$value" ]]; then + value="$(read_env_value "$key")" + fi + printf '%s\n' "${value:-$default_value}" +} + +reject_control_characters() { + local name="$1" + local value="$2" + if [[ "$value" == *$'\n'* || "$value" == *$'\r'* ]]; then + printf '%s must not contain line breaks\n' "$name" >&2 + exit 1 + fi +} + +prepare_lubelogger() { + local raw path username password username_hash password_hash auth_file temp_file + + raw="$(configured_value APP_DATA_DIR ./data)" + username="$(configured_value LUBELOGGER_ROOT_USERNAME admin)" + password="$(configured_value LUBELOGGER_ROOT_PASSWORD '')" + + [[ -n "$raw" ]] || { + printf '%s\n' 'APP_DATA_DIR must not be empty' >&2 + exit 1 + } + if [[ "$raw" = /* ]]; then + printf '%s\n' 'APP_DATA_DIR must be relative to the application version directory' >&2 + exit 1 + fi + + path="$(realpath -m -- "$ROOT_DIR/${raw#./}")" + case "$path" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; + esac + + install -d -m 0750 "$path" + path="$(realpath -e -- "$path")" + case "$path" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; + esac + + [[ -n "$username" && ${#username} -le 128 ]] || { + printf '%s\n' 'LUBELOGGER_ROOT_USERNAME must contain 1 to 128 characters' >&2 + exit 1 + } + [[ ${#password} -ge 12 && ${#password} -le 256 ]] || { + printf '%s\n' 'LUBELOGGER_ROOT_PASSWORD must contain 12 to 256 characters' >&2 + exit 1 + } + reject_control_characters LUBELOGGER_ROOT_USERNAME "$username" + reject_control_characters LUBELOGGER_ROOT_PASSWORD "$password" + + username_hash="$(printf '%s' "$username" | sha256sum | cut -d ' ' -f 1)" + password_hash="$(printf '%s' "$password" | sha256sum | cut -d ' ' -f 1)" + [[ "$username_hash" =~ ^[0-9a-f]{64}$ && "$password_hash" =~ ^[0-9a-f]{64}$ ]] || { + printf '%s\n' 'Failed to generate LubeLogger credential hashes' >&2 + exit 1 + } + + auth_file="$path/.lubelogger-auth.env" + if [[ -L "$auth_file" ]]; then + printf '%s\n' 'LubeLogger authentication file must not be a symbolic link' >&2 + exit 1 + fi + if [[ -e "$auth_file" && ! -f "$auth_file" ]]; then + printf '%s\n' 'LubeLogger authentication path must be a regular file' >&2 + exit 1 + fi + + umask 077 + temp_file="$(mktemp "$path/.lubelogger-auth.env.tmp.XXXXXX")" + trap 'rm -f -- "${temp_file:-}"' EXIT + printf '%s\n' \ + 'EnableAuth=true' \ + "UserNameHash=$username_hash" \ + "UserPasswordHash=$password_hash" \ + 'DisableRegistration=true' \ + 'LUBELOGGER_OPEN_REGISTRATION=false' >"$temp_file" + chmod 0600 "$temp_file" + chown 1000:1000 "$temp_file" + mv -f -- "$temp_file" "$auth_file" + trap - EXIT + + chown 1000:1000 "$path" + chmod 0750 "$path" + [[ "$(stat -c '%a' -- "$auth_file")" = 600 ]] || { + printf '%s\n' 'LubeLogger authentication file permissions must be 0600' >&2 + exit 1 + } +} + +prepare_lubelogger diff --git a/apps/lubelogger/1.7.4/scripts/uninstall.sh b/apps/lubelogger/1.7.4/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/lubelogger/1.7.4/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/lubelogger/1.7.4/scripts/upgrade.sh b/apps/lubelogger/1.7.4/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/lubelogger/1.7.4/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/lychee-linuxserver/7.10.0/docker-compose.yml b/apps/lychee-linuxserver/7.10.0/docker-compose.yml index 9775f1982..a845f4880 100644 --- a/apps/lychee-linuxserver/7.10.0/docker-compose.yml +++ b/apps/lychee-linuxserver/7.10.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: lychee: - image: "linuxserver/lychee:7.10.0" + image: "linuxserver/lychee:7.11.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/mariadb-linuxserver/11.8.8/docker-compose.yml b/apps/mariadb-linuxserver/11.8.8/docker-compose.yml index bb4e0bec2..e4a048c11 100644 --- a/apps/mariadb-linuxserver/11.8.8/docker-compose.yml +++ b/apps/mariadb-linuxserver/11.8.8/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: mariadb: - image: "linuxserver/mariadb:11.8.8" + image: "linuxserver/mariadb:11.8.9" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/mastodon-linuxserver/4.7.3/.env.sample b/apps/mastodon-linuxserver/4.7.3/.env.sample new file mode 100644 index 000000000..1ad21ac97 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/.env.sample @@ -0,0 +1,31 @@ +CONTAINER_NAME= +PANEL_APP_PORT_HTTP=80 +PANEL_APP_PORT_HTTPS=443 +CONFIG_PATH=./data/config +LOCAL_DOMAIN=example.com +WEB_DOMAIN= +REDIS_HOST=mastodon-redis +REDIS_PORT=6379 +DB_HOST=mastodon-db +DB_PORT=5432 +DB_USER=mastodon +DB_NAME=mastodon +DB_PASSWORD=replace-with-db-password +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=replace-with-generated-active-record-primary-key +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=replace-with-generated-active-record-deterministic-key +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=replace-with-generated-active-record-salt +SECRET_KEY_BASE=replace-with-generated-secret-key-base +OTP_SECRET=replace-with-generated-otp-secret +VAPID_PRIVATE_KEY=replace-with-generated-vapid-private-key +VAPID_PUBLIC_KEY=replace-with-generated-vapid-public-key +SMTP_SERVER=mail.example.com +SMTP_PORT=25 +SMTP_LOGIN= +SMTP_PASSWORD= +SMTP_FROM_ADDRESS=notifications@example.com +MASTODON_PROMETHEUS_EXPORTER_ENABLED=false +SIDEKIQ_THREADS=5 +DB_POOL=5 +DB_DATA_PATH=./data/db +REDIS_DATA_PATH=./data/redis +TIME_ZONE=Asia/Shanghai diff --git a/apps/mastodon-linuxserver/4.7.3/data.yml b/apps/mastodon-linuxserver/4.7.3/data.yml new file mode 100644 index 000000000..a533c1819 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/data.yml @@ -0,0 +1,505 @@ +additionalProperties: + formFields: + - default: 80 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + type: number + rule: paramPort + - default: 443 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS 端口 + label: + en: HTTPS Port + zh: HTTPS 端口 + zh-Hant: HTTPS 連接埠 + ja: HTTPS ポート + ko: HTTPS 포트 + ru: HTTPS-порт + ms: Port HTTPS + pt-br: Porta HTTPS + required: true + type: number + rule: paramPort + - default: ./data/config + edit: true + envKey: CONFIG_PATH + labelEn: Config folder path + labelZh: 配置文件路径 + label: + en: Config folder path + zh: 配置文件路径 + zh-Hant: 設定檔資料夾路徑 + ja: 設定フォルダーのパス + ko: 설정 폴더 경로 + ru: Путь к папке конфигурации + ms: Laluan folder konfigurasi + pt-br: Caminho da pasta de configuração + required: true + type: text + - default: '' + edit: true + envKey: LOCAL_DOMAIN + labelEn: Local domain + labelZh: 实例域名 + label: + en: Local domain + zh: 实例域名 + zh-Hant: 實例網域 + ja: ローカルドメイン + ko: 로컬 도메인 + ru: Локальный домен + ms: Domain setempat + pt-br: Dominio local + required: true + type: text + - default: '' + edit: true + envKey: WEB_DOMAIN + labelEn: Web domain + labelZh: Web 域名 + label: + en: Web domain + zh: Web 域名 + zh-Hant: Web 網域 + ja: Web ドメイン + ko: Web 도메인 + ru: Web-домен + ms: Domain web + pt-br: Dominio web + required: false + type: text + - default: "false" + edit: true + envKey: MASTODON_PROMETHEUS_EXPORTER_ENABLED + labelEn: Prometheus exporter + labelZh: Prometheus 导出器 + label: + en: Prometheus exporter + zh: Prometheus 导出器 + zh-Hant: Prometheus 匯出器 + ja: Prometheus エクスポーター + ko: Prometheus exporter + ru: Экспортер Prometheus + ms: Pengeksport Prometheus + pt-br: Exportador Prometheus + required: false + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" + - default: mastodon-db + edit: true + envKey: DB_HOST + labelEn: Database host + labelZh: 数据库主机 + label: + en: Database host + zh: 数据库主机 + zh-Hant: 資料庫主機 + ja: データベースホスト + ko: 데이터베이스 호스트 + ru: Хост базы данных + ms: Hos pangkalan data + pt-br: Host do banco + required: true + type: text + - default: 5432 + edit: true + envKey: DB_PORT + labelEn: Database port + labelZh: 数据库端口 + label: + en: Database port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco + required: true + type: number + - default: mastodon + edit: true + envKey: DB_USER + labelEn: Database username + labelZh: 数据库用户名 + label: + en: Database username + zh: 数据库用户名 + zh-Hant: 資料庫使用者名稱 + ja: データベースユーザー名 + ko: 데이터베이스 사용자 이름 + ru: Имя пользователя базы + ms: Nama pengguna pangkalan data + pt-br: Usuario do banco + required: true + type: text + - default: mastodon + edit: true + envKey: DB_NAME + labelEn: Database name + labelZh: 数据库名称 + label: + en: Database name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco + required: true + type: text + - default: mastodon-redis + edit: true + envKey: REDIS_HOST + labelEn: Redis host + labelZh: Redis 主机 + label: + en: Redis host + zh: Redis 主机 + zh-Hant: Redis 主機 + ja: Redis ホスト + ko: Redis 호스트 + ru: Хост Redis + ms: Hos Redis + pt-br: Host do Redis + required: true + type: text + - default: 6379 + edit: true + envKey: REDIS_PORT + labelEn: Redis port + labelZh: Redis 端口 + label: + en: Redis port + zh: Redis 端口 + zh-Hant: Redis 連接埠 + ja: Redis ポート + ko: Redis 포트 + ru: Порт Redis + ms: Port Redis + pt-br: Porta do Redis + required: true + type: number + - default: '' + edit: true + envKey: DB_PASSWORD + labelEn: Database password + labelZh: 数据库密码 + label: + en: Database password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: SECRET_KEY_BASE + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: OTP_SECRET + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: VAPID_PRIVATE_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: VAPID_PUBLIC_KEY + labelEn: Secret value + labelZh: 密钥值 + label: + en: Secret value + zh: 密钥值 + zh-Hant: 金鑰值 + ja: シークレット値 + ko: 시크릿 값 + ru: Секретное значение + ms: Nilai rahsia + pt-br: Valor secreto + required: true + random: true + type: password + rule: paramComplexity + - default: '' + edit: true + envKey: SMTP_SERVER + labelEn: SMTP server + labelZh: SMTP 服务器 + label: + en: SMTP server + zh: SMTP 服务器 + zh-Hant: SMTP 伺服器 + ja: SMTP サーバー + ko: SMTP 서버 + ru: SMTP-сервер + ms: Pelayan SMTP + pt-br: Servidor SMTP + required: true + type: text + - default: 25 + edit: true + envKey: SMTP_PORT + labelEn: SMTP port + labelZh: SMTP 端口 + label: + en: SMTP port + zh: SMTP 端口 + zh-Hant: SMTP 連接埠 + ja: SMTP ポート + ko: SMTP 포트 + ru: Порт SMTP + ms: Port SMTP + pt-br: Porta SMTP + required: true + type: number + - default: '' + edit: true + envKey: SMTP_LOGIN + labelEn: SMTP login + labelZh: SMTP 登录名 + label: + en: SMTP login + zh: SMTP 登录名 + zh-Hant: SMTP 登入名 + ja: SMTP ログイン + ko: SMTP 로그인 + ru: Логин SMTP + ms: Log masuk SMTP + pt-br: Login SMTP + required: false + type: text + - default: '' + edit: true + envKey: SMTP_PASSWORD + labelEn: SMTP password + labelZh: SMTP 密码 + label: + en: SMTP password + zh: SMTP 密码 + zh-Hant: SMTP 密碼 + ja: SMTP パスワード + ko: SMTP 비밀번호 + ru: Пароль SMTP + ms: Kata laluan SMTP + pt-br: Senha SMTP + required: false + type: password + - default: '' + edit: true + envKey: SMTP_FROM_ADDRESS + labelEn: SMTP from address + labelZh: SMTP 发件地址 + label: + en: SMTP from address + zh: SMTP 发件地址 + zh-Hant: SMTP 寄件地址 + ja: SMTP 送信元アドレス + ko: SMTP 발신 주소 + ru: SMTP адрес отправителя + ms: Alamat pengirim SMTP + pt-br: Endereco remetente SMTP + required: true + type: text + - default: 5 + edit: true + envKey: SIDEKIQ_THREADS + labelEn: Sidekiq threads + labelZh: Sidekiq 线程数 + label: + en: Sidekiq threads + zh: Sidekiq 线程数 + zh-Hant: Sidekiq 執行緒數 + ja: Sidekiq スレッド数 + ko: Sidekiq 스레드 수 + ru: Потоки Sidekiq + ms: Benang Sidekiq + pt-br: Threads Sidekiq + required: true + type: number + - default: 5 + edit: true + envKey: DB_POOL + labelEn: Database pool size + labelZh: 数据库连接池大小 + label: + en: Database pool size + zh: 数据库连接池大小 + zh-Hant: 資料庫連線池大小 + ja: データベースプールサイズ + ko: 데이터베이스 풀 크기 + ru: Размер пула базы данных + ms: Saiz kolam pangkalan data + pt-br: Tamanho do pool do banco + required: true + type: number + - default: ./data/db + edit: true + envKey: DB_DATA_PATH + labelEn: Database data path + labelZh: 数据库数据目录 + label: + en: Database data path + zh: 数据库数据目录 + zh-Hant: 資料庫資料目錄 + ja: データベースデータディレクトリ + ko: 데이터베이스 데이터 디렉터リ + ru: Каталог данных базы + ms: Laluan data pangkalan data + pt-br: Caminho de dados do banco + required: true + type: text + - default: ./data/redis + edit: true + envKey: REDIS_DATA_PATH + labelEn: Redis data path + labelZh: Redis 数据目录 + label: + en: Redis data path + zh: Redis 数据目录 + zh-Hant: Redis 資料目錄 + ja: Redis データディレクトリ + ko: Redis 데이터 디렉터리 + ru: Каталог данных Redis + ms: Laluan data Redis + pt-br: Caminho de dados Redis + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/mastodon-linuxserver/4.7.3/data/.gitkeep b/apps/mastodon-linuxserver/4.7.3/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/mastodon-linuxserver/4.7.3/docker-compose.yml b/apps/mastodon-linuxserver/4.7.3/docker-compose.yml new file mode 100644 index 000000000..22b6e6ef1 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/docker-compose.yml @@ -0,0 +1,72 @@ +networks: + 1panel-network: + external: true +services: + mastodon: + image: "linuxserver/mastodon:4.7.3" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + - "${PANEL_APP_PORT_HTTPS}:443" + volumes: + - "${CONFIG_PATH}:/config" + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TIME_ZONE} + - LOCAL_DOMAIN=${LOCAL_DOMAIN} + - REDIS_HOST=${REDIS_HOST} + - REDIS_PORT=${REDIS_PORT} + - DB_HOST=${DB_HOST} + - DB_USER=${DB_USER} + - DB_NAME=${DB_NAME} + - DB_PASS=${DB_PASSWORD} + - DB_PORT=${DB_PORT} + - ES_ENABLED=false + - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} + - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} + - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} + - SECRET_KEY_BASE=${SECRET_KEY_BASE} + - OTP_SECRET=${OTP_SECRET} + - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} + - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} + - SMTP_SERVER=${SMTP_SERVER} + - SMTP_PORT=${SMTP_PORT} + - SMTP_LOGIN=${SMTP_LOGIN:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} + - S3_ENABLED=false + - WEB_DOMAIN=${WEB_DOMAIN:-} + - MASTODON_PROMETHEUS_EXPORTER_ENABLED=${MASTODON_PROMETHEUS_EXPORTER_ENABLED} + - SIDEKIQ_THREADS=${SIDEKIQ_THREADS} + - DB_POOL=${DB_POOL} + depends_on: + - mastodon-db + - mastodon-redis + labels: + createdBy: "Apps" + mastodon-db: + image: "postgres:16-alpine" + restart: always + networks: + - 1panel-network + volumes: + - "${DB_DATA_PATH}:/var/lib/postgresql/data" + environment: + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + labels: + createdBy: "Apps" + mastodon-redis: + image: "redis:7-alpine" + restart: always + networks: + - 1panel-network + volumes: + - "${REDIS_DATA_PATH}:/data" + labels: + createdBy: "Apps" diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/init.sh b/apps/mastodon-linuxserver/4.7.3/scripts/init.sh new file mode 100755 index 000000000..bee255c50 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/init.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4" +LEGACY_DB_PASSWORD="mastodon-change-me" +LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo" +LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0" +LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1" +LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0" +LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac" +LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E=" +LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8=" + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +run_mastodon_generator() { + docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1" +} + +extract_generator_value() { + local output="$1" + local key="$2" + printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1 +} + +is_active_record_secret_valid() { + local value="$1" + [[ "$value" =~ ^[A-Za-z0-9]{32}$ ]] +} + +is_hex_secret_valid() { + local value="$1" + [[ "$value" =~ ^[a-f0-9]{128}$ ]] +} + +is_vapid_key_valid() { + local value="$1" + [[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]] +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}" +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}" +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}" +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}" +SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}" +OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}" +VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}" +VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}" + +if [[ -f "$ENV_FILE" ]]; then + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi + + active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)" + active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)" + active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)" + cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)" + cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)" + cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)" + if is_active_record_secret_valid "$active_primary" \ + && is_active_record_secret_valid "$active_deterministic" \ + && is_active_record_secret_valid "$active_salt" \ + && [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + && [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + && [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + elif is_active_record_secret_valid "$cached_active_primary" \ + && is_active_record_secret_valid "$cached_active_deterministic" \ + && is_active_record_secret_valid "$cached_active_salt"; then + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt" + elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + || [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + || [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \ + || ! is_active_record_secret_valid "$active_primary" \ + || ! is_active_record_secret_valid "$active_deterministic" \ + || ! is_active_record_secret_valid "$active_salt"; then + active_record_output="$(run_mastodon_generator generate-active-record)" + active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")" + active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")" + active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")" + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + fi + + secret_key_base="$(read_env_value SECRET_KEY_BASE || true)" + if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then + cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)" + if is_hex_secret_valid "$cached_secret_key_base"; then + set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base" + else + secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "SECRET_KEY_BASE" "$secret_key_base" + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + else + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + + otp_secret="$(read_env_value OTP_SECRET || true)" + if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then + cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)" + if is_hex_secret_valid "$cached_otp_secret"; then + set_env_value "OTP_SECRET" "$cached_otp_secret" + else + otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "OTP_SECRET" "$otp_secret" + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + else + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + + vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)" + vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)" + cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)" + cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)" + if is_vapid_key_valid "$vapid_private_key" \ + && is_vapid_key_valid "$vapid_public_key" \ + && [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + && [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key" + elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then + set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key" + set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key" + elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + || [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \ + || ! is_vapid_key_valid "$vapid_private_key" \ + || ! is_vapid_key_valid "$vapid_public_key"; then + vapid_output="$(run_mastodon_generator generate-vapid)" + set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + fi +fi + +paths=( + "${CONFIG_PATH:-./data/config}" + "${DB_DATA_PATH:-./data/db}" + "${REDIS_DATA_PATH:-./data/redis}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh b/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh b/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh new file mode 100755 index 000000000..8e4e6de98 --- /dev/null +++ b/apps/mastodon-linuxserver/4.7.3/scripts/upgrade.sh @@ -0,0 +1,251 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" +MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4" +LEGACY_DB_PASSWORD="mastodon-change-me" +LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo" +LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0" +LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1" +LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0" +LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac" +LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E=" +LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8=" + +ensure_env_default() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key migration" + return + fi + + if grep -qE "^${key}=" "$ENV_FILE"; then + echo "$key already exists" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +read_env_value() { + local key="$1" + + if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then + return + fi + + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%\'}" + current="${current#\'}" + printf '%s\n' "$current" +} + +read_cache_value() { + local cache_file="$1" + if [[ ! -s "$cache_file" ]]; then + return + fi + sed -n '1p' "$cache_file" | tr -d '\r\n' +} + +write_cache_value() { + local cache_file="$1" + local value="$2" + + mkdir -p "$(dirname "$cache_file")" + printf '%s\n' "$value" > "$cache_file" + chmod 600 "$cache_file" 2>/dev/null || true +} + +escape_sed_replacement() { + printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g' +} + +set_env_value() { + local key="$1" + local value="$2" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key generation" + return + fi + + local replacement + replacement="$(escape_sed_replacement "$value")" + if grep -qE "^${key}=" "$ENV_FILE"; then + sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE" + echo "Updated $key" + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +generate_alnum_secret() { + local secret="" + local chunk="" + while [[ ${#secret} -lt 32 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" +} + +run_mastodon_generator() { + docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1" +} + +extract_generator_value() { + local output="$1" + local key="$2" + printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1 +} + +is_active_record_secret_valid() { + local value="$1" + [[ "$value" =~ ^[A-Za-z0-9]{32}$ ]] +} + +is_hex_secret_valid() { + local value="$1" + [[ "$value" =~ ^[a-f0-9]{128}$ ]] +} + +is_vapid_key_valid() { + local value="$1" + [[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]] +} + +CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}" +CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}" +SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}" +DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}" +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}" +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}" +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}" +SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}" +OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}" +VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}" +VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}" + +if [[ -f "$ENV_FILE" ]]; then + db_password="$(read_env_value DB_PASSWORD || true)" + cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)" + if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + elif [[ -n "$cached_db_password" ]]; then + set_env_value "DB_PASSWORD" "$cached_db_password" + else + db_password="$(generate_alnum_secret)" + set_env_value "DB_PASSWORD" "$db_password" + write_cache_value "$DB_PASSWORD_CACHE" "$db_password" + fi + + active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)" + active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)" + active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)" + cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)" + cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)" + cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)" + if is_active_record_secret_valid "$active_primary" \ + && is_active_record_secret_valid "$active_deterministic" \ + && is_active_record_secret_valid "$active_salt" \ + && [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + && [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + && [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + elif is_active_record_secret_valid "$cached_active_primary" \ + && is_active_record_secret_valid "$cached_active_deterministic" \ + && is_active_record_secret_valid "$cached_active_salt"; then + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt" + elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \ + || [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \ + || [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \ + || ! is_active_record_secret_valid "$active_primary" \ + || ! is_active_record_secret_valid "$active_deterministic" \ + || ! is_active_record_secret_valid "$active_salt"; then + active_record_output="$(run_mastodon_generator generate-active-record)" + active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")" + active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")" + active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")" + set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary" + set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic" + set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic" + write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt" + fi + + secret_key_base="$(read_env_value SECRET_KEY_BASE || true)" + if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then + cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)" + if is_hex_secret_valid "$cached_secret_key_base"; then + set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base" + else + secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "SECRET_KEY_BASE" "$secret_key_base" + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + else + write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base" + fi + + otp_secret="$(read_env_value OTP_SECRET || true)" + if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then + cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)" + if is_hex_secret_valid "$cached_otp_secret"; then + set_env_value "OTP_SECRET" "$cached_otp_secret" + else + otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')" + set_env_value "OTP_SECRET" "$otp_secret" + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + else + write_cache_value "$OTP_SECRET_CACHE" "$otp_secret" + fi + + vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)" + vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)" + cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)" + cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)" + if is_vapid_key_valid "$vapid_private_key" \ + && is_vapid_key_valid "$vapid_public_key" \ + && [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + && [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key" + elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then + set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key" + set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key" + elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \ + || [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \ + || ! is_vapid_key_valid "$vapid_private_key" \ + || ! is_vapid_key_valid "$vapid_public_key"; then + vapid_output="$(run_mastodon_generator generate-vapid)" + set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")" + write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")" + fi + + ensure_env_default "REDIS_HOST" "mastodon-redis" + ensure_env_default "REDIS_PORT" "6379" + ensure_env_default "DB_HOST" "mastodon-db" + ensure_env_default "DB_PORT" "5432" + ensure_env_default "DB_USER" "mastodon" + ensure_env_default "DB_NAME" "mastodon" + ensure_env_default "MASTODON_PROMETHEUS_EXPORTER_ENABLED" "false" +else + echo "$ENV_FILE not found; skipped LinuxServer environment migration" +fi diff --git a/apps/mattermost/11.11.1/docker-compose.yml b/apps/mattermost/11.11.1/docker-compose.yml index d22af0f28..7625deb18 100644 --- a/apps/mattermost/11.11.1/docker-compose.yml +++ b/apps/mattermost/11.11.1/docker-compose.yml @@ -1,6 +1,6 @@ services: mattermost: - image: "mattermost/mattermost-team-edition:11.11.1" + image: "mattermost/mattermost-team-edition:12.0.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/.env.sample b/apps/mazanoke/1.1.7-digest-ca83cdd/.env.sample new file mode 100644 index 000000000..d5882ae47 --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/.env.sample @@ -0,0 +1,4 @@ +PANEL_APP_PORT_HTTP=3474 +MAZANOKE_USERNAME=mazanoke +MAZANOKE_PASSWORD= +CONTAINER_NAME= diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/data.yml b/apps/mazanoke/1.1.7-digest-ca83cdd/data.yml new file mode 100644 index 000000000..8fd04f91b --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/data.yml @@ -0,0 +1,53 @@ +additionalProperties: + formFields: + - default: 3474 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: mazanoke + edit: true + envKey: MAZANOKE_USERNAME + labelEn: Username + labelZh: 用户名 + label: + en: Username + zh: 用户名 + zh-Hant: 使用者名稱 + ja: ユーザー名 + ko: 사용자 이름 + ru: Имя пользователя + ms: Nama pengguna + pt-br: Nome de usuário + required: true + type: text + - default: "" + edit: true + envKey: MAZANOKE_PASSWORD + labelEn: Password + labelZh: 密码 + label: + en: Password + zh: 密码 + zh-Hant: 密碼 + ja: パスワード + ko: 비밀번호 + ru: Пароль + ms: Kata laluan + pt-br: Senha + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/docker-compose.yml b/apps/mazanoke/1.1.7-digest-ca83cdd/docker-compose.yml new file mode 100644 index 000000000..a849d4ef2 --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/docker-compose.yml @@ -0,0 +1,33 @@ +services: + mazanoke: + image: "ghcr.io/civilblur/mazanoke:v1.1.7@sha256:ca83cddd90cd1e4b43102884bf39025731a1c7569f3bbda9d6530de72528cbab" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + environment: + - USERNAME=${MAZANOKE_USERNAME} + - PASSWORD=${MAZANOKE_PASSWORD} + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - CHOWN + - SETUID + - SETGID + - NET_BIND_SERVICE + healthcheck: + test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/assets/images/favicon-16x16.png"] + interval: 30s + timeout: 5s + start_period: 10s + retries: 3 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/init.sh b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/init.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/init.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/uninstall.sh b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/uninstall.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/upgrade.sh b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/upgrade.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/mazanoke/1.1.7-digest-ca83cdd/scripts/upgrade.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/mdcz/latest/docker-compose.yml b/apps/mdcz/latest/docker-compose.yml index a5e253828..a84053b74 100644 --- a/apps/mdcz/latest/docker-compose.yml +++ b/apps/mdcz/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: mdcz: - image: "ghcr.io/shotheadman/mdcz:latest@sha256:dcd353deaf4f8bb62e998f4b97b99b4fcca4b8faaa49cd56e17c1543c8156265" + image: "ghcr.io/shotheadman/mdcz:latest@sha256:a385f8519fbc56c52bd83827aa80c7cce6fab60c9ff24ba67536061151a4b312" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/medusa-linuxserver/1.0.26/docker-compose.yml b/apps/medusa-linuxserver/1.0.26/docker-compose.yml index f4687ecd7..05e63cc96 100644 --- a/apps/medusa-linuxserver/1.0.26/docker-compose.yml +++ b/apps/medusa-linuxserver/1.0.26/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: medusa: - image: "linuxserver/medusa:1.0.26" + image: "linuxserver/medusa:1.0.27" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/memgraph/3.13.1/docker-compose.yml b/apps/memgraph/3.13.1/docker-compose.yml index f54ca0abe..825cece86 100644 --- a/apps/memgraph/3.13.1/docker-compose.yml +++ b/apps/memgraph/3.13.1/docker-compose.yml @@ -1,6 +1,6 @@ services: memgraph: - image: "memgraph/memgraph:3.13.1" + image: "memgraph/memgraph:3.14.0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/memgraph/3.13.2/docker-compose.yml b/apps/memgraph/3.13.2/docker-compose.yml index 0ac200403..825cece86 100644 --- a/apps/memgraph/3.13.2/docker-compose.yml +++ b/apps/memgraph/3.13.2/docker-compose.yml @@ -1,6 +1,6 @@ services: memgraph: - image: "memgraph/memgraph:3.13.2" + image: "memgraph/memgraph:3.14.0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/milvus/3.0.2/docker-compose.yml b/apps/milvus/3.0.2/docker-compose.yml index 1afad344a..dae4ead8c 100644 --- a/apps/milvus/3.0.2/docker-compose.yml +++ b/apps/milvus/3.0.2/docker-compose.yml @@ -1,6 +1,6 @@ services: milvus-etcd: - image: "quay.io/coreos/etcd:v3.5.33" + image: "quay.io/coreos/etcd:v3.5.34" container_name: ${CONTAINER_NAME}-etcd environment: - ETCD_AUTO_COMPACTION_MODE=revision diff --git a/apps/modrinth-linuxserver/0.21.9/.env.sample b/apps/modrinth-linuxserver/0.21.10/.env.sample similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/.env.sample rename to apps/modrinth-linuxserver/0.21.10/.env.sample diff --git a/apps/modrinth-linuxserver/0.21.9/data.yml b/apps/modrinth-linuxserver/0.21.10/data.yml similarity index 100% rename from apps/modrinth-linuxserver/0.21.9/data.yml rename to apps/modrinth-linuxserver/0.21.10/data.yml diff --git a/apps/modrinth-linuxserver/0.21.10/data/.gitkeep b/apps/modrinth-linuxserver/0.21.10/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/modrinth-linuxserver/0.21.9/docker-compose.yml b/apps/modrinth-linuxserver/0.21.10/docker-compose.yml similarity index 93% rename from apps/modrinth-linuxserver/0.21.9/docker-compose.yml rename to apps/modrinth-linuxserver/0.21.10/docker-compose.yml index bdd16c22b..abd2a53b3 100644 --- a/apps/modrinth-linuxserver/0.21.9/docker-compose.yml +++ b/apps/modrinth-linuxserver/0.21.10/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: modrinth: - image: "linuxserver/modrinth:0.21.9" + image: "linuxserver/modrinth:0.22.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/modrinth-linuxserver/0.21.10/scripts/init.sh b/apps/modrinth-linuxserver/0.21.10/scripts/init.sh new file mode 100755 index 000000000..e5658f096 --- /dev/null +++ b/apps/modrinth-linuxserver/0.21.10/scripts/init.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +paths=( + "${CONFIG_PATH:-./data/config}" +) + +mkdir -p "${paths[@]}" +for path in "${paths[@]}"; do + case "$path" in + ./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;; + esac +done diff --git a/apps/modrinth-linuxserver/0.21.10/scripts/uninstall.sh b/apps/modrinth-linuxserver/0.21.10/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/modrinth-linuxserver/0.21.10/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/modrinth-linuxserver/0.21.10/scripts/upgrade.sh b/apps/modrinth-linuxserver/0.21.10/scripts/upgrade.sh new file mode 100755 index 000000000..84e9da3a5 --- /dev/null +++ b/apps/modrinth-linuxserver/0.21.10/scripts/upgrade.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${ENV_FILE:-./.env}" + +generate_secret() { + if [[ -r /dev/urandom ]]; then + local secret="" + local chunk="" + while [[ ${#secret} -lt 24 ]]; do + chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((24 - ${#secret})) || true)" + secret="${secret}${chunk}" + done + printf '%s\n' "$secret" + else + printf '%s\n' 'linuxserver-change-me' + fi +} + +ensure_env_default() { + local key="$1" + local value="$2" + local fill_empty="${3:-false}" + + if [[ ! -f "$ENV_FILE" ]]; then + echo "$ENV_FILE not found; skipped $key migration" + return + fi + + if grep -qE "^${key}=" "$ENV_FILE"; then + local current + current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + current="${current%\"}" + current="${current#\"}" + current="${current%'}" + current="${current#'}" + if [[ "$fill_empty" == "true" && -z "$current" ]]; then + sed -i -E "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + echo "Updated empty $key" + else + echo "$key already exists" + fi + return + fi + + printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE" + echo "Added $key" +} + +if [[ -f "$ENV_FILE" ]]; then + ensure_env_default "CUSTOM_USER" "admin" true + ensure_env_default "PASSWORD" "$(generate_secret)" true + ensure_env_default "LC_ALL" "zh_CN.UTF-8" false +else + echo "$ENV_FILE not found; skipped LinuxServer environment migration" +fi diff --git a/apps/mosdns-t/0.7.1/docker-compose.yml b/apps/mosdns-t/0.7.1/docker-compose.yml index 9e8dd40ef..dc7a61de5 100644 --- a/apps/mosdns-t/0.7.1/docker-compose.yml +++ b/apps/mosdns-t/0.7.1/docker-compose.yml @@ -1,6 +1,6 @@ services: mosdns-t: - image: "jasonxtt/mosdns-t:v0.7.1" + image: "jasonxtt/mosdns-t:v0.7.3" container_name: ${CONTAINER_NAME} restart: unless-stopped extra_hosts: diff --git a/apps/moviepilot/3.1.2/.env.sample b/apps/moviepilot/3.1.3/.env.sample similarity index 100% rename from apps/moviepilot/3.1.2/.env.sample rename to apps/moviepilot/3.1.3/.env.sample diff --git a/apps/moviepilot/3.1.2/data.yml b/apps/moviepilot/3.1.3/data.yml similarity index 100% rename from apps/moviepilot/3.1.2/data.yml rename to apps/moviepilot/3.1.3/data.yml diff --git a/apps/moviepilot/3.1.2/docker-compose.yml b/apps/moviepilot/3.1.3/docker-compose.yml similarity index 99% rename from apps/moviepilot/3.1.2/docker-compose.yml rename to apps/moviepilot/3.1.3/docker-compose.yml index a9ab4846d..118340f94 100644 --- a/apps/moviepilot/3.1.2/docker-compose.yml +++ b/apps/moviepilot/3.1.3/docker-compose.yml @@ -1,6 +1,6 @@ services: moviepilot: - image: jxxghp/moviepilot:3.1.2 + image: jxxghp/moviepilot:3.1.3 container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/mt-photos/1.57.1/docker-compose.yml b/apps/mt-photos/1.57.1/docker-compose.yml index 25c1311d8..6cdf99be0 100644 --- a/apps/mt-photos/1.57.1/docker-compose.yml +++ b/apps/mt-photos/1.57.1/docker-compose.yml @@ -1,6 +1,6 @@ services: mt-photos: - image: "mtphotos/mt-photos:1.57.1" + image: "mtphotos/mt-photos:1.57.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/mtab/3.2.0/docker-compose.yml b/apps/mtab/3.2.0/docker-compose.yml index cd6df941b..221bfa333 100644 --- a/apps/mtab/3.2.0/docker-compose.yml +++ b/apps/mtab/3.2.0/docker-compose.yml @@ -1,6 +1,6 @@ services: mtabserver: - image: itushan/mtab:3.2.0 + image: itushan/mtab:3.2.6 container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/nextcloud/35.0.1/.env.sample b/apps/nextcloud/35.0.2/.env.sample similarity index 100% rename from apps/nextcloud/35.0.1/.env.sample rename to apps/nextcloud/35.0.2/.env.sample diff --git a/apps/nextcloud/35.0.1/data.yml b/apps/nextcloud/35.0.2/data.yml similarity index 100% rename from apps/nextcloud/35.0.1/data.yml rename to apps/nextcloud/35.0.2/data.yml diff --git a/apps/nextcloud/35.0.1/docker-compose.yml b/apps/nextcloud/35.0.2/docker-compose.yml similarity index 90% rename from apps/nextcloud/35.0.1/docker-compose.yml rename to apps/nextcloud/35.0.2/docker-compose.yml index f2a940513..4ee09dbda 100644 --- a/apps/nextcloud/35.0.1/docker-compose.yml +++ b/apps/nextcloud/35.0.2/docker-compose.yml @@ -13,7 +13,7 @@ services: - PUID=1000 - PGID=1000 - TZ=Asia/Shanghai - image: "linuxserver/nextcloud:35.0.1" + image: "linuxserver/nextcloud:35.0.2" labels: createdBy: "Apps" diff --git a/apps/nezha-dash/3.1.12/docker-compose.yml b/apps/nezha-dash/3.1.12/docker-compose.yml index 3199e18a0..a1a827240 100644 --- a/apps/nezha-dash/3.1.12/docker-compose.yml +++ b/apps/nezha-dash/3.1.12/docker-compose.yml @@ -20,7 +20,7 @@ services: - NEXT_PUBLIC_CustomLogo=${NEXT_PUBLIC_CustomLogo} - NEXT_PUBLIC_CustomTitle=${NEXT_PUBLIC_CustomTitle} - NEXT_PUBLIC_CustomDescription=${NEXT_PUBLIC_CustomDescription} - image: "hamster1963/nezha-dash:v3.1.12" + image: "hamster1963/nezha-dash:v3.1.13" labels: createdBy: "Apps" diff --git a/apps/npc/0.26.38/docker-compose.yml b/apps/npc/0.26.38/docker-compose.yml index 358d83aad..f704abac5 100644 --- a/apps/npc/0.26.38/docker-compose.yml +++ b/apps/npc/0.26.38/docker-compose.yml @@ -1,6 +1,6 @@ services: npc: - image: yisier1/npc:v0.26.38 + image: yisier1/npc:v0.26.39 restart: always network_mode: "host" command: -server=${NPS_SERVER_ADDR} -vkey=${NPS_VKEY} -tls_enable=${TLS_ENABLE_SWITCH} diff --git a/apps/nps/0.26.38/docker-compose.yml b/apps/nps/0.26.38/docker-compose.yml index 00e2e52eb..84b9d3051 100644 --- a/apps/nps/0.26.38/docker-compose.yml +++ b/apps/nps/0.26.38/docker-compose.yml @@ -1,6 +1,6 @@ services: nps: - image: yisier1/nps:v0.26.38 + image: yisier1/nps:v0.26.39 container_name: ${CONTAINER_NAME} restart: always volumes: diff --git a/apps/nut-webgui/0.10.2/docker-compose.yml b/apps/nut-webgui/0.10.2/docker-compose.yml index 61c8a256e..0cf2eaae5 100644 --- a/apps/nut-webgui/0.10.2/docker-compose.yml +++ b/apps/nut-webgui/0.10.2/docker-compose.yml @@ -1,6 +1,6 @@ services: nut-webgui: - image: "ghcr.io/superioone/nut_webgui:0.10.2" + image: "ghcr.io/superioone/nut_webgui:0.10.4" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/nvpress/6.1.1/docker-compose.yml b/apps/nvpress/6.1.1/docker-compose.yml index 1e5e59912..f58ee5152 100644 --- a/apps/nvpress/6.1.1/docker-compose.yml +++ b/apps/nvpress/6.1.1/docker-compose.yml @@ -10,7 +10,7 @@ services: - "${DATA_PATH}/nv-content:/usr/src/app/nv-content" - "${DATA_PATH}/nv-themes:/usr/src/app/nv-themes" - "${DATA_PATH}/nv-plugins:/usr/src/app/nv-plugins" - image: pandastd/nvpress:6.1.1 + image: pandastd/nvpress:6.1.2 labels: createdBy: "Apps" diff --git a/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml b/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml index d7b543f28..1d9e5c992 100644 --- a/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml +++ b/apps/nzbget-linuxserver/26.3.20261002/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: nzbget: - image: "linuxserver/nzbget:26.3.20261002" + image: "linuxserver/nzbget:26.3.20261016" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/odoo/latest/docker-compose.yml b/apps/odoo/latest/docker-compose.yml index 14b6d99da..90ec53850 100644 --- a/apps/odoo/latest/docker-compose.yml +++ b/apps/odoo/latest/docker-compose.yml @@ -28,6 +28,7 @@ services: printf '%s\n' '[options]' printf '%s\n' 'addons_path = /mnt/extra-addons' printf '%s\n' 'data_dir = /var/lib/odoo' + printf '%s\n' 'http_interface = 0.0.0.0' printf 'admin_passwd = %s\n' "$${ODOO_MASTER_PASSWORD}" } > /var/lib/odoo/odoo.conf chown odoo:odoo /var/lib/odoo/odoo.conf diff --git a/apps/onedev/16.8.6/docker-compose.yml b/apps/onedev/16.8.6/docker-compose.yml index 00f978d2b..ed67f3549 100644 --- a/apps/onedev/16.8.6/docker-compose.yml +++ b/apps/onedev/16.8.6/docker-compose.yml @@ -10,7 +10,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - "${DATA_PATH}:/opt/onedev" - image: 1dev/server:16.8.6 + image: 1dev/server:16.8.7 labels: createdBy: "Apps" diff --git a/apps/onedev/README.md b/apps/onedev/README.md index aeca2593f..7b1348c9d 100644 --- a/apps/onedev/README.md +++ b/apps/onedev/README.md @@ -25,6 +25,10 @@ 升级或迁移前,请在 1Panel 中备份上述数据目录。 +## 安全风险 + +- 本应用将宿主机 `/var/run/docker.sock` 挂载到容器。容器可通过 Docker API 控制宿主机上的其他容器,通常等同于获得宿主机 root 权限。仅在可信环境部署,并限制对面板和应用的访问。 + ## 使用说明 - 安装完成后,在 1Panel 应用页面查看运行状态、端口和日志。 - 首次启用前,请按安装表单填写域名、账号、密码、Token、数据目录等参数。 diff --git a/apps/only-office-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/only-office-kasm/1.19.0-rolling-weekly/docker-compose.yml index 2ef88c0d7..38e25e2a5 100644 --- a/apps/only-office-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/only-office-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: only-office-kasm: - image: "kasmweb/only-office:1.19.0-rolling-weekly@sha256:b9a80baac5ffc6dfdc47912e9cfc2057afd4cae1ac79bc36203e0e850af9cabf" + image: "kasmweb/only-office:1.19.0-rolling-weekly@sha256:4b0abda304bf82e75171eaf389cc7e4886a84e743be6acacb39c98575d5d339f" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/onlyoffice-linuxserver/9.4.0/docker-compose.yml b/apps/onlyoffice-linuxserver/9.4.0/docker-compose.yml index 942ab4c5b..0a187c1ed 100644 --- a/apps/onlyoffice-linuxserver/9.4.0/docker-compose.yml +++ b/apps/onlyoffice-linuxserver/9.4.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: onlyoffice: - image: "linuxserver/onlyoffice:9.4.0" + image: "linuxserver/onlyoffice:10.0.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/onlyoffice/9.4.0.1/docker-compose.yml b/apps/onlyoffice/9.4.0.1/docker-compose.yml index f8aa78d71..384562e1d 100644 --- a/apps/onlyoffice/9.4.0.1/docker-compose.yml +++ b/apps/onlyoffice/9.4.0.1/docker-compose.yml @@ -13,7 +13,7 @@ services: - ${DATA_PATH}/db:/var/lib/postgresql environment: - JWT_SECRET=${JWT_SECRET} - image: onlyoffice/documentserver:9.4.0.1 + image: onlyoffice/documentserver:10.0.0.1 labels: createdBy: "Apps" diff --git a/apps/openclaw/2026.9.8-sandbox/docker-compose.yml b/apps/openclaw/2026.9.8-sandbox/docker-compose.yml index 95f096d4d..3b096a104 100644 --- a/apps/openclaw/2026.9.8-sandbox/docker-compose.yml +++ b/apps/openclaw/2026.9.8-sandbox/docker-compose.yml @@ -1,7 +1,7 @@ services: openclaw: container_name: ${CONTAINER_NAME} - image: "ghcr.io/okxlin/openclaw-sandbox:v2026.9.8-sandbox" + image: "ghcr.io/okxlin/openclaw-sandbox:v2026.9.9-sandbox" restart: unless-stopped networks: - 1panel-network diff --git a/apps/openclaw/2026.9.8-sandbox/scripts/init.sh b/apps/openclaw/2026.9.8-sandbox/scripts/init.sh index 9aa6a91c7..6b5ec4d3e 100644 --- a/apps/openclaw/2026.9.8-sandbox/scripts/init.sh +++ b/apps/openclaw/2026.9.8-sandbox/scripts/init.sh @@ -5,6 +5,20 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" COMPOSE_CMD=(docker compose -f "$COMPOSE_FILE" --project-directory "$ROOT_DIR") +# Resolve the service key because 1Panel may rename Compose services on install. +SERVICES="$("${COMPOSE_CMD[@]}" config --services)" +SERVICE_NAME="" +SERVICE_COUNT=0 +while IFS= read -r COMPOSE_SERVICE; do + [ -n "$COMPOSE_SERVICE" ] || continue + SERVICE_NAME="$COMPOSE_SERVICE" + SERVICE_COUNT=$((SERVICE_COUNT + 1)) +done <<< "$SERVICES" +if [ "$SERVICE_COUNT" -ne 1 ]; then + echo "Expected exactly one Compose service, found ${SERVICE_COUNT}" >&2 + exit 1 +fi + # 读取环境变量并提供默认值(与 docker-compose/.env 保持一致) CONFIG_DIR_RAW="${OPENCLAW_CONFIG_DIR:-./data/conf}" WORKSPACE_DIR_RAW="${OPENCLAW_WORKSPACE_DIR:-./data/workspace}" @@ -48,23 +62,23 @@ mkdir -p "$CONFIG_DIR" "$WORKSPACE_DIR" chown -R 1000:1000 "$ROOT_DIR/data" 2>/dev/null || true echo "==> 执行 onboard 初始化" -"${COMPOSE_CMD[@]}" run --rm openclaw \ - node dist/index.js onboard --mode local --no-install-daemon +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ + node dist/index.js onboard --mode local --no-install-daemon --non-interactive --accept-risk --auth-choice skip --skip-health echo "==> 初始化 gateway.mode / gateway.bind" -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.mode local -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.bind "$BIND" # 如果是从 Sandbox 目录部署 (含 docker.sock 挂载),自动应用 Sandbox 最佳安全配置 if grep -q "DOCKER_SOCK_PATH" "$COMPOSE_FILE"; then echo "==> 检测到 Sandbox 模式,初始化 Sandbox 极简安全配置" - "${COMPOSE_CMD[@]}" run --rm openclaw \ + "${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set agents.defaults.sandbox.mode "non-main" - "${COMPOSE_CMD[@]}" run --rm openclaw \ + "${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set agents.defaults.sandbox.scope "agent" - "${COMPOSE_CMD[@]}" run --rm openclaw \ + "${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set agents.defaults.sandbox.workspaceAccess "none" fi diff --git a/apps/openclaw/2026.9.9/scripts/init.sh b/apps/openclaw/2026.9.9/scripts/init.sh index 1b2e30f53..c52c6b5e4 100644 --- a/apps/openclaw/2026.9.9/scripts/init.sh +++ b/apps/openclaw/2026.9.9/scripts/init.sh @@ -5,6 +5,20 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" COMPOSE_CMD=(docker compose -f "$COMPOSE_FILE" --project-directory "$ROOT_DIR") +# Resolve the service key because 1Panel may rename Compose services on install. +SERVICES="$("${COMPOSE_CMD[@]}" config --services)" +SERVICE_NAME="" +SERVICE_COUNT=0 +while IFS= read -r COMPOSE_SERVICE; do + [ -n "$COMPOSE_SERVICE" ] || continue + SERVICE_NAME="$COMPOSE_SERVICE" + SERVICE_COUNT=$((SERVICE_COUNT + 1)) +done <<< "$SERVICES" +if [ "$SERVICE_COUNT" -ne 1 ]; then + echo "Expected exactly one Compose service, found ${SERVICE_COUNT}" >&2 + exit 1 +fi + # 读取环境变量并提供默认值(与 docker-compose/.env 保持一致) CONFIG_DIR_RAW="${OPENCLAW_CONFIG_DIR:-./data/conf}" WORKSPACE_DIR_RAW="${OPENCLAW_WORKSPACE_DIR:-./data/workspace}" @@ -31,13 +45,13 @@ mkdir -p "$CONFIG_DIR" "$WORKSPACE_DIR" chown -R 1000:1000 "$ROOT_DIR/data" 2>/dev/null || true echo "==> 执行 onboard 初始化" -"${COMPOSE_CMD[@]}" run --rm openclaw \ - node dist/index.js onboard --mode local --no-install-daemon +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ + node dist/index.js onboard --mode local --no-install-daemon --non-interactive --accept-risk --auth-choice skip --skip-health echo "==> 初始化 gateway.mode / gateway.bind" -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.mode local -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.bind "$BIND" echo "==> 初始化完成" diff --git a/apps/openclaw/README.md b/apps/openclaw/README.md index 3e5b457c9..17356c3c7 100644 --- a/apps/openclaw/README.md +++ b/apps/openclaw/README.md @@ -12,6 +12,12 @@ - 可选版本:`latest`;固定版本以应用商店当前版本目录和安装表单为准。 - 安装后按应用表单中的端口访问 Web UI、SSH 或对应服务。 +## 高风险权限 + +> **高风险:本应用挂载宿主机 Docker Socket。** + +容器内能访问 Docker Socket 的进程可以调用宿主机 Docker API 管理容器和镜像,通常可借此获得等同宿主机 root 的控制能力。请只在可信环境部署,并限制可访问和修改本应用配置的用户。 + ## 端口 | 变量 | 说明 | 默认值 | 必填 | | --- | --- | --- | --- | diff --git a/apps/openclaw/README_en.md b/apps/openclaw/README_en.md index 2f24c0ed0..a4b51e4da 100644 --- a/apps/openclaw/README_en.md +++ b/apps/openclaw/README_en.md @@ -16,6 +16,12 @@ If you want a personal, single-user assistant that feels local, fast, and always - **Companion apps** — macOS menu bar app + iOS/Android nodes. - **Onboarding + skills** — wizard-driven setup with bundled/managed/workspace skills. +## High-Risk Permission + +> **High risk: this app mounts the host Docker socket.** + +A process inside the container with access to the socket can control the host Docker daemon and may obtain root-equivalent control of the host. Deploy only in a trusted environment and restrict who can access or change this app's configuration. + ## Post-Deployment Guide After deployment, to ensure OpenClaw operates correctly and can be accessed externally, please follow these configuration steps: diff --git a/apps/openclaw/latest/scripts/init.sh b/apps/openclaw/latest/scripts/init.sh index 1b2e30f53..c52c6b5e4 100644 --- a/apps/openclaw/latest/scripts/init.sh +++ b/apps/openclaw/latest/scripts/init.sh @@ -5,6 +5,20 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" COMPOSE_CMD=(docker compose -f "$COMPOSE_FILE" --project-directory "$ROOT_DIR") +# Resolve the service key because 1Panel may rename Compose services on install. +SERVICES="$("${COMPOSE_CMD[@]}" config --services)" +SERVICE_NAME="" +SERVICE_COUNT=0 +while IFS= read -r COMPOSE_SERVICE; do + [ -n "$COMPOSE_SERVICE" ] || continue + SERVICE_NAME="$COMPOSE_SERVICE" + SERVICE_COUNT=$((SERVICE_COUNT + 1)) +done <<< "$SERVICES" +if [ "$SERVICE_COUNT" -ne 1 ]; then + echo "Expected exactly one Compose service, found ${SERVICE_COUNT}" >&2 + exit 1 +fi + # 读取环境变量并提供默认值(与 docker-compose/.env 保持一致) CONFIG_DIR_RAW="${OPENCLAW_CONFIG_DIR:-./data/conf}" WORKSPACE_DIR_RAW="${OPENCLAW_WORKSPACE_DIR:-./data/workspace}" @@ -31,13 +45,13 @@ mkdir -p "$CONFIG_DIR" "$WORKSPACE_DIR" chown -R 1000:1000 "$ROOT_DIR/data" 2>/dev/null || true echo "==> 执行 onboard 初始化" -"${COMPOSE_CMD[@]}" run --rm openclaw \ - node dist/index.js onboard --mode local --no-install-daemon +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ + node dist/index.js onboard --mode local --no-install-daemon --non-interactive --accept-risk --auth-choice skip --skip-health echo "==> 初始化 gateway.mode / gateway.bind" -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.mode local -"${COMPOSE_CMD[@]}" run --rm openclaw \ +"${COMPOSE_CMD[@]}" run --rm "$SERVICE_NAME" \ node dist/index.js config set gateway.bind "$BIND" echo "==> 初始化完成" diff --git a/apps/opencloud/7.3.0/docker-compose.yml b/apps/opencloud/7.3.0/docker-compose.yml index 68f5e680c..19c346c13 100644 --- a/apps/opencloud/7.3.0/docker-compose.yml +++ b/apps/opencloud/7.3.0/docker-compose.yml @@ -1,6 +1,6 @@ services: traefik: - image: "traefik:v3.7.9" + image: "traefik:v3.7.15" container_name: ${CONTAINER_NAME}-traefik restart: unless-stopped command: @@ -46,7 +46,7 @@ services: createdBy: "Apps" opencloud: - image: "opencloudeu/opencloud-rolling:8.0.1" + image: "opencloudeu/opencloud-rolling:8.1.0" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/opencloud/README.md b/apps/opencloud/README.md index c92bcff67..3e28621a2 100644 --- a/apps/opencloud/README.md +++ b/apps/opencloud/README.md @@ -15,7 +15,7 @@ OpenCloud 是一个开源的文件管理、共享与协作平台,提供 Web - OpenCloud 源码仓库由 `opencloud-eu` 官方组织维护,采用 Apache License 2.0。 - 本应用基于官方 `opencloud-compose` 的基础服务与 Traefik TLS 部署方式,Compose 证据固定到提交 `8d2d89f283faa410bc9ed9f63e8247f6518d5c43`。 -- OpenCloud `v7.3.0` 于 2026-07-14 发布;官方在 `opencloudeu/opencloud-rolling:7.3.0` 发布该版本,镜像内报告 `Edition: rolling`。官方 production 镜像仓库在适配时仍停留于 `7.2.2`,因此这里明确使用官方 rolling 仓库的固定 `7.3.0` tag,而不是浮动 tag。 +- OpenCloud [`v8.1.0`](https://github.com/opencloud-eu/opencloud/releases/tag/v8.1.0) 于 2026-10-05 作为 rolling release 发布。本应用使用官方镜像 `opencloudeu/opencloud-rolling:8.1.0` 的版本化 tag,不使用浮动 `latest` tag。 - OpenCloud 与 Traefik 镜像均提供 `linux/amd64` 和 `linux/arm64` manifest。 ## 部署拓扑 @@ -81,7 +81,7 @@ OpenCloud service 不使用 privileged、host network、额外 capability 或设 OpenCloud is an open-source platform for file management, sharing, and collaboration. This package follows the official OpenCloud Compose topology: one fixed-version OpenCloud container and one Traefik container providing the HTTPS endpoint required by the built-in identity provider. -The package uses the official `opencloudeu/opencloud-rolling:7.3.0` image because OpenCloud 7.3.0 was published only in the official rolling image repository at adaptation time. The tag is fixed; neither packaged version follows a floating image tag. +OpenCloud [v8.1.0](https://github.com/opencloud-eu/opencloud/releases/tag/v8.1.0) is an official rolling release published on 2026-10-05. This package uses the versioned official image tag `opencloudeu/opencloud-rolling:8.1.0` rather than a floating `latest` tag. A trusted certificate is required for production. Back up the complete `${APP_DATA_DIR}` before upgrading. The lifecycle scripts do not delete persisted data or recursively rewrite existing files. diff --git a/apps/opencloud/latest/docker-compose.yml b/apps/opencloud/latest/docker-compose.yml index 68f5e680c..19c346c13 100644 --- a/apps/opencloud/latest/docker-compose.yml +++ b/apps/opencloud/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: traefik: - image: "traefik:v3.7.9" + image: "traefik:v3.7.15" container_name: ${CONTAINER_NAME}-traefik restart: unless-stopped command: @@ -46,7 +46,7 @@ services: createdBy: "Apps" opencloud: - image: "opencloudeu/opencloud-rolling:8.0.1" + image: "opencloudeu/opencloud-rolling:8.1.0" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/openlist/4.2.6-aio/docker-compose.yml b/apps/openlist/4.2.6-aio/docker-compose.yml index 166ba3ad6..045d80668 100644 --- a/apps/openlist/4.2.6-aio/docker-compose.yml +++ b/apps/openlist/4.2.6-aio/docker-compose.yml @@ -1,6 +1,6 @@ services: openlist: - image: "openlistteam/openlist:v4.2.6-aio" + image: "openlistteam/openlist:v4.2.7-aio" container_name: ${CONTAINER_NAME} restart: always user: "0:0" diff --git a/apps/openlist/4.2.6-aria2/docker-compose.yml b/apps/openlist/4.2.6-aria2/docker-compose.yml index 0b44071f8..b99824c11 100644 --- a/apps/openlist/4.2.6-aria2/docker-compose.yml +++ b/apps/openlist/4.2.6-aria2/docker-compose.yml @@ -1,6 +1,6 @@ services: openlist: - image: "openlistteam/openlist:v4.2.6-aria2" + image: "openlistteam/openlist:v4.2.7-aria2" container_name: ${CONTAINER_NAME} restart: always user: "0:0" diff --git a/apps/openlist/4.2.6-ffmpeg/docker-compose.yml b/apps/openlist/4.2.6-ffmpeg/docker-compose.yml index dd861c9ed..74d6bf535 100644 --- a/apps/openlist/4.2.6-ffmpeg/docker-compose.yml +++ b/apps/openlist/4.2.6-ffmpeg/docker-compose.yml @@ -1,6 +1,6 @@ services: openlist: - image: "openlistteam/openlist:v4.2.6-ffmpeg" + image: "openlistteam/openlist:v4.2.7-ffmpeg" container_name: ${CONTAINER_NAME} restart: always user: "0:0" diff --git a/apps/openlist/4.2.6/docker-compose.yml b/apps/openlist/4.2.6/docker-compose.yml index 5050fa7be..ef19e6fc0 100644 --- a/apps/openlist/4.2.6/docker-compose.yml +++ b/apps/openlist/4.2.6/docker-compose.yml @@ -1,6 +1,6 @@ services: openlist: - image: "openlistteam/openlist:v4.2.6" + image: "openlistteam/openlist:v4.2.7" container_name: ${CONTAINER_NAME} restart: always user: "0:0" diff --git a/apps/openlist/4.2.7-aio/data.yml b/apps/openlist/4.2.7-aio/data.yml new file mode 100644 index 000000000..00d710fa9 --- /dev/null +++ b/apps/openlist/4.2.7-aio/data.yml @@ -0,0 +1,84 @@ +additionalProperties: + formFields: + - default: 40337 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: WebUI Port + labelZh: 网页端口 + required: true + rule: paramPort + type: number + label: + en: WebUI Port + ja: WebUI ポート + ms: Port WebUI + pt-br: Porta WebUI + ru: Порт WebUI + ko: WebUI 포트 + zh-Hant: WebUI 埠 + zh: WebUI 端口 + - default: 5246 + edit: true + envKey: PANEL_APP_PORT_S3 + labelEn: S3 Port + labelZh: S3 端口 + required: true + rule: paramPort + type: number + label: + en: S3 Port + ja: S3 ポート + ms: Port S3 + pt-br: Porta S3 + ru: Порт S3 + ko: S3 포트 + zh-Hant: S3 埠 + zh: S3 端口 + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + ja: タイムゾーン + ms: Zon masa + pt-br: Fuso horário + ru: Часовой пояс + ko: 시간대 + zh: 时区 + zh-Hant: 時區 + required: true + type: text + - default: ./data/data + edit: true + envKey: DATA_PATH + labelEn: Data folder path + labelZh: 数据文件夹路径 + required: true + type: text + label: + en: Data folder path + ja: データフォルダパス + ms: Laluan folder data + pt-br: Caminho da pasta de dados + ru: Путь к папке данных + ko: 데이터 폴더 경로 + zh-Hant: 資料夾路徑 + zh: 数据文件夹路径 + - default: ./data/mnt + edit: true + envKey: MOUNT_PATH + labelEn: Mount folder path + labelZh: 挂载文件夹路径 + required: true + type: text + label: + en: Mount folder path + ja: マウントフォルダパス + ms: Laluan folder lekapan + pt-br: Caminho da pasta de montagem + ru: Путь к точке монтирования + ko: 마운트 폴더 경로 + zh-Hant: 掛載資料夾路徑 + zh: 挂载文件夹路径 \ No newline at end of file diff --git a/apps/openlist/4.2.7-aio/docker-compose.yml b/apps/openlist/4.2.7-aio/docker-compose.yml new file mode 100644 index 000000000..045d80668 --- /dev/null +++ b/apps/openlist/4.2.7-aio/docker-compose.yml @@ -0,0 +1,24 @@ +services: + openlist: + image: "openlistteam/openlist:v4.2.7-aio" + container_name: ${CONTAINER_NAME} + restart: always + user: "0:0" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + - "${PANEL_APP_PORT_S3}:5246" + volumes: + - "${DATA_PATH}:/opt/openlist/data" + - "${MOUNT_PATH}:/mnt/data" + environment: + - TZ=${TIME_ZONE} + - PUID=0 + - PGID=0 + - UMASK=022 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/openlist/4.2.7-aio/env.sample b/apps/openlist/4.2.7-aio/env.sample new file mode 100644 index 000000000..18508705a --- /dev/null +++ b/apps/openlist/4.2.7-aio/env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME="openlist" +DATA_PATH="./data/data" +MOUNT_PATH="./data/mnt" +PANEL_APP_PORT_HTTP=40337 +PANEL_APP_PORT_S3=5246 +TIME_ZONE="Asia/Shanghai" diff --git a/apps/openlist/4.2.7-aria2/data.yml b/apps/openlist/4.2.7-aria2/data.yml new file mode 100644 index 000000000..00d710fa9 --- /dev/null +++ b/apps/openlist/4.2.7-aria2/data.yml @@ -0,0 +1,84 @@ +additionalProperties: + formFields: + - default: 40337 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: WebUI Port + labelZh: 网页端口 + required: true + rule: paramPort + type: number + label: + en: WebUI Port + ja: WebUI ポート + ms: Port WebUI + pt-br: Porta WebUI + ru: Порт WebUI + ko: WebUI 포트 + zh-Hant: WebUI 埠 + zh: WebUI 端口 + - default: 5246 + edit: true + envKey: PANEL_APP_PORT_S3 + labelEn: S3 Port + labelZh: S3 端口 + required: true + rule: paramPort + type: number + label: + en: S3 Port + ja: S3 ポート + ms: Port S3 + pt-br: Porta S3 + ru: Порт S3 + ko: S3 포트 + zh-Hant: S3 埠 + zh: S3 端口 + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + ja: タイムゾーン + ms: Zon masa + pt-br: Fuso horário + ru: Часовой пояс + ko: 시간대 + zh: 时区 + zh-Hant: 時區 + required: true + type: text + - default: ./data/data + edit: true + envKey: DATA_PATH + labelEn: Data folder path + labelZh: 数据文件夹路径 + required: true + type: text + label: + en: Data folder path + ja: データフォルダパス + ms: Laluan folder data + pt-br: Caminho da pasta de dados + ru: Путь к папке данных + ko: 데이터 폴더 경로 + zh-Hant: 資料夾路徑 + zh: 数据文件夹路径 + - default: ./data/mnt + edit: true + envKey: MOUNT_PATH + labelEn: Mount folder path + labelZh: 挂载文件夹路径 + required: true + type: text + label: + en: Mount folder path + ja: マウントフォルダパス + ms: Laluan folder lekapan + pt-br: Caminho da pasta de montagem + ru: Путь к точке монтирования + ko: 마운트 폴더 경로 + zh-Hant: 掛載資料夾路徑 + zh: 挂载文件夹路径 \ No newline at end of file diff --git a/apps/openlist/4.2.7-aria2/docker-compose.yml b/apps/openlist/4.2.7-aria2/docker-compose.yml new file mode 100644 index 000000000..b99824c11 --- /dev/null +++ b/apps/openlist/4.2.7-aria2/docker-compose.yml @@ -0,0 +1,24 @@ +services: + openlist: + image: "openlistteam/openlist:v4.2.7-aria2" + container_name: ${CONTAINER_NAME} + restart: always + user: "0:0" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + - "${PANEL_APP_PORT_S3}:5246" + volumes: + - "${DATA_PATH}:/opt/openlist/data" + - "${MOUNT_PATH}:/mnt/data" + environment: + - TZ=${TIME_ZONE} + - PUID=0 + - PGID=0 + - UMASK=022 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/openlist/4.2.7-aria2/env.sample b/apps/openlist/4.2.7-aria2/env.sample new file mode 100644 index 000000000..18508705a --- /dev/null +++ b/apps/openlist/4.2.7-aria2/env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME="openlist" +DATA_PATH="./data/data" +MOUNT_PATH="./data/mnt" +PANEL_APP_PORT_HTTP=40337 +PANEL_APP_PORT_S3=5246 +TIME_ZONE="Asia/Shanghai" diff --git a/apps/openlist/4.2.7-ffmpeg/data.yml b/apps/openlist/4.2.7-ffmpeg/data.yml new file mode 100644 index 000000000..00d710fa9 --- /dev/null +++ b/apps/openlist/4.2.7-ffmpeg/data.yml @@ -0,0 +1,84 @@ +additionalProperties: + formFields: + - default: 40337 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: WebUI Port + labelZh: 网页端口 + required: true + rule: paramPort + type: number + label: + en: WebUI Port + ja: WebUI ポート + ms: Port WebUI + pt-br: Porta WebUI + ru: Порт WebUI + ko: WebUI 포트 + zh-Hant: WebUI 埠 + zh: WebUI 端口 + - default: 5246 + edit: true + envKey: PANEL_APP_PORT_S3 + labelEn: S3 Port + labelZh: S3 端口 + required: true + rule: paramPort + type: number + label: + en: S3 Port + ja: S3 ポート + ms: Port S3 + pt-br: Porta S3 + ru: Порт S3 + ko: S3 포트 + zh-Hant: S3 埠 + zh: S3 端口 + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + ja: タイムゾーン + ms: Zon masa + pt-br: Fuso horário + ru: Часовой пояс + ko: 시간대 + zh: 时区 + zh-Hant: 時區 + required: true + type: text + - default: ./data/data + edit: true + envKey: DATA_PATH + labelEn: Data folder path + labelZh: 数据文件夹路径 + required: true + type: text + label: + en: Data folder path + ja: データフォルダパス + ms: Laluan folder data + pt-br: Caminho da pasta de dados + ru: Путь к папке данных + ko: 데이터 폴더 경로 + zh-Hant: 資料夾路徑 + zh: 数据文件夹路径 + - default: ./data/mnt + edit: true + envKey: MOUNT_PATH + labelEn: Mount folder path + labelZh: 挂载文件夹路径 + required: true + type: text + label: + en: Mount folder path + ja: マウントフォルダパス + ms: Laluan folder lekapan + pt-br: Caminho da pasta de montagem + ru: Путь к точке монтирования + ko: 마운트 폴더 경로 + zh-Hant: 掛載資料夾路徑 + zh: 挂载文件夹路径 \ No newline at end of file diff --git a/apps/openlist/4.2.7-ffmpeg/docker-compose.yml b/apps/openlist/4.2.7-ffmpeg/docker-compose.yml new file mode 100644 index 000000000..74d6bf535 --- /dev/null +++ b/apps/openlist/4.2.7-ffmpeg/docker-compose.yml @@ -0,0 +1,24 @@ +services: + openlist: + image: "openlistteam/openlist:v4.2.7-ffmpeg" + container_name: ${CONTAINER_NAME} + restart: always + user: "0:0" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + - "${PANEL_APP_PORT_S3}:5246" + volumes: + - "${DATA_PATH}:/opt/openlist/data" + - "${MOUNT_PATH}:/mnt/data" + environment: + - TZ=${TIME_ZONE} + - PUID=0 + - PGID=0 + - UMASK=022 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/openlist/4.2.7-ffmpeg/env.sample b/apps/openlist/4.2.7-ffmpeg/env.sample new file mode 100644 index 000000000..18508705a --- /dev/null +++ b/apps/openlist/4.2.7-ffmpeg/env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME="openlist" +DATA_PATH="./data/data" +MOUNT_PATH="./data/mnt" +PANEL_APP_PORT_HTTP=40337 +PANEL_APP_PORT_S3=5246 +TIME_ZONE="Asia/Shanghai" diff --git a/apps/openlist/4.2.7/data.yml b/apps/openlist/4.2.7/data.yml new file mode 100644 index 000000000..00d710fa9 --- /dev/null +++ b/apps/openlist/4.2.7/data.yml @@ -0,0 +1,84 @@ +additionalProperties: + formFields: + - default: 40337 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: WebUI Port + labelZh: 网页端口 + required: true + rule: paramPort + type: number + label: + en: WebUI Port + ja: WebUI ポート + ms: Port WebUI + pt-br: Porta WebUI + ru: Порт WebUI + ko: WebUI 포트 + zh-Hant: WebUI 埠 + zh: WebUI 端口 + - default: 5246 + edit: true + envKey: PANEL_APP_PORT_S3 + labelEn: S3 Port + labelZh: S3 端口 + required: true + rule: paramPort + type: number + label: + en: S3 Port + ja: S3 ポート + ms: Port S3 + pt-br: Porta S3 + ru: Порт S3 + ko: S3 포트 + zh-Hant: S3 埠 + zh: S3 端口 + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: Time zone + ja: タイムゾーン + ms: Zon masa + pt-br: Fuso horário + ru: Часовой пояс + ko: 시간대 + zh: 时区 + zh-Hant: 時區 + required: true + type: text + - default: ./data/data + edit: true + envKey: DATA_PATH + labelEn: Data folder path + labelZh: 数据文件夹路径 + required: true + type: text + label: + en: Data folder path + ja: データフォルダパス + ms: Laluan folder data + pt-br: Caminho da pasta de dados + ru: Путь к папке данных + ko: 데이터 폴더 경로 + zh-Hant: 資料夾路徑 + zh: 数据文件夹路径 + - default: ./data/mnt + edit: true + envKey: MOUNT_PATH + labelEn: Mount folder path + labelZh: 挂载文件夹路径 + required: true + type: text + label: + en: Mount folder path + ja: マウントフォルダパス + ms: Laluan folder lekapan + pt-br: Caminho da pasta de montagem + ru: Путь к точке монтирования + ko: 마운트 폴더 경로 + zh-Hant: 掛載資料夾路徑 + zh: 挂载文件夹路径 \ No newline at end of file diff --git a/apps/openlist/4.2.7/docker-compose.yml b/apps/openlist/4.2.7/docker-compose.yml new file mode 100644 index 000000000..ef19e6fc0 --- /dev/null +++ b/apps/openlist/4.2.7/docker-compose.yml @@ -0,0 +1,24 @@ +services: + openlist: + image: "openlistteam/openlist:v4.2.7" + container_name: ${CONTAINER_NAME} + restart: always + user: "0:0" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + - "${PANEL_APP_PORT_S3}:5246" + volumes: + - "${DATA_PATH}:/opt/openlist/data" + - "${MOUNT_PATH}:/mnt/data" + environment: + - TZ=${TIME_ZONE} + - PUID=0 + - PGID=0 + - UMASK=022 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/openlist/4.2.7/env.sample b/apps/openlist/4.2.7/env.sample new file mode 100644 index 000000000..18508705a --- /dev/null +++ b/apps/openlist/4.2.7/env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME="openlist" +DATA_PATH="./data/data" +MOUNT_PATH="./data/mnt" +PANEL_APP_PORT_HTTP=40337 +PANEL_APP_PORT_S3=5246 +TIME_ZONE="Asia/Shanghai" diff --git a/apps/openobserve/1.0.4/docker-compose.yml b/apps/openobserve/1.0.4/docker-compose.yml index 6b7c65c21..a50470681 100644 --- a/apps/openobserve/1.0.4/docker-compose.yml +++ b/apps/openobserve/1.0.4/docker-compose.yml @@ -1,6 +1,6 @@ services: openobserve: - image: "openobserve/openobserve:v1.0.4" + image: "openobserve/openobserve:v1.1.2" container_name: ${CONTAINER_NAME} init: true restart: unless-stopped diff --git a/apps/opera-linuxserver/137.0.6036/docker-compose.yml b/apps/opera-linuxserver/137.0.6036/docker-compose.yml index accce6627..9b02dded1 100644 --- a/apps/opera-linuxserver/137.0.6036/docker-compose.yml +++ b/apps/opera-linuxserver/137.0.6036/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: opera: - image: "linuxserver/opera:137.0.6036" + image: "linuxserver/opera:138.0.6044" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/overleaf/6.2.2/docker-compose.yml b/apps/overleaf/6.2.2/docker-compose.yml index 8fef633b8..e3ff0ca98 100644 --- a/apps/overleaf/6.2.2/docker-compose.yml +++ b/apps/overleaf/6.2.2/docker-compose.yml @@ -1,6 +1,6 @@ services: sharelatex: - image: "sharelatex/sharelatex:6.2.2" + image: "sharelatex/sharelatex:6.3.1" container_name: ${CONTAINER_NAME} restart: always depends_on: diff --git a/apps/oxicloud/0.9.2/docker-compose.yml b/apps/oxicloud/0.9.2/docker-compose.yml index 138bc3b87..55e68d44c 100644 --- a/apps/oxicloud/0.9.2/docker-compose.yml +++ b/apps/oxicloud/0.9.2/docker-compose.yml @@ -1,6 +1,6 @@ services: oxicloud: - image: "diocrafts/oxicloud:0.9.2@sha256:57f948fdb1db35a0caac7f2c4b305017f4b4e6828847d87b53f56989baea6105" + image: "diocrafts/oxicloud:0.9.3@sha256:dc68f9764d45eff2e5bff01debdacce6fa46bd0fc8f4304c99823eadc601ddbd" container_name: ${CONTAINER_NAME} restart: unless-stopped entrypoint: ["/bin/sh", "/usr/local/bin/oxicloud-package-entrypoint.sh"] diff --git a/apps/oxidns/1.6.0/docker-compose.yml b/apps/oxidns/1.6.0/docker-compose.yml index a9bda2f9c..1d9adec5f 100644 --- a/apps/oxidns/1.6.0/docker-compose.yml +++ b/apps/oxidns/1.6.0/docker-compose.yml @@ -1,6 +1,6 @@ services: oxidns: - image: "ghcr.io/svenshi/oxidns:v1.6.0" + image: "ghcr.io/svenshi/oxidns:v1.6.1" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:9199" diff --git a/apps/pi-hole/2026.09.0/docker-compose.yml b/apps/pi-hole/2026.09.0/docker-compose.yml index db0c35986..f346c1fb1 100644 --- a/apps/pi-hole/2026.09.0/docker-compose.yml +++ b/apps/pi-hole/2026.09.0/docker-compose.yml @@ -1,6 +1,6 @@ services: pihole: - image: "pihole/pihole:2026.09.0" + image: "pihole/pihole:2026.10.0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/piclist/2.4.2/docker-compose.yml b/apps/piclist/2.4.2/docker-compose.yml index ac4f41c9f..9ad001797 100644 --- a/apps/piclist/2.4.2/docker-compose.yml +++ b/apps/piclist/2.4.2/docker-compose.yml @@ -1,6 +1,6 @@ services: piclist: - image: "kuingsmile/piclist:v2.4.2" + image: "kuingsmile/piclist:v3.0.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/pinta-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/pinta-kasm/1.19.0-rolling-weekly/docker-compose.yml index 10ba7dac3..54bb6c141 100644 --- a/apps/pinta-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/pinta-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: pinta-kasm: - image: "kasmweb/pinta:1.19.0-rolling-weekly@sha256:53f68d7fcc0316cc7c6b9d590c9c2240c33302007eb30213f1447656de13a6d6" + image: "kasmweb/pinta:1.19.0-rolling-weekly@sha256:b53def011a3badcd05fa30487bd024fbab869f4a4b94696d54478b68aa6236a7" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/plex/1.43.4/docker-compose.yml b/apps/plex/1.43.4/docker-compose.yml index db6e3b60d..595abab6f 100644 --- a/apps/plex/1.43.4/docker-compose.yml +++ b/apps/plex/1.43.4/docker-compose.yml @@ -1,6 +1,6 @@ services: plex: - image: "linuxserver/plex:1.43.4" + image: "linuxserver/plex:1.43.5" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/podfetch/5.2.3/docker-compose.yml b/apps/podfetch/5.2.3/docker-compose.yml index 9eb7f26f2..1a77597d7 100644 --- a/apps/podfetch/5.2.3/docker-compose.yml +++ b/apps/podfetch/5.2.3/docker-compose.yml @@ -1,6 +1,6 @@ services: podfetch: - image: "docker.io/samuel19982/podfetch:v5.2.3@sha256:20d60e087e9c5af2bb1fd02c864140e358fa80ffb2532c6af253e705c0b897c0" + image: "docker.io/samuel19982/podfetch:v5.2.3@sha256:76aa17f25ebe70a336b5ce37ba2826e705dc40ca20359ac237b00e67127143be" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "${PUID}:${PGID}" diff --git a/apps/postman-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/postman-kasm/1.19.0-rolling-weekly/docker-compose.yml index e1fb09d6f..f206aa53c 100644 --- a/apps/postman-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/postman-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: postman-kasm: - image: "kasmweb/postman:1.19.0-rolling-weekly@sha256:7c7e064a4d6ace48b3d58edbce15c6b152b10a7cfa04157c9798b04963db1ceb" + image: "kasmweb/postman:1.19.0-rolling-weekly@sha256:6a0f5122eb80555e479149183dc48a392068047d37436cd97a684179486f1953" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/prest/2.5.0/.env.sample b/apps/prest/2.5.2/.env.sample similarity index 100% rename from apps/prest/2.5.0/.env.sample rename to apps/prest/2.5.2/.env.sample diff --git a/apps/prest/2.5.0/data.yml b/apps/prest/2.5.2/data.yml similarity index 100% rename from apps/prest/2.5.0/data.yml rename to apps/prest/2.5.2/data.yml diff --git a/apps/prest/2.5.0/docker-compose.yml b/apps/prest/2.5.2/docker-compose.yml similarity index 92% rename from apps/prest/2.5.0/docker-compose.yml rename to apps/prest/2.5.2/docker-compose.yml index dc4ad9b19..ca5b8804e 100644 --- a/apps/prest/2.5.0/docker-compose.yml +++ b/apps/prest/2.5.2/docker-compose.yml @@ -1,6 +1,6 @@ services: prest: - image: "prest/prest:v2.5.0@sha256:069e736cc9305c0416087c0bcebf7260cf560e3958c369c58a5b1ba174bcfe9d" + image: "prest/prest:v2.5.2@sha256:c66d3878fa66b0b42dc276691dbbededc24760d9c1c423fa5b87a4184edd1296" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "65532:65532" diff --git a/apps/prest/2.5.0/scripts/init.sh b/apps/prest/2.5.2/scripts/init.sh similarity index 100% rename from apps/prest/2.5.0/scripts/init.sh rename to apps/prest/2.5.2/scripts/init.sh diff --git a/apps/prest/2.5.0/scripts/token.sh b/apps/prest/2.5.2/scripts/token.sh similarity index 100% rename from apps/prest/2.5.0/scripts/token.sh rename to apps/prest/2.5.2/scripts/token.sh diff --git a/apps/prest/2.5.0/scripts/uninstall.sh b/apps/prest/2.5.2/scripts/uninstall.sh similarity index 100% rename from apps/prest/2.5.0/scripts/uninstall.sh rename to apps/prest/2.5.2/scripts/uninstall.sh diff --git a/apps/prest/2.5.0/scripts/upgrade.sh b/apps/prest/2.5.2/scripts/upgrade.sh similarity index 100% rename from apps/prest/2.5.0/scripts/upgrade.sh rename to apps/prest/2.5.2/scripts/upgrade.sh diff --git a/apps/prest/latest/docker-compose.yml b/apps/prest/latest/docker-compose.yml index edeefb445..ebad8200a 100644 --- a/apps/prest/latest/docker-compose.yml +++ b/apps/prest/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: prest: - image: "prest/prest:v2.5.0" + image: "prest/prest:v2.5.2" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "65532:65532" diff --git a/apps/privatebin/2.0.6/docker-compose.yml b/apps/privatebin/2.0.6/docker-compose.yml index 02270bee8..98c554275 100644 --- a/apps/privatebin/2.0.6/docker-compose.yml +++ b/apps/privatebin/2.0.6/docker-compose.yml @@ -1,6 +1,6 @@ services: privatebin: - image: "privatebin/nginx-fpm-alpine:2.0.6@sha256:13290e2f04bfd98cf8fc7e8d216fb76b2b2d12373d4923b859cd41c2d984fde8" + image: "privatebin/nginx-fpm-alpine:2.1.0@sha256:0f80fb240c3c3073c53a82893da7c7d62f03f4e05403855b720e4371541ba412" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/prometheus/3.15.0/docker-compose.yml b/apps/prometheus/3.15.0/docker-compose.yml index 9c26397c1..f1ad9b30f 100644 --- a/apps/prometheus/3.15.0/docker-compose.yml +++ b/apps/prometheus/3.15.0/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:9090" volumes: - "./data:/etc/prometheus" - image: prom/prometheus:v3.15.0 + image: prom/prometheus:v3.15.1 labels: createdBy: "Apps" diff --git a/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml b/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml index 650f2fd06..12b92413e 100644 --- a/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml +++ b/apps/qdirstat-linuxserver/2.0.20260928/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: qdirstat: - image: "linuxserver/qdirstat:2.0.20260928" + image: "linuxserver/qdirstat:2.0.20261018" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/questdb/10.0.1/docker-compose.yml b/apps/questdb/10.0.1/docker-compose.yml index 7c0a5e362..234cff0c9 100644 --- a/apps/questdb/10.0.1/docker-compose.yml +++ b/apps/questdb/10.0.1/docker-compose.yml @@ -1,6 +1,6 @@ services: questdb: - image: "questdb/questdb:10.0.1" + image: "questdb/questdb:10.1.0" container_name: ${CONTAINER_NAME} init: true restart: unless-stopped diff --git a/apps/rembg/2.0.85/docker-compose.yml b/apps/rembg/2.0.85/docker-compose.yml index 38343fe00..6139bdf6d 100644 --- a/apps/rembg/2.0.85/docker-compose.yml +++ b/apps/rembg/2.0.85/docker-compose.yml @@ -1,6 +1,6 @@ services: rembg: - image: "danielgatis/rembg:2.0.85" + image: "danielgatis/rembg:2.0.86" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/remmina-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/remmina-kasm/1.19.0-rolling-weekly/docker-compose.yml index e7e6d421b..f58fa906b 100644 --- a/apps/remmina-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/remmina-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: remmina-kasm: - image: "kasmweb/remmina:1.19.0-rolling-weekly@sha256:0033445ece3bbd5c2340579d7dde4331717a7d8d7fd65cdc311967dc6323484e" + image: "kasmweb/remmina:1.19.0-rolling-weekly@sha256:0ba60a090fd9a9458949b81093d0be8c1a3242887acae3a9257408f61f61a8c2" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/resin/1.2.0/docker-compose.yml b/apps/resin/1.2.0/docker-compose.yml index e47e519da..65666a21a 100644 --- a/apps/resin/1.2.0/docker-compose.yml +++ b/apps/resin/1.2.0/docker-compose.yml @@ -1,6 +1,6 @@ services: resin: - image: "ghcr.io/resinat/resin:1.2.0" + image: "ghcr.io/resinat/resin:1.3.0" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/rocketchat/8.7.0/docker-compose.yml b/apps/rocketchat/8.7.0/docker-compose.yml index 482ef269a..524304a33 100644 --- a/apps/rocketchat/8.7.0/docker-compose.yml +++ b/apps/rocketchat/8.7.0/docker-compose.yml @@ -1,6 +1,6 @@ services: rocketchat: - image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1" + image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0" container_name: ${CONTAINER_NAME} restart: always depends_on: diff --git a/apps/rocketchat/8.8.0/docker-compose.yml b/apps/rocketchat/8.8.0/docker-compose.yml index 482ef269a..524304a33 100644 --- a/apps/rocketchat/8.8.0/docker-compose.yml +++ b/apps/rocketchat/8.8.0/docker-compose.yml @@ -1,6 +1,6 @@ services: rocketchat: - image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1" + image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0" container_name: ${CONTAINER_NAME} restart: always depends_on: diff --git a/apps/rocketchat/8.9.0/.env.sample b/apps/rocketchat/8.9.0/.env.sample new file mode 100644 index 000000000..bf415f917 --- /dev/null +++ b/apps/rocketchat/8.9.0/.env.sample @@ -0,0 +1,10 @@ +PANEL_APP_PORT_HTTP=18087 +ROOT_URL=http://127.0.0.1:18087 +TZ=Asia/Shanghai +APP_DATA_DIR=./data +REG_TOKEN= +ADMIN_USERNAME=admin +ADMIN_NAME=Admin +ADMIN_EMAIL=admin@example.com +ADMIN_PASS=rocket_admin_password +CONTAINER_NAME=rocketchat diff --git a/apps/rocketchat/8.9.0/data.yml b/apps/rocketchat/8.9.0/data.yml new file mode 100644 index 000000000..16f50a9db --- /dev/null +++ b/apps/rocketchat/8.9.0/data.yml @@ -0,0 +1,150 @@ +additionalProperties: + formFields: + - default: 18087 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: http://127.0.0.1:18087 + edit: true + envKey: ROOT_URL + labelEn: Public URL + labelZh: 外部访问地址 + label: + en: Public URL + zh: 外部访问地址 + zh-Hant: 外部存取位址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publico + required: true + rule: paramExtUrl + type: text + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horario + required: true + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text + - default: "" + edit: true + envKey: REG_TOKEN + labelEn: Registration Token + labelZh: 注册令牌 + label: + en: Registration Token + zh: 注册令牌 + zh-Hant: 註冊權杖 + ja: 登録トークン + ko: 등록 토큰 + ru: Токен регистрации + ms: Token pendaftaran + pt-br: Token de registro + required: false + type: text + - default: admin + edit: true + envKey: ADMIN_USERNAME + labelEn: Admin Username + labelZh: 管理员用户名 + label: + en: Admin Username + zh: 管理员用户名 + zh-Hant: 管理員使用者名稱 + ja: 管理者ユーザー名 + ko: 관리자 사용자 이름 + ru: Имя администратора + ms: Nama pengguna pentadbir + pt-br: Nome de usuario administrador + required: true + type: text + - default: Admin + edit: true + envKey: ADMIN_NAME + labelEn: Admin Name + labelZh: 管理员姓名 + label: + en: Admin Name + zh: 管理员姓名 + zh-Hant: 管理員姓名 + ja: 管理者名 + ko: 관리자 이름 + ru: Имя администратора + ms: Nama pentadbir + pt-br: Nome do administrador + required: true + type: text + - default: admin@example.com + edit: true + envKey: ADMIN_EMAIL + labelEn: Admin Email + labelZh: 管理员邮箱 + label: + en: Admin Email + zh: 管理员邮箱 + zh-Hant: 管理員信箱 + ja: 管理者メール + ko: 관리자 이메일 + ru: Email администратора + ms: E-mel pentadbir + pt-br: Email do administrador + required: true + type: text + - default: rocket_admin_password + edit: true + envKey: ADMIN_PASS + labelEn: Admin Password + labelZh: 管理员密码 + label: + en: Admin Password + zh: 管理员密码 + zh-Hant: 管理員密碼 + ja: 管理者パスワード + ko: 관리자 비밀번호 + ru: Пароль администратора + ms: Kata laluan pentadbir + pt-br: Senha do administrador + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/rocketchat/8.9.0/data/.gitkeep b/apps/rocketchat/8.9.0/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/rocketchat/8.9.0/docker-compose.yml b/apps/rocketchat/8.9.0/docker-compose.yml new file mode 100644 index 000000000..524304a33 --- /dev/null +++ b/apps/rocketchat/8.9.0/docker-compose.yml @@ -0,0 +1,142 @@ +services: + rocketchat: + image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0" + container_name: ${CONTAINER_NAME} + restart: always + depends_on: + mongodb: + condition: service_healthy + nats: + condition: service_healthy + networks: + - 1panel-network + - rocketchat-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + entrypoint: sh + command: + - -c + - | + protocol=$(echo $$MONGO_URL | awk -F'://' '{print $$1}') + if [ "$$protocol" = "mongodb+srv" ]; then + echo "MongoDB is using SRV protocol, starting Rocket.Chat without local TCP wait" + exec node main.js + fi + host_port=$(echo $$MONGO_URL | awk -F/ '{print $$3}') + if echo $$host_port | grep -q @; then + host_port=$(echo $$host_port | awk -F@ '{print $$2}') + fi + host=$(echo $$host_port | awk -F: '{print $$1}') + port=$(echo $$host_port | awk -F: '{print $$2}') + if [ -z "$$port" ]; then + port=27017 + fi + echo "Waiting for MongoDB ($$host:$$port) before starting Rocket.Chat" + for i in $(seq 1 60); do + if nc -z $$host $$port >/dev/null 2>&1; then + exec node main.js + fi + sleep 5 + done + echo "MongoDB did not become reachable in time" + exit 1 + environment: + - TZ=${TZ} + - ROOT_URL=${ROOT_URL} + - PORT=3000 + - DEPLOY_METHOD=docker + - DEPLOY_PLATFORM=compose + - REG_TOKEN=${REG_TOKEN} + - MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0 + - TRANSPORTER=monolith+nats://nats:4222 + - INSTANCE_IP= + - ADMIN_USERNAME=${ADMIN_USERNAME} + - ADMIN_NAME=${ADMIN_NAME} + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_PASS=${ADMIN_PASS} + healthcheck: + test: ["CMD-SHELL", "nc -z -w 10 127.0.0.1 3000"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 90s + labels: + createdBy: "Apps" + + mongodb: + image: "docker.io/mongodb/mongodb-community-server:8.2-ubi8" + container_name: ${CONTAINER_NAME}-mongodb + user: root + restart: always + networks: + - rocketchat-network + volumes: + - "${APP_DATA_DIR}/mongodb:/data/db" + entrypoint: + - sh + - -ec + - | + install -d -o mongod -g mongod -m 755 /data/db + if [ "$(stat -c '%u:%g' /data/db)" != "1000:1000" ]; then + chown -R mongod:mongod /data/db + fi + su -s /bin/sh mongod -c 'exec mongod --replSet rs0 --bind_ip_all' & + pid=$$! + until mongosh 'mongodb://127.0.0.1:27017/?directConnection=true' --quiet --eval 'db.adminCommand("ping")' >/dev/null 2>&1; do + sleep 2 + done + cat >/tmp/rs-init.js </dev/null 2>&1; then + break + fi + sleep 2 + done + wait "$$pid" + healthcheck: + test: ["CMD-SHELL", "mongosh 'mongodb://127.0.0.1:27017/?directConnection=true' --quiet --eval 'try { quit(rs.status().ok === 1 ? 0 : 1) } catch (error) { quit(1) }'"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 60s + labels: + createdBy: "Apps" + + nats: + image: "docker.io/nats:2.11-alpine" + container_name: ${CONTAINER_NAME}-nats + restart: always + command: --http_port 8222 + networks: + - rocketchat-network + healthcheck: + test: ["CMD-SHELL", "nc -z -w 10 127.0.0.1 4222"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 10s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true + rocketchat-network: + driver: bridge diff --git a/apps/rocketchat/8.9.0/scripts/.gitkeep b/apps/rocketchat/8.9.0/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/rocketchat/8.9.0/scripts/init.sh b/apps/rocketchat/8.9.0/scripts/init.sh new file mode 100755 index 000000000..0aaff5e31 --- /dev/null +++ b/apps/rocketchat/8.9.0/scripts/init.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +DATA_DIR="${APP_DATA_DIR:-./data}" +mkdir -p "${DATA_DIR}/mongodb" +chown -R 1000:1000 "${DATA_DIR}/mongodb" 2>/dev/null || true diff --git a/apps/rocketchat/8.9.0/scripts/uninstall.sh b/apps/rocketchat/8.9.0/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/rocketchat/8.9.0/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/unleash/8.1.0/scripts/init.sh b/apps/rocketchat/8.9.0/scripts/upgrade.sh similarity index 100% rename from apps/unleash/8.1.0/scripts/init.sh rename to apps/rocketchat/8.9.0/scripts/upgrade.sh diff --git a/apps/roundcube/latest/docker-compose.yml b/apps/roundcube/latest/docker-compose.yml index f2bcda56f..e0217795d 100644 --- a/apps/roundcube/latest/docker-compose.yml +++ b/apps/roundcube/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: roundcube: - image: "roundcube/roundcubemail:1.7.4-apache-nonroot" + image: "roundcube/roundcubemail:1.7.5-apache-nonroot" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/scummvm-linuxserver/2026.3.0/docker-compose.yml b/apps/scummvm-linuxserver/2026.3.0/docker-compose.yml index bc5775eb0..09a305f26 100644 --- a/apps/scummvm-linuxserver/2026.3.0/docker-compose.yml +++ b/apps/scummvm-linuxserver/2026.3.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: scummvm: - image: "linuxserver/scummvm:2026.3.0" + image: "linuxserver/scummvm:2026.4.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/seafile/13.0.28/docker-compose.yml b/apps/seafile/13.0.28/docker-compose.yml index 7e2ef8757..ece050fbf 100644 --- a/apps/seafile/13.0.28/docker-compose.yml +++ b/apps/seafile/13.0.28/docker-compose.yml @@ -34,15 +34,7 @@ services: - REDIS_PASSWORD=${PANEL_REDIS_ROOT_PASSWORD:-} - MEMCACHED_HOST=${MEMCACHED_HOST:-memcached} - MEMCACHED_PORT=${MEMCACHED_PORT:-11211} - command: - - /bin/bash - - -c - - | - if test -d /opt/seafile/seafile-server-13.0.25/pro; then - mv /opt/seafile/seafile-server-13.0.25/pro /opt/seafile/seafile-server-13.0.25/pro.disabled - fi - exec /sbin/my_init -- /scripts/enterpoint.sh - image: seafileltd/seafile-mc:13.0.28 + image: seafileltd/seafile-mc:14.0.9 labels: createdBy: "Apps" diff --git a/apps/seafile/README.md b/apps/seafile/README.md index ac9965ff4..3e6b59442 100644 --- a/apps/seafile/README.md +++ b/apps/seafile/README.md @@ -75,13 +75,13 @@ Seafile is an open source file sync, sharing, and collaboration platform. This p - 新安装会使用 `seafile_ccnet_db`、`seafile_seafile_db` 和 `seafile_seahub_db`,避免与同一 MySQL runtime 中的 SeaTable 等应用发生库名冲突。旧安装升级时会保留原有 `ccnet_db`、`seafile_db` 和 `seahub_db` 名称,不会自动迁移数据库。 - `LocalMySQL` 仅用于兼容直接选择本地商店 runtime 的场景;它不是 1Panel 的数据库生命周期类型,必须手工保证 root 密码和端口与所选 runtime 一致。 - 旧安装升级时,`upgrade.sh` 不覆盖任何已有 `.env` 值;仅在字段缺失时保留旧数据库名、补 `PANEL_DB_PORT=3306`、保留旧 Memcached 缓存路径,并生成新的 64 位十六进制 JWT 私钥。升级前仍应备份数据目录和三个数据库。 -- `latest` 目录已固定到 `13.0.25`,避免 Docker Hub 上长期滞后的浮动 `latest` 标签落到 11.x 镜像。历史 `latest` 安装跨主版本升级前必须按 Seafile 官方升级顺序确认中间版本并完整备份;不要依赖自动镜像更新直接跨主版本。 +- `latest` 目录已固定到 `14.0.9`,避免 Docker Hub 上长期滞后的浮动 `latest` 标签落到旧版镜像。历史 `latest` 安装跨主版本升级前必须按 Seafile 官方升级顺序确认中间版本并完整备份;不要依赖自动镜像更新直接跨主版本。 - 新安装使用 Redis。`MEMCACHED_HOST` 与 `MEMCACHED_PORT` 仅保留给历史安装迁移,不建议新部署继续使用 Memcached。 -- 官方 `seafileltd/seafile-mc:13.0.25` CE 镜像仍包含 `pro` 目录,初始化会被上游脚本误判为专业版并重复创建 Seahub 表,容器重建后也可能误启专业版组件。本适配在每次容器启动时仅将镜像可写层内的该目录改名为临时路径,随后按 CE 流程启动;不修改 `/shared` 数据,升级和重启会保留现有数据,也不会执行数据库删表。 +- Seafile 14 CE 使用镜像自带的启动命令;升级时保留镜像内的 `pro` 目录。 - 如需对外开放访问,请同步检查防火墙、安全组和反向代理配置。 ## 参考资料 - 官网: - 文档: -- Docker 13 部署文档: +- Docker 14 部署文档: - 源码: diff --git a/apps/seafile/latest/docker-compose.yml b/apps/seafile/latest/docker-compose.yml index 7e2ef8757..ece050fbf 100644 --- a/apps/seafile/latest/docker-compose.yml +++ b/apps/seafile/latest/docker-compose.yml @@ -34,15 +34,7 @@ services: - REDIS_PASSWORD=${PANEL_REDIS_ROOT_PASSWORD:-} - MEMCACHED_HOST=${MEMCACHED_HOST:-memcached} - MEMCACHED_PORT=${MEMCACHED_PORT:-11211} - command: - - /bin/bash - - -c - - | - if test -d /opt/seafile/seafile-server-13.0.25/pro; then - mv /opt/seafile/seafile-server-13.0.25/pro /opt/seafile/seafile-server-13.0.25/pro.disabled - fi - exec /sbin/my_init -- /scripts/enterpoint.sh - image: seafileltd/seafile-mc:13.0.28 + image: seafileltd/seafile-mc:14.0.9 labels: createdBy: "Apps" diff --git a/apps/serverkit/1.11.10/docker-compose.yml b/apps/serverkit/1.11.10/docker-compose.yml index 024c11860..a1170d02b 100644 --- a/apps/serverkit/1.11.10/docker-compose.yml +++ b/apps/serverkit/1.11.10/docker-compose.yml @@ -1,6 +1,6 @@ services: serverkit: - image: "jhd3197/serverkit:1.11.10" + image: "jhd3197/serverkit:1.11.20" container_name: ${CONTAINER_NAME} ports: - "${PANEL_APP_PORT_HTTP}:5000" diff --git a/apps/shlink/5.1.5/docker-compose.yml b/apps/shlink/5.1.5/docker-compose.yml index 59090b74f..3eee05d03 100644 --- a/apps/shlink/5.1.5/docker-compose.yml +++ b/apps/shlink/5.1.5/docker-compose.yml @@ -1,6 +1,6 @@ services: shlink: - image: "shlinkio/shlink:5.1.5" + image: "shlinkio/shlink:5.1.8" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/shlink/5.1.6/docker-compose.yml b/apps/shlink/5.1.6/docker-compose.yml index 260c3e83a..3eee05d03 100644 --- a/apps/shlink/5.1.6/docker-compose.yml +++ b/apps/shlink/5.1.6/docker-compose.yml @@ -1,6 +1,6 @@ services: shlink: - image: "shlinkio/shlink:5.1.6" + image: "shlinkio/shlink:5.1.8" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/signal-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/signal-kasm/1.19.0-rolling-weekly/docker-compose.yml index 9bec1af33..e49806e28 100644 --- a/apps/signal-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/signal-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: signal-kasm: - image: "kasmweb/signal:1.19.0-rolling-weekly@sha256:2c2df7095530337aa60f145c66fb7906665a3f57cf0a3d5235fc4acf47a7478f" + image: "kasmweb/signal:1.19.0-rolling-weekly@sha256:6766d6b9c705bc9ac84e407dea37c87e1f87dbe2ad700bc6de9ad6bd6aeab31a" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/signal-linuxserver/8.30.0/docker-compose.yml b/apps/signal-linuxserver/8.30.0/docker-compose.yml index e8e6b121a..a6dc1e571 100644 --- a/apps/signal-linuxserver/8.30.0/docker-compose.yml +++ b/apps/signal-linuxserver/8.30.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: signal: - image: "linuxserver/signal:8.30.0" + image: "linuxserver/signal:8.32.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/silverbullet/2.11.1/.env.sample b/apps/silverbullet/2.12.0/.env.sample similarity index 100% rename from apps/silverbullet/2.11.1/.env.sample rename to apps/silverbullet/2.12.0/.env.sample diff --git a/apps/silverbullet/2.11.1/data.yml b/apps/silverbullet/2.12.0/data.yml similarity index 100% rename from apps/silverbullet/2.11.1/data.yml rename to apps/silverbullet/2.12.0/data.yml diff --git a/apps/silverbullet/2.11.1/docker-compose.yml b/apps/silverbullet/2.12.0/docker-compose.yml similarity index 90% rename from apps/silverbullet/2.11.1/docker-compose.yml rename to apps/silverbullet/2.12.0/docker-compose.yml index 538e1ba56..eef0a71af 100644 --- a/apps/silverbullet/2.11.1/docker-compose.yml +++ b/apps/silverbullet/2.12.0/docker-compose.yml @@ -1,6 +1,6 @@ services: silverbullet: - image: "ghcr.io/silverbulletmd/silverbullet:2.11.1" + image: "ghcr.io/silverbulletmd/silverbullet:2.12.1" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/silverbullet/latest/docker-compose.yml b/apps/silverbullet/latest/docker-compose.yml index b4051d357..3af675925 100644 --- a/apps/silverbullet/latest/docker-compose.yml +++ b/apps/silverbullet/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: silverbullet: - image: "ghcr.io/silverbulletmd/silverbullet:latest@sha256:1b97ce0f7b5085c76e0e9363c88ed97673db62a3b0f78c7bb12868827fff8b37" + image: "ghcr.io/silverbulletmd/silverbullet:latest@sha256:370a3bf2daa37b7dbba62183085c57d5d43862189ea127e0765646d230561d9d" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/simplex-smp-server/7.0.0/.env.sample b/apps/simplex-smp-server/7.0.0/.env.sample index 12d323409..ec6eb24e8 100644 --- a/apps/simplex-smp-server/7.0.0/.env.sample +++ b/apps/simplex-smp-server/7.0.0/.env.sample @@ -3,4 +3,4 @@ APP_CONFIG_DIR=./data/config APP_STATE_DIR=./data/state ADDR= PASS= -CONTAINER_NAME= +CONTAINER_NAME=simplex-smp-server diff --git a/apps/simplex-smp-server/7.0.0/docker-compose.yml b/apps/simplex-smp-server/7.0.0/docker-compose.yml index 598a35a3c..c6859669d 100644 --- a/apps/simplex-smp-server/7.0.0/docker-compose.yml +++ b/apps/simplex-smp-server/7.0.0/docker-compose.yml @@ -5,6 +5,7 @@ services: environment: - ADDR=${ADDR} - PASS=${PASS} + - WEB_MANUAL=1 ports: - "${PANEL_APP_PORT_SMP}:5223" volumes: diff --git a/apps/simplex-smp-server/7.0.1/.env.sample b/apps/simplex-smp-server/7.0.1/.env.sample new file mode 100644 index 000000000..ec6eb24e8 --- /dev/null +++ b/apps/simplex-smp-server/7.0.1/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_SMP=5223 +APP_CONFIG_DIR=./data/config +APP_STATE_DIR=./data/state +ADDR= +PASS= +CONTAINER_NAME=simplex-smp-server diff --git a/apps/simplex-smp-server/7.0.1/data.yml b/apps/simplex-smp-server/7.0.1/data.yml new file mode 100644 index 000000000..f0ce21b91 --- /dev/null +++ b/apps/simplex-smp-server/7.0.1/data.yml @@ -0,0 +1,83 @@ +additionalProperties: + formFields: + - default: 5223 + edit: true + envKey: PANEL_APP_PORT_SMP + labelEn: SMP Port + labelZh: SMP 端口 + label: + en: SMP Port + zh: SMP 端口 + zh-Hant: SMP 埠 + ja: SMP ポート + ko: SMP 포트 + ru: SMP-порт + ms: Port SMP + pt-br: Porta SMP + required: true + rule: paramPort + type: number + - default: ./data/config + edit: true + envKey: APP_CONFIG_DIR + labelEn: Config Directory + labelZh: 配置目录 + label: + en: Config Directory + zh: 配置目录 + zh-Hant: 設定目錄 + ja: 設定ディレクトリ + ko: 설정 디렉터리 + ru: Каталог конфигурации + ms: Direktori Konfigurasi + pt-br: Diretório de Configuração + required: true + type: text + - default: ./data/state + edit: true + envKey: APP_STATE_DIR + labelEn: State Directory + labelZh: 状态目录 + label: + en: State Directory + zh: 状态目录 + zh-Hant: 狀態目錄 + ja: 状態ディレクトリ + ko: 상태 디렉터리 + ru: Каталог состояния + ms: Direktori Keadaan + pt-br: Diretório de Estado + required: true + type: text + - default: '' + edit: true + envKey: ADDR + labelEn: Server Domain or IP + labelZh: 服务器域名或 IP + label: + en: Server Domain or IP + zh: 服务器域名或 IP + zh-Hant: 伺服器網域或 IP + ja: サーバードメインまたは IP + ko: 서버 도메인 또는 IP + ru: Домен или IP сервера + ms: Domain atau IP Pelayan + pt-br: Dominio ou IP do Servidor + required: true + type: text + - default: '' + edit: true + envKey: PASS + labelEn: Create Queue Password + labelZh: 队列创建密码 + label: + en: Create Queue Password + zh: 队列创建密码 + zh-Hant: 佇列建立密碼 + ja: キュー作成パスワード + ko: 큐 생성 비밀번호 + ru: Пароль создания очередей + ms: Kata Laluan Penciptaan Barisan + pt-br: Senha para Criacao de Filas + required: false + type: text diff --git a/apps/simplex-smp-server/7.0.1/data/.gitkeep b/apps/simplex-smp-server/7.0.1/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/simplex-smp-server/7.0.1/docker-compose.yml b/apps/simplex-smp-server/7.0.1/docker-compose.yml new file mode 100644 index 000000000..472e9f632 --- /dev/null +++ b/apps/simplex-smp-server/7.0.1/docker-compose.yml @@ -0,0 +1,27 @@ +services: + simplex-smp-server: + image: "simplexchat/smp-server:v7.0.1" + container_name: ${CONTAINER_NAME} + environment: + - ADDR=${ADDR} + - PASS=${PASS} + - WEB_MANUAL=1 + ports: + - "${PANEL_APP_PORT_SMP}:5223" + volumes: + - "${APP_CONFIG_DIR}:/etc/opt/simplex" + - "${APP_STATE_DIR}:/var/opt/simplex" + healthcheck: + test: + - CMD-SHELL + - >- + bash -lc 'exec 3<>/dev/tcp/127.0.0.1/5223; exec 3>&-; exec 3<&-' + labels: + createdBy: Apps + networks: + - 1panel-network + restart: unless-stopped + +networks: + 1panel-network: + external: true diff --git a/apps/simplex-smp-server/7.0.1/scripts/.gitkeep b/apps/simplex-smp-server/7.0.1/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/simplex-smp-server/7.0.1/scripts/uninstall.sh b/apps/simplex-smp-server/7.0.1/scripts/uninstall.sh new file mode 100755 index 000000000..024242bb3 --- /dev/null +++ b/apps/simplex-smp-server/7.0.1/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -euo pipefail + +docker-compose down --volumes diff --git a/apps/simplex-smp-server/7.0.0/scripts/init.sh b/apps/simplex-smp-server/7.0.1/scripts/upgrade.sh similarity index 75% rename from apps/simplex-smp-server/7.0.0/scripts/init.sh rename to apps/simplex-smp-server/7.0.1/scripts/upgrade.sh index 61e44bc53..7a197b4b1 100755 --- a/apps/simplex-smp-server/7.0.0/scripts/init.sh +++ b/apps/simplex-smp-server/7.0.1/scripts/upgrade.sh @@ -11,3 +11,4 @@ fi CONFIG_DIR="${APP_CONFIG_DIR:-./data/config}" STATE_DIR="${APP_STATE_DIR:-./data/state}" mkdir -p "$CONFIG_DIR" "$STATE_DIR" +rm -f "$STATE_DIR"/smp-server-stats.log "$STATE_DIR"/smp-server-stats.daily.log diff --git a/apps/simplex-smp-server/README.md b/apps/simplex-smp-server/README.md index 5ac36ffb2..8286ad4f7 100644 --- a/apps/simplex-smp-server/README.md +++ b/apps/simplex-smp-server/README.md @@ -17,6 +17,9 @@ smp://指纹[:创建队列密码]@ADDR:PANEL_APP_PORT_SMP `ADDR` 默认值为 `127.0.0.1`,仅适用于本地初始化与测试。正式安装时请直接填写你的公网域名或公网 IP,SimpleX 客户端通过该地址与 SMP 端口连接。 +## 运行模式 +此包使用 SimpleX 官方 Docker manual 模式,SMP 主要监听端口为 5223。本包不启动 Caddy/Let's Encrypt,也不提供 443 的服务器信息页。首次启动仍会初始化 SMP 指纹和 `smp-server.ini`。 + ## 数据持久化 - `APP_CONFIG_DIR`:持久化 `/etc/opt/simplex`,保存证书、指纹与 `smp-server.ini` - `APP_STATE_DIR`:持久化 `/var/opt/simplex`,保存 store log、统计与运行状态文件 @@ -34,8 +37,13 @@ SimpleX SMP Server is the official self-hosted relay server for SimpleX. This ad - Optional password for creating new queues - `ADDR` must be set to your public domain or public IP during installation +## Runtime mode +This package uses SimpleX's official Docker manual setup, with SMP on TCP 5223. It does not include Caddy/Let's Encrypt or the automatically managed HTTPS server information page on port 443. First startup still initializes the SMP fingerprint and `smp-server.ini`. + ## 版本说明 -固定版本使用官方镜像 `simplexchat/smp-server:v7.0.0`,`latest` 当前同样基于 7.0.0 发布线。 +固定版本目录更新至官方镜像 `simplexchat/smp-server:v7.0.1`;`latest` 继续跟随官方滚动标签,可能独立变化。 + +The fixed version directory uses the official `simplexchat/smp-server:v7.0.1` image. The `latest` directory follows the upstream rolling tag and may move independently. ## 参考资料 - 官网: diff --git a/apps/siyuan/3.8.6/.env.sample b/apps/siyuan/3.8.6/.env.sample index 15b6323bf..9a96ec32f 100644 --- a/apps/siyuan/3.8.6/.env.sample +++ b/apps/siyuan/3.8.6/.env.sample @@ -1,3 +1,4 @@ CONTAINER_NAME="siyuan" PANEL_APP_PORT_HTTP="40138" DATA_PATH="./data" +SIYUAN_ACCESS_AUTH_CODE="replace-with-a-strong-access-code" diff --git a/apps/siyuan/3.8.6/data.yml b/apps/siyuan/3.8.6/data.yml index 559772324..26a5cfe3b 100644 --- a/apps/siyuan/3.8.6/data.yml +++ b/apps/siyuan/3.8.6/data.yml @@ -14,6 +14,10 @@ additionalProperties: ru: 'Порт' ms: 'Port' pt-br: 'Porta' + tr: 'Bağlantı noktası' + es-es: 'Puerto' + fa: 'درگاه' + lo: 'ພອດ' required: true rule: paramPort type: number @@ -31,5 +35,31 @@ additionalProperties: ru: 'Путь к папке данных' ms: 'Laluan folder data' pt-br: 'Caminho da pasta de dados' + tr: 'Veri klasör yolu' + es-es: 'Ruta de la carpeta de datos' + fa: 'مسیر پوشهٔ داده' + lo: 'ເສັ້ນທາງໂຟນເດີຂໍ້ມູນ' required: true type: text + - default: "" + edit: true + envKey: SIYUAN_ACCESS_AUTH_CODE + labelEn: Lock screen password + labelZh: 锁屏密码 + label: + en: 'Lock screen password' + zh: '锁屏密码' + zh-Hant: '鎖定螢幕密碼' + ja: 'ロック画面のパスワード' + ko: '잠금 화면 비밀번호' + ru: 'Пароль экрана блокировки' + ms: 'Kata laluan skrin kunci' + pt-br: 'Senha da tela de bloqueio' + tr: 'Ekran kilidi parolası' + es-es: 'Contraseña de pantalla de bloqueo' + fa: 'گذرواژهٔ قفل صفحه' + lo: 'ລະຫັດຜ່ານໜ້າຈໍລັອກ' + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/siyuan/3.8.6/docker-compose.yml b/apps/siyuan/3.8.6/docker-compose.yml index 6321e9245..6d0f6e8c7 100644 --- a/apps/siyuan/3.8.6/docker-compose.yml +++ b/apps/siyuan/3.8.6/docker-compose.yml @@ -8,9 +8,12 @@ services: - "${PANEL_APP_PORT_HTTP}:6806" volumes: - "${DATA_PATH}:/siyuan/workspace" - user: "1000:1000" - command: "--workspace=/siyuan/workspace/" - image: b3log/siyuan:v3.8.6 + environment: + - SIYUAN_ACCESS_AUTH_CODE=${SIYUAN_ACCESS_AUTH_CODE} + command: + - serve + - --workspace=/siyuan/workspace/ + image: "b3log/siyuan:v3.8.8" labels: createdBy: "Apps" diff --git a/apps/siyuan/3.8.7/.env.sample b/apps/siyuan/3.8.7/.env.sample new file mode 100644 index 000000000..9a96ec32f --- /dev/null +++ b/apps/siyuan/3.8.7/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME="siyuan" +PANEL_APP_PORT_HTTP="40138" +DATA_PATH="./data" +SIYUAN_ACCESS_AUTH_CODE="replace-with-a-strong-access-code" diff --git a/apps/siyuan/3.8.7/data.yml b/apps/siyuan/3.8.7/data.yml new file mode 100644 index 000000000..26a5cfe3b --- /dev/null +++ b/apps/siyuan/3.8.7/data.yml @@ -0,0 +1,65 @@ +additionalProperties: + formFields: + - default: 40138 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: 'Port' + zh: '端口' + zh-Hant: '埠' + ja: 'ポート' + ko: '포트' + ru: 'Порт' + ms: 'Port' + pt-br: 'Porta' + tr: 'Bağlantı noktası' + es-es: 'Puerto' + fa: 'درگاه' + lo: 'ພອດ' + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: DATA_PATH + labelEn: Data folder path + labelZh: 数据文件夹路径 + label: + en: 'Data folder path' + zh: '数据文件夹路径' + zh-Hant: '資料夾路徑' + ja: 'データフォルダパス' + ko: '데이터 폴더 경로' + ru: 'Путь к папке данных' + ms: 'Laluan folder data' + pt-br: 'Caminho da pasta de dados' + tr: 'Veri klasör yolu' + es-es: 'Ruta de la carpeta de datos' + fa: 'مسیر پوشهٔ داده' + lo: 'ເສັ້ນທາງໂຟນເດີຂໍ້ມູນ' + required: true + type: text + - default: "" + edit: true + envKey: SIYUAN_ACCESS_AUTH_CODE + labelEn: Lock screen password + labelZh: 锁屏密码 + label: + en: 'Lock screen password' + zh: '锁屏密码' + zh-Hant: '鎖定螢幕密碼' + ja: 'ロック画面のパスワード' + ko: '잠금 화면 비밀번호' + ru: 'Пароль экрана блокировки' + ms: 'Kata laluan skrin kunci' + pt-br: 'Senha da tela de bloqueio' + tr: 'Ekran kilidi parolası' + es-es: 'Contraseña de pantalla de bloqueo' + fa: 'گذرواژهٔ قفل صفحه' + lo: 'ລະຫັດຜ່ານໜ້າຈໍລັອກ' + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/siyuan/3.8.7/docker-compose.yml b/apps/siyuan/3.8.7/docker-compose.yml new file mode 100644 index 000000000..0d729524c --- /dev/null +++ b/apps/siyuan/3.8.7/docker-compose.yml @@ -0,0 +1,22 @@ +services: + siyuan: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:6806" + volumes: + - "${DATA_PATH}:/siyuan/workspace" + environment: + - SIYUAN_ACCESS_AUTH_CODE=${SIYUAN_ACCESS_AUTH_CODE} + command: + - serve + - --workspace=/siyuan/workspace/ + image: "b3log/siyuan:v3.8.8" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/siyuan/README.md b/apps/siyuan/README.md index b060e102d..0bb3223ea 100644 --- a/apps/siyuan/README.md +++ b/apps/siyuan/README.md @@ -1,21 +1,24 @@ # 思源笔记 SiYuan -## 应用简介 +## 产品介绍 一款隐私优先的个人知识管理系统。 -英文说明:A privacy-first personal knowledge management system. +## 主要功能 +- 通过 Web 界面创建和整理个人知识笔记。 +- 在 1Panel 中管理访问端口、锁屏密码和持久化数据目录。 -## 部署说明 +## 访问说明 - 本应用使用 Docker Compose 在 1Panel 中部署。 - 应用分类:工具。 - 支持架构:amd64。 -- 可选版本:`latest`;固定版本以应用商店当前版本目录和安装表单为准。 -- 安装后按应用表单中的端口访问 Web UI、SSH 或对应服务。 +- 可选版本:固定版本和 `latest`。 +- 安装后按应用表单中的端口访问 Web UI,并输入安装时设置的访问认证码。 ## 端口 | 变量 | 说明 | 默认值 | 必填 | | --- | --- | --- | --- | | PANEL_APP_PORT_HTTP | 端口 | 40138 | 是 | +| SIYUAN_ACCESS_AUTH_CODE | 锁屏密码 | 随机生成 | 是 | ## 数据持久化 | 变量 | 说明 | 默认值 | 必填 | @@ -25,17 +28,7 @@ 升级或迁移前,请在 1Panel 中备份上述数据目录。 ## 使用说明 -- 赋予文件夹权限 - -容器要求以`1000:1000`用户身份运行,第一次创建可能失败,需要将应用数据文件夹`data`赋予用户权限, - -终端运行以下命令,按需修改 - -``` -chown -R 1000:1000 /opt/1panel/apps/local/siyuan/siyuan/data -``` - -然后重建应用即可。 +- 首次访问时输入安装表单设置的访问认证码。升级时保留现有数据目录和认证码。 ## 参考资料 - 官网: diff --git a/apps/siyuan/README_en.md b/apps/siyuan/README_en.md new file mode 100644 index 000000000..a412ae904 --- /dev/null +++ b/apps/siyuan/README_en.md @@ -0,0 +1,18 @@ +# SiYuan + +## Introduction +SiYuan is a privacy-first personal knowledge management system. + +## Features +- Create and organize personal knowledge notes through a web interface. +- Manage the access port, lock screen password, and persistent data directory in 1Panel. + +## Usage +- The app is deployed with Docker Compose in 1Panel and supports amd64. +- Choose a fixed version or `latest` in the app store. +- Open the Web UI at the configured port and enter the access code set during installation. +- `DATA_PATH` stores the SiYuan workspace. Back it up before upgrades or migration, and keep the configured data path and access code when upgrading. + +## References +- Website: +- Documentation: diff --git a/apps/siyuan/data.yml b/apps/siyuan/data.yml index 34c1006c4..7408801a0 100644 --- a/apps/siyuan/data.yml +++ b/apps/siyuan/data.yml @@ -19,6 +19,10 @@ additionalProperties: ru: Персональная система управления знаниями с приоритетом конфиденциальности ms: Sistem pengurusan pengetahuan peribadi berteraskan privasi pt-br: Sistema de gestão de conhecimento pessoal com foco em privacidade + tr: Gizliliğe öncelik veren kişisel bilgi yönetim sistemi + es-es: Sistema personal de gestión del conocimiento que prioriza la privacidad + fa: سامانه مدیریت دانش شخصی با اولویت حریم خصوصی + lo: ລະບົບຈັດການຄວາມຮູ້ສ່ວນຕົວທີ່ເນັ້ນຄວາມເປັນສ່ວນຕົວ type: tool crossVersionUpdate: true limit: 0 diff --git a/apps/siyuan/latest/.env.sample b/apps/siyuan/latest/.env.sample index 15b6323bf..9a96ec32f 100644 --- a/apps/siyuan/latest/.env.sample +++ b/apps/siyuan/latest/.env.sample @@ -1,3 +1,4 @@ CONTAINER_NAME="siyuan" PANEL_APP_PORT_HTTP="40138" DATA_PATH="./data" +SIYUAN_ACCESS_AUTH_CODE="replace-with-a-strong-access-code" diff --git a/apps/siyuan/latest/data.yml b/apps/siyuan/latest/data.yml index 559772324..26a5cfe3b 100644 --- a/apps/siyuan/latest/data.yml +++ b/apps/siyuan/latest/data.yml @@ -14,6 +14,10 @@ additionalProperties: ru: 'Порт' ms: 'Port' pt-br: 'Porta' + tr: 'Bağlantı noktası' + es-es: 'Puerto' + fa: 'درگاه' + lo: 'ພອດ' required: true rule: paramPort type: number @@ -31,5 +35,31 @@ additionalProperties: ru: 'Путь к папке данных' ms: 'Laluan folder data' pt-br: 'Caminho da pasta de dados' + tr: 'Veri klasör yolu' + es-es: 'Ruta de la carpeta de datos' + fa: 'مسیر پوشهٔ داده' + lo: 'ເສັ້ນທາງໂຟນເດີຂໍ້ມູນ' required: true type: text + - default: "" + edit: true + envKey: SIYUAN_ACCESS_AUTH_CODE + labelEn: Lock screen password + labelZh: 锁屏密码 + label: + en: 'Lock screen password' + zh: '锁屏密码' + zh-Hant: '鎖定螢幕密碼' + ja: 'ロック画面のパスワード' + ko: '잠금 화면 비밀번호' + ru: 'Пароль экрана блокировки' + ms: 'Kata laluan skrin kunci' + pt-br: 'Senha da tela de bloqueio' + tr: 'Ekran kilidi parolası' + es-es: 'Contraseña de pantalla de bloqueo' + fa: 'گذرواژهٔ قفل صفحه' + lo: 'ລະຫັດຜ່ານໜ້າຈໍລັອກ' + random: true + required: true + rule: paramComplexity + type: password diff --git a/apps/siyuan/latest/docker-compose.yml b/apps/siyuan/latest/docker-compose.yml index 278391bfd..d977c4523 100644 --- a/apps/siyuan/latest/docker-compose.yml +++ b/apps/siyuan/latest/docker-compose.yml @@ -8,9 +8,12 @@ services: - "${PANEL_APP_PORT_HTTP}:6806" volumes: - "${DATA_PATH}:/siyuan/workspace" - user: "1000:1000" - command: "--workspace=/siyuan/workspace/" - image: b3log/siyuan:latest + environment: + - SIYUAN_ACCESS_AUTH_CODE=${SIYUAN_ACCESS_AUTH_CODE} + command: + - serve + - --workspace=/siyuan/workspace/ + image: "b3log/siyuan:latest" labels: createdBy: "Apps" diff --git a/apps/slack-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/slack-kasm/1.19.0-rolling-weekly/docker-compose.yml index e21005a55..9b43333cd 100644 --- a/apps/slack-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/slack-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: slack-kasm: - image: "kasmweb/slack:1.19.0-rolling-weekly@sha256:311946741dd693428d71691e21bca11639a4f95de457c72edb1a495c1a33863a" + image: "kasmweb/slack:1.19.0-rolling-weekly@sha256:172923efdccccc74a6df84fddf9e832358ab8169a11ac87fdc198b57d41f5f2a" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/socket-proxy-linuxserver/3.4.6/.env.sample b/apps/socket-proxy-linuxserver/3.4.7/.env.sample similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/.env.sample rename to apps/socket-proxy-linuxserver/3.4.7/.env.sample diff --git a/apps/socket-proxy-linuxserver/3.4.6/data.yml b/apps/socket-proxy-linuxserver/3.4.7/data.yml similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/data.yml rename to apps/socket-proxy-linuxserver/3.4.7/data.yml diff --git a/apps/socket-proxy-linuxserver/3.4.7/data/.gitkeep b/apps/socket-proxy-linuxserver/3.4.7/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/socket-proxy-linuxserver/3.4.6/docker-compose.yml b/apps/socket-proxy-linuxserver/3.4.7/docker-compose.yml similarity index 96% rename from apps/socket-proxy-linuxserver/3.4.6/docker-compose.yml rename to apps/socket-proxy-linuxserver/3.4.7/docker-compose.yml index 53bdf2bd0..dfe082f26 100644 --- a/apps/socket-proxy-linuxserver/3.4.6/docker-compose.yml +++ b/apps/socket-proxy-linuxserver/3.4.7/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: socket-proxy: - image: "linuxserver/socket-proxy:3.4.6" + image: "linuxserver/socket-proxy:3.4.7" container_name: ${CONTAINER_NAME} restart: always read_only: true diff --git a/apps/socket-proxy-linuxserver/3.4.6/scripts/init.sh b/apps/socket-proxy-linuxserver/3.4.7/scripts/init.sh similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/scripts/init.sh rename to apps/socket-proxy-linuxserver/3.4.7/scripts/init.sh diff --git a/apps/socket-proxy-linuxserver/3.4.7/scripts/uninstall.sh b/apps/socket-proxy-linuxserver/3.4.7/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/socket-proxy-linuxserver/3.4.7/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/socket-proxy-linuxserver/3.4.6/scripts/upgrade.sh b/apps/socket-proxy-linuxserver/3.4.7/scripts/upgrade.sh similarity index 100% rename from apps/socket-proxy-linuxserver/3.4.6/scripts/upgrade.sh rename to apps/socket-proxy-linuxserver/3.4.7/scripts/upgrade.sh diff --git a/apps/songloft/2.13.1/docker-compose.yml b/apps/songloft/2.13.1/docker-compose.yml index 69fdf5ca2..18917e558 100644 --- a/apps/songloft/2.13.1/docker-compose.yml +++ b/apps/songloft/2.13.1/docker-compose.yml @@ -1,6 +1,6 @@ services: songloft: - image: "songloft/songloft:2.13.1" + image: "songloft/songloft:2.13.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/speedtest-tracker-linuxserver/1.15.0/docker-compose.yml b/apps/speedtest-tracker-linuxserver/1.15.0/docker-compose.yml index 11aa5075d..3cdbbfcca 100644 --- a/apps/speedtest-tracker-linuxserver/1.15.0/docker-compose.yml +++ b/apps/speedtest-tracker-linuxserver/1.15.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: speedtest-tracker: - image: "linuxserver/speedtest-tracker:1.15.0" + image: "linuxserver/speedtest-tracker:1.15.1" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/sqlpage/0.46.3/config/migrations/0000_init.sql b/apps/sqlpage/0.46.3/config/migrations/0000_init.sql new file mode 100644 index 000000000..f9d638a85 --- /dev/null +++ b/apps/sqlpage/0.46.3/config/migrations/0000_init.sql @@ -0,0 +1,5 @@ +create table todos( + id integer primary key, + title text not null, + created_at timestamp default current_timestamp +); diff --git a/apps/sqlpage/0.46.3/config/sqlpage.json b/apps/sqlpage/0.46.3/config/sqlpage.json new file mode 100644 index 000000000..d94aa8f8c --- /dev/null +++ b/apps/sqlpage/0.46.3/config/sqlpage.json @@ -0,0 +1,3 @@ +{ + "database_url": "sqlite:///var/www/sqlpage.db?mode=rwc" +} diff --git a/apps/sqlpage/0.46.3/docker-compose.yml b/apps/sqlpage/0.46.3/docker-compose.yml index 820cc3b79..06ec42c86 100644 --- a/apps/sqlpage/0.46.3/docker-compose.yml +++ b/apps/sqlpage/0.46.3/docker-compose.yml @@ -1,6 +1,6 @@ services: sqlpage: - image: "lovasoa/sqlpage:v0.46.3@sha256:354c683a50f541be01d427b3b664f1d5b36c28739f45bfb809be14b99b6ff649" + image: "lovasoa/sqlpage:v0.47.0@sha256:88a535d169d2cc5a12c17cff76992dd5c1b3c20864ce5a5e729b070990b37fe5" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/sqlpage/0.46.3/site-template/delete.sql b/apps/sqlpage/0.46.3/site-template/delete.sql new file mode 100644 index 000000000..051348e2d --- /dev/null +++ b/apps/sqlpage/0.46.3/site-template/delete.sql @@ -0,0 +1,16 @@ +delete from todos +where id = $todo_id and $confirm = 'yes' +returning + 'redirect' as component, + '/' as link; + +select 'dynamic' as component, sqlpage.run_sql('shell.sql') as properties; + +select + 'alert' as component, + 'red' as color, + 'Confirm deletion' as title, + 'Are you sure you want to delete the following todo item?\n\n> ' || title as description_md, + '?todo_id=' || $todo_id || '&confirm=yes' as link, + 'Delete' as link_text +from todos where id = $todo_id; diff --git a/apps/sqlpage/0.46.3/site-template/index.sql b/apps/sqlpage/0.46.3/site-template/index.sql new file mode 100644 index 000000000..812957835 --- /dev/null +++ b/apps/sqlpage/0.46.3/site-template/index.sql @@ -0,0 +1,20 @@ +select 'dynamic' as component, sqlpage.run_sql('shell.sql') as properties; + +select 'list' as component, + 'Todo' as title, + 'No todo yet...' as empty_title; + +select + title, + 'todo_form.sql?todo_id=' || id as edit_link, + 'delete.sql?todo_id=' || id as delete_link +from todos; + +select + 'button' as component, + 'center' as justify; +select + 'todo_form.sql' as link, + 'green' as color, + 'Add new todo' as title, + 'circle-plus' as icon; diff --git a/apps/sqlpage/0.46.3/site-template/shell.sql b/apps/sqlpage/0.46.3/site-template/shell.sql new file mode 100644 index 000000000..5531a6d25 --- /dev/null +++ b/apps/sqlpage/0.46.3/site-template/shell.sql @@ -0,0 +1,4 @@ +select 'shell' as component, + printf('Todo list (%d)', count(*)) as title, + 'timeline' as menu_item +from todos; diff --git a/apps/sqlpage/0.46.3/site-template/timeline.sql b/apps/sqlpage/0.46.3/site-template/timeline.sql new file mode 100644 index 000000000..f472c2489 --- /dev/null +++ b/apps/sqlpage/0.46.3/site-template/timeline.sql @@ -0,0 +1,12 @@ +select 'dynamic' as component, sqlpage.run_sql('shell.sql') as properties; + +select 'timeline' as component; +select + title, + 'todo_form.sql?todo_id=' || id as link, + created_at as date, + 'calendar' as icon, + 'green' as color, + printf('%d days ago', julianday('now') - julianday(created_at)) as description +from todos +order by created_at desc; diff --git a/apps/sqlpage/0.46.3/site-template/todo_form.sql b/apps/sqlpage/0.46.3/site-template/todo_form.sql new file mode 100644 index 000000000..969069b60 --- /dev/null +++ b/apps/sqlpage/0.46.3/site-template/todo_form.sql @@ -0,0 +1,22 @@ +insert or replace into todos(id, title) +select $todo_id, :todo +where :todo is not null +returning + 'redirect' as component, + '/' as link; + +select 'dynamic' as component, sqlpage.run_sql('shell.sql') as properties; + +select + 'form' as component, + 'Todo' as title, + case when $todo_id is null then + 'Add new todo' + else + 'Edit todo' + end as validate; +select + 'Todo item' as label, + 'todo' as name, + 'What do you have to do?' as placeholder, + (select title from todos where id = $todo_id) as value; diff --git a/apps/stump/0.1.10/docker-compose.yml b/apps/stump/0.1.10/docker-compose.yml index 74d964d15..688e0a80f 100644 --- a/apps/stump/0.1.10/docker-compose.yml +++ b/apps/stump/0.1.10/docker-compose.yml @@ -1,6 +1,6 @@ services: stump: - image: aaronleopold/stump:0.1.10 + image: aaronleopold/stump:0.1.11 container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/sub2api/0.2.15/.env.sample b/apps/sub2api/0.2.15/.env.sample new file mode 100644 index 000000000..88ba30f7b --- /dev/null +++ b/apps/sub2api/0.2.15/.env.sample @@ -0,0 +1,21 @@ +CONTAINER_NAME="sub2api" +PANEL_APP_PORT_HTTP="8080" +TZ="Asia/Shanghai" +SERVER_MODE="release" +RUN_MODE="standard" +PANEL_DB_TYPE="postgresql" +PANEL_DB_HOST="" +PANEL_DB_NAME="sub2api" +PANEL_DB_USER="sub2api" +PANEL_DB_PORT="5432" +PANEL_DB_USER_PASSWORD="" +REDIS_HOST="" +REDIS_PORT="6379" +PANEL_REDIS_ROOT_PASSWORD="" +REDIS_DB="0" +ADMIN_EMAIL="admin@sub2api.local" +ADMIN_PASSWORD="" +JWT_SECRET="" +TOTP_ENCRYPTION_KEY="" +UPDATE_PROXY_URL="" +APP_DATA_DIR_1="./data" diff --git a/apps/sub2api/0.2.15/data.yml b/apps/sub2api/0.2.15/data.yml new file mode 100644 index 000000000..fa85840e1 --- /dev/null +++ b/apps/sub2api/0.2.15/data.yml @@ -0,0 +1,348 @@ +additionalProperties: + formFields: + - child: + default: "" + envKey: PANEL_DB_HOST + labelEn: Database Service + labelZh: 数据库服务 + label: + en: Database Service + zh: 数据库服务 + zh-Hant: 資料庫服務 + ja: データベースサービス + ko: 데이터베이스 서비스 + ru: Служба базы данных + ms: Perkhidmatan Pangkalan Data + pt-br: Serviço de Banco de Dados + required: true + type: service + default: postgresql + envKey: PANEL_DB_TYPE + labelEn: Database + labelZh: 数据库服务 + label: + en: Database + zh: 数据库服务 + zh-Hant: 資料庫服務 + ja: データベース + ko: 데이터베이스 + ru: База данных + ms: Pangkalan Data + pt-br: Banco de dados + required: true + type: apps + values: + - label: PostgreSQL + value: postgresql + - default: sub2api + edit: true + envKey: PANEL_DB_NAME + labelEn: Database Name + labelZh: 数据库名 + label: + en: Database Name + zh: 数据库名 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: sub2api + edit: true + envKey: PANEL_DB_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Nama pengguna pangkalan data + pt-br: Usuário do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: 5432 + edit: true + envKey: PANEL_DB_PORT + labelEn: Database Port + labelZh: 数据库端口 + label: + en: Database Port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco de dados + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: PANEL_DB_USER_PASSWORD + labelEn: Database Password + labelZh: 数据库用户密码 + label: + en: Database Password + zh: 数据库用户密码 + zh-Hant: 資料庫使用者密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + random: true + required: true + rule: paramComplexity + type: password + - default: "" + edit: true + envKey: REDIS_HOST + key: redis + labelEn: Redis Service + labelZh: Redis服务 + label: + en: Redis Service + zh: Redis服务 + zh-Hant: Redis 服務 + ja: Redis サービス + ko: Redis 서비스 + ru: Служба Redis + ms: Perkhidmatan Redis + pt-br: Serviço Redis + required: true + type: service + - default: 6379 + edit: true + envKey: REDIS_PORT + labelEn: Redis Service Port + labelZh: Redis服务端口 + label: + en: Redis Service Port + zh: Redis服务端口 + zh-Hant: Redis 服務連接埠 + ja: Redis サービスポート + ko: Redis 서비스 포트 + ru: Порт службы Redis + ms: Port Perkhidmatan Redis + pt-br: Porta do serviço Redis + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: PANEL_REDIS_ROOT_PASSWORD + labelEn: Redis Password + labelZh: Redis 密码 + label: + en: Redis Password + zh: Redis 密码 + zh-Hant: Redis 密碼 + ja: Redis パスワード + ko: Redis 비밀번호 + ru: Пароль Redis + ms: Kata laluan Redis + pt-br: Senha do Redis + random: true + required: true + type: password + - default: 0 + edit: true + envKey: REDIS_DB + labelEn: Redis Database + labelZh: Redis 数据库 + label: + en: Redis Database + zh: Redis 数据库 + zh-Hant: Redis 資料庫 + ja: Redis データベース + ko: Redis 데이터베이스 + ru: База данных Redis + ms: Pangkalan Data Redis + pt-br: Banco de dados Redis + required: true + type: number + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 访问端口 + label: + en: Web Port + zh: Web 访问端口 + zh-Hant: Web 訪問埠 + ja: Webポート + ko: 웹 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + required: true + rule: paramPort + type: number + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon masa + pt-br: Fuso horário + required: true + type: text + - default: release + edit: true + envKey: SERVER_MODE + labelEn: Server Mode + labelZh: 服务模式 + label: + en: Server Mode + zh: 服务模式 + zh-Hant: 服務模式 + ja: サーバーモード + ko: 서버 모드 + ru: Режим сервера + ms: Mod pelayan + pt-br: Modo do servidor + required: true + type: select + values: + - label: release + value: release + - label: debug + value: debug + - default: standard + edit: true + envKey: RUN_MODE + labelEn: Run Mode + labelZh: 运行模式 + label: + en: Run Mode + zh: 运行模式 + zh-Hant: 運行模式 + ja: 実行モード + ko: 실행 모드 + ru: Режим работы + ms: Mod jalanan + pt-br: Modo de execução + required: true + type: select + values: + - label: standard + value: standard + - label: simple + value: simple + - default: admin@sub2api.local + edit: true + envKey: ADMIN_EMAIL + labelEn: Admin Email + labelZh: 管理员邮箱 + label: + en: Admin Email + zh: 管理员邮箱 + zh-Hant: 管理員郵箱 + ja: 管理者メール + ko: 관리자 이메일 + ru: Email администратора + ms: E-mel pentadbir + pt-br: E-mail do administrador + required: true + type: text + - default: "" + edit: true + envKey: ADMIN_PASSWORD + labelEn: Admin Password + labelZh: 管理员密码 + label: + en: Admin Password + zh: 管理员密码 + zh-Hant: 管理員密碼 + ja: 管理者パスワード + ko: 관리자 비밀번호 + ru: Пароль администратора + ms: Kata laluan pentadbir + pt-br: Senha do administrador + random: true + required: false + rule: paramComplexity + type: password + - default: "" + edit: true + envKey: JWT_SECRET + labelEn: JWT Secret (blank to auto-generate) + labelZh: JWT 密钥(留空自动生成) + label: + en: JWT Secret (blank to auto-generate) + zh: JWT 密钥(留空自动生成) + zh-Hant: JWT 金鑰(留空自動產生) + ja: JWT シークレット(空欄で自動生成) + ko: JWT 시크릿(비워 두면 자동 생성) + ru: JWT-секрет (пусто = создать автоматически) + ms: Rahsia JWT (kosong untuk jana automatik) + pt-br: Segredo JWT (vazio para gerar automaticamente) + required: false + type: password + - default: "" + edit: true + envKey: TOTP_ENCRYPTION_KEY + labelEn: TOTP Encryption Key (blank to auto-generate) + labelZh: TOTP 加密密钥(留空自动生成) + label: + en: TOTP Encryption Key (blank to auto-generate) + zh: TOTP 加密密钥(留空自动生成) + zh-Hant: TOTP 加密金鑰(留空自動產生) + ja: TOTP 暗号化キー(空欄で自動生成) + ko: TOTP 암호화 키(비워 두면 자동 생성) + ru: Ключ TOTP (пусто = создать автоматически) + ms: Kunci TOTP (kosong untuk jana automatik) + pt-br: Chave TOTP (vazia para gerar automaticamente) + required: false + type: password + - default: "" + edit: true + envKey: UPDATE_PROXY_URL + labelEn: Update Proxy URL + labelZh: 更新代理地址 + label: + en: Update Proxy URL + zh: 更新代理地址 + zh-Hant: 更新代理地址 + ja: 更新プロキシ URL + ko: 업데이트 프록시 URL + ru: URL прокси для обновлений + ms: URL proksi kemas kini + pt-br: URL do proxy de atualização + required: false + rule: paramExtUrl + type: text + - default: ./data + edit: true + envKey: APP_DATA_DIR_1 + labelEn: App Data Directory + labelZh: 应用数据目录 + label: + en: App Data Directory + zh: 应用数据目录 + zh-Hant: 應用資料目錄 + ja: アプリデータディレクトリ + ko: 앱 데이터 디렉터리 + ru: Каталог данных приложения + ms: Direktori data aplikasi + pt-br: Diretório de dados da aplicação + required: true + type: text diff --git a/apps/sub2api/0.2.15/data/.gitkeep b/apps/sub2api/0.2.15/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/sub2api/0.2.15/docker-compose.yml b/apps/sub2api/0.2.15/docker-compose.yml new file mode 100644 index 000000000..0b016188c --- /dev/null +++ b/apps/sub2api/0.2.15/docker-compose.yml @@ -0,0 +1,46 @@ +services: + sub2api: + image: "ghcr.io/wei-shaw/sub2api:0.2.15" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + environment: + - AUTO_SETUP=true + - SERVER_HOST=0.0.0.0 + - SERVER_PORT=8080 + - SERVER_MODE=${SERVER_MODE} + - RUN_MODE=${RUN_MODE} + - DATABASE_HOST=${PANEL_DB_HOST} + - DATABASE_PORT=${PANEL_DB_PORT} + - DATABASE_USER=${PANEL_DB_USER} + - DATABASE_PASSWORD=${PANEL_DB_USER_PASSWORD} + - DATABASE_DBNAME=${PANEL_DB_NAME} + - DATABASE_SSLMODE=disable + - REDIS_HOST=${REDIS_HOST} + - REDIS_PORT=${REDIS_PORT} + - REDIS_PASSWORD=${PANEL_REDIS_ROOT_PASSWORD} + - REDIS_DB=${REDIS_DB} + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_PASSWORD=${ADMIN_PASSWORD:-} + - JWT_SECRET=${JWT_SECRET} + - JWT_EXPIRE_HOUR=24 + - TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY} + - TZ=${TZ} + - UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-} + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - "${APP_DATA_DIR_1}:/app/data" + labels: + createdBy: "Apps" + healthcheck: + test: ["CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +networks: + 1panel-network: + external: true diff --git a/apps/sub2api/0.2.15/scripts/init.sh b/apps/sub2api/0.2.15/scripts/init.sh new file mode 100755 index 000000000..d90eb5906 --- /dev/null +++ b/apps/sub2api/0.2.15/scripts/init.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -euo pipefail + +mkdir -p "${APP_DATA_DIR_1:-./data}" +touch .env +chmod 600 .env + +generate_secret() { + if command -v openssl >/dev/null 2>&1; then + openssl rand -hex 32 + else + od -An -N32 -tx1 /dev/urandom | tr -d ' \n' + fi +} + +read_env_value() { + key="$1" + [ -f .env ] || return 0 + sed -n "s/^${key}=//p" .env | tail -n 1 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" +} + +write_env_value() { + key="$1" + value="$2" + touch .env + tmp_file="$(mktemp .env.XXXXXX)" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ "^" key "=" { print key "=" value; updated = 1; next } + { print } + END { if (!updated) print key "=" value } + ' .env >"$tmp_file" + chmod 600 "$tmp_file" + mv "$tmp_file" .env +} + +jwt_secret="${JWT_SECRET:-$(read_env_value JWT_SECRET)}" +if [ "${#jwt_secret}" -lt 32 ]; then + write_env_value JWT_SECRET "$(generate_secret)" +fi + +totp_key="${TOTP_ENCRYPTION_KEY:-$(read_env_value TOTP_ENCRYPTION_KEY)}" +if [ -z "$totp_key" ]; then + write_env_value TOTP_ENCRYPTION_KEY "$(generate_secret)" +fi diff --git a/apps/sub2api/0.2.15/scripts/uninstall.sh b/apps/sub2api/0.2.15/scripts/uninstall.sh new file mode 100755 index 000000000..20a0bf525 --- /dev/null +++ b/apps/sub2api/0.2.15/scripts/uninstall.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -euo pipefail + +# 当前适配只卸载 Sub2API 容器本身。 +# 数据库与 Redis 为 1Panel 依赖应用,不在此处删除。 +docker-compose down --volumes diff --git a/apps/sub2api/0.2.15/scripts/upgrade.sh b/apps/sub2api/0.2.15/scripts/upgrade.sh new file mode 100755 index 000000000..d90eb5906 --- /dev/null +++ b/apps/sub2api/0.2.15/scripts/upgrade.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -euo pipefail + +mkdir -p "${APP_DATA_DIR_1:-./data}" +touch .env +chmod 600 .env + +generate_secret() { + if command -v openssl >/dev/null 2>&1; then + openssl rand -hex 32 + else + od -An -N32 -tx1 /dev/urandom | tr -d ' \n' + fi +} + +read_env_value() { + key="$1" + [ -f .env ] || return 0 + sed -n "s/^${key}=//p" .env | tail -n 1 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" +} + +write_env_value() { + key="$1" + value="$2" + touch .env + tmp_file="$(mktemp .env.XXXXXX)" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ "^" key "=" { print key "=" value; updated = 1; next } + { print } + END { if (!updated) print key "=" value } + ' .env >"$tmp_file" + chmod 600 "$tmp_file" + mv "$tmp_file" .env +} + +jwt_secret="${JWT_SECRET:-$(read_env_value JWT_SECRET)}" +if [ "${#jwt_secret}" -lt 32 ]; then + write_env_value JWT_SECRET "$(generate_secret)" +fi + +totp_key="${TOTP_ENCRYPTION_KEY:-$(read_env_value TOTP_ENCRYPTION_KEY)}" +if [ -z "$totp_key" ]; then + write_env_value TOTP_ENCRYPTION_KEY "$(generate_secret)" +fi diff --git a/apps/sublime-text-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/sublime-text-kasm/1.19.0-rolling-weekly/docker-compose.yml index 4912f2d7e..037828c60 100644 --- a/apps/sublime-text-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/sublime-text-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: sublime-text-kasm: - image: "kasmweb/sublime-text:1.19.0-rolling-weekly@sha256:184edb41c147b4e0634e6522a65569dc0074f703c3fbb528ac687816662a8f39" + image: "kasmweb/sublime-text:1.19.0-rolling-weekly@sha256:d2b88c165c2142ca6fd5710d4a599943876ef7ef6a70d6003098cf3ce840971c" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/synapse/1.162.0/.env.sample b/apps/synapse/1.163.1/.env.sample similarity index 100% rename from apps/synapse/1.162.0/.env.sample rename to apps/synapse/1.163.1/.env.sample diff --git a/apps/synapse/1.162.0/data.yml b/apps/synapse/1.163.1/data.yml similarity index 100% rename from apps/synapse/1.162.0/data.yml rename to apps/synapse/1.163.1/data.yml diff --git a/apps/synapse/1.162.0/docker-compose.yml b/apps/synapse/1.163.1/docker-compose.yml similarity index 93% rename from apps/synapse/1.162.0/docker-compose.yml rename to apps/synapse/1.163.1/docker-compose.yml index e0604c4fd..79eb91bf2 100644 --- a/apps/synapse/1.162.0/docker-compose.yml +++ b/apps/synapse/1.163.1/docker-compose.yml @@ -12,7 +12,7 @@ services: - TZ=Asia/Shanghai - UID=1000 - GID=1000 - image: matrixdotorg/synapse:v1.162.0 + image: matrixdotorg/synapse:v1.163.1 logging: options: max-size: "10m" diff --git a/apps/syncclipboard-server/3.3.1/docker-compose.yml b/apps/syncclipboard-server/3.3.1/docker-compose.yml index 273a2dc5d..eb736893b 100644 --- a/apps/syncclipboard-server/3.3.1/docker-compose.yml +++ b/apps/syncclipboard-server/3.3.1/docker-compose.yml @@ -1,6 +1,6 @@ services: syncclipboard-server: - image: jericx/syncclipboard-server:v3.3.1 + image: jericx/syncclipboard-server:v3.3.3 container_name: ${CONTAINER_NAME} restart: always networks: @@ -8,7 +8,7 @@ services: ports: - "${PANEL_APP_PORT_HTTP}:5033" volumes: - - ${CONFIG_FILE_PATH}:/app/appsettings.json + - ${CONFIG_FILE_PATH}:/app/data/appsettings.json labels: createdBy: "Apps" diff --git a/apps/syncthing/2.1.6/docker-compose.yml b/apps/syncthing/2.1.6/docker-compose.yml index a69811558..f43dbeae8 100644 --- a/apps/syncthing/2.1.6/docker-compose.yml +++ b/apps/syncthing/2.1.6/docker-compose.yml @@ -18,7 +18,7 @@ services: - ${SYNCTHING_PORT}:22000/tcp - ${SYNCTHING_PORT}:22000/udp - ${SYNCTHING_PORT_EXTRA_UDP}:21027/udp - image: "linuxserver/syncthing:2.1.6" + image: "linuxserver/syncthing:2.1.7" labels: createdBy: "Apps" diff --git a/apps/talebook/26.09.23/.env.sample b/apps/talebook/26.10.18/.env.sample similarity index 100% rename from apps/talebook/26.09.23/.env.sample rename to apps/talebook/26.10.18/.env.sample diff --git a/apps/talebook/26.09.23/data.yml b/apps/talebook/26.10.18/data.yml similarity index 100% rename from apps/talebook/26.09.23/data.yml rename to apps/talebook/26.10.18/data.yml diff --git a/apps/talebook/26.09.23/docker-compose.yml b/apps/talebook/26.10.18/docker-compose.yml similarity index 88% rename from apps/talebook/26.09.23/docker-compose.yml rename to apps/talebook/26.10.18/docker-compose.yml index 4b269bd16..4a5310df8 100644 --- a/apps/talebook/26.09.23/docker-compose.yml +++ b/apps/talebook/26.10.18/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:80" volumes: - "${DATA_PATH}:/data" - image: talebook/talebook:v26.09.23 + image: talebook/talebook:v26.10.18 labels: createdBy: "Apps" diff --git a/apps/telegram-linuxserver/7.2.9/docker-compose.yml b/apps/telegram-linuxserver/7.2.9/docker-compose.yml index 2091cf9b1..6ef2f488e 100644 --- a/apps/telegram-linuxserver/7.2.9/docker-compose.yml +++ b/apps/telegram-linuxserver/7.2.9/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: telegram: - image: "linuxserver/telegram:7.2.9" + image: "linuxserver/telegram:7.3.2" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/this-week-in-past/3.0.3/docker-compose.yml b/apps/this-week-in-past/3.0.3/docker-compose.yml index e4c591381..a81f9ae8f 100644 --- a/apps/this-week-in-past/3.0.3/docker-compose.yml +++ b/apps/this-week-in-past/3.0.3/docker-compose.yml @@ -1,6 +1,6 @@ services: this-week-in-past: - image: "rouhim/this-week-in-past:3.0.3@sha256:9dc78637bf7f07e941a19b7948a4ff111e521a772188ca85cb56ef44fa2d0402" + image: "rouhim/this-week-in-past:4.0.4@sha256:52ceda41a5fe346cb2160763725f87a9801c5fb94c56962ded001caede9eee2b" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/thunderbird-linuxserver/1140.16.0/docker-compose.yml b/apps/thunderbird-linuxserver/1140.16.0/docker-compose.yml index 795c903b3..e5d9ec55c 100644 --- a/apps/thunderbird-linuxserver/1140.16.0/docker-compose.yml +++ b/apps/thunderbird-linuxserver/1140.16.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: thunderbird: - image: "linuxserver/thunderbird:1140.16.0" + image: "linuxserver/thunderbird:1140.17.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/tianji/1.33.9/.env.sample b/apps/tianji/1.33.11/.env.sample similarity index 100% rename from apps/tianji/1.33.9/.env.sample rename to apps/tianji/1.33.11/.env.sample diff --git a/apps/tianji/1.33.9/data.yml b/apps/tianji/1.33.11/data.yml similarity index 100% rename from apps/tianji/1.33.9/data.yml rename to apps/tianji/1.33.11/data.yml diff --git a/apps/tianji/1.33.9/docker-compose.yml b/apps/tianji/1.33.11/docker-compose.yml similarity index 92% rename from apps/tianji/1.33.9/docker-compose.yml rename to apps/tianji/1.33.11/docker-compose.yml index 9abf870b7..5619f24f8 100644 --- a/apps/tianji/1.33.9/docker-compose.yml +++ b/apps/tianji/1.33.11/docker-compose.yml @@ -2,7 +2,7 @@ services: tianji: container_name: ${CONTAINER_NAME} restart: always - image: moonrailgun/tianji:1.33.9 + image: moonrailgun/tianji:1.33.11 networks: - 1panel-network ports: diff --git a/apps/tianji/1.33.11/scripts/init.sh b/apps/tianji/1.33.11/scripts/init.sh new file mode 100755 index 000000000..2a53d75f3 --- /dev/null +++ b/apps/tianji/1.33.11/scripts/init.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/apps/tianji/1.33.11/scripts/uninstall.sh b/apps/tianji/1.33.11/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/tianji/1.33.11/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/tianji/1.33.9/scripts/upgrade.sh b/apps/tianji/1.33.11/scripts/upgrade.sh similarity index 100% rename from apps/tianji/1.33.9/scripts/upgrade.sh rename to apps/tianji/1.33.11/scripts/upgrade.sh diff --git a/apps/tianji/1.33.8/docker-compose.yml b/apps/tianji/1.33.8/docker-compose.yml index 33cfc4d51..5619f24f8 100644 --- a/apps/tianji/1.33.8/docker-compose.yml +++ b/apps/tianji/1.33.8/docker-compose.yml @@ -2,7 +2,7 @@ services: tianji: container_name: ${CONTAINER_NAME} restart: always - image: moonrailgun/tianji:1.33.8 + image: moonrailgun/tianji:1.33.11 networks: - 1panel-network ports: diff --git a/apps/ting-reader/2.0.5/docker-compose.yml b/apps/ting-reader/2.0.5/docker-compose.yml index c26e6e13c..42aa22cb9 100644 --- a/apps/ting-reader/2.0.5/docker-compose.yml +++ b/apps/ting-reader/2.0.5/docker-compose.yml @@ -18,7 +18,7 @@ services: - TEMP_DIR=/app/temp - DATA_DIR=/app/data - STORAGE_DIR=/app/storage - image: dqsq2e2/ting-reader:2.0.5 + image: dqsq2e2/ting-reader:2.0.6 labels: createdBy: "Apps" diff --git a/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml b/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml index 6c170428c..cb0772584 100644 --- a/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml +++ b/apps/ungoogled-chromium-linuxserver/154.0.8037/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: ungoogled-chromium: - image: "linuxserver/ungoogled-chromium:154.0.8037" + image: "linuxserver/ungoogled-chromium:155.0.8059" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/unleash/8.1.0/.env.sample b/apps/unleash/8.2.1/.env.sample similarity index 100% rename from apps/unleash/8.1.0/.env.sample rename to apps/unleash/8.2.1/.env.sample diff --git a/apps/unleash/8.1.0/data.yml b/apps/unleash/8.2.1/data.yml similarity index 100% rename from apps/unleash/8.1.0/data.yml rename to apps/unleash/8.2.1/data.yml diff --git a/apps/unleash/8.1.0/docker-compose.yml b/apps/unleash/8.2.1/docker-compose.yml similarity index 95% rename from apps/unleash/8.1.0/docker-compose.yml rename to apps/unleash/8.2.1/docker-compose.yml index ce4972eb2..abfeef1a0 100644 --- a/apps/unleash/8.1.0/docker-compose.yml +++ b/apps/unleash/8.2.1/docker-compose.yml @@ -1,6 +1,6 @@ services: unleash: - image: "unleashorg/unleash-server:8.1.0" + image: "unleashorg/unleash-server:8.2.1" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/unleash/8.2.1/scripts/init.sh b/apps/unleash/8.2.1/scripts/init.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/unleash/8.2.1/scripts/init.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/unleash/8.1.0/scripts/uninstall.sh b/apps/unleash/8.2.1/scripts/uninstall.sh similarity index 100% rename from apps/unleash/8.1.0/scripts/uninstall.sh rename to apps/unleash/8.2.1/scripts/uninstall.sh diff --git a/apps/unleash/8.2.1/scripts/upgrade.sh b/apps/unleash/8.2.1/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/unleash/8.2.1/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/v2raya/2.5.9/docker-compose.yml b/apps/v2raya/2.5.9/docker-compose.yml index 768875aaf..45696b8b2 100644 --- a/apps/v2raya/2.5.9/docker-compose.yml +++ b/apps/v2raya/2.5.9/docker-compose.yml @@ -12,6 +12,6 @@ services: - "${DATA_PATH}/v2raya:/etc/v2raya" - /lib/modules:/lib/modules:ro - /etc/resolv.conf:/etc/resolv.conf - image: mzz2017/v2raya:v2.5.9 + image: mzz2017/v2raya:v2.5.10 labels: createdBy: "Apps" diff --git a/apps/verdaccio/6.10.5/docker-compose.yml b/apps/verdaccio/6.10.5/docker-compose.yml index 4dc02d85f..3b223942a 100644 --- a/apps/verdaccio/6.10.5/docker-compose.yml +++ b/apps/verdaccio/6.10.5/docker-compose.yml @@ -1,6 +1,6 @@ services: verdaccio: - image: "verdaccio/verdaccio:6.10.5" + image: "verdaccio/verdaccio:6.10.7" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/vikunja/2.7.0/docker-compose.yml b/apps/vikunja/2.7.0/docker-compose.yml index 8efa53783..055ec3972 100644 --- a/apps/vikunja/2.7.0/docker-compose.yml +++ b/apps/vikunja/2.7.0/docker-compose.yml @@ -1,6 +1,6 @@ services: vikunja: - image: "vikunja/vikunja:2.7.0" + image: "vikunja/vikunja:2.7.1" container_name: ${CONTAINER_NAME} init: true restart: unless-stopped diff --git a/apps/vivaldi-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/vivaldi-kasm/1.19.0-rolling-weekly/docker-compose.yml index 2a8b9bc8e..4616111a5 100644 --- a/apps/vivaldi-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/vivaldi-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: vivaldi-kasm: - image: "kasmweb/vivaldi:1.19.0-rolling-weekly@sha256:334f95028356a5f767a88a69af9bbd98af78c08cdbff5c9f4e485af185294581" + image: "kasmweb/vivaldi:1.19.0-rolling-weekly@sha256:188bb7f5df8e237915c1c34c72598ea249b2dfe528afec09f4f10ecef9954726" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/vlc-kasm/1.19.0-rolling-weekly/docker-compose.yml b/apps/vlc-kasm/1.19.0-rolling-weekly/docker-compose.yml index a2f943fc4..98236b69b 100644 --- a/apps/vlc-kasm/1.19.0-rolling-weekly/docker-compose.yml +++ b/apps/vlc-kasm/1.19.0-rolling-weekly/docker-compose.yml @@ -1,6 +1,6 @@ services: vlc-kasm: - image: "kasmweb/vlc:1.19.0-rolling-weekly@sha256:893ef8722bc8c21bb832a522857a4a994e9fc5721d17ce855e10818db0b16bdf" + image: "kasmweb/vlc:1.19.0-rolling-weekly@sha256:3c3ec980953821c9cb2b68776dfbde19a9f26d7385c3a371c5407f94b5e630dc" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/vlc-linuxserver/3.0.23/docker-compose.yml b/apps/vlc-linuxserver/3.0.23/docker-compose.yml index ffde7f888..e4dd60f90 100644 --- a/apps/vlc-linuxserver/3.0.23/docker-compose.yml +++ b/apps/vlc-linuxserver/3.0.23/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: vlc: - image: "linuxserver/vlc:3.0.23" + image: "linuxserver/vlc:3.0.24" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/vocechat/0.5.36-arm64/docker-compose.yml b/apps/vocechat/0.5.36-arm64/docker-compose.yml index 74c0b6b1d..1885ffefb 100644 --- a/apps/vocechat/0.5.36-arm64/docker-compose.yml +++ b/apps/vocechat/0.5.36-arm64/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:3000" volumes: - "${DATA_PATH}:/home/vocechat-server/data" - image: privoce/vocechat-server:v0.5.36-arm64 + image: privoce/vocechat-server:v0.5.37-arm64 labels: createdBy: "Apps" diff --git a/apps/vocechat/0.5.36/docker-compose.yml b/apps/vocechat/0.5.36/docker-compose.yml index 8830e2009..c38d12d24 100644 --- a/apps/vocechat/0.5.36/docker-compose.yml +++ b/apps/vocechat/0.5.36/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PANEL_APP_PORT_HTTP}:3000" volumes: - "${DATA_PATH}:/home/vocechat-server/data" - image: privoce/vocechat-server:v0.5.36 + image: privoce/vocechat-server:v0.5.37 labels: createdBy: "Apps" diff --git a/apps/vscode-linuxserver/1.140.0/docker-compose.yml b/apps/vscode-linuxserver/1.140.0/docker-compose.yml index a3c37a791..25e188a26 100644 --- a/apps/vscode-linuxserver/1.140.0/docker-compose.yml +++ b/apps/vscode-linuxserver/1.140.0/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: vscode: - image: "linuxserver/vscode:1.140.0" + image: "linuxserver/vscode:1.142.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/vscodium-linuxserver/1.135.06055/docker-compose.yml b/apps/vscodium-linuxserver/1.135.06055/docker-compose.yml index acceae1a3..23c310dd7 100644 --- a/apps/vscodium-linuxserver/1.135.06055/docker-compose.yml +++ b/apps/vscodium-linuxserver/1.135.06055/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: vscodium: - image: "linuxserver/vscodium:1.135.06055" + image: "linuxserver/vscodium:1.140.06880" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/vscodium-web-linuxserver/1.135.06055/docker-compose.yml b/apps/vscodium-web-linuxserver/1.135.06055/docker-compose.yml index 127a047b9..d3fcfc4d4 100644 --- a/apps/vscodium-web-linuxserver/1.135.06055/docker-compose.yml +++ b/apps/vscodium-web-linuxserver/1.135.06055/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: vscodium-web: - image: "linuxserver/vscodium-web:1.135.06055" + image: "linuxserver/vscodium-web:1.140.06880" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/wakapi/2.18.1/docker-compose.yml b/apps/wakapi/2.18.1/docker-compose.yml index 4b272a065..7e378cdf7 100644 --- a/apps/wakapi/2.18.1/docker-compose.yml +++ b/apps/wakapi/2.18.1/docker-compose.yml @@ -1,6 +1,6 @@ services: wakapi: - image: "n1try/wakapi:2.18.1" + image: "n1try/wakapi:2.18.2" container_name: ${CONTAINER_NAME} init: true restart: unless-stopped diff --git a/apps/wakapi/2.18.2/.env.sample b/apps/wakapi/2.18.2/.env.sample new file mode 100644 index 000000000..d29ebede9 --- /dev/null +++ b/apps/wakapi/2.18.2/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_HTTP=3000 +WAKAPI_PASSWORD_SALT=replace-with-a-random-32-character-salt +WAKAPI_INSECURE_COOKIES=true +WAKAPI_ALLOW_SIGNUP=true +APP_DATA_DIR=./data +CONTAINER_NAME=wakapi diff --git a/apps/wakapi/2.18.2/data.yml b/apps/wakapi/2.18.2/data.yml new file mode 100644 index 000000000..782366078 --- /dev/null +++ b/apps/wakapi/2.18.2/data.yml @@ -0,0 +1,95 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 端口 + label: + en: Web Port + zh: Web 端口 + zh-Hant: Web 連接埠 + ja: Web ポート + ko: Web 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + required: true + rule: paramPort + type: number + - default: change-me + edit: true + envKey: WAKAPI_PASSWORD_SALT + labelEn: Password Salt + labelZh: 密码盐 + label: + en: Password Salt + zh: 密码盐 + zh-Hant: 密碼鹽 + ja: パスワードソルト + ko: 비밀번호 솔트 + ru: Соль паролей + ms: Garam kata laluan + pt-br: Sal de senha + random: true + required: true + rule: paramComplexity + type: password + - default: "true" + edit: true + envKey: WAKAPI_INSECURE_COOKIES + labelEn: Allow Insecure Cookies + labelZh: 允许非安全 Cookie + label: + en: Allow Insecure Cookies + zh: 允许非安全 Cookie + zh-Hant: 允許非安全 Cookie + ja: 非セキュア Cookie を許可 + ko: 보안이 적용되지 않은 Cookie 허용 + ru: Разрешить небезопасные cookie + ms: Benarkan kuki tidak selamat + pt-br: Permitir cookies inseguros + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "true" + edit: true + envKey: WAKAPI_ALLOW_SIGNUP + labelEn: Allow Sign-up + labelZh: 允许注册 + label: + en: Allow Sign-up + zh: 允许注册 + zh-Hant: 允許註冊 + ja: ユーザー登録を許可 + ko: 회원가입 허용 + ru: Разрешить регистрацию + ms: Benarkan pendaftaran + pt-br: Permitir cadastro + required: true + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretório de dados + required: true + type: text diff --git a/apps/wakapi/2.18.2/data/.gitkeep b/apps/wakapi/2.18.2/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/2.18.2/docker-compose.yml b/apps/wakapi/2.18.2/docker-compose.yml new file mode 100644 index 000000000..7e378cdf7 --- /dev/null +++ b/apps/wakapi/2.18.2/docker-compose.yml @@ -0,0 +1,30 @@ +services: + wakapi: + image: "n1try/wakapi:2.18.2" + container_name: ${CONTAINER_NAME} + init: true + restart: unless-stopped + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + environment: + - WAKAPI_DB_TYPE=sqlite3 + - WAKAPI_DB_NAME=/data/wakapi.db + - WAKAPI_PASSWORD_SALT=${WAKAPI_PASSWORD_SALT} + - WAKAPI_INSECURE_COOKIES=${WAKAPI_INSECURE_COOKIES} + - WAKAPI_ALLOW_SIGNUP=${WAKAPI_ALLOW_SIGNUP} + volumes: + - "${APP_DATA_DIR}:/data" + healthcheck: + test: ["CMD", "/app/healthcheck"] + interval: 60s + timeout: 3s + start_period: 120s + retries: 3 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/wakapi/2.18.2/scripts/.gitkeep b/apps/wakapi/2.18.2/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/2.18.2/scripts/init.sh b/apps/wakapi/2.18.2/scripts/init.sh new file mode 100755 index 000000000..2b1b96b3a --- /dev/null +++ b/apps/wakapi/2.18.2/scripts/init.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" +APP_DATA_DIR_RAW="${APP_DATA_DIR:-}" +WAKAPI_UID=65532 +WAKAPI_GID=65532 + +if [[ -z "$APP_DATA_DIR_RAW" && -f "$ENV_FILE" ]]; then + APP_DATA_DIR_RAW="$(grep '^APP_DATA_DIR=' "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)" +fi +APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW:-./data}" + +case "$APP_DATA_DIR_RAW" in + \"*\") + APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\"}" + APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\"}" + ;; + \'*\') + APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\'}" + APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\'}" + ;; +esac + +if [[ -z "$APP_DATA_DIR_RAW" ]]; then + echo "APP_DATA_DIR must not be empty" >&2 + exit 1 +fi + +case "$APP_DATA_DIR_RAW" in + /*) + APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")" + APP_DATA_DIR_SCOPE="absolute" + ;; + *) + APP_DATA_DIR_ABS="$(realpath -m -- "$ROOT_DIR/${APP_DATA_DIR_RAW#./}")" + case "$APP_DATA_DIR_ABS" in + "$ROOT_DIR"/*) ;; + *) + echo "Relative APP_DATA_DIR must stay inside the application directory" >&2 + exit 1 + ;; + esac + APP_DATA_DIR_SCOPE="application" + ;; +esac + +DATA_DIR_EXISTED=false +[[ -d "$APP_DATA_DIR_ABS" ]] && DATA_DIR_EXISTED=true +mkdir -p "$APP_DATA_DIR_ABS" + +if [[ "$APP_DATA_DIR_SCOPE" == "application" || "$DATA_DIR_EXISTED" == "false" ]]; then + chown -R "$WAKAPI_UID:$WAKAPI_GID" "$APP_DATA_DIR_ABS" +else + CURRENT_UID="$(stat -c '%u' "$APP_DATA_DIR_ABS")" + CURRENT_GID="$(stat -c '%g' "$APP_DATA_DIR_ABS")" + if [[ "$CURRENT_UID" != "$WAKAPI_UID" || "$CURRENT_GID" != "$WAKAPI_GID" ]]; then + echo "Existing absolute APP_DATA_DIR must be owned by ${WAKAPI_UID}:${WAKAPI_GID}" >&2 + exit 1 + fi +fi diff --git a/apps/wakapi/2.18.2/scripts/uninstall.sh b/apps/wakapi/2.18.2/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/wakapi/2.18.2/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/wakapi/2.18.2/scripts/upgrade.sh b/apps/wakapi/2.18.2/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/wakapi/2.18.2/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/wallos/5.8.4/.env.sample b/apps/wallos/5.8.4/.env.sample new file mode 100644 index 000000000..97343631a --- /dev/null +++ b/apps/wallos/5.8.4/.env.sample @@ -0,0 +1,3 @@ +CONTAINER_NAME="wallos" +PANEL_APP_PORT_HTTP="40212" +TIME_ZONE="Asia/Shanghai" \ No newline at end of file diff --git a/apps/wallos/5.8.4/data.yml b/apps/wallos/5.8.4/data.yml new file mode 100644 index 000000000..1a628ffcf --- /dev/null +++ b/apps/wallos/5.8.4/data.yml @@ -0,0 +1,35 @@ +additionalProperties: + formFields: + - default: 40212 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: 'Port' + zh: '端口' + zh-Hant: '埠' + ja: 'ポート' + ko: '포트' + ru: 'Порт' + ms: 'Port' + pt-br: 'Porta' + required: true + rule: paramPort + type: number + - default: Asia/Shanghai + edit: true + envKey: TIME_ZONE + labelEn: Time zone + labelZh: 时区 + label: + en: 'Time zone' + zh: '时区' + zh-Hant: '時區' + ja: 'タイムゾーン' + ko: '시간대' + ru: 'Часовой пояс' + ms: 'Zon masa' + pt-br: 'Fuso horário' + required: true + type: text diff --git a/apps/wallos/5.8.4/docker-compose.yml b/apps/wallos/5.8.4/docker-compose.yml new file mode 100644 index 000000000..c1d45edc9 --- /dev/null +++ b/apps/wallos/5.8.4/docker-compose.yml @@ -0,0 +1,20 @@ +services: + wallos: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - ./data/db:/var/www/html/db + - ./data/logos:/var/www/html/images/uploads/logos + environment: + - TZ=${TIME_ZONE} + image: bellamy/wallos:5.8.4 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/wastebin/3.8.0/docker-compose.yml b/apps/wastebin/3.8.0/docker-compose.yml index 981fde186..1a56b08db 100644 --- a/apps/wastebin/3.8.0/docker-compose.yml +++ b/apps/wastebin/3.8.0/docker-compose.yml @@ -1,6 +1,6 @@ services: wastebin: - image: "quxfoo/wastebin:3.8.0" + image: "quxfoo/wastebin:3.9.0" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "10001:10001" diff --git a/apps/wavelog/3.1.0/docker-compose.yml b/apps/wavelog/3.1.0/docker-compose.yml index a9bb43372..0ee06f31a 100644 --- a/apps/wavelog/3.1.0/docker-compose.yml +++ b/apps/wavelog/3.1.0/docker-compose.yml @@ -1,7 +1,7 @@ services: wavelog: container_name: ${CONTAINER_NAME} - image: ghcr.io/wavelog/wavelog:3.2.2 + image: ghcr.io/wavelog/wavelog:3.2.4 environment: - CI_ENV=docker - TZ=${TZ:-Asia/Shanghai} diff --git a/apps/wavelog/3.2.3/docker-compose.yml b/apps/wavelog/3.2.3/docker-compose.yml index 06f2be173..0ee06f31a 100644 --- a/apps/wavelog/3.2.3/docker-compose.yml +++ b/apps/wavelog/3.2.3/docker-compose.yml @@ -1,7 +1,7 @@ services: wavelog: container_name: ${CONTAINER_NAME} - image: ghcr.io/wavelog/wavelog:3.2.3 + image: ghcr.io/wavelog/wavelog:3.2.4 environment: - CI_ENV=docker - TZ=${TZ:-Asia/Shanghai} diff --git a/apps/weaviate/1.40.0/docker-compose.yml b/apps/weaviate/1.40.0/docker-compose.yml index a379c9893..d28d2674f 100644 --- a/apps/weaviate/1.40.0/docker-compose.yml +++ b/apps/weaviate/1.40.0/docker-compose.yml @@ -1,6 +1,6 @@ services: weaviate: - image: "semitechnologies/weaviate:1.40.0" + image: "semitechnologies/weaviate:1.40.2" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/webclaw/0.6.23/docker-compose.yml b/apps/webclaw/0.6.23/docker-compose.yml index 05840a533..dba716618 100644 --- a/apps/webclaw/0.6.23/docker-compose.yml +++ b/apps/webclaw/0.6.23/docker-compose.yml @@ -1,6 +1,6 @@ services: webclaw: - image: "ghcr.io/0xmassi/webclaw:v0.6.23@sha256:8990d38cb24bf986caa2d172bbfabac357adb964da38372334b1cb6aa75d0c3e" + image: "ghcr.io/0xmassi/webclaw:v0.7.0@sha256:2e1a626c58f06ecfc5c3104541806a83ccfaf82736c3a3ab80c21bd8ea4be642" container_name: ${CONTAINER_NAME} restart: unless-stopped command: diff --git a/apps/weblate/latest/docker-compose.yml b/apps/weblate/latest/docker-compose.yml index 29df5f452..5748619a4 100644 --- a/apps/weblate/latest/docker-compose.yml +++ b/apps/weblate/latest/docker-compose.yml @@ -1,6 +1,6 @@ services: cache: - image: "valkey/valkey:9.1.1" + image: "valkey/valkey:9.1.2" container_name: ${CONTAINER_NAME}-cache restart: always read_only: true diff --git a/apps/whodb/0.131.0/docker-compose.yml b/apps/whodb/0.131.0/docker-compose.yml index af644fd64..f0b09cb53 100644 --- a/apps/whodb/0.131.0/docker-compose.yml +++ b/apps/whodb/0.131.0/docker-compose.yml @@ -1,6 +1,6 @@ services: whodb: - image: "clidey/whodb:0.131.0" + image: "clidey/whodb:0.136.0" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/woodpecker/3.18.1/.env.sample b/apps/woodpecker/3.19.0/.env.sample similarity index 100% rename from apps/woodpecker/3.18.1/.env.sample rename to apps/woodpecker/3.19.0/.env.sample diff --git a/apps/woodpecker/3.18.1/data.yml b/apps/woodpecker/3.19.0/data.yml similarity index 100% rename from apps/woodpecker/3.18.1/data.yml rename to apps/woodpecker/3.19.0/data.yml diff --git a/apps/woodpecker/3.18.1/docker-compose.yml b/apps/woodpecker/3.19.0/docker-compose.yml similarity index 96% rename from apps/woodpecker/3.18.1/docker-compose.yml rename to apps/woodpecker/3.19.0/docker-compose.yml index fd7d087db..816c37bc7 100644 --- a/apps/woodpecker/3.18.1/docker-compose.yml +++ b/apps/woodpecker/3.19.0/docker-compose.yml @@ -63,7 +63,7 @@ services: # Postgres #- WOODPECKER_DATABASE_DRIVER=postgres #- WOODPECKER_DATABASE_DATASOURCE=postgres://root:password@1.2.3.4:5432/postgres?sslmode=disable - image: woodpeckerci/woodpecker-server:v3.18.1 + image: woodpeckerci/woodpecker-server:v3.19.0 labels: createdBy: "Apps" @@ -82,7 +82,7 @@ services: environment: - WOODPECKER_SERVER=woodpecker-server:9000 - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET} - image: woodpeckerci/woodpecker-agent:v3.18.1 + image: woodpeckerci/woodpecker-agent:v3.19.0 labels: createdBy: "Apps" diff --git a/apps/workout-tracker/2.9.0/docker-compose.yml b/apps/workout-tracker/2.9.0/docker-compose.yml index 819b686e4..384a22aeb 100644 --- a/apps/workout-tracker/2.9.0/docker-compose.yml +++ b/apps/workout-tracker/2.9.0/docker-compose.yml @@ -1,6 +1,6 @@ services: workout-tracker: - image: "ghcr.io/jovandeginste/workout-tracker:2.9.0@sha256:60a12f51dcf1553ee322b92a689e96cfa541aee5b0961d4ff0e345f4a28107b3" + image: "ghcr.io/jovandeginste/workout-tracker:2.10.1@sha256:7fc2c00ce6a8bc26f6939631d4b988fef395bbd895b6a3847d778deb28488ec8" container_name: ${CONTAINER_NAME} restart: unless-stopped user: "1000:1000" diff --git a/apps/xiaomi-album-syncer/0.19.0/docker-compose.yml b/apps/xiaomi-album-syncer/0.19.0/docker-compose.yml index 00f1fcb3d..565c7b64c 100644 --- a/apps/xiaomi-album-syncer/0.19.0/docker-compose.yml +++ b/apps/xiaomi-album-syncer/0.19.0/docker-compose.yml @@ -1,6 +1,6 @@ services: xiaomi-album-syncer: - image: "coolfan1024/xiaomi-album-syncer:0.19.0" + image: "coolfan1024/xiaomi-album-syncer:0.21.0" container_name: ${CONTAINER_NAME} restart: unless-stopped environment: diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/.env.sample b/apps/yamtrack/0.26.3-redis8-digest-3811787/.env.sample new file mode 100644 index 000000000..19611bfff --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/.env.sample @@ -0,0 +1,7 @@ +CONTAINER_NAME=yamtrack +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=8000 +APP_DATA_DIR=./data +YAMTRACK_SECRET=generate +YAMTRACK_TIMEZONE=UTC +YAMTRACK_REGISTRATION=True diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/data.yml b/apps/yamtrack/0.26.3-redis8-digest-3811787/data.yml new file mode 100644 index 000000000..fdd1771e4 --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/data.yml @@ -0,0 +1,104 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 8000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text + - default: generate + edit: true + envKey: YAMTRACK_SECRET + labelEn: Application Secret + labelZh: 应用密钥 + label: + en: Application Secret + zh: 应用密钥 + zh-Hant: 應用程式金鑰 + ja: アプリケーションシークレット + ko: 애플리케이션 비밀 키 + ru: Секрет приложения + ms: Rahsia aplikasi + pt-br: Segredo do aplicativo + required: true + type: password + - default: UTC + edit: true + envKey: YAMTRACK_TIMEZONE + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horario + required: true + type: text + - default: "True" + edit: true + envKey: YAMTRACK_REGISTRATION + labelEn: Allow Registration + labelZh: 允许用户注册 + label: + en: Allow Registration + zh: 允许用户注册 + zh-Hant: 允許使用者註冊 + ja: ユーザー登録を許可 + ko: 사용자 등록 허용 + ru: Разрешить регистрацию + ms: Benarkan pendaftaran + pt-br: Permitir cadastro + required: true + type: select + values: + - label: "True" + value: "True" + - label: "False" + value: "False" diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/data/.gitkeep b/apps/yamtrack/0.26.3-redis8-digest-3811787/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/docker-compose.yml b/apps/yamtrack/0.26.3-redis8-digest-3811787/docker-compose.yml new file mode 100644 index 000000000..8ef88326b --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/docker-compose.yml @@ -0,0 +1,59 @@ +services: + yamtrack: + image: "ghcr.io/fuzzygrim/yamtrack:0.26.3@sha256:78497b454b2b52d3b1062f6fd238351d714cff0895db4369e49ace36f4622e75" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + depends_on: + yamtrack-redis: + condition: service_healthy + networks: + - 1panel-network + - yamtrack-internal + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:8000" + environment: + - TZ=${YAMTRACK_TIMEZONE} + - SECRET=${YAMTRACK_SECRET} + - REDIS_URL=redis://yamtrack-redis:6379 + - REGISTRATION=${YAMTRACK_REGISTRATION} + - PUID=1000 + - PGID=1000 + - SOCIAL_PROVIDERS= + - SOCIALACCOUNT_ONLY=False + volumes: + - "${APP_DATA_DIR}/yamtrack:/yamtrack/db" + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1:8000/health/ || exit 1"] + interval: 10s + timeout: 10s + retries: 5 + start_period: 60s + security_opt: + - no-new-privileges:true + labels: + createdBy: "Apps" + + yamtrack-redis: + image: "redis:8-alpine@sha256:3811787313eba226a2ef38658c6ccb91cd5e110edc89c37767de373120a0e5a0" + container_name: ${CONTAINER_NAME}-redis + restart: unless-stopped + networks: + - yamtrack-internal + volumes: + - "${APP_DATA_DIR}/redis:/data" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + security_opt: + - no-new-privileges:true + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true + yamtrack-internal: + internal: true diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/init.sh b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/init.sh new file mode 100755 index 000000000..99a782973 --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/init.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value="" + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ROOT_DIR}/.yamtrack-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { + if (!written) print key "=" value + } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" + +data_dir_raw="$(read_env_value APP_DATA_DIR)" +[[ -n "$data_dir_raw" ]] || data_dir_raw="./data" +case "$data_dir_raw" in + /*) fail "APP_DATA_DIR must be relative to the application version directory" ;; + *$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'") fail "APP_DATA_DIR contains unsupported characters" ;; +esac + +data_dir_abs="$(realpath -m -- "${ROOT_DIR}/${data_dir_raw#./}")" +case "$data_dir_abs" in + "${ROOT_DIR}"/*) ;; + *) fail "APP_DATA_DIR must stay inside the application version directory" ;; +esac +[[ "$data_dir_abs" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root" +[[ ! -e "$data_dir_abs" || -d "$data_dir_abs" ]] || fail "APP_DATA_DIR must be a directory" + +timezone="$(read_env_value YAMTRACK_TIMEZONE)" +[[ "$timezone" =~ ^[A-Za-z0-9_+/-]+$ ]] || fail "YAMTRACK_TIMEZONE is invalid" + +registration="$(read_env_value YAMTRACK_REGISTRATION)" +[[ "$registration" == "True" || "$registration" == "False" ]] || fail "YAMTRACK_REGISTRATION must be True or False" + +secret="$(read_env_value YAMTRACK_SECRET)" +if [[ -z "$secret" || "$secret" == "generate" ]]; then + command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate YAMTRACK_SECRET" + secret="$(openssl rand -hex 32)" +fi +[[ ${#secret} -ge 50 ]] || fail "YAMTRACK_SECRET must contain at least 50 characters" +[[ "$secret" =~ ^[A-Za-z0-9._~!@%+=:,/-]+$ ]] || fail "YAMTRACK_SECRET contains unsupported dotenv characters" + +install -d -m 0750 -- "$data_dir_abs" "$data_dir_abs/yamtrack" "$data_dir_abs/redis" +resolved_data_dir="$(realpath -e -- "$data_dir_abs")" +case "$resolved_data_dir" in + "${ROOT_DIR}"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac + +if [[ "$(id -u)" -eq 0 ]]; then + chown 1000:1000 "$data_dir_abs/yamtrack" + chown 999:1000 "$data_dir_abs/redis" +else + [[ "$(stat -c '%u:%g' "$data_dir_abs/yamtrack")" == "1000:1000" ]] || fail "Yamtrack init must run as root to prepare the data directory" + [[ "$(stat -c '%u:%g' "$data_dir_abs/redis")" == "999:1000" ]] || fail "Yamtrack init must run as root to prepare the Redis directory" +fi +chmod 750 "$data_dir_abs/yamtrack" "$data_dir_abs/redis" +set_env_value YAMTRACK_SECRET "$secret" diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/uninstall.sh b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/uninstall.sh new file mode 100755 index 000000000..e1b2df0f6 --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +docker-compose down diff --git a/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/upgrade.sh b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/upgrade.sh new file mode 100755 index 000000000..e73ce2436 --- /dev/null +++ b/apps/yamtrack/0.26.3-redis8-digest-3811787/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +exec "${SCRIPT_DIR}/init.sh" diff --git a/apps/yopass/14.8.0/docker-compose.yml b/apps/yopass/14.8.0/docker-compose.yml index 95dc09947..b3471c00a 100644 --- a/apps/yopass/14.8.0/docker-compose.yml +++ b/apps/yopass/14.8.0/docker-compose.yml @@ -1,6 +1,6 @@ services: yopass: - image: "jhaals/yopass:14.10.0" + image: "jhaals/yopass:16.0.0" container_name: ${CONTAINER_NAME} restart: unless-stopped ports: diff --git a/apps/zen-linuxserver/1.23.1/docker-compose.yml b/apps/zen-linuxserver/1.23.1/docker-compose.yml index ee37ec59e..49c164392 100644 --- a/apps/zen-linuxserver/1.23.1/docker-compose.yml +++ b/apps/zen-linuxserver/1.23.1/docker-compose.yml @@ -3,7 +3,7 @@ networks: external: true services: zen: - image: "linuxserver/zen:1.23.1" + image: "linuxserver/zen:1.23.4" container_name: ${CONTAINER_NAME} restart: always networks: diff --git a/apps/zeroclaw/0.8.5/docker-compose.yml b/apps/zeroclaw/0.8.5/docker-compose.yml index 4f7947857..2b76fd95c 100644 --- a/apps/zeroclaw/0.8.5/docker-compose.yml +++ b/apps/zeroclaw/0.8.5/docker-compose.yml @@ -1,6 +1,6 @@ services: zeroclaw: - image: "ghcr.io/zeroclaw-labs/zeroclaw:v0.8.5" + image: "ghcr.io/zeroclaw-labs/zeroclaw:v0.8.6" container_name: ${CONTAINER_NAME} labels: createdBy: "Apps" diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/.env.sample b/apps/zipline/4.6.5-postgres16-digest-721873c/.env.sample new file mode 100644 index 000000000..845005ffa --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/.env.sample @@ -0,0 +1,9 @@ +CONTAINER_NAME=zipline +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=3000 +POSTGRES_DB=zipline +POSTGRES_USER=zipline +POSTGRES_PASSWORD=generate +CORE_SECRET=generate +FILES_MAX_FILE_SIZE=100mb +APP_DATA_DIR=./data diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/data.yml b/apps/zipline/4.6.5-postgres16-digest-721873c/data.yml new file mode 100644 index 000000000..8f8719194 --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/data.yml @@ -0,0 +1,135 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: 100mb + edit: true + envKey: FILES_MAX_FILE_SIZE + labelEn: Maximum File Size + labelZh: 最大文件大小 + label: + en: Maximum File Size + zh: 最大文件大小 + zh-Hant: 最大檔案大小 + ja: 最大ファイルサイズ + ko: 최대 파일 크기 + ru: Максимальный размер файла + ms: Saiz fail maksimum + pt-br: Tamanho maximo do arquivo + required: true + type: text + - default: zipline + edit: true + envKey: POSTGRES_DB + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: zipline + edit: true + envKey: POSTGRES_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: generate + edit: true + envKey: POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: generate + edit: true + envKey: CORE_SECRET + labelEn: Session Secret + labelZh: 会话密钥 + label: + en: Session Secret + zh: 会话密钥 + zh-Hant: 工作階段密鑰 + ja: セッションシークレット + ko: 세션 비밀 키 + ru: Секрет сеанса + ms: Rahsia sesi + pt-br: Segredo da sessao + required: true + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/docker-compose.yml b/apps/zipline/4.6.5-postgres16-digest-721873c/docker-compose.yml new file mode 100644 index 000000000..a1b337010 --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/docker-compose.yml @@ -0,0 +1,79 @@ +services: + zipline-postgres: + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - zipline-internal + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + read_only: true + tmpfs: + - /tmp:mode=1777,size=64m + - /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + labels: + createdBy: "Apps" + + zipline: + image: ghcr.io/diced/zipline:4.6.5@sha256:bfd5b0f7b5b8b3ed058a81667c78a14a7f997115d8433bec273620ec81be51d4 + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + depends_on: + zipline-postgres: + condition: service_healthy + networks: + - zipline-internal + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:3000" + environment: + - CORE_HOSTNAME=0.0.0.0 + - CORE_PORT=3000 + - CORE_SECRET=${CORE_SECRET} + - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@zipline-postgres:5432/${POSTGRES_DB} + - DATASOURCE_TYPE=local + - DATASOURCE_LOCAL_DIRECTORY=/zipline/uploads + - CORE_TEMP_DIRECTORY=/tmp/zipline + - FILES_MAX_FILE_SIZE=${FILES_MAX_FILE_SIZE} + - FEATURES_HEALTHCHECK=true + volumes: + - "${APP_DATA_DIR}/uploads:/zipline/uploads" + - "${APP_DATA_DIR}/public:/zipline/public" + - "${APP_DATA_DIR}/themes:/zipline/themes" + read_only: true + tmpfs: + - /tmp:mode=1777,size=256m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/healthcheck"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + zipline-internal: + internal: true + 1panel-network: + external: true diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/credentials.sh b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/credentials.sh new file mode 100644 index 000000000..f0d6b6cf2 --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/credentials.sh @@ -0,0 +1,290 @@ +#!/usr/bin/env bash +set -euo pipefail + +ZIPLINE_STORED_POSTGRES_PASSWORD="" +ZIPLINE_STORED_CORE_SECRET="" +ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="" +ZIPLINE_SNAPSHOT_CORE_SECRET="" + +zipline_fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +zipline_read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" ' + index($0, key "=") == 1 { + count++ + value = substr($0, length(key) + 2) + } + END { + if (count != 1) exit 1 + print value + } + ' "$file")" || return 1 + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +zipline_resolve_data_dir() { + local root_dir="$1" + local raw="$2" + local clean current resolved part + local -a parts=() + [[ -n "$raw" && "$raw" != /* ]] || zipline_fail "APP_DATA_DIR must be a non-empty relative path" + [[ ! "$raw" =~ [[:cntrl:]] ]] || zipline_fail "APP_DATA_DIR contains control characters" + case "$raw" in + *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + zipline_fail "APP_DATA_DIR contains unsupported characters" + ;; + esac + clean="${raw#./}" + [[ -n "$clean" ]] || zipline_fail "APP_DATA_DIR must not resolve to the version root" + IFS=/ read -r -a parts <<< "$clean" + current="$root_dir" + for part in "${parts[@]}"; do + [[ -n "$part" && "$part" != . && "$part" != .. ]] || zipline_fail "APP_DATA_DIR contains traversal" + current="$current/$part" + [[ ! -L "$current" ]] || zipline_fail "APP_DATA_DIR must not contain symbolic-link components" + done + resolved="$(realpath -m -- "$root_dir/$clean")" || zipline_fail "APP_DATA_DIR could not be resolved" + case "$resolved" in + "$root_dir"/*) ;; + *) zipline_fail "APP_DATA_DIR must stay inside the application version directory" ;; + esac + printf '%s\n' "$resolved" +} + +zipline_validate_postgres_password() { + [[ "$1" =~ ^[A-Za-z0-9]{32,128}$ ]] +} + +zipline_validate_core_secret() { + [[ "$1" =~ ^[A-Fa-f0-9]{64,128}$ ]] +} + +zipline_generate_postgres_password() { + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge 48 ]] || zipline_fail "unable to generate sufficient database password material" + printf '%s\n' "${material:0:48}" +} + +zipline_write_env_credentials() { + local env_file="$1" + local postgres_password="$2" + local core_secret="$3" + local temp_file + temp_file="$(mktemp "${env_file}.zipline-tmp.XXXXXX")" || zipline_fail "unable to stage Zipline environment update" + if ! ZIPLINE_DB_PASSWORD="$postgres_password" ZIPLINE_CORE_SECRET="$core_secret" awk ' + BEGIN { + db_password = ENVIRON["ZIPLINE_DB_PASSWORD"] + core_secret = ENVIRON["ZIPLINE_CORE_SECRET"] + } + /^POSTGRES_PASSWORD=/ { + if (!db_written++) print "POSTGRES_PASSWORD=" db_password + next + } + /^CORE_SECRET=/ { + if (!core_written++) print "CORE_SECRET=" core_secret + next + } + { print } + END { + if (!db_written) print "POSTGRES_PASSWORD=" db_password + if (!core_written) print "CORE_SECRET=" core_secret + } + ' "$env_file" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to render Zipline environment update" + fi + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$env_file" + chmod 600 "$env_file" +} + +zipline_write_secret_state() { + local data_dir="$1" + local postgres_password="$2" + local core_secret="$3" + local secret_file="$data_dir/.zipline-secrets" + local temp_file + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "Zipline data directory is unavailable" + [[ ! -L "$secret_file" ]] || zipline_fail "Zipline secret state must not be a symbolic link" + temp_file="$(mktemp "${secret_file}.tmp.XXXXXX")" || zipline_fail "unable to stage Zipline secret state" + if ! printf 'POSTGRES_PASSWORD=%s\nCORE_SECRET=%s\n' "$postgres_password" "$core_secret" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to write Zipline secret state" + fi + chmod 600 "$temp_file" + chown 0:0 -- "$temp_file" + mv -f -- "$temp_file" "$secret_file" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_load_secret_state() { + local data_dir="$1" + local secret_file="$data_dir/.zipline-secrets" + [[ -e "$secret_file" || -L "$secret_file" ]] || return 1 + [[ -f "$secret_file" && ! -L "$secret_file" ]] || zipline_fail "Zipline secret state is not a regular file" + ZIPLINE_STORED_POSTGRES_PASSWORD="$(zipline_read_env_value "$secret_file" POSTGRES_PASSWORD)" || + zipline_fail "Zipline secret state is incomplete" + ZIPLINE_STORED_CORE_SECRET="$(zipline_read_env_value "$secret_file" CORE_SECRET)" || + zipline_fail "Zipline secret state is incomplete" + zipline_validate_postgres_password "$ZIPLINE_STORED_POSTGRES_PASSWORD" || + zipline_fail "Zipline secret state has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_STORED_CORE_SECRET" || + zipline_fail "Zipline secret state has an invalid session secret" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_init_credentials() { + local env_file="$1" + local data_dir="$2" + local postgres_password core_secret stored + local postgres_default=false core_default=false + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + postgres_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || + zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || + zipline_fail "CORE_SECRET is missing from Zipline .env" + [[ -n "$postgres_password" && "$postgres_password" != generate ]] || postgres_default=true + [[ -n "$core_secret" && "$core_secret" != generate ]] || core_default=true + + if zipline_load_secret_state "$data_dir"; then + if [[ "$postgres_default" == true ]]; then + postgres_password="$ZIPLINE_STORED_POSTGRES_PASSWORD" + elif [[ "$postgres_password" != "$ZIPLINE_STORED_POSTGRES_PASSWORD" ]]; then + zipline_fail "POSTGRES_PASSWORD cannot change while Zipline data is retained" + fi + if [[ "$core_default" == true ]]; then + core_secret="$ZIPLINE_STORED_CORE_SECRET" + elif [[ "$core_secret" != "$ZIPLINE_STORED_CORE_SECRET" ]]; then + zipline_fail "CORE_SECRET cannot change while Zipline data is retained" + fi + else + if [[ -f "$data_dir/postgres/PG_VERSION" && ( "$postgres_default" == true || "$core_default" == true ) ]]; then + zipline_fail "existing PostgreSQL data has no Zipline secret state; supply its original credentials to preserve it" + fi + if [[ "$postgres_default" == true ]]; then + postgres_password="$(zipline_generate_postgres_password)" + fi + if [[ "$core_default" == true ]]; then + core_secret="$(openssl rand -hex 32)" + fi + fi + + zipline_validate_postgres_password "$postgres_password" || + zipline_fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + zipline_validate_core_secret "$core_secret" || + zipline_fail "CORE_SECRET must contain 64 to 128 hexadecimal characters" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" +} + +zipline_find_upgrade_snapshot() { + local root_dir="$1" + local env_file="$2" + local current_name="$3" + local current_data_dir="$4" + local current_db="$5" + local current_user="$6" + local temp_root snapshot old_env old_compose old_name old_data old_db old_user + local snapshot_time env_time best_time=-1 tied=false snapshot_uid snapshot_mode old_env_uid old_env_mode + local best_snapshot="" + local -a candidates=() + + temp_root="$(realpath -e -- "${TMPDIR:-/tmp}")" || zipline_fail "1Panel upgrade snapshot directory is unavailable" + [[ -d "$temp_root" ]] || zipline_fail "1Panel upgrade snapshot directory is unavailable" + env_time="$(stat -c '%Y' "$env_file")" || zipline_fail "Zipline .env timestamp is unavailable" + shopt -s nullglob + candidates=("$temp_root"/1panel-app-upgrade-*) + shopt -u nullglob + for snapshot in "${candidates[@]}"; do + [[ -d "$snapshot" && ! -L "$snapshot" ]] || continue + snapshot_uid="$(stat -c '%u' "$snapshot")" || continue + snapshot_mode="$(stat -c '%a' "$snapshot")" || continue + [[ "$snapshot_uid" == 0 ]] || continue + (( (8#$snapshot_mode & 077) == 0 )) || continue + old_env="$snapshot/.env" + old_compose="$snapshot/docker-compose.yml" + [[ -f "$old_env" && ! -L "$old_env" && -f "$old_compose" && ! -L "$old_compose" ]] || continue + old_env_uid="$(stat -c '%u' "$old_env")" || continue + old_env_mode="$(stat -c '%a' "$old_env")" || continue + [[ "$old_env_uid" == 0 ]] || continue + (( (8#$old_env_mode & 077) == 0 )) || continue + grep -Eq 'image:[[:space:]]*"?ghcr\.io/diced/zipline:4\.(6\.5|7\.0|8\.0)@sha256:[[:xdigit:]]{64}"?[[:space:]]*$' "$old_compose" || continue + old_name="$(zipline_read_env_value "$old_env" CONTAINER_NAME)" || continue + [[ "$old_name" == "$current_name" ]] || continue + old_data="$(zipline_read_env_value "$old_env" APP_DATA_DIR)" || zipline_fail "matching 1Panel snapshot lacks APP_DATA_DIR" + old_db="$(zipline_read_env_value "$old_env" POSTGRES_DB)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_DB" + old_user="$(zipline_read_env_value "$old_env" POSTGRES_USER)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_USER" + [[ "$old_data" == "$current_data_dir" && "$old_db" == "$current_db" && "$old_user" == "$current_user" ]] || + zipline_fail "matching 1Panel snapshot does not preserve the configured data path and database identity" + ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="$(zipline_read_env_value "$old_env" POSTGRES_PASSWORD)" || + zipline_fail "matching 1Panel snapshot lacks its database password" + ZIPLINE_SNAPSHOT_CORE_SECRET="$(zipline_read_env_value "$old_env" CORE_SECRET)" || + zipline_fail "matching 1Panel snapshot lacks its session secret" + zipline_validate_postgres_password "$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" || + zipline_fail "matching 1Panel snapshot has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_SNAPSHOT_CORE_SECRET" || + zipline_fail "matching 1Panel snapshot has an invalid session secret" + snapshot_time="$(stat -c '%Y' "$snapshot")" || zipline_fail "1Panel upgrade snapshot timestamp is unavailable" + if (( snapshot_time > best_time )); then + best_time="$snapshot_time" + best_snapshot="$snapshot" + tied=false + elif (( snapshot_time == best_time )); then + tied=true + fi + done + [[ -n "$best_snapshot" ]] || zipline_fail "could not find this Zipline install's 1Panel upgrade snapshot" + [[ "$tied" == false ]] || zipline_fail "multiple matching 1Panel upgrade snapshots are ambiguous" + (( best_time <= env_time && env_time - best_time <= 600 )) || + zipline_fail "matching 1Panel upgrade snapshot is stale" +} + +zipline_restore_upgrade_credentials() { + local root_dir="$1" + local env_file="$2" + local raw_data_dir data_dir current_name current_db current_user + local postgres_password core_secret current_password current_core_secret + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + current_name="$(zipline_read_env_value "$env_file" CONTAINER_NAME)" || zipline_fail "CONTAINER_NAME is missing from Zipline .env" + [[ "$current_name" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]] || zipline_fail "CONTAINER_NAME is invalid" + raw_data_dir="$(zipline_read_env_value "$env_file" APP_DATA_DIR)" || zipline_fail "APP_DATA_DIR is missing from Zipline .env" + data_dir="$(zipline_resolve_data_dir "$root_dir" "$raw_data_dir")" + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "configured Zipline data directory is missing" + current_db="$(zipline_read_env_value "$env_file" POSTGRES_DB)" || zipline_fail "POSTGRES_DB is missing from Zipline .env" + current_user="$(zipline_read_env_value "$env_file" POSTGRES_USER)" || zipline_fail "POSTGRES_USER is missing from Zipline .env" + current_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + current_core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || zipline_fail "CORE_SECRET is missing from Zipline .env" + + zipline_find_upgrade_snapshot "$root_dir" "$env_file" "$current_name" "$raw_data_dir" "$current_db" "$current_user" + postgres_password="$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" + core_secret="$ZIPLINE_SNAPSHOT_CORE_SECRET" + if zipline_load_secret_state "$data_dir"; then + [[ "$ZIPLINE_STORED_POSTGRES_PASSWORD" == "$postgres_password" && "$ZIPLINE_STORED_CORE_SECRET" == "$core_secret" ]] || + zipline_fail "Zipline secret state does not match the 1Panel upgrade snapshot" + fi + if [[ -n "$current_password" && "$current_password" != generate && "$current_password" != "$postgres_password" ]]; then + zipline_fail "POSTGRES_PASSWORD changed during upgrade; refusing to alter the existing database credential" + fi + if [[ -n "$current_core_secret" && "$current_core_secret" != generate && "$current_core_secret" != "$core_secret" ]]; then + zipline_fail "CORE_SECRET changed during upgrade; refusing to rotate the existing session secret" + fi + zipline_validate_postgres_password "$postgres_password" || zipline_fail "saved database password is invalid" + zipline_validate_core_secret "$core_secret" || zipline_fail "saved session secret is invalid" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" + printf '%s\n' "Zipline secrets were restored from the matching 1Panel upgrade snapshot." +} diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/init.sh b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/init.sh new file mode 100755 index 000000000..868a2fb3f --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/init.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +validate_ipv4() { + local key="$1" + local value="$2" + local octet + local -a octets + [[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address" + IFS=. read -r -a octets <<< "$value" + for octet in "${octets[@]}"; do + ((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet" + done +} + +validate_port() { + local value="$1" + [[ "$value" =~ ^[0-9]+$ ]] || fail "PANEL_APP_PORT_HTTP must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "PANEL_APP_PORT_HTTP must be between 1 and 65535" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Zipline init must run as root" +command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" +source "${ROOT_DIR}/scripts/credentials.sh" + +validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" +validate_port "$(read_env_value PANEL_APP_PORT_HTTP)" + +postgres_db="$(read_env_value POSTGRES_DB)" +postgres_user="$(read_env_value POSTGRES_USER)" +[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" +[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" + +max_file_size="$(read_env_value FILES_MAX_FILE_SIZE)" +[[ "$max_file_size" =~ ^[1-9][0-9]*(b|kb|mb|gb|tb)$ ]] || + fail "FILES_MAX_FILE_SIZE must be a positive byte quantity such as 100mb" + +data_raw="$(read_env_value APP_DATA_DIR)" +[[ -n "$data_raw" && "$data_raw" != /* ]] || fail "APP_DATA_DIR must be a non-empty relative path" +case "$data_raw" in + *$'\n'* | *$'\r'* | *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + fail "APP_DATA_DIR contains unsupported characters" + ;; +esac + +relative_data="${data_raw#./}" +[[ -n "$relative_data" ]] || fail "APP_DATA_DIR must not resolve to the version root" +current="$ROOT_DIR" +IFS=/ read -r -a components <<< "$relative_data" +for component in "${components[@]}"; do + [[ -n "$component" && "$component" != . && "$component" != .. ]] || fail "APP_DATA_DIR contains traversal" + current="$current/$component" + [[ ! -L "$current" ]] || fail "APP_DATA_DIR must not contain symbolic-link components" +done + +data_dir="$(realpath -m -- "$ROOT_DIR/$relative_data")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR must stay inside the application version directory" ;; +esac + +install -d -m 0700 -- "$data_dir/postgres" +install -d -m 0750 -- "$data_dir/uploads" "$data_dir/public" "$data_dir/themes" +resolved_data="$(realpath -e -- "$data_dir")" +case "$resolved_data" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +chown -R 70:70 -- "$resolved_data/postgres" +chown -R 1000:1000 -- "$resolved_data/uploads" "$resolved_data/public" "$resolved_data/themes" +chmod 0700 -- "$resolved_data/postgres" + +zipline_init_credentials "$ENV_FILE" "$resolved_data" +chmod 600 "$ENV_FILE" diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/uninstall.sh b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/uninstall.sh new file mode 100755 index 000000000..42b518ef4 --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/uninstall.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 1Panel removes the installation directory after this hook, including relative APP_DATA_DIR contents. +# Back up required data outside the application directory before uninstalling. +exit 0 diff --git a/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/upgrade.sh b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/upgrade.sh new file mode 100755 index 000000000..2a53d75f3 --- /dev/null +++ b/apps/zipline/4.6.5-postgres16-digest-721873c/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/apps/zipline/4.6.5/docker-compose.yml b/apps/zipline/4.6.5/docker-compose.yml index d9212ff42..a1b337010 100644 --- a/apps/zipline/4.6.5/docker-compose.yml +++ b/apps/zipline/4.6.5/docker-compose.yml @@ -1,6 +1,6 @@ services: zipline-postgres: - image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777 + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 container_name: ${CONTAINER_NAME}-postgres restart: unless-stopped user: "70:70" diff --git a/apps/zipline/4.6.5/scripts/credentials.sh b/apps/zipline/4.6.5/scripts/credentials.sh new file mode 100644 index 000000000..f0d6b6cf2 --- /dev/null +++ b/apps/zipline/4.6.5/scripts/credentials.sh @@ -0,0 +1,290 @@ +#!/usr/bin/env bash +set -euo pipefail + +ZIPLINE_STORED_POSTGRES_PASSWORD="" +ZIPLINE_STORED_CORE_SECRET="" +ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="" +ZIPLINE_SNAPSHOT_CORE_SECRET="" + +zipline_fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +zipline_read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" ' + index($0, key "=") == 1 { + count++ + value = substr($0, length(key) + 2) + } + END { + if (count != 1) exit 1 + print value + } + ' "$file")" || return 1 + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +zipline_resolve_data_dir() { + local root_dir="$1" + local raw="$2" + local clean current resolved part + local -a parts=() + [[ -n "$raw" && "$raw" != /* ]] || zipline_fail "APP_DATA_DIR must be a non-empty relative path" + [[ ! "$raw" =~ [[:cntrl:]] ]] || zipline_fail "APP_DATA_DIR contains control characters" + case "$raw" in + *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + zipline_fail "APP_DATA_DIR contains unsupported characters" + ;; + esac + clean="${raw#./}" + [[ -n "$clean" ]] || zipline_fail "APP_DATA_DIR must not resolve to the version root" + IFS=/ read -r -a parts <<< "$clean" + current="$root_dir" + for part in "${parts[@]}"; do + [[ -n "$part" && "$part" != . && "$part" != .. ]] || zipline_fail "APP_DATA_DIR contains traversal" + current="$current/$part" + [[ ! -L "$current" ]] || zipline_fail "APP_DATA_DIR must not contain symbolic-link components" + done + resolved="$(realpath -m -- "$root_dir/$clean")" || zipline_fail "APP_DATA_DIR could not be resolved" + case "$resolved" in + "$root_dir"/*) ;; + *) zipline_fail "APP_DATA_DIR must stay inside the application version directory" ;; + esac + printf '%s\n' "$resolved" +} + +zipline_validate_postgres_password() { + [[ "$1" =~ ^[A-Za-z0-9]{32,128}$ ]] +} + +zipline_validate_core_secret() { + [[ "$1" =~ ^[A-Fa-f0-9]{64,128}$ ]] +} + +zipline_generate_postgres_password() { + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge 48 ]] || zipline_fail "unable to generate sufficient database password material" + printf '%s\n' "${material:0:48}" +} + +zipline_write_env_credentials() { + local env_file="$1" + local postgres_password="$2" + local core_secret="$3" + local temp_file + temp_file="$(mktemp "${env_file}.zipline-tmp.XXXXXX")" || zipline_fail "unable to stage Zipline environment update" + if ! ZIPLINE_DB_PASSWORD="$postgres_password" ZIPLINE_CORE_SECRET="$core_secret" awk ' + BEGIN { + db_password = ENVIRON["ZIPLINE_DB_PASSWORD"] + core_secret = ENVIRON["ZIPLINE_CORE_SECRET"] + } + /^POSTGRES_PASSWORD=/ { + if (!db_written++) print "POSTGRES_PASSWORD=" db_password + next + } + /^CORE_SECRET=/ { + if (!core_written++) print "CORE_SECRET=" core_secret + next + } + { print } + END { + if (!db_written) print "POSTGRES_PASSWORD=" db_password + if (!core_written) print "CORE_SECRET=" core_secret + } + ' "$env_file" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to render Zipline environment update" + fi + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$env_file" + chmod 600 "$env_file" +} + +zipline_write_secret_state() { + local data_dir="$1" + local postgres_password="$2" + local core_secret="$3" + local secret_file="$data_dir/.zipline-secrets" + local temp_file + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "Zipline data directory is unavailable" + [[ ! -L "$secret_file" ]] || zipline_fail "Zipline secret state must not be a symbolic link" + temp_file="$(mktemp "${secret_file}.tmp.XXXXXX")" || zipline_fail "unable to stage Zipline secret state" + if ! printf 'POSTGRES_PASSWORD=%s\nCORE_SECRET=%s\n' "$postgres_password" "$core_secret" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to write Zipline secret state" + fi + chmod 600 "$temp_file" + chown 0:0 -- "$temp_file" + mv -f -- "$temp_file" "$secret_file" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_load_secret_state() { + local data_dir="$1" + local secret_file="$data_dir/.zipline-secrets" + [[ -e "$secret_file" || -L "$secret_file" ]] || return 1 + [[ -f "$secret_file" && ! -L "$secret_file" ]] || zipline_fail "Zipline secret state is not a regular file" + ZIPLINE_STORED_POSTGRES_PASSWORD="$(zipline_read_env_value "$secret_file" POSTGRES_PASSWORD)" || + zipline_fail "Zipline secret state is incomplete" + ZIPLINE_STORED_CORE_SECRET="$(zipline_read_env_value "$secret_file" CORE_SECRET)" || + zipline_fail "Zipline secret state is incomplete" + zipline_validate_postgres_password "$ZIPLINE_STORED_POSTGRES_PASSWORD" || + zipline_fail "Zipline secret state has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_STORED_CORE_SECRET" || + zipline_fail "Zipline secret state has an invalid session secret" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_init_credentials() { + local env_file="$1" + local data_dir="$2" + local postgres_password core_secret stored + local postgres_default=false core_default=false + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + postgres_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || + zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || + zipline_fail "CORE_SECRET is missing from Zipline .env" + [[ -n "$postgres_password" && "$postgres_password" != generate ]] || postgres_default=true + [[ -n "$core_secret" && "$core_secret" != generate ]] || core_default=true + + if zipline_load_secret_state "$data_dir"; then + if [[ "$postgres_default" == true ]]; then + postgres_password="$ZIPLINE_STORED_POSTGRES_PASSWORD" + elif [[ "$postgres_password" != "$ZIPLINE_STORED_POSTGRES_PASSWORD" ]]; then + zipline_fail "POSTGRES_PASSWORD cannot change while Zipline data is retained" + fi + if [[ "$core_default" == true ]]; then + core_secret="$ZIPLINE_STORED_CORE_SECRET" + elif [[ "$core_secret" != "$ZIPLINE_STORED_CORE_SECRET" ]]; then + zipline_fail "CORE_SECRET cannot change while Zipline data is retained" + fi + else + if [[ -f "$data_dir/postgres/PG_VERSION" && ( "$postgres_default" == true || "$core_default" == true ) ]]; then + zipline_fail "existing PostgreSQL data has no Zipline secret state; supply its original credentials to preserve it" + fi + if [[ "$postgres_default" == true ]]; then + postgres_password="$(zipline_generate_postgres_password)" + fi + if [[ "$core_default" == true ]]; then + core_secret="$(openssl rand -hex 32)" + fi + fi + + zipline_validate_postgres_password "$postgres_password" || + zipline_fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + zipline_validate_core_secret "$core_secret" || + zipline_fail "CORE_SECRET must contain 64 to 128 hexadecimal characters" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" +} + +zipline_find_upgrade_snapshot() { + local root_dir="$1" + local env_file="$2" + local current_name="$3" + local current_data_dir="$4" + local current_db="$5" + local current_user="$6" + local temp_root snapshot old_env old_compose old_name old_data old_db old_user + local snapshot_time env_time best_time=-1 tied=false snapshot_uid snapshot_mode old_env_uid old_env_mode + local best_snapshot="" + local -a candidates=() + + temp_root="$(realpath -e -- "${TMPDIR:-/tmp}")" || zipline_fail "1Panel upgrade snapshot directory is unavailable" + [[ -d "$temp_root" ]] || zipline_fail "1Panel upgrade snapshot directory is unavailable" + env_time="$(stat -c '%Y' "$env_file")" || zipline_fail "Zipline .env timestamp is unavailable" + shopt -s nullglob + candidates=("$temp_root"/1panel-app-upgrade-*) + shopt -u nullglob + for snapshot in "${candidates[@]}"; do + [[ -d "$snapshot" && ! -L "$snapshot" ]] || continue + snapshot_uid="$(stat -c '%u' "$snapshot")" || continue + snapshot_mode="$(stat -c '%a' "$snapshot")" || continue + [[ "$snapshot_uid" == 0 ]] || continue + (( (8#$snapshot_mode & 077) == 0 )) || continue + old_env="$snapshot/.env" + old_compose="$snapshot/docker-compose.yml" + [[ -f "$old_env" && ! -L "$old_env" && -f "$old_compose" && ! -L "$old_compose" ]] || continue + old_env_uid="$(stat -c '%u' "$old_env")" || continue + old_env_mode="$(stat -c '%a' "$old_env")" || continue + [[ "$old_env_uid" == 0 ]] || continue + (( (8#$old_env_mode & 077) == 0 )) || continue + grep -Eq 'image:[[:space:]]*"?ghcr\.io/diced/zipline:4\.(6\.5|7\.0|8\.0)@sha256:[[:xdigit:]]{64}"?[[:space:]]*$' "$old_compose" || continue + old_name="$(zipline_read_env_value "$old_env" CONTAINER_NAME)" || continue + [[ "$old_name" == "$current_name" ]] || continue + old_data="$(zipline_read_env_value "$old_env" APP_DATA_DIR)" || zipline_fail "matching 1Panel snapshot lacks APP_DATA_DIR" + old_db="$(zipline_read_env_value "$old_env" POSTGRES_DB)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_DB" + old_user="$(zipline_read_env_value "$old_env" POSTGRES_USER)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_USER" + [[ "$old_data" == "$current_data_dir" && "$old_db" == "$current_db" && "$old_user" == "$current_user" ]] || + zipline_fail "matching 1Panel snapshot does not preserve the configured data path and database identity" + ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="$(zipline_read_env_value "$old_env" POSTGRES_PASSWORD)" || + zipline_fail "matching 1Panel snapshot lacks its database password" + ZIPLINE_SNAPSHOT_CORE_SECRET="$(zipline_read_env_value "$old_env" CORE_SECRET)" || + zipline_fail "matching 1Panel snapshot lacks its session secret" + zipline_validate_postgres_password "$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" || + zipline_fail "matching 1Panel snapshot has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_SNAPSHOT_CORE_SECRET" || + zipline_fail "matching 1Panel snapshot has an invalid session secret" + snapshot_time="$(stat -c '%Y' "$snapshot")" || zipline_fail "1Panel upgrade snapshot timestamp is unavailable" + if (( snapshot_time > best_time )); then + best_time="$snapshot_time" + best_snapshot="$snapshot" + tied=false + elif (( snapshot_time == best_time )); then + tied=true + fi + done + [[ -n "$best_snapshot" ]] || zipline_fail "could not find this Zipline install's 1Panel upgrade snapshot" + [[ "$tied" == false ]] || zipline_fail "multiple matching 1Panel upgrade snapshots are ambiguous" + (( best_time <= env_time && env_time - best_time <= 600 )) || + zipline_fail "matching 1Panel upgrade snapshot is stale" +} + +zipline_restore_upgrade_credentials() { + local root_dir="$1" + local env_file="$2" + local raw_data_dir data_dir current_name current_db current_user + local postgres_password core_secret current_password current_core_secret + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + current_name="$(zipline_read_env_value "$env_file" CONTAINER_NAME)" || zipline_fail "CONTAINER_NAME is missing from Zipline .env" + [[ "$current_name" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]] || zipline_fail "CONTAINER_NAME is invalid" + raw_data_dir="$(zipline_read_env_value "$env_file" APP_DATA_DIR)" || zipline_fail "APP_DATA_DIR is missing from Zipline .env" + data_dir="$(zipline_resolve_data_dir "$root_dir" "$raw_data_dir")" + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "configured Zipline data directory is missing" + current_db="$(zipline_read_env_value "$env_file" POSTGRES_DB)" || zipline_fail "POSTGRES_DB is missing from Zipline .env" + current_user="$(zipline_read_env_value "$env_file" POSTGRES_USER)" || zipline_fail "POSTGRES_USER is missing from Zipline .env" + current_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + current_core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || zipline_fail "CORE_SECRET is missing from Zipline .env" + + zipline_find_upgrade_snapshot "$root_dir" "$env_file" "$current_name" "$raw_data_dir" "$current_db" "$current_user" + postgres_password="$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" + core_secret="$ZIPLINE_SNAPSHOT_CORE_SECRET" + if zipline_load_secret_state "$data_dir"; then + [[ "$ZIPLINE_STORED_POSTGRES_PASSWORD" == "$postgres_password" && "$ZIPLINE_STORED_CORE_SECRET" == "$core_secret" ]] || + zipline_fail "Zipline secret state does not match the 1Panel upgrade snapshot" + fi + if [[ -n "$current_password" && "$current_password" != generate && "$current_password" != "$postgres_password" ]]; then + zipline_fail "POSTGRES_PASSWORD changed during upgrade; refusing to alter the existing database credential" + fi + if [[ -n "$current_core_secret" && "$current_core_secret" != generate && "$current_core_secret" != "$core_secret" ]]; then + zipline_fail "CORE_SECRET changed during upgrade; refusing to rotate the existing session secret" + fi + zipline_validate_postgres_password "$postgres_password" || zipline_fail "saved database password is invalid" + zipline_validate_core_secret "$core_secret" || zipline_fail "saved session secret is invalid" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" + printf '%s\n' "Zipline secrets were restored from the matching 1Panel upgrade snapshot." +} diff --git a/apps/zipline/4.6.5/scripts/init.sh b/apps/zipline/4.6.5/scripts/init.sh index 3da7c0b55..868a2fb3f 100755 --- a/apps/zipline/4.6.5/scripts/init.sh +++ b/apps/zipline/4.6.5/scripts/init.sh @@ -20,27 +20,6 @@ read_env_value() { printf '%s\n' "$value" } -set_env_value() { - local key="$1" - local value="$2" - local temp_file - temp_file="$(mktemp "${ROOT_DIR}/.zipline-env.tmp.XXXXXX")" - awk -v key="$key" -v value="$value" ' - BEGIN { written = 0 } - $0 ~ "^" key "=" { - if (!written) { - print key "=" value - written = 1 - } - next - } - { print } - END { if (!written) print key "=" value } - ' "$ENV_FILE" > "$temp_file" - chmod 600 "$temp_file" - mv -f -- "$temp_file" "$ENV_FILE" -} - validate_ipv4() { local key="$1" local value="$2" @@ -59,18 +38,11 @@ validate_port() { ((10#$value >= 1 && 10#$value <= 65535)) || fail "PANEL_APP_PORT_HTTP must be between 1 and 65535" } -generate_alphanumeric() { - local length="$1" - local material - material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" - [[ ${#material} -ge $length ]] || fail "unable to generate sufficient random material" - printf '%s\n' "${material:0:length}" -} - [[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" [[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" [[ "$(id -u)" -eq 0 ]] || fail "Zipline init must run as root" command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" +source "${ROOT_DIR}/scripts/credentials.sh" validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" validate_port "$(read_env_value PANEL_APP_PORT_HTTP)" @@ -80,20 +52,6 @@ postgres_user="$(read_env_value POSTGRES_USER)" [[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" [[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" -postgres_password="$(read_env_value POSTGRES_PASSWORD)" -if [[ -z "$postgres_password" || "$postgres_password" == generate ]]; then - postgres_password="$(generate_alphanumeric 48)" -fi -[[ "$postgres_password" =~ ^[A-Za-z0-9]{32,128}$ ]] || - fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" - -core_secret="$(read_env_value CORE_SECRET)" -if [[ -z "$core_secret" || "$core_secret" == generate ]]; then - core_secret="$(openssl rand -hex 32)" -fi -[[ "$core_secret" =~ ^[A-Fa-f0-9]{64,128}$ ]] || - fail "CORE_SECRET must contain 64 to 128 hexadecimal characters" - max_file_size="$(read_env_value FILES_MAX_FILE_SIZE)" [[ "$max_file_size" =~ ^[1-9][0-9]*(b|kb|mb|gb|tb)$ ]] || fail "FILES_MAX_FILE_SIZE must be a positive byte quantity such as 100mb" @@ -133,6 +91,5 @@ chown -R 70:70 -- "$resolved_data/postgres" chown -R 1000:1000 -- "$resolved_data/uploads" "$resolved_data/public" "$resolved_data/themes" chmod 0700 -- "$resolved_data/postgres" -set_env_value POSTGRES_PASSWORD "$postgres_password" -set_env_value CORE_SECRET "$core_secret" +zipline_init_credentials "$ENV_FILE" "$resolved_data" chmod 600 "$ENV_FILE" diff --git a/apps/zipline/4.6.5/scripts/uninstall.sh b/apps/zipline/4.6.5/scripts/uninstall.sh index 9365d9925..42b518ef4 100755 --- a/apps/zipline/4.6.5/scripts/uninstall.sh +++ b/apps/zipline/4.6.5/scripts/uninstall.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -# Persistent data is intentionally retained for recovery or reinstallation. +# 1Panel removes the installation directory after this hook, including relative APP_DATA_DIR contents. +# Back up required data outside the application directory before uninstalling. exit 0 diff --git a/apps/zipline/4.7.0/.env.sample b/apps/zipline/4.7.0/.env.sample new file mode 100644 index 000000000..845005ffa --- /dev/null +++ b/apps/zipline/4.7.0/.env.sample @@ -0,0 +1,9 @@ +CONTAINER_NAME=zipline +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=3000 +POSTGRES_DB=zipline +POSTGRES_USER=zipline +POSTGRES_PASSWORD=generate +CORE_SECRET=generate +FILES_MAX_FILE_SIZE=100mb +APP_DATA_DIR=./data diff --git a/apps/zipline/4.7.0/data.yml b/apps/zipline/4.7.0/data.yml new file mode 100644 index 000000000..8f8719194 --- /dev/null +++ b/apps/zipline/4.7.0/data.yml @@ -0,0 +1,135 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: 100mb + edit: true + envKey: FILES_MAX_FILE_SIZE + labelEn: Maximum File Size + labelZh: 最大文件大小 + label: + en: Maximum File Size + zh: 最大文件大小 + zh-Hant: 最大檔案大小 + ja: 最大ファイルサイズ + ko: 최대 파일 크기 + ru: Максимальный размер файла + ms: Saiz fail maksimum + pt-br: Tamanho maximo do arquivo + required: true + type: text + - default: zipline + edit: true + envKey: POSTGRES_DB + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: zipline + edit: true + envKey: POSTGRES_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: generate + edit: true + envKey: POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: generate + edit: true + envKey: CORE_SECRET + labelEn: Session Secret + labelZh: 会话密钥 + label: + en: Session Secret + zh: 会话密钥 + zh-Hant: 工作階段密鑰 + ja: セッションシークレット + ko: 세션 비밀 키 + ru: Секрет сеанса + ms: Rahsia sesi + pt-br: Segredo da sessao + required: true + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/zipline/4.7.0/docker-compose.yml b/apps/zipline/4.7.0/docker-compose.yml new file mode 100644 index 000000000..8efd1c7de --- /dev/null +++ b/apps/zipline/4.7.0/docker-compose.yml @@ -0,0 +1,79 @@ +services: + zipline-postgres: + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - zipline-internal + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + read_only: true + tmpfs: + - /tmp:mode=1777,size=64m + - /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + labels: + createdBy: "Apps" + + zipline: + image: ghcr.io/diced/zipline:4.7.0@sha256:526fa08d2d3cf46bd959b2008987ca8d2a38b1ba840991b68cd9ff4b8c6a3fab + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + depends_on: + zipline-postgres: + condition: service_healthy + networks: + - zipline-internal + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:3000" + environment: + - CORE_HOSTNAME=0.0.0.0 + - CORE_PORT=3000 + - CORE_SECRET=${CORE_SECRET} + - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@zipline-postgres:5432/${POSTGRES_DB} + - DATASOURCE_TYPE=local + - DATASOURCE_LOCAL_DIRECTORY=/zipline/uploads + - CORE_TEMP_DIRECTORY=/tmp/zipline + - FILES_MAX_FILE_SIZE=${FILES_MAX_FILE_SIZE} + - FEATURES_HEALTHCHECK=true + volumes: + - "${APP_DATA_DIR}/uploads:/zipline/uploads" + - "${APP_DATA_DIR}/public:/zipline/public" + - "${APP_DATA_DIR}/themes:/zipline/themes" + read_only: true + tmpfs: + - /tmp:mode=1777,size=256m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/healthcheck"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + zipline-internal: + internal: true + 1panel-network: + external: true diff --git a/apps/zipline/4.7.0/scripts/credentials.sh b/apps/zipline/4.7.0/scripts/credentials.sh new file mode 100644 index 000000000..f0d6b6cf2 --- /dev/null +++ b/apps/zipline/4.7.0/scripts/credentials.sh @@ -0,0 +1,290 @@ +#!/usr/bin/env bash +set -euo pipefail + +ZIPLINE_STORED_POSTGRES_PASSWORD="" +ZIPLINE_STORED_CORE_SECRET="" +ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="" +ZIPLINE_SNAPSHOT_CORE_SECRET="" + +zipline_fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +zipline_read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" ' + index($0, key "=") == 1 { + count++ + value = substr($0, length(key) + 2) + } + END { + if (count != 1) exit 1 + print value + } + ' "$file")" || return 1 + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +zipline_resolve_data_dir() { + local root_dir="$1" + local raw="$2" + local clean current resolved part + local -a parts=() + [[ -n "$raw" && "$raw" != /* ]] || zipline_fail "APP_DATA_DIR must be a non-empty relative path" + [[ ! "$raw" =~ [[:cntrl:]] ]] || zipline_fail "APP_DATA_DIR contains control characters" + case "$raw" in + *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + zipline_fail "APP_DATA_DIR contains unsupported characters" + ;; + esac + clean="${raw#./}" + [[ -n "$clean" ]] || zipline_fail "APP_DATA_DIR must not resolve to the version root" + IFS=/ read -r -a parts <<< "$clean" + current="$root_dir" + for part in "${parts[@]}"; do + [[ -n "$part" && "$part" != . && "$part" != .. ]] || zipline_fail "APP_DATA_DIR contains traversal" + current="$current/$part" + [[ ! -L "$current" ]] || zipline_fail "APP_DATA_DIR must not contain symbolic-link components" + done + resolved="$(realpath -m -- "$root_dir/$clean")" || zipline_fail "APP_DATA_DIR could not be resolved" + case "$resolved" in + "$root_dir"/*) ;; + *) zipline_fail "APP_DATA_DIR must stay inside the application version directory" ;; + esac + printf '%s\n' "$resolved" +} + +zipline_validate_postgres_password() { + [[ "$1" =~ ^[A-Za-z0-9]{32,128}$ ]] +} + +zipline_validate_core_secret() { + [[ "$1" =~ ^[A-Fa-f0-9]{64,128}$ ]] +} + +zipline_generate_postgres_password() { + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge 48 ]] || zipline_fail "unable to generate sufficient database password material" + printf '%s\n' "${material:0:48}" +} + +zipline_write_env_credentials() { + local env_file="$1" + local postgres_password="$2" + local core_secret="$3" + local temp_file + temp_file="$(mktemp "${env_file}.zipline-tmp.XXXXXX")" || zipline_fail "unable to stage Zipline environment update" + if ! ZIPLINE_DB_PASSWORD="$postgres_password" ZIPLINE_CORE_SECRET="$core_secret" awk ' + BEGIN { + db_password = ENVIRON["ZIPLINE_DB_PASSWORD"] + core_secret = ENVIRON["ZIPLINE_CORE_SECRET"] + } + /^POSTGRES_PASSWORD=/ { + if (!db_written++) print "POSTGRES_PASSWORD=" db_password + next + } + /^CORE_SECRET=/ { + if (!core_written++) print "CORE_SECRET=" core_secret + next + } + { print } + END { + if (!db_written) print "POSTGRES_PASSWORD=" db_password + if (!core_written) print "CORE_SECRET=" core_secret + } + ' "$env_file" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to render Zipline environment update" + fi + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$env_file" + chmod 600 "$env_file" +} + +zipline_write_secret_state() { + local data_dir="$1" + local postgres_password="$2" + local core_secret="$3" + local secret_file="$data_dir/.zipline-secrets" + local temp_file + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "Zipline data directory is unavailable" + [[ ! -L "$secret_file" ]] || zipline_fail "Zipline secret state must not be a symbolic link" + temp_file="$(mktemp "${secret_file}.tmp.XXXXXX")" || zipline_fail "unable to stage Zipline secret state" + if ! printf 'POSTGRES_PASSWORD=%s\nCORE_SECRET=%s\n' "$postgres_password" "$core_secret" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to write Zipline secret state" + fi + chmod 600 "$temp_file" + chown 0:0 -- "$temp_file" + mv -f -- "$temp_file" "$secret_file" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_load_secret_state() { + local data_dir="$1" + local secret_file="$data_dir/.zipline-secrets" + [[ -e "$secret_file" || -L "$secret_file" ]] || return 1 + [[ -f "$secret_file" && ! -L "$secret_file" ]] || zipline_fail "Zipline secret state is not a regular file" + ZIPLINE_STORED_POSTGRES_PASSWORD="$(zipline_read_env_value "$secret_file" POSTGRES_PASSWORD)" || + zipline_fail "Zipline secret state is incomplete" + ZIPLINE_STORED_CORE_SECRET="$(zipline_read_env_value "$secret_file" CORE_SECRET)" || + zipline_fail "Zipline secret state is incomplete" + zipline_validate_postgres_password "$ZIPLINE_STORED_POSTGRES_PASSWORD" || + zipline_fail "Zipline secret state has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_STORED_CORE_SECRET" || + zipline_fail "Zipline secret state has an invalid session secret" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_init_credentials() { + local env_file="$1" + local data_dir="$2" + local postgres_password core_secret stored + local postgres_default=false core_default=false + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + postgres_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || + zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || + zipline_fail "CORE_SECRET is missing from Zipline .env" + [[ -n "$postgres_password" && "$postgres_password" != generate ]] || postgres_default=true + [[ -n "$core_secret" && "$core_secret" != generate ]] || core_default=true + + if zipline_load_secret_state "$data_dir"; then + if [[ "$postgres_default" == true ]]; then + postgres_password="$ZIPLINE_STORED_POSTGRES_PASSWORD" + elif [[ "$postgres_password" != "$ZIPLINE_STORED_POSTGRES_PASSWORD" ]]; then + zipline_fail "POSTGRES_PASSWORD cannot change while Zipline data is retained" + fi + if [[ "$core_default" == true ]]; then + core_secret="$ZIPLINE_STORED_CORE_SECRET" + elif [[ "$core_secret" != "$ZIPLINE_STORED_CORE_SECRET" ]]; then + zipline_fail "CORE_SECRET cannot change while Zipline data is retained" + fi + else + if [[ -f "$data_dir/postgres/PG_VERSION" && ( "$postgres_default" == true || "$core_default" == true ) ]]; then + zipline_fail "existing PostgreSQL data has no Zipline secret state; supply its original credentials to preserve it" + fi + if [[ "$postgres_default" == true ]]; then + postgres_password="$(zipline_generate_postgres_password)" + fi + if [[ "$core_default" == true ]]; then + core_secret="$(openssl rand -hex 32)" + fi + fi + + zipline_validate_postgres_password "$postgres_password" || + zipline_fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + zipline_validate_core_secret "$core_secret" || + zipline_fail "CORE_SECRET must contain 64 to 128 hexadecimal characters" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" +} + +zipline_find_upgrade_snapshot() { + local root_dir="$1" + local env_file="$2" + local current_name="$3" + local current_data_dir="$4" + local current_db="$5" + local current_user="$6" + local temp_root snapshot old_env old_compose old_name old_data old_db old_user + local snapshot_time env_time best_time=-1 tied=false snapshot_uid snapshot_mode old_env_uid old_env_mode + local best_snapshot="" + local -a candidates=() + + temp_root="$(realpath -e -- "${TMPDIR:-/tmp}")" || zipline_fail "1Panel upgrade snapshot directory is unavailable" + [[ -d "$temp_root" ]] || zipline_fail "1Panel upgrade snapshot directory is unavailable" + env_time="$(stat -c '%Y' "$env_file")" || zipline_fail "Zipline .env timestamp is unavailable" + shopt -s nullglob + candidates=("$temp_root"/1panel-app-upgrade-*) + shopt -u nullglob + for snapshot in "${candidates[@]}"; do + [[ -d "$snapshot" && ! -L "$snapshot" ]] || continue + snapshot_uid="$(stat -c '%u' "$snapshot")" || continue + snapshot_mode="$(stat -c '%a' "$snapshot")" || continue + [[ "$snapshot_uid" == 0 ]] || continue + (( (8#$snapshot_mode & 077) == 0 )) || continue + old_env="$snapshot/.env" + old_compose="$snapshot/docker-compose.yml" + [[ -f "$old_env" && ! -L "$old_env" && -f "$old_compose" && ! -L "$old_compose" ]] || continue + old_env_uid="$(stat -c '%u' "$old_env")" || continue + old_env_mode="$(stat -c '%a' "$old_env")" || continue + [[ "$old_env_uid" == 0 ]] || continue + (( (8#$old_env_mode & 077) == 0 )) || continue + grep -Eq 'image:[[:space:]]*"?ghcr\.io/diced/zipline:4\.(6\.5|7\.0|8\.0)@sha256:[[:xdigit:]]{64}"?[[:space:]]*$' "$old_compose" || continue + old_name="$(zipline_read_env_value "$old_env" CONTAINER_NAME)" || continue + [[ "$old_name" == "$current_name" ]] || continue + old_data="$(zipline_read_env_value "$old_env" APP_DATA_DIR)" || zipline_fail "matching 1Panel snapshot lacks APP_DATA_DIR" + old_db="$(zipline_read_env_value "$old_env" POSTGRES_DB)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_DB" + old_user="$(zipline_read_env_value "$old_env" POSTGRES_USER)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_USER" + [[ "$old_data" == "$current_data_dir" && "$old_db" == "$current_db" && "$old_user" == "$current_user" ]] || + zipline_fail "matching 1Panel snapshot does not preserve the configured data path and database identity" + ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="$(zipline_read_env_value "$old_env" POSTGRES_PASSWORD)" || + zipline_fail "matching 1Panel snapshot lacks its database password" + ZIPLINE_SNAPSHOT_CORE_SECRET="$(zipline_read_env_value "$old_env" CORE_SECRET)" || + zipline_fail "matching 1Panel snapshot lacks its session secret" + zipline_validate_postgres_password "$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" || + zipline_fail "matching 1Panel snapshot has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_SNAPSHOT_CORE_SECRET" || + zipline_fail "matching 1Panel snapshot has an invalid session secret" + snapshot_time="$(stat -c '%Y' "$snapshot")" || zipline_fail "1Panel upgrade snapshot timestamp is unavailable" + if (( snapshot_time > best_time )); then + best_time="$snapshot_time" + best_snapshot="$snapshot" + tied=false + elif (( snapshot_time == best_time )); then + tied=true + fi + done + [[ -n "$best_snapshot" ]] || zipline_fail "could not find this Zipline install's 1Panel upgrade snapshot" + [[ "$tied" == false ]] || zipline_fail "multiple matching 1Panel upgrade snapshots are ambiguous" + (( best_time <= env_time && env_time - best_time <= 600 )) || + zipline_fail "matching 1Panel upgrade snapshot is stale" +} + +zipline_restore_upgrade_credentials() { + local root_dir="$1" + local env_file="$2" + local raw_data_dir data_dir current_name current_db current_user + local postgres_password core_secret current_password current_core_secret + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + current_name="$(zipline_read_env_value "$env_file" CONTAINER_NAME)" || zipline_fail "CONTAINER_NAME is missing from Zipline .env" + [[ "$current_name" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]] || zipline_fail "CONTAINER_NAME is invalid" + raw_data_dir="$(zipline_read_env_value "$env_file" APP_DATA_DIR)" || zipline_fail "APP_DATA_DIR is missing from Zipline .env" + data_dir="$(zipline_resolve_data_dir "$root_dir" "$raw_data_dir")" + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "configured Zipline data directory is missing" + current_db="$(zipline_read_env_value "$env_file" POSTGRES_DB)" || zipline_fail "POSTGRES_DB is missing from Zipline .env" + current_user="$(zipline_read_env_value "$env_file" POSTGRES_USER)" || zipline_fail "POSTGRES_USER is missing from Zipline .env" + current_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + current_core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || zipline_fail "CORE_SECRET is missing from Zipline .env" + + zipline_find_upgrade_snapshot "$root_dir" "$env_file" "$current_name" "$raw_data_dir" "$current_db" "$current_user" + postgres_password="$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" + core_secret="$ZIPLINE_SNAPSHOT_CORE_SECRET" + if zipline_load_secret_state "$data_dir"; then + [[ "$ZIPLINE_STORED_POSTGRES_PASSWORD" == "$postgres_password" && "$ZIPLINE_STORED_CORE_SECRET" == "$core_secret" ]] || + zipline_fail "Zipline secret state does not match the 1Panel upgrade snapshot" + fi + if [[ -n "$current_password" && "$current_password" != generate && "$current_password" != "$postgres_password" ]]; then + zipline_fail "POSTGRES_PASSWORD changed during upgrade; refusing to alter the existing database credential" + fi + if [[ -n "$current_core_secret" && "$current_core_secret" != generate && "$current_core_secret" != "$core_secret" ]]; then + zipline_fail "CORE_SECRET changed during upgrade; refusing to rotate the existing session secret" + fi + zipline_validate_postgres_password "$postgres_password" || zipline_fail "saved database password is invalid" + zipline_validate_core_secret "$core_secret" || zipline_fail "saved session secret is invalid" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" + printf '%s\n' "Zipline secrets were restored from the matching 1Panel upgrade snapshot." +} diff --git a/apps/zipline/4.7.0/scripts/init.sh b/apps/zipline/4.7.0/scripts/init.sh new file mode 100755 index 000000000..868a2fb3f --- /dev/null +++ b/apps/zipline/4.7.0/scripts/init.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +validate_ipv4() { + local key="$1" + local value="$2" + local octet + local -a octets + [[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address" + IFS=. read -r -a octets <<< "$value" + for octet in "${octets[@]}"; do + ((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet" + done +} + +validate_port() { + local value="$1" + [[ "$value" =~ ^[0-9]+$ ]] || fail "PANEL_APP_PORT_HTTP must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "PANEL_APP_PORT_HTTP must be between 1 and 65535" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Zipline init must run as root" +command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" +source "${ROOT_DIR}/scripts/credentials.sh" + +validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" +validate_port "$(read_env_value PANEL_APP_PORT_HTTP)" + +postgres_db="$(read_env_value POSTGRES_DB)" +postgres_user="$(read_env_value POSTGRES_USER)" +[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" +[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" + +max_file_size="$(read_env_value FILES_MAX_FILE_SIZE)" +[[ "$max_file_size" =~ ^[1-9][0-9]*(b|kb|mb|gb|tb)$ ]] || + fail "FILES_MAX_FILE_SIZE must be a positive byte quantity such as 100mb" + +data_raw="$(read_env_value APP_DATA_DIR)" +[[ -n "$data_raw" && "$data_raw" != /* ]] || fail "APP_DATA_DIR must be a non-empty relative path" +case "$data_raw" in + *$'\n'* | *$'\r'* | *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + fail "APP_DATA_DIR contains unsupported characters" + ;; +esac + +relative_data="${data_raw#./}" +[[ -n "$relative_data" ]] || fail "APP_DATA_DIR must not resolve to the version root" +current="$ROOT_DIR" +IFS=/ read -r -a components <<< "$relative_data" +for component in "${components[@]}"; do + [[ -n "$component" && "$component" != . && "$component" != .. ]] || fail "APP_DATA_DIR contains traversal" + current="$current/$component" + [[ ! -L "$current" ]] || fail "APP_DATA_DIR must not contain symbolic-link components" +done + +data_dir="$(realpath -m -- "$ROOT_DIR/$relative_data")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR must stay inside the application version directory" ;; +esac + +install -d -m 0700 -- "$data_dir/postgres" +install -d -m 0750 -- "$data_dir/uploads" "$data_dir/public" "$data_dir/themes" +resolved_data="$(realpath -e -- "$data_dir")" +case "$resolved_data" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +chown -R 70:70 -- "$resolved_data/postgres" +chown -R 1000:1000 -- "$resolved_data/uploads" "$resolved_data/public" "$resolved_data/themes" +chmod 0700 -- "$resolved_data/postgres" + +zipline_init_credentials "$ENV_FILE" "$resolved_data" +chmod 600 "$ENV_FILE" diff --git a/apps/zipline/4.7.0/scripts/uninstall.sh b/apps/zipline/4.7.0/scripts/uninstall.sh new file mode 100755 index 000000000..42b518ef4 --- /dev/null +++ b/apps/zipline/4.7.0/scripts/uninstall.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 1Panel removes the installation directory after this hook, including relative APP_DATA_DIR contents. +# Back up required data outside the application directory before uninstalling. +exit 0 diff --git a/apps/zipline/4.7.0/scripts/upgrade.sh b/apps/zipline/4.7.0/scripts/upgrade.sh new file mode 100755 index 000000000..60ed3b247 --- /dev/null +++ b/apps/zipline/4.7.0/scripts/upgrade.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +source "${ROOT_DIR}/scripts/credentials.sh" + +zipline_restore_upgrade_credentials "$ROOT_DIR" "$ENV_FILE" diff --git a/apps/zipline/4.8.0/.env.sample b/apps/zipline/4.8.0/.env.sample new file mode 100644 index 000000000..845005ffa --- /dev/null +++ b/apps/zipline/4.8.0/.env.sample @@ -0,0 +1,9 @@ +CONTAINER_NAME=zipline +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=3000 +POSTGRES_DB=zipline +POSTGRES_USER=zipline +POSTGRES_PASSWORD=generate +CORE_SECRET=generate +FILES_MAX_FILE_SIZE=100mb +APP_DATA_DIR=./data diff --git a/apps/zipline/4.8.0/data.yml b/apps/zipline/4.8.0/data.yml new file mode 100644 index 000000000..8f8719194 --- /dev/null +++ b/apps/zipline/4.8.0/data.yml @@ -0,0 +1,135 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: 100mb + edit: true + envKey: FILES_MAX_FILE_SIZE + labelEn: Maximum File Size + labelZh: 最大文件大小 + label: + en: Maximum File Size + zh: 最大文件大小 + zh-Hant: 最大檔案大小 + ja: 最大ファイルサイズ + ko: 최대 파일 크기 + ru: Максимальный размер файла + ms: Saiz fail maksimum + pt-br: Tamanho maximo do arquivo + required: true + type: text + - default: zipline + edit: true + envKey: POSTGRES_DB + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: zipline + edit: true + envKey: POSTGRES_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: generate + edit: true + envKey: POSTGRES_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + type: password + - default: generate + edit: true + envKey: CORE_SECRET + labelEn: Session Secret + labelZh: 会话密钥 + label: + en: Session Secret + zh: 会话密钥 + zh-Hant: 工作階段密鑰 + ja: セッションシークレット + ko: 세션 비밀 키 + ru: Секрет сеанса + ms: Rahsia sesi + pt-br: Segredo da sessao + required: true + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text diff --git a/apps/zipline/4.8.0/docker-compose.yml b/apps/zipline/4.8.0/docker-compose.yml new file mode 100644 index 000000000..da3423d7e --- /dev/null +++ b/apps/zipline/4.8.0/docker-compose.yml @@ -0,0 +1,79 @@ +services: + zipline-postgres: + image: postgres:16-alpine@sha256:deb28ee471183cb0c303113dac9b2433c51eeeeb678def7af9a9ec5820367815 + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + user: "70:70" + networks: + - zipline-internal + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + volumes: + - "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data" + read_only: true + tmpfs: + - /tmp:mode=1777,size=64m + - /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + labels: + createdBy: "Apps" + + zipline: + image: ghcr.io/diced/zipline:4.8.0@sha256:8ae3cdf8cdbba2ac98754e9fc4ccec67bbaa86b6e6ab1ccf9683ddcff85eee72 + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + depends_on: + zipline-postgres: + condition: service_healthy + networks: + - zipline-internal + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:3000" + environment: + - CORE_HOSTNAME=0.0.0.0 + - CORE_PORT=3000 + - CORE_SECRET=${CORE_SECRET} + - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@zipline-postgres:5432/${POSTGRES_DB} + - DATASOURCE_TYPE=local + - DATASOURCE_LOCAL_DIRECTORY=/zipline/uploads + - CORE_TEMP_DIRECTORY=/tmp/zipline + - FILES_MAX_FILE_SIZE=${FILES_MAX_FILE_SIZE} + - FEATURES_HEALTHCHECK=true + volumes: + - "${APP_DATA_DIR}/uploads:/zipline/uploads" + - "${APP_DATA_DIR}/public:/zipline/public" + - "${APP_DATA_DIR}/themes:/zipline/themes" + read_only: true + tmpfs: + - /tmp:mode=1777,size=256m + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/healthcheck"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + zipline-internal: + internal: true + 1panel-network: + external: true diff --git a/apps/zipline/4.8.0/scripts/credentials.sh b/apps/zipline/4.8.0/scripts/credentials.sh new file mode 100644 index 000000000..f0d6b6cf2 --- /dev/null +++ b/apps/zipline/4.8.0/scripts/credentials.sh @@ -0,0 +1,290 @@ +#!/usr/bin/env bash +set -euo pipefail + +ZIPLINE_STORED_POSTGRES_PASSWORD="" +ZIPLINE_STORED_CORE_SECRET="" +ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="" +ZIPLINE_SNAPSHOT_CORE_SECRET="" + +zipline_fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +zipline_read_env_value() { + local file="$1" + local key="$2" + local value + [[ -f "$file" && ! -L "$file" ]] || return 1 + value="$(awk -v key="$key" ' + index($0, key "=") == 1 { + count++ + value = substr($0, length(key) + 2) + } + END { + if (count != 1) exit 1 + print value + } + ' "$file")" || return 1 + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +zipline_resolve_data_dir() { + local root_dir="$1" + local raw="$2" + local clean current resolved part + local -a parts=() + [[ -n "$raw" && "$raw" != /* ]] || zipline_fail "APP_DATA_DIR must be a non-empty relative path" + [[ ! "$raw" =~ [[:cntrl:]] ]] || zipline_fail "APP_DATA_DIR contains control characters" + case "$raw" in + *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + zipline_fail "APP_DATA_DIR contains unsupported characters" + ;; + esac + clean="${raw#./}" + [[ -n "$clean" ]] || zipline_fail "APP_DATA_DIR must not resolve to the version root" + IFS=/ read -r -a parts <<< "$clean" + current="$root_dir" + for part in "${parts[@]}"; do + [[ -n "$part" && "$part" != . && "$part" != .. ]] || zipline_fail "APP_DATA_DIR contains traversal" + current="$current/$part" + [[ ! -L "$current" ]] || zipline_fail "APP_DATA_DIR must not contain symbolic-link components" + done + resolved="$(realpath -m -- "$root_dir/$clean")" || zipline_fail "APP_DATA_DIR could not be resolved" + case "$resolved" in + "$root_dir"/*) ;; + *) zipline_fail "APP_DATA_DIR must stay inside the application version directory" ;; + esac + printf '%s\n' "$resolved" +} + +zipline_validate_postgres_password() { + [[ "$1" =~ ^[A-Za-z0-9]{32,128}$ ]] +} + +zipline_validate_core_secret() { + [[ "$1" =~ ^[A-Fa-f0-9]{64,128}$ ]] +} + +zipline_generate_postgres_password() { + local material + material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)" + [[ ${#material} -ge 48 ]] || zipline_fail "unable to generate sufficient database password material" + printf '%s\n' "${material:0:48}" +} + +zipline_write_env_credentials() { + local env_file="$1" + local postgres_password="$2" + local core_secret="$3" + local temp_file + temp_file="$(mktemp "${env_file}.zipline-tmp.XXXXXX")" || zipline_fail "unable to stage Zipline environment update" + if ! ZIPLINE_DB_PASSWORD="$postgres_password" ZIPLINE_CORE_SECRET="$core_secret" awk ' + BEGIN { + db_password = ENVIRON["ZIPLINE_DB_PASSWORD"] + core_secret = ENVIRON["ZIPLINE_CORE_SECRET"] + } + /^POSTGRES_PASSWORD=/ { + if (!db_written++) print "POSTGRES_PASSWORD=" db_password + next + } + /^CORE_SECRET=/ { + if (!core_written++) print "CORE_SECRET=" core_secret + next + } + { print } + END { + if (!db_written) print "POSTGRES_PASSWORD=" db_password + if (!core_written) print "CORE_SECRET=" core_secret + } + ' "$env_file" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to render Zipline environment update" + fi + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$env_file" + chmod 600 "$env_file" +} + +zipline_write_secret_state() { + local data_dir="$1" + local postgres_password="$2" + local core_secret="$3" + local secret_file="$data_dir/.zipline-secrets" + local temp_file + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "Zipline data directory is unavailable" + [[ ! -L "$secret_file" ]] || zipline_fail "Zipline secret state must not be a symbolic link" + temp_file="$(mktemp "${secret_file}.tmp.XXXXXX")" || zipline_fail "unable to stage Zipline secret state" + if ! printf 'POSTGRES_PASSWORD=%s\nCORE_SECRET=%s\n' "$postgres_password" "$core_secret" > "$temp_file"; then + rm -f -- "$temp_file" + zipline_fail "unable to write Zipline secret state" + fi + chmod 600 "$temp_file" + chown 0:0 -- "$temp_file" + mv -f -- "$temp_file" "$secret_file" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_load_secret_state() { + local data_dir="$1" + local secret_file="$data_dir/.zipline-secrets" + [[ -e "$secret_file" || -L "$secret_file" ]] || return 1 + [[ -f "$secret_file" && ! -L "$secret_file" ]] || zipline_fail "Zipline secret state is not a regular file" + ZIPLINE_STORED_POSTGRES_PASSWORD="$(zipline_read_env_value "$secret_file" POSTGRES_PASSWORD)" || + zipline_fail "Zipline secret state is incomplete" + ZIPLINE_STORED_CORE_SECRET="$(zipline_read_env_value "$secret_file" CORE_SECRET)" || + zipline_fail "Zipline secret state is incomplete" + zipline_validate_postgres_password "$ZIPLINE_STORED_POSTGRES_PASSWORD" || + zipline_fail "Zipline secret state has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_STORED_CORE_SECRET" || + zipline_fail "Zipline secret state has an invalid session secret" + chmod 600 "$secret_file" + chown 0:0 -- "$secret_file" +} + +zipline_init_credentials() { + local env_file="$1" + local data_dir="$2" + local postgres_password core_secret stored + local postgres_default=false core_default=false + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + postgres_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || + zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || + zipline_fail "CORE_SECRET is missing from Zipline .env" + [[ -n "$postgres_password" && "$postgres_password" != generate ]] || postgres_default=true + [[ -n "$core_secret" && "$core_secret" != generate ]] || core_default=true + + if zipline_load_secret_state "$data_dir"; then + if [[ "$postgres_default" == true ]]; then + postgres_password="$ZIPLINE_STORED_POSTGRES_PASSWORD" + elif [[ "$postgres_password" != "$ZIPLINE_STORED_POSTGRES_PASSWORD" ]]; then + zipline_fail "POSTGRES_PASSWORD cannot change while Zipline data is retained" + fi + if [[ "$core_default" == true ]]; then + core_secret="$ZIPLINE_STORED_CORE_SECRET" + elif [[ "$core_secret" != "$ZIPLINE_STORED_CORE_SECRET" ]]; then + zipline_fail "CORE_SECRET cannot change while Zipline data is retained" + fi + else + if [[ -f "$data_dir/postgres/PG_VERSION" && ( "$postgres_default" == true || "$core_default" == true ) ]]; then + zipline_fail "existing PostgreSQL data has no Zipline secret state; supply its original credentials to preserve it" + fi + if [[ "$postgres_default" == true ]]; then + postgres_password="$(zipline_generate_postgres_password)" + fi + if [[ "$core_default" == true ]]; then + core_secret="$(openssl rand -hex 32)" + fi + fi + + zipline_validate_postgres_password "$postgres_password" || + zipline_fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters" + zipline_validate_core_secret "$core_secret" || + zipline_fail "CORE_SECRET must contain 64 to 128 hexadecimal characters" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" +} + +zipline_find_upgrade_snapshot() { + local root_dir="$1" + local env_file="$2" + local current_name="$3" + local current_data_dir="$4" + local current_db="$5" + local current_user="$6" + local temp_root snapshot old_env old_compose old_name old_data old_db old_user + local snapshot_time env_time best_time=-1 tied=false snapshot_uid snapshot_mode old_env_uid old_env_mode + local best_snapshot="" + local -a candidates=() + + temp_root="$(realpath -e -- "${TMPDIR:-/tmp}")" || zipline_fail "1Panel upgrade snapshot directory is unavailable" + [[ -d "$temp_root" ]] || zipline_fail "1Panel upgrade snapshot directory is unavailable" + env_time="$(stat -c '%Y' "$env_file")" || zipline_fail "Zipline .env timestamp is unavailable" + shopt -s nullglob + candidates=("$temp_root"/1panel-app-upgrade-*) + shopt -u nullglob + for snapshot in "${candidates[@]}"; do + [[ -d "$snapshot" && ! -L "$snapshot" ]] || continue + snapshot_uid="$(stat -c '%u' "$snapshot")" || continue + snapshot_mode="$(stat -c '%a' "$snapshot")" || continue + [[ "$snapshot_uid" == 0 ]] || continue + (( (8#$snapshot_mode & 077) == 0 )) || continue + old_env="$snapshot/.env" + old_compose="$snapshot/docker-compose.yml" + [[ -f "$old_env" && ! -L "$old_env" && -f "$old_compose" && ! -L "$old_compose" ]] || continue + old_env_uid="$(stat -c '%u' "$old_env")" || continue + old_env_mode="$(stat -c '%a' "$old_env")" || continue + [[ "$old_env_uid" == 0 ]] || continue + (( (8#$old_env_mode & 077) == 0 )) || continue + grep -Eq 'image:[[:space:]]*"?ghcr\.io/diced/zipline:4\.(6\.5|7\.0|8\.0)@sha256:[[:xdigit:]]{64}"?[[:space:]]*$' "$old_compose" || continue + old_name="$(zipline_read_env_value "$old_env" CONTAINER_NAME)" || continue + [[ "$old_name" == "$current_name" ]] || continue + old_data="$(zipline_read_env_value "$old_env" APP_DATA_DIR)" || zipline_fail "matching 1Panel snapshot lacks APP_DATA_DIR" + old_db="$(zipline_read_env_value "$old_env" POSTGRES_DB)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_DB" + old_user="$(zipline_read_env_value "$old_env" POSTGRES_USER)" || zipline_fail "matching 1Panel snapshot lacks POSTGRES_USER" + [[ "$old_data" == "$current_data_dir" && "$old_db" == "$current_db" && "$old_user" == "$current_user" ]] || + zipline_fail "matching 1Panel snapshot does not preserve the configured data path and database identity" + ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD="$(zipline_read_env_value "$old_env" POSTGRES_PASSWORD)" || + zipline_fail "matching 1Panel snapshot lacks its database password" + ZIPLINE_SNAPSHOT_CORE_SECRET="$(zipline_read_env_value "$old_env" CORE_SECRET)" || + zipline_fail "matching 1Panel snapshot lacks its session secret" + zipline_validate_postgres_password "$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" || + zipline_fail "matching 1Panel snapshot has an invalid database password" + zipline_validate_core_secret "$ZIPLINE_SNAPSHOT_CORE_SECRET" || + zipline_fail "matching 1Panel snapshot has an invalid session secret" + snapshot_time="$(stat -c '%Y' "$snapshot")" || zipline_fail "1Panel upgrade snapshot timestamp is unavailable" + if (( snapshot_time > best_time )); then + best_time="$snapshot_time" + best_snapshot="$snapshot" + tied=false + elif (( snapshot_time == best_time )); then + tied=true + fi + done + [[ -n "$best_snapshot" ]] || zipline_fail "could not find this Zipline install's 1Panel upgrade snapshot" + [[ "$tied" == false ]] || zipline_fail "multiple matching 1Panel upgrade snapshots are ambiguous" + (( best_time <= env_time && env_time - best_time <= 600 )) || + zipline_fail "matching 1Panel upgrade snapshot is stale" +} + +zipline_restore_upgrade_credentials() { + local root_dir="$1" + local env_file="$2" + local raw_data_dir data_dir current_name current_db current_user + local postgres_password core_secret current_password current_core_secret + [[ -f "$env_file" && ! -L "$env_file" ]] || zipline_fail "Zipline .env file is unavailable" + current_name="$(zipline_read_env_value "$env_file" CONTAINER_NAME)" || zipline_fail "CONTAINER_NAME is missing from Zipline .env" + [[ "$current_name" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]] || zipline_fail "CONTAINER_NAME is invalid" + raw_data_dir="$(zipline_read_env_value "$env_file" APP_DATA_DIR)" || zipline_fail "APP_DATA_DIR is missing from Zipline .env" + data_dir="$(zipline_resolve_data_dir "$root_dir" "$raw_data_dir")" + [[ -d "$data_dir" && ! -L "$data_dir" ]] || zipline_fail "configured Zipline data directory is missing" + current_db="$(zipline_read_env_value "$env_file" POSTGRES_DB)" || zipline_fail "POSTGRES_DB is missing from Zipline .env" + current_user="$(zipline_read_env_value "$env_file" POSTGRES_USER)" || zipline_fail "POSTGRES_USER is missing from Zipline .env" + current_password="$(zipline_read_env_value "$env_file" POSTGRES_PASSWORD)" || zipline_fail "POSTGRES_PASSWORD is missing from Zipline .env" + current_core_secret="$(zipline_read_env_value "$env_file" CORE_SECRET)" || zipline_fail "CORE_SECRET is missing from Zipline .env" + + zipline_find_upgrade_snapshot "$root_dir" "$env_file" "$current_name" "$raw_data_dir" "$current_db" "$current_user" + postgres_password="$ZIPLINE_SNAPSHOT_POSTGRES_PASSWORD" + core_secret="$ZIPLINE_SNAPSHOT_CORE_SECRET" + if zipline_load_secret_state "$data_dir"; then + [[ "$ZIPLINE_STORED_POSTGRES_PASSWORD" == "$postgres_password" && "$ZIPLINE_STORED_CORE_SECRET" == "$core_secret" ]] || + zipline_fail "Zipline secret state does not match the 1Panel upgrade snapshot" + fi + if [[ -n "$current_password" && "$current_password" != generate && "$current_password" != "$postgres_password" ]]; then + zipline_fail "POSTGRES_PASSWORD changed during upgrade; refusing to alter the existing database credential" + fi + if [[ -n "$current_core_secret" && "$current_core_secret" != generate && "$current_core_secret" != "$core_secret" ]]; then + zipline_fail "CORE_SECRET changed during upgrade; refusing to rotate the existing session secret" + fi + zipline_validate_postgres_password "$postgres_password" || zipline_fail "saved database password is invalid" + zipline_validate_core_secret "$core_secret" || zipline_fail "saved session secret is invalid" + zipline_write_env_credentials "$env_file" "$postgres_password" "$core_secret" + zipline_write_secret_state "$data_dir" "$postgres_password" "$core_secret" + printf '%s\n' "Zipline secrets were restored from the matching 1Panel upgrade snapshot." +} diff --git a/apps/zipline/4.8.0/scripts/init.sh b/apps/zipline/4.8.0/scripts/init.sh new file mode 100755 index 000000000..868a2fb3f --- /dev/null +++ b/apps/zipline/4.8.0/scripts/init.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +validate_ipv4() { + local key="$1" + local value="$2" + local octet + local -a octets + [[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address" + IFS=. read -r -a octets <<< "$value" + for octet in "${octets[@]}"; do + ((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet" + done +} + +validate_port() { + local value="$1" + [[ "$value" =~ ^[0-9]+$ ]] || fail "PANEL_APP_PORT_HTTP must be an integer" + ((10#$value >= 1 && 10#$value <= 65535)) || fail "PANEL_APP_PORT_HTTP must be between 1 and 65535" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Zipline init must run as root" +command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials" +source "${ROOT_DIR}/scripts/credentials.sh" + +validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)" +validate_port "$(read_env_value PANEL_APP_PORT_HTTP)" + +postgres_db="$(read_env_value POSTGRES_DB)" +postgres_user="$(read_env_value POSTGRES_USER)" +[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid" +[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid" + +max_file_size="$(read_env_value FILES_MAX_FILE_SIZE)" +[[ "$max_file_size" =~ ^[1-9][0-9]*(b|kb|mb|gb|tb)$ ]] || + fail "FILES_MAX_FILE_SIZE must be a positive byte quantity such as 100mb" + +data_raw="$(read_env_value APP_DATA_DIR)" +[[ -n "$data_raw" && "$data_raw" != /* ]] || fail "APP_DATA_DIR must be a non-empty relative path" +case "$data_raw" in + *$'\n'* | *$'\r'* | *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*) + fail "APP_DATA_DIR contains unsupported characters" + ;; +esac + +relative_data="${data_raw#./}" +[[ -n "$relative_data" ]] || fail "APP_DATA_DIR must not resolve to the version root" +current="$ROOT_DIR" +IFS=/ read -r -a components <<< "$relative_data" +for component in "${components[@]}"; do + [[ -n "$component" && "$component" != . && "$component" != .. ]] || fail "APP_DATA_DIR contains traversal" + current="$current/$component" + [[ ! -L "$current" ]] || fail "APP_DATA_DIR must not contain symbolic-link components" +done + +data_dir="$(realpath -m -- "$ROOT_DIR/$relative_data")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR must stay inside the application version directory" ;; +esac + +install -d -m 0700 -- "$data_dir/postgres" +install -d -m 0750 -- "$data_dir/uploads" "$data_dir/public" "$data_dir/themes" +resolved_data="$(realpath -e -- "$data_dir")" +case "$resolved_data" in + "$ROOT_DIR"/*) ;; + *) fail "APP_DATA_DIR resolves outside the application version directory" ;; +esac +chown -R 70:70 -- "$resolved_data/postgres" +chown -R 1000:1000 -- "$resolved_data/uploads" "$resolved_data/public" "$resolved_data/themes" +chmod 0700 -- "$resolved_data/postgres" + +zipline_init_credentials "$ENV_FILE" "$resolved_data" +chmod 600 "$ENV_FILE" diff --git a/apps/zipline/4.8.0/scripts/uninstall.sh b/apps/zipline/4.8.0/scripts/uninstall.sh new file mode 100755 index 000000000..42b518ef4 --- /dev/null +++ b/apps/zipline/4.8.0/scripts/uninstall.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 1Panel removes the installation directory after this hook, including relative APP_DATA_DIR contents. +# Back up required data outside the application directory before uninstalling. +exit 0 diff --git a/apps/zipline/4.8.0/scripts/upgrade.sh b/apps/zipline/4.8.0/scripts/upgrade.sh new file mode 100755 index 000000000..8d6e8ec31 --- /dev/null +++ b/apps/zipline/4.8.0/scripts/upgrade.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +source "${ROOT_DIR}/scripts/credentials.sh" + +zipline_restore_upgrade_credentials "$ROOT_DIR" "$ENV_FILE" + +command -v docker >/dev/null 2>&1 || zipline_fail "Docker is required to verify the Zipline migration before upgrading" +command -v timeout >/dev/null 2>&1 || zipline_fail "timeout is required to verify the Zipline migration before upgrading" + +compose=(docker compose --project-directory "$ROOT_DIR" -f "$ROOT_DIR/docker-compose.yml") +"${compose[@]}" up -d zipline-postgres + +postgres_db="$(zipline_read_env_value "$ENV_FILE" POSTGRES_DB)" || + zipline_fail "POSTGRES_DB is missing from Zipline .env" +postgres_user="$(zipline_read_env_value "$ENV_FILE" POSTGRES_USER)" || + zipline_fail "POSTGRES_USER is missing from Zipline .env" + +ready=false +for attempt in {1..60}; do + if "${compose[@]}" exec -T zipline-postgres pg_isready -q -U "$postgres_user" -d "$postgres_db"; then + ready=true + break + fi + sleep 2 +done +[[ "$ready" == true ]] || zipline_fail "Zipline PostgreSQL did not become ready for the staged upgrade" + +migration_state="$(timeout 15 "${compose[@]}" exec -T zipline-postgres sh -ec 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atq -c "SELECT CASE WHEN count(*) = 1 AND bool_and(finished_at IS NOT NULL AND rolled_back_at IS NULL AND applied_steps_count >= 1) THEN '\''ok'\'' ELSE '\''bad'\'' END FROM public._prisma_migrations WHERE migration_name = '\''20260806081144_remove_version_api'\'' AND checksum = '\''08368d44344f934f07757dc014bd3a2022b14a8b377e1f8f5fb2d4836c3dc1ce'\'';"')" || + zipline_fail "Zipline 4.7 migration history could not be verified; keep the existing version and repair the database before upgrading" +[[ "$migration_state" == ok ]] || + zipline_fail "Zipline 4.7 migration is missing or incomplete; keep the existing version and allow its migration to finish before upgrading" + +printf '%s\n' "Zipline 4.7 migration verified; proceeding with the 4.8 upgrade." diff --git a/apps/zipline/README.md b/apps/zipline/README.md index cdff969ed..3825cb1b8 100644 --- a/apps/zipline/README.md +++ b/apps/zipline/README.md @@ -16,7 +16,9 @@ Zipline 是自托管的文件分享和 URL 缩短平台,提供用户管理、 ## 数据持久化 -`APP_DATA_DIR` 中的 `postgres`、`uploads`、`public` 和 `themes` 分别保存数据库、上传文件、公开资源和自定义主题。卸载不会删除这些目录,请定期独立备份。 +`APP_DATA_DIR` 中的 `postgres`、`uploads`、`public` 和 `themes` 分别保存数据库、上传文件、公开资源和自定义主题。升级时保持现有 `APP_DATA_DIR` 不变。1Panel 卸载应用时会删除应用安装目录,因此位于该目录内的相对数据目录也会被删除;卸载前请先将数据备份到应用目录之外。 + +从 `4.6.5` 升级到 `4.8.0` 时,先升级到 `4.7.0` 并等待应用完成数据库迁移、恢复健康后,再升级到 `4.8.0`。升级前请备份 PostgreSQL 数据;升级期间保留现有 `APP_DATA_DIR` 和数据库身份配置。 ## 安全与部署风险 @@ -38,12 +40,13 @@ Zipline is a self-hosted file sharing and URL shortening platform with user mana ## Usage Notes - Access the service at `http://:`. It binds to `127.0.0.1` by default; publish it through an HTTPS reverse proxy for remote access. The first account created becomes the administrator. -- `APP_DATA_DIR` contains PostgreSQL data, uploads, public assets, and custom themes. Uninstallation preserves these directories. +- `APP_DATA_DIR` contains PostgreSQL data, uploads, public assets, and custom themes. Keep its existing value during upgrades. 1Panel removes the app installation directory during uninstall, including relative data directories beneath it; back up the data outside the app directory before uninstalling. +- To upgrade an existing `4.6.5` installation to `4.8.0`, upgrade to `4.7.0` first and wait for its database migrations and health check to finish. Back up PostgreSQL before upgrading, and keep the existing `APP_DATA_DIR` and database identity settings. - Zipline runs as UID/GID `1000:1000`; PostgreSQL runs as `70:70` on an internal-only network. Both containers drop all capabilities, use read-only root filesystems, and enable `no-new-privileges`. ## Security Note -The audited official image snapshot contains upstream vulnerabilities. Two Critical `CVE-2026-59873` findings are confined to npm/corepack tooling that the default service entrypoint does not invoke. `CVE-2026-15074` in `@fastify/static 9.1.3` bypasses route-scoped guards but cannot escape a configured static root; Zipline exposes `build/client` as intentionally public, registers its temporary root with `serve:false`, and sends authenticated exports by a fixed server-selected filename. Other High findings are confined to build tooling, Prisma migration configuration, static schema/router dependencies, or features such as HTTP/2 and RSC that the default service does not enable. These are default-path reachability exceptions, not claims that the packages are absent from later images. A new image resolved from the moving `latest` tag, running package managers in the container, loading untrusted Prisma configuration, or replacing the official entrypoint invalidates them. +The prior audit applies to the official `4.6.5` image snapshot only. It found two Critical `CVE-2026-59873` findings confined to npm/corepack tooling that the default service entrypoint does not invoke. `CVE-2026-15074` in `@fastify/static 9.1.3` bypasses route-scoped guards but cannot escape a configured static root; Zipline exposes `build/client` as intentionally public, registers its temporary root with `serve:false`, and sends authenticated exports by a fixed server-selected filename. Other High findings are confined to build tooling, Prisma migration configuration, static schema/router dependencies, or features such as HTTP/2 and RSC that the default service does not enable. These are default-path reachability exceptions, not claims that the packages are absent from later images. The `4.7.0` and `4.8.0` image CVEs have not been assessed in this maintenance run. ## References diff --git a/apps/zipline/latest/scripts/uninstall.sh b/apps/zipline/latest/scripts/uninstall.sh index 9365d9925..42b518ef4 100755 --- a/apps/zipline/latest/scripts/uninstall.sh +++ b/apps/zipline/latest/scripts/uninstall.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -# Persistent data is intentionally retained for recovery or reinstallation. +# 1Panel removes the installation directory after this hook, including relative APP_DATA_DIR contents. +# Back up required data outside the application directory before uninstalling. exit 0