From 18fcd99f55ed20ba24ef29f89b8f05980872efbe Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:28:52 +0800 Subject: [PATCH] Update grocy app Rebuilt-from-PR: #6278 Source-PR: https://github.com/okxlin/appstore/pull/6278 Maintainer-workflow: appstore-pr-maintainer --- apps/grocy/4.6.0/.env.sample | 6 -- apps/grocy/4.6.0/data.yml | 83 ------------------ apps/grocy/4.6.0/scripts/init.sh | 33 ------- apps/grocy/4.7.0/.env.sample | 6 ++ apps/grocy/4.7.0/data.yml | 83 ++++++++++++++++++ apps/grocy/{4.6.0 => 4.7.0}/data/.gitkeep | 0 .../grocy/{4.6.0 => 4.7.0}/docker-compose.yml | 2 +- apps/grocy/{4.6.0 => 4.7.0}/scripts/.gitkeep | 0 apps/grocy/4.7.0/scripts/init.sh | 80 +++++++++++++++++ .../{4.6.0 => 4.7.0}/scripts/uninstall.sh | 0 .../grocy/{4.6.0 => 4.7.0}/scripts/upgrade.sh | 0 apps/grocy/README_en.md | 17 ---- apps/grocy/data.yml | 35 ++++---- apps/grocy/logo.png | Bin 4644 -> 3481 bytes 14 files changed, 189 insertions(+), 156 deletions(-) delete mode 100644 apps/grocy/4.6.0/.env.sample delete mode 100644 apps/grocy/4.6.0/data.yml delete mode 100755 apps/grocy/4.6.0/scripts/init.sh create mode 100644 apps/grocy/4.7.0/.env.sample create mode 100644 apps/grocy/4.7.0/data.yml rename apps/grocy/{4.6.0 => 4.7.0}/data/.gitkeep (100%) rename apps/grocy/{4.6.0 => 4.7.0}/docker-compose.yml (89%) rename apps/grocy/{4.6.0 => 4.7.0}/scripts/.gitkeep (100%) create mode 100755 apps/grocy/4.7.0/scripts/init.sh rename apps/grocy/{4.6.0 => 4.7.0}/scripts/uninstall.sh (100%) rename apps/grocy/{4.6.0 => 4.7.0}/scripts/upgrade.sh (100%) delete mode 100644 apps/grocy/README_en.md diff --git a/apps/grocy/4.6.0/.env.sample b/apps/grocy/4.6.0/.env.sample deleted file mode 100644 index 755b4b6df..000000000 --- a/apps/grocy/4.6.0/.env.sample +++ /dev/null @@ -1,6 +0,0 @@ -CONTAINER_NAME="grocy" -APP_DATA_DIR="./data" -PANEL_APP_PORT_HTTP=80 -TZ="Asia/Shanghai" -PUID="1000" -PGID="1000" diff --git a/apps/grocy/4.6.0/data.yml b/apps/grocy/4.6.0/data.yml deleted file mode 100644 index 50d714c44..000000000 --- a/apps/grocy/4.6.0/data.yml +++ /dev/null @@ -1,83 +0,0 @@ -additionalProperties: - formFields: - - default: 80 - 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: 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: "1000" - edit: true - envKey: PUID - labelEn: User ID - labelZh: 用户 ID - label: - en: User ID - zh: 用户 ID - zh-Hant: 使用者 ID - ja: ユーザー ID - ko: 사용자 ID - ru: ID пользователя - ms: ID Pengguna - pt-br: ID do usuário - required: true - type: number - - default: "1000" - edit: true - envKey: PGID - labelEn: Group ID - labelZh: 用户组 ID - label: - en: Group ID - zh: 用户组 ID - zh-Hant: 使用者群組 ID - ja: グループ ID - ko: 그룹 ID - ru: ID группы - ms: ID Kumpulan - pt-br: ID do grupo - required: true - 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 diff --git a/apps/grocy/4.6.0/scripts/init.sh b/apps/grocy/4.6.0/scripts/init.sh deleted file mode 100755 index 84e0d148d..000000000 --- a/apps/grocy/4.6.0/scripts/init.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" - -DATA_DIR="${APP_DATA_DIR:-./data}" -PUID="${PUID:-1000}" -PGID="${PGID:-1000}" - -# Strip matching single or double quotes from .env values if present -if [[ -f "$ENV_FILE" ]]; then - while IFS='=' read -r key value; do - [[ -z "$key" || "$key" =~ ^# ]] && continue - value="${value#\"}" - value="${value%\"}" - value="${value#\'}" - value="${value%\'}" - case "$key" in - APP_DATA_DIR) DATA_DIR="$value" ;; - PUID) PUID="$value" ;; - PGID) PGID="$value" ;; - esac - done < "$ENV_FILE" -fi - -# Resolve relative path against app version root -if [[ ! "$DATA_DIR" = /* ]]; then - DATA_DIR="$ROOT_DIR/$DATA_DIR" -fi - -mkdir -p "$DATA_DIR" -chown -R "${PUID}:${PGID}" "$DATA_DIR" diff --git a/apps/grocy/4.7.0/.env.sample b/apps/grocy/4.7.0/.env.sample new file mode 100644 index 000000000..5acf43ec7 --- /dev/null +++ b/apps/grocy/4.7.0/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=grocy-compose-check +PANEL_APP_PORT_HTTP=80 +APP_DATA_DIR=./data +PUID=1000 +PGID=1000 +TZ=Asia/Shanghai diff --git a/apps/grocy/4.7.0/data.yml b/apps/grocy/4.7.0/data.yml new file mode 100644 index 000000000..d9c7c6e69 --- /dev/null +++ b/apps/grocy/4.7.0/data.yml @@ -0,0 +1,83 @@ +additionalProperties: + formFields: + - default: 80 + 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: 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: '1000' + edit: true + envKey: PUID + labelEn: User ID + labelZh: 用户 ID + label: + en: User ID + zh: 用户 ID + zh-Hant: 使用者 ID + ja: ユーザー ID + ko: 사용자 ID + ru: ID пользователя + ms: ID Pengguna + pt-br: ID do usuário + required: true + type: number + - default: '1000' + edit: true + envKey: PGID + labelEn: Group ID + labelZh: 用户组 ID + label: + en: Group ID + zh: 用户组 ID + zh-Hant: 使用者群組 ID + ja: グループ ID + ko: 그룹 ID + ru: ID группы + ms: ID Kumpulan + pt-br: ID do grupo + required: true + 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 diff --git a/apps/grocy/4.6.0/data/.gitkeep b/apps/grocy/4.7.0/data/.gitkeep similarity index 100% rename from apps/grocy/4.6.0/data/.gitkeep rename to apps/grocy/4.7.0/data/.gitkeep diff --git a/apps/grocy/4.6.0/docker-compose.yml b/apps/grocy/4.7.0/docker-compose.yml similarity index 89% rename from apps/grocy/4.6.0/docker-compose.yml rename to apps/grocy/4.7.0/docker-compose.yml index 791118319..12db23de3 100644 --- a/apps/grocy/4.6.0/docker-compose.yml +++ b/apps/grocy/4.7.0/docker-compose.yml @@ -1,6 +1,6 @@ services: grocy: - image: "lscr.io/linuxserver/grocy:4.6.0" + image: "lscr.io/linuxserver/grocy:4.7.0" container_name: ${CONTAINER_NAME} restart: unless-stopped networks: diff --git a/apps/grocy/4.6.0/scripts/.gitkeep b/apps/grocy/4.7.0/scripts/.gitkeep similarity index 100% rename from apps/grocy/4.6.0/scripts/.gitkeep rename to apps/grocy/4.7.0/scripts/.gitkeep diff --git a/apps/grocy/4.7.0/scripts/init.sh b/apps/grocy/4.7.0/scripts/init.sh new file mode 100755 index 000000000..f8bcc9578 --- /dev/null +++ b/apps/grocy/4.7.0/scripts/init.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -euo pipefail +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +export PATH + +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" + [[ -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" +} + +PUID="$(configured_value PUID 1000)" +PGID="$(configured_value PGID 1000)" +[[ "$PUID" =~ ^[0-9]+$ ]] || fail "PUID must be numeric" +[[ "$PGID" =~ ^[0-9]+$ ]] || fail "PGID must be numeric" + +DATA_DIR_RAW="$(configured_value APP_DATA_DIR ./data)" +DATA_DIR="$(resolve_app_path APP_DATA_DIR "$DATA_DIR_RAW")" +mkdir -p -- "$DATA_DIR" +[[ "$(resolve_app_path APP_DATA_DIR "$DATA_DIR_RAW")" == "$DATA_DIR" ]] || fail "unsafe APP_DATA_DIR path" +chown -R --no-dereference "$PUID:$PGID" "$DATA_DIR" diff --git a/apps/grocy/4.6.0/scripts/uninstall.sh b/apps/grocy/4.7.0/scripts/uninstall.sh similarity index 100% rename from apps/grocy/4.6.0/scripts/uninstall.sh rename to apps/grocy/4.7.0/scripts/uninstall.sh diff --git a/apps/grocy/4.6.0/scripts/upgrade.sh b/apps/grocy/4.7.0/scripts/upgrade.sh similarity index 100% rename from apps/grocy/4.6.0/scripts/upgrade.sh rename to apps/grocy/4.7.0/scripts/upgrade.sh diff --git a/apps/grocy/README_en.md b/apps/grocy/README_en.md deleted file mode 100644 index be36f0694..000000000 --- a/apps/grocy/README_en.md +++ /dev/null @@ -1,17 +0,0 @@ -# Grocy - -Grocy is a self-hosted household ERP application for groceries, shopping lists, chores, recipes, meal planning, and inventory. - -## Access - -The web interface listens on `PANEL_APP_PORT_HTTP` (default `80`). Follow the upstream documentation for the current initial login credentials and change them after login. - -## Persistence - -Configuration and SQLite data are stored in `APP_DATA_DIR`, mounted at `/config`. `PUID` and `PGID` control file ownership. - -## References - -- [Website](https://grocy.info) -- [GitHub](https://github.com/grocy/grocy) -- [Container image](https://github.com/linuxserver/docker-grocy) diff --git a/apps/grocy/data.yml b/apps/grocy/data.yml index 6790771ac..27504142b 100644 --- a/apps/grocy/data.yml +++ b/apps/grocy/data.yml @@ -1,31 +1,34 @@ name: Grocy tags: - - Tool +- Tool title: 家用 ERP 与物资管理系统 description: 家用 ERP 与物资管理系统 additionalProperties: key: grocy name: Grocy tags: - - Tool + - Tool + type: tool + website: https://grocy.info + document: https://github.com/grocy/grocy + github: https://github.com/grocy/grocy shortDescZh: 家用 ERP 与物资管理系统 shortDescEn: Home ERP and grocery management system + crossVersionUpdate: true + limit: 0 + architectures: + - amd64 + - arm64 description: - en: A self-hosted home ERP system for managing groceries, household chores, and inventory. + en: A self-hosted home ERP system for managing groceries, household chores, and + inventory. zh: 一款自托管的家用 ERP 系统,用于管理食品、家务和库存物资。 zh-Hant: 一款自託管的家用 ERP 系統,用於管理食品、家務和庫存物資。 ja: 自宅ホスト型の家庭用 ERP システム。食料品、家事、在庫を管理。 ko: 식료품, 가사, 재고를 관리하는 자체 호스트형 가정용 ERP 시스템. - ru: Самостоятельно развёртываемая домашняя ERP-система для управления продуктами, делами и запасами. - ms: Sistem ERP rumah yang dihoskan sendiri untuk menguruskan barangan runcit, kerja rumah dan inventori. - pt-br: Sistema ERP doméstico autohospedado para gerenciar mantimentos, tarefas domésticas e inventário. - type: tool - crossVersionUpdate: true - limit: 0 - recommend: 0 - website: https://grocy.info - github: https://github.com/grocy/grocy - document: https://github.com/grocy/grocy - architectures: - - amd64 - - arm64 + ru: Самостоятельно развёртываемая домашняя ERP-система для управления продуктами, + делами и запасами. + ms: Sistem ERP rumah yang dihoskan sendiri untuk menguruskan barangan runcit, + kerja rumah dan inventori. + pt-br: Sistema ERP doméstico autohospedado para gerenciar mantimentos, tarefas + domésticas e inventário. diff --git a/apps/grocy/logo.png b/apps/grocy/logo.png index a982e330a6789e459cd066adbd5078395f2af894..a389ac250c51a8e7445e2246072a788967c6ff8a 100644 GIT binary patch literal 3481 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 Up#t-Ybo2*csAq<5&~d^4AB*~@i2wiq