Compare commits

..

17 Commits

Author SHA1 Message Date
CanbiZ (MickLesk) a9d8e7cf2e restore meilisearch fix 2026-07-30 12:24:34 +02:00
CanbiZ (MickLesk) b128b720b8 Update tools.func 2026-07-30 12:23:28 +02:00
CanbiZ (MickLesk) 1d4d5d7e89 Update tools.func 2026-07-30 12:22:47 +02:00
CanbiZ (MickLesk) 3a5a609c5f tools.func: support dynamic gitlab URL 2026-07-30 12:21:14 +02:00
community-scripts-pr-app[bot] d7bc6b5967 Update CHANGELOG.md (#16163)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:51:30 +00:00
Tim Moore 8045921784 Medusa: convert the non-free unrar source to deb822 format (#16093)
Follow-up to #16072, as requested in review.

Converts the one-line non-free.list entry to a deb822 non-free.sources
block matching mylar3 and sabnzbd, and renames the cleanup rm to match
the new filename.

Also drops the redundant main and contrib components, which the base
debian.sources already provides. Declaring them a second time made apt
emit "Target Packages ... is configured multiple times" warnings on
every build; only non-free is needed for unrar.

Co-authored-by: Tim Moore <tim.moore@ingenuity.com.au>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 10:51:00 +02:00
Michel Roegl-Brunner 089bcd1c09 Revise pull request template for AI compliance
Updated the pull request template to include AI assistance confirmation and additional instructions for AI-generated scripts.
2026-07-30 10:49:28 +02:00
community-scripts-pr-app[bot] b85de433db Update CHANGELOG.md (#16162)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:37:40 +00:00
Darkatek7 763732fc5b romm: remove stale 1.x alembic migrations on update (#16101)
* fix(romm): remove stale 1.x alembic migrations on update

RomM releases before v2.4 shipped dot-version migration files
(1.6.2_.py, 1.7.1_.py, 1.8_.py, 1.8.1_.py, 1.8.2_.py, 1.8.3_.py,
2.0.0_.py) alongside the 4-digit series. When the romm app is
upgraded from a pre-v2.4 install to a current release, these
files survive in /opt/romm/backend/alembic/versions/ and cause
alembic to fail with 'Multiple head revisions are present for
given argument head', preventing the backend from starting.

Add a defensive cleanup step in the update function that runs
right after fetch_and_deploy_gh_release. The cleanup removes any
leftover 1.x and 2.0.0_ migration files plus cached __pycache__
directories. It is a no-op on clean installs of the current
release, and only affects files in the alembic/versions/ folder.

* Apply suggestion from @CrazyWolf13

Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>

---------

Co-authored-by: Sam Heinz <sam@samheinz.com>
Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-07-30 10:37:17 +02:00
community-scripts-pr-app[bot] e456b1fc96 Update CHANGELOG.md (#16161)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:31:38 +00:00
CanbiZ (MickLesk) 30ab4a612d core: add configurable host CA inheritance during bootstrap (#15889)
* Add host CA inheritance for container builds

Introduces `var_inherit_host_ca` (default `auto`) across variable loading, validation, defaults, and persisted app vars. The advanced settings flow now includes a dedicated Host CA Inheritance step and surfaces the selection in the final summary.

Adds `_apply_host_ca_certs_in_container()` to copy host certificates from `/usr/local/share/ca-certificates/*.crt` into the container and refresh trust with `update-ca-certificates` when available. This runs during container setup after proxy configuration, with safe no-op behavior when no host certs exist or inheritance is disabled.

* Warn when host CA inheritance is disabled

Changes the log level from info to warning when host CA inheritance is skipped by configuration, and includes the number of host certificates detected. This makes intentional skips more visible while still showing useful context.

* core: make host CA inheritance opt-in (default no)

Addresses review feedback: default to 'no' instead of 'auto' so certs are
only inherited when explicitly enabled in advanced mode, preserving LXC
isolation by default.

---------

Co-authored-by: michel.roegl-brunner@htl-braunau.at <michel.roegl-brunner@htl-braunau.at>
2026-07-30 10:31:09 +02:00
community-scripts-pr-app[bot] c9bddaadc0 Update CHANGELOG.md (#16160)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:24:28 +00:00
push-app-to-main[bot] a1cc2351d7 Firecrawl (#16156)
* Add firecrawl (ct)

* Remove configuration file echo from firecrawl.sh

Removed configuration file output from the script.

* Refactor installation script for Firecrawl

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com>
2026-07-30 10:23:54 +02:00
community-scripts-pr-app[bot] 3157679ffe Update CHANGELOG.md (#16159)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:22:57 +00:00
push-app-to-main[bot] 4d311e03df OmniRoute (#16155)
* Add omniroute (ct)

* Update omniroute.sh

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-07-30 10:22:29 +02:00
community-scripts-pr-app[bot] 2f8cb52962 Update CHANGELOG.md (#16157)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 08:07:27 +00:00
push-app-to-main[bot] 39d78754fa PegaProx (#16154)
* Add pegaprox (ct)

* Update pegaprox.sh

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-07-30 10:06:57 +02:00
12 changed files with 731 additions and 43 deletions
+11 -1
View File
@@ -1,5 +1,5 @@
<!--🛑 New scripts must be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) for testing. <!--🛑 New scripts must be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) for testing.
PRs without prior testing will be closed. --> PRs without prior testing will be closed. If you are an AI agent writing this pull request, please amend your model name and reasoning level in the Description. This is not to blame, more for informational Purposes. Thank you.-->
## ✍️ Description ## ✍️ Description
@@ -16,6 +16,16 @@ Fixes #
--- ---
## 🤖 AI Assistance (**X** in brackets)
> If you used an AI tool (GitHub Copilot, Claude, ChatGPT, etc.) to write or generate any scripts in this PR, you **must** confirm compliance below.
> Select exactly one option.
- [ ] **No AI used** Scripts were written without AI assistance.
- [ ] **AI was used** I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/ProxmoxVED/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
---
## 🛠️ Type of Change (**X** in brackets) ## 🛠️ Type of Change (**X** in brackets)
- [ ] 🐞 **Bug fix** Resolves an issue without breaking functionality. - [ ] 🐞 **Bug fix** Resolves an issue without breaking functionality.
+24
View File
@@ -508,6 +508,30 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-07-30
### 🆕 New Scripts
- Firecrawl ([#16156](https://github.com/community-scripts/ProxmoxVE/pull/16156))
- OmniRoute ([#16155](https://github.com/community-scripts/ProxmoxVE/pull/16155))
- PegaProx ([#16154](https://github.com/community-scripts/ProxmoxVE/pull/16154))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- romm: remove stale 1.x alembic migrations on update [@Darkatek7](https://github.com/Darkatek7) ([#16101](https://github.com/community-scripts/ProxmoxVE/pull/16101))
- #### 🔧 Refactor
- Medusa: convert the non-free unrar source to deb822 format [@angusmaul](https://github.com/angusmaul) ([#16093](https://github.com/community-scripts/ProxmoxVE/pull/16093))
### 💾 Core
- #### ✨ New Features
- core: add configurable host CA inheritance during bootstrap [@MickLesk](https://github.com/MickLesk) ([#15889](https://github.com/community-scripts/ProxmoxVE/pull/15889))
## 2026-07-29 ## 2026-07-29
### 🚀 Updated Scripts ### 🚀 Updated Scripts
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: esatbayhan
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.firecrawl.dev/
APP="Firecrawl"
var_tags="${var_tags:-scraping;ai;crawler}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-30}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-no}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/firecrawl/apps/api ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "firecrawl" "firecrawl/firecrawl"; then
msg_info "Stopping Services"
systemctl stop firecrawl firecrawl-playwright
msg_ok "Stopped Services"
create_backup /opt/firecrawl/.env
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "firecrawl" "firecrawl/firecrawl" "tarball" "latest" "/opt/firecrawl"
restore_backup
FDB_VERSION="$(awk -F= '/^ARG FDB_VERSION=/{print $2; exit}' /opt/firecrawl/apps/api/Dockerfile)"
if [[ -z "$FDB_VERSION" ]]; then
msg_error "FDB_VERSION pin not found in upstream Dockerfile"
exit 1
fi
if [[ "$(dpkg-query -W -f='${Version}' foundationdb-clients 2>/dev/null)" != "${FDB_VERSION}-"* ]]; then
FDB_ARCH="$(get_system_arch)"
[[ "$FDB_ARCH" == "arm64" ]] && FDB_ARCH="aarch64"
fetch_and_deploy_gh_release "foundationdb-clients" "apple/foundationdb" "binary" "$FDB_VERSION" "/opt/foundationdb-clients" "foundationdb-clients_${FDB_VERSION}-1_${FDB_ARCH}.deb"
fi
msg_info "Building Go Library"
cd /opt/firecrawl/apps/api/sharedLibs/go-html-to-md
$STD go build -o libhtml-to-markdown.so -buildmode=c-shared html-to-markdown.go
msg_ok "Built Go Library"
msg_info "Building Firecrawl API"
source "$HOME/.cargo/env"
cd /opt/firecrawl/apps/api
$STD pnpm install --frozen-lockfile
$STD pnpm build
CI=true $STD pnpm prune --prod --ignore-scripts
msg_ok "Built Firecrawl API"
msg_info "Building Playwright Service"
cd /opt/firecrawl/apps/playwright-service-ts
$STD npm install
$STD npx playwright install chromium --with-deps
$STD npm run build
$STD npm prune --omit=dev
msg_ok "Built Playwright Service"
msg_info "Starting Services"
systemctl start firecrawl-playwright firecrawl
msg_ok "Started Services"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW}Access it using the following URL:${CL}"
echo -e "${GATEWAY}${BGN}http://${IP}:3002${CL}"
+6
View File
@@ -0,0 +1,6 @@
_______ __
/ ____(_)_______ ______________ __ __/ /
/ /_ / / ___/ _ \/ ___/ ___/ __ `/ | /| / / /
/ __/ / / / / __/ /__/ / / /_/ /| |/ |/ / /
/_/ /_/_/ \___/\___/_/ \__,_/ |__/|__/_/
+6
View File
@@ -0,0 +1,6 @@
____ _ ____ __
/ __ \____ ___ ____ (_) __ \____ __ __/ /____
/ / / / __ `__ \/ __ \/ / /_/ / __ \/ / / / __/ _ \
/ /_/ / / / / / / / / / / _, _/ /_/ / /_/ / /_/ __/
\____/_/ /_/ /_/_/ /_/_/_/ |_|\____/\__,_/\__/\___/
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/diegosouzapw/OmniRoute
APP="OmniRoute"
var_tags="${var_tags:-ai;gateway;llm}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-no}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/omniroute ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Checking for Updates"
local LATEST
LATEST=$(npm view omniroute version 2>/dev/null)
if [[ -z "$LATEST" ]]; then
msg_error "Could not determine latest OmniRoute version"
exit
fi
if [[ "$LATEST" == "$(omniroute --version 2>/dev/null)" ]]; then
msg_ok "Already up to date (${LATEST})"
exit
fi
msg_ok "New version available: ${LATEST}"
msg_info "Stopping Service"
systemctl stop omniroute
msg_ok "Stopped Service"
msg_info "Updating OmniRoute to ${LATEST}"
$STD npm install -g omniroute@latest
msg_ok "Updated OmniRoute to ${LATEST}"
msg_info "Starting Service"
systemctl start omniroute
msg_ok "Started Service"
msg_ok "Updated successfully!"
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:20128${CL}"
echo -e "${INFO}${YW} The admin password is stored in /opt/omniroute/.env (INITIAL_PASSWORD)${CL}"
+4
View File
@@ -44,6 +44,10 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm"
find /opt/romm/backend/alembic/versions -maxdepth 1 -type f -name '1.*.py' -delete 2>/dev/null || true
find /opt/romm/backend/alembic/versions -maxdepth 1 -type f -name '2.0.0_.py' -delete 2>/dev/null || true
find /opt/romm/backend -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true
restore_backup restore_backup
msg_info "Updating ROMM" msg_info "Updating ROMM"
+230
View File
@@ -0,0 +1,230 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: esatbayhan
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.firecrawl.dev/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
cmake \
git \
nftables \
pkg-config \
procps \
python3 \
rabbitmq-server \
redis-server
msg_ok "Installed Dependencies"
NODE_VERSION="22" NODE_MODULE="pnpm@11.4.0" setup_nodejs
setup_go
RUST_PROFILE="minimal" setup_rust
PG_VERSION="17" PG_MODULES="cron" setup_postgresql
fetch_and_deploy_gh_release "firecrawl" "firecrawl/firecrawl" "tarball" "latest" "/opt/firecrawl"
msg_info "Configuring FDB"
FDB_VERSION="$(awk -F= '/^ARG FDB_VERSION=/{print $2; exit}' /opt/firecrawl/apps/api/Dockerfile)"
if [[ -z "$FDB_VERSION" ]]; then
msg_error "FDB_VERSION pin not found in upstream Dockerfile"
exit 1
fi
FDB_ARCH="$(get_system_arch)"
[[ "$FDB_ARCH" == "arm64" ]] && FDB_ARCH="aarch64"
fetch_and_deploy_gh_release "foundationdb-clients" "apple/foundationdb" "binary" "$FDB_VERSION" "/opt/foundationdb-clients" "foundationdb-clients_${FDB_VERSION}-1_${FDB_ARCH}.deb"
msg_ok "Configured FDB"
PG_DB_NAME="firecrawl" PG_DB_USER="firecrawl" PG_DB_EXTENSIONS="pgcrypto,pg_cron" PG_DB_CREDS_FILE="/dev/null" setup_postgresql_db
msg_info "Configuring pg_cron"
$STD runuser -u postgres -- psql -c "ALTER SYSTEM SET cron.use_background_workers = 'on';"
systemctl restart postgresql
until runuser -u postgres -- psql -c "SELECT 1;" &>/dev/null; do sleep 1; done
msg_ok "Configured pg_cron"
msg_info "Configuring RabbitMQ"
systemctl enable -q --now rabbitmq-server
until rabbitmqctl status &>/dev/null; do sleep 1; done
RABBITMQ_PASSWORD="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c24)"
$STD rabbitmqctl add_user firecrawl "$RABBITMQ_PASSWORD"
$STD rabbitmqctl set_permissions -p / firecrawl ".*" ".*" ".*"
msg_ok "Configured RabbitMQ"
systemctl enable -q --now redis-server
msg_info "Importing NuQ Schema"
$STD runuser -u postgres -- psql -d firecrawl -f /opt/firecrawl/apps/nuq-postgres/nuq.sql
$STD runuser -u postgres -- psql -d firecrawl -c "GRANT USAGE ON SCHEMA nuq TO firecrawl;"
$STD runuser -u postgres -- psql -d firecrawl -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA nuq TO firecrawl;"
$STD runuser -u postgres -- psql -d firecrawl -c "GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA nuq TO firecrawl;"
$STD runuser -u postgres -- psql -d firecrawl -c "ALTER DEFAULT PRIVILEGES IN SCHEMA nuq GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO firecrawl;"
$STD runuser -u postgres -- psql -d firecrawl -c "ALTER DEFAULT PRIVILEGES IN SCHEMA nuq GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO firecrawl;"
msg_ok "Imported NuQ Schema"
msg_info "Generating Configuration"
cat <<EOF >/opt/firecrawl/.env
ENV=local
HOST=0.0.0.0
PORT=3002
USE_DB_AUTHENTICATION=false
BULL_AUTH_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
REDIS_URL=redis://127.0.0.1:6379
REDIS_RATE_LIMIT_URL=redis://127.0.0.1:6379
PLAYWRIGHT_MICROSERVICE_URL=http://127.0.0.1:3000/scrape
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=${PG_DB_USER}
POSTGRES_PASSWORD=${PG_DB_PASS}
POSTGRES_DB=${PG_DB_NAME}
NUQ_DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}
NUQ_DATABASE_URL_LISTEN=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}
NUQ_BACKEND=pg
NUQ_RABBITMQ_URL=amqp://firecrawl:${RABBITMQ_PASSWORD}@127.0.0.1:5672/%2f
WORKER_PORT=3005
EXTRACT_WORKER_PORT=3004
NUQ_WORKER_START_PORT=3006
NUQ_WORKER_COUNT=5
NUQ_PREFETCH_WORKER_PORT=3011
NUQ_RECONCILER_WORKER_PORT=3012
HARNESS_STARTUP_TIMEOUT_MS=60000
LOGGING_LEVEL=INFO
MAX_CONCURRENT_PAGES=10
ALLOW_LOCAL_WEBHOOKS=false
BLOCK_MEDIA=false
OPENAI_API_KEY=
OPENAI_BASE_URL=
OLLAMA_BASE_URL=
MODEL_NAME=
MODEL_EMBEDDING_NAME=
SEARXNG_ENDPOINT=
SEARXNG_ENGINES=
SEARXNG_CATEGORIES=
PROXY_SERVER=
PROXY_USERNAME=
PROXY_PASSWORD=
SELF_HOSTED_WEBHOOK_URL=
SELF_HOSTED_WEBHOOK_HMAC_SECRET=
EOF
chmod 600 /opt/firecrawl/.env
msg_ok "Generated Configuration"
msg_info "Building Go Library"
cd /opt/firecrawl/apps/api/sharedLibs/go-html-to-md
$STD go build -o libhtml-to-markdown.so -buildmode=c-shared html-to-markdown.go
msg_ok "Built Go Library"
msg_info "Building Firecrawl API"
cd /opt/firecrawl/apps/api
$STD pnpm install --frozen-lockfile
$STD pnpm build
CI=true $STD pnpm prune --prod --ignore-scripts
msg_ok "Built Firecrawl API"
msg_info "Building Playwright Service"
cd /opt/firecrawl/apps/playwright-service-ts
$STD npm install
$STD npx playwright install chromium --with-deps
$STD npm run build
$STD npm prune --omit=dev
msg_ok "Built Playwright Service"
msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/firecrawl-playwright.service
[Unit]
Description=Firecrawl Playwright Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/firecrawl/apps/playwright-service-ts
EnvironmentFile=/opt/firecrawl/.env
Environment=PORT=3000
# /opt/firecrawl/.env also contains the API PORT=3002; force Playwright's private port here.
ExecStart=/usr/bin/env PORT=3000 /usr/bin/node /opt/firecrawl/apps/playwright-service-ts/dist/api.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/firecrawl.service
[Unit]
Description=Firecrawl API and Workers
After=network.target postgresql.service redis-server.service rabbitmq-server.service firecrawl-playwright.service
Requires=postgresql.service redis-server.service rabbitmq-server.service firecrawl-playwright.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/firecrawl/apps/api
EnvironmentFile=/opt/firecrawl/.env
Environment=NODE_ENV=production
# Upstream uses --start-docker to skip install/build and start compiled node dist entrypoints.
ExecStart=/usr/bin/node /opt/firecrawl/apps/api/dist/src/harness.js --start-docker
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now firecrawl-playwright
systemctl enable -q --now firecrawl
msg_ok "Created Services"
msg_info "Configuring Firewall"
cat <<'EOF' >/etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet firecrawl_filter {
chain input {
type filter hook input priority 0; policy drop;
iif "lo" accept
ct state established,related accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
udp sport 67 udp dport 68 accept
udp sport 547 udp dport 546 accept
tcp dport { 22, 3002 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
EOF
systemctl enable -q nftables
systemctl restart nftables
msg_ok "Configured Firewall"
motd_ssh
customize
cleanup_lxc
+7 -3
View File
@@ -19,12 +19,16 @@ $STD apt install -y \
git-core \ git-core \
mediainfo mediainfo
cat <<EOF >/etc/apt/sources.list.d/non-free.list cat <<EOF >/etc/apt/sources.list.d/non-free.sources
deb https://deb.debian.org/debian trixie main contrib non-free non-free-firmware Types: deb
URIs: https://deb.debian.org/debian
Suites: trixie
Components: non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF EOF
$STD apt update $STD apt update
$STD apt install -y unrar $STD apt install -y unrar
rm /etc/apt/sources.list.d/non-free.list rm /etc/apt/sources.list.d/non-free.sources
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
msg_info "Installing Medusa" msg_info "Installing Medusa"
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/diegosouzapw/OmniRoute
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
NODE_VERSION="24" setup_nodejs
msg_info "Installing OmniRoute"
$STD npm install -g omniroute@latest
msg_ok "Installed OmniRoute"
msg_info "Configuring OmniRoute"
mkdir -p /opt/omniroute
cat <<EOF >/opt/omniroute/.env
JWT_SECRET=$(openssl rand -base64 48)
API_KEY_SECRET=$(openssl rand -hex 32)
STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)
STORAGE_ENCRYPTION_KEY_VERSION=v1
INITIAL_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-20)
PORT=20128
OMNIROUTE_SERVER_HOST=0.0.0.0
EOF
chmod 600 /opt/omniroute/.env
msg_ok "Configured OmniRoute"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/omniroute.service
[Unit]
Description=OmniRoute AI Gateway
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/omniroute
Environment=DATA_DIR=/opt/omniroute
ExecStart=/usr/bin/omniroute
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now omniroute
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc
+146 -15
View File
@@ -1130,6 +1130,7 @@ base_settings() {
APT_CACHER=${var_apt_cacher:-""} APT_CACHER=${var_apt_cacher:-""}
APT_CACHER_IP=${var_apt_cacher_ip:-""} APT_CACHER_IP=${var_apt_cacher_ip:-""}
INHERIT_HOST_CA="${var_inherit_host_ca:-no}"
# Runtime check: Verify APT cacher is reachable if configured # Runtime check: Verify APT cacher is reachable if configured
if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then
@@ -1212,7 +1213,7 @@ load_vars_file() {
# Allowed var_* keys # Allowed var_* keys
local VAR_WHITELIST=( local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1409,6 +1410,12 @@ load_vars_file() {
continue continue
fi fi
;; ;;
var_inherit_host_ca)
if [[ "$var_val" != "yes" && "$var_val" != "no" && "$var_val" != "auto" ]]; then
msg_warn "Invalid host CA inheritance value '$var_val' in $file (must be yes/no/auto), ignoring"
continue
fi
;;
var_container_storage | var_template_storage) var_container_storage | var_template_storage)
# Validate that the storage exists and is active on the current node # Validate that the storage exists and is active on the current node
local _storage_status local _storage_status
@@ -1448,7 +1455,7 @@ default_var_settings() {
# Allowed var_* keys (alphabetically sorted) # Allowed var_* keys (alphabetically sorted)
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
local VAR_WHITELIST=( local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
@@ -1531,6 +1538,7 @@ var_ssh=no
# HTTP/HTTPS proxy (optional - for networks requiring a proxy) # HTTP/HTTPS proxy (optional - for networks requiring a proxy)
# var_http_proxy=http://proxy.local:8080 # var_http_proxy=http://proxy.local:8080
# var_http_no_proxy=localhost,127.0.0.1,.local # var_http_no_proxy=localhost,127.0.0.1,.local
# var_inherit_host_ca=no
# Features/Tags/verbosity # Features/Tags/verbosity
var_fuse=no var_fuse=no
@@ -1631,7 +1639,7 @@ get_app_defaults_path() {
if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
declare -ag VAR_WHITELIST=( declare -ag VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1781,6 +1789,7 @@ _build_current_app_vars_tmp() {
_apt_cacher_ip="${APT_CACHER_IP:-}" _apt_cacher_ip="${APT_CACHER_IP:-}"
_http_proxy="${HTTP_PROXY:-${var_http_proxy:-}}" _http_proxy="${HTTP_PROXY:-${var_http_proxy:-}}"
_http_no_proxy="${HTTP_NO_PROXY:-${var_http_no_proxy:-}}" _http_no_proxy="${HTTP_NO_PROXY:-${var_http_no_proxy:-}}"
_inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-no}}"
_fuse="${ENABLE_FUSE:-no}" _fuse="${ENABLE_FUSE:-no}"
_tun="${ENABLE_TUN:-no}" _tun="${ENABLE_TUN:-no}"
_gpu="${ENABLE_GPU:-no}" _gpu="${ENABLE_GPU:-no}"
@@ -1834,6 +1843,7 @@ _build_current_app_vars_tmp() {
[ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")"
[ -n "$_http_proxy" ] && echo "var_http_proxy=$(_sanitize_value "$_http_proxy")" [ -n "$_http_proxy" ] && echo "var_http_proxy=$(_sanitize_value "$_http_proxy")"
[ -n "$_http_no_proxy" ] && echo "var_http_no_proxy=$(_sanitize_value "$_http_no_proxy")" [ -n "$_http_no_proxy" ] && echo "var_http_no_proxy=$(_sanitize_value "$_http_no_proxy")"
[ -n "$_inherit_host_ca" ] && echo "var_inherit_host_ca=$(_sanitize_value "$_inherit_host_ca")"
[ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")"
[ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")"
@@ -1998,7 +2008,7 @@ advanced_settings() {
TAGS="community-script${var_tags:+;${var_tags}}" TAGS="community-script${var_tags:+;${var_tags}}"
fi fi
local STEP=1 local STEP=1
local MAX_STEP=30 local MAX_STEP=31
# Store values for back navigation - inherit from var_* app defaults # Store values for back navigation - inherit from var_* app defaults
local _ct_type="${var_unprivileged:-1}" local _ct_type="${var_unprivileged:-1}"
@@ -2020,6 +2030,7 @@ advanced_settings() {
local _apt_cacher_ip="${var_apt_cacher_ip:-}" local _apt_cacher_ip="${var_apt_cacher_ip:-}"
local _http_proxy="${var_http_proxy:-}" local _http_proxy="${var_http_proxy:-}"
local _http_no_proxy="${var_http_no_proxy:-}" local _http_no_proxy="${var_http_no_proxy:-}"
local _inherit_host_ca="${var_inherit_host_ca:-no}"
local _mtu="${var_mtu:-}" local _mtu="${var_mtu:-}"
local _sd="${var_searchdomain:-}" local _sd="${var_searchdomain:-}"
local _ns="${var_ns:-}" local _ns="${var_ns:-}"
@@ -2849,9 +2860,47 @@ advanced_settings() {
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 25: Container Timezone # STEP 25: Host CA Inheritance
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
25) 25)
local host_ca_count=0
local host_ca_dir="/usr/local/share/ca-certificates"
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_ca_count=$((host_ca_count + 1))
done
shopt -u nullglob
if [[ $host_ca_count -eq 0 ]]; then
_inherit_host_ca="no"
((STEP++))
continue
fi
local host_ca_default_flag="--defaultno"
[[ "$_inherit_host_ca" == "yes" ]] && host_ca_default_flag=""
if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
--title "HOST CA INHERITANCE" \
--ok-button "Next" --cancel-button "Back" \
$host_ca_default_flag \
--yesno "\nInherit host CA certificates into this container?\n\nDetected on host: ${host_ca_count} certificate(s) in:\n${host_ca_dir}\n\nRecommended for private PKI / TLS-inspection environments.\n\n(App default: ${var_inherit_host_ca:-no})" 16 72; then
_inherit_host_ca="yes"
else
if [ $? -eq 1 ]; then
_inherit_host_ca="no"
else
((STEP--))
continue
fi
fi
((STEP++))
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 26: Container Timezone
# ═══════════════════════════════════════════════════════════════════════════
26)
local tz_hint="$_ct_timezone" local tz_hint="$_ct_timezone"
[[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)"
@@ -2874,9 +2923,9 @@ advanced_settings() {
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 26: Container Protection # STEP 27: Container Protection
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
26) 27)
local protect_default_flag="--defaultno" local protect_default_flag="--defaultno"
[[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag=""
@@ -2898,9 +2947,9 @@ advanced_settings() {
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 27: Device Node Creation (mknod) # STEP 28: Device Node Creation (mknod)
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
27) 28)
local mknod_default_flag="--defaultno" local mknod_default_flag="--defaultno"
[[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" [[ "$_enable_mknod" == "1" ]] && mknod_default_flag=""
@@ -2922,9 +2971,9 @@ advanced_settings() {
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 28: Mount Filesystems # STEP 29: Mount Filesystems
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
28) 29)
local mount_hint="" local mount_hint=""
[[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)"
@@ -2945,9 +2994,9 @@ advanced_settings() {
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 29: Optional host-side post-install hook (path on the Proxmox HOST) # STEP 30: Optional host-side post-install hook (path on the Proxmox HOST)
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
29) 30)
local _hook_prompt="Optional: absolute path to a *.sh file ON THE PROXMOX HOST. local _hook_prompt="Optional: absolute path to a *.sh file ON THE PROXMOX HOST.
It runs as root on the HOST (NOT in the LXC) after the container It runs as root on the HOST (NOT in the LXC) after the container
@@ -2997,9 +3046,9 @@ Leave empty to skip."
;; ;;
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 30: Verbose Mode & Confirmation # STEP 31: Verbose Mode & Confirmation
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
30) 31)
local verbose_default_flag="--defaultno" local verbose_default_flag="--defaultno"
[[ "$_verbose" == "yes" ]] && verbose_default_flag="" [[ "$_verbose" == "yes" ]] && verbose_default_flag=""
@@ -3028,6 +3077,7 @@ Leave empty to skip."
local apt_display="${_apt_cacher:-no}" local apt_display="${_apt_cacher:-no}"
[[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip"
local http_proxy_display="${_http_proxy:-(none)}" local http_proxy_display="${_http_proxy:-(none)}"
local inherit_ca_display="${_inherit_host_ca:-no}"
local post_install_display="${_post_install:-(none)}" local post_install_display="${_post_install:-(none)}"
local post_install_warn="" local post_install_warn=""
@@ -3058,6 +3108,7 @@ Advanced:
Timezone: $tz_display Timezone: $tz_display
APT Cacher: $apt_display APT Cacher: $apt_display
HTTP Proxy: $http_proxy_display HTTP Proxy: $http_proxy_display
Inherit Host CAs: $inherit_ca_display
Verbose: $_verbose Verbose: $_verbose
Post-Install Script: ${post_install_display}${post_install_warn}" Post-Install Script: ${post_install_display}${post_install_warn}"
@@ -3103,6 +3154,7 @@ Advanced:
APT_CACHER_IP="$_apt_cacher_ip" APT_CACHER_IP="$_apt_cacher_ip"
HTTP_PROXY="$_http_proxy" HTTP_PROXY="$_http_proxy"
HTTP_NO_PROXY="$_http_no_proxy" HTTP_NO_PROXY="$_http_no_proxy"
INHERIT_HOST_CA="$_inherit_host_ca"
VERBOSE="$_verbose" VERBOSE="$_verbose"
var_post_install="$_post_install" var_post_install="$_post_install"
@@ -3121,6 +3173,7 @@ Advanced:
var_sdn_vnet="$_sdn_vnet" var_sdn_vnet="$_sdn_vnet"
var_http_proxy="$_http_proxy" var_http_proxy="$_http_proxy"
var_http_no_proxy="$_http_no_proxy" var_http_no_proxy="$_http_no_proxy"
var_inherit_host_ca="$_inherit_host_ca"
# Format optional values # Format optional values
[[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU=""
@@ -4160,6 +4213,83 @@ EOF
msg_ok "Applied HTTP proxy in container" msg_ok "Applied HTTP proxy in container"
} }
# ------------------------------------------------------------------------------
# _apply_host_ca_certs_in_container()
#
# - Copies administrator-provided CA certificates from the Proxmox host into the
# container before base package bootstrap
# - Source: /usr/local/share/ca-certificates/*.crt (Debian convention)
# - Refreshes the container trust store when update-ca-certificates is available
# - No-op when no host certificates are present; failures are non-fatal
# ------------------------------------------------------------------------------
_apply_host_ca_certs_in_container() {
local host_ca_dir="/usr/local/share/ca-certificates"
[[ -z "${CTID:-}" ]] && return 0
local inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-no}}"
local -a host_certs=()
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_certs+=("$cert")
done
shopt -u nullglob
[[ ${#host_certs[@]} -eq 0 ]] && return 0
# Opt-in only: copy host CA certs solely when explicitly enabled.
# Any other value (no/auto/unset) is a silent no-op to preserve LXC isolation.
case "${inherit_host_ca,,}" in
yes | true | 1 | on) ;;
*)
return 0
;;
esac
msg_info "Inheriting host CA certificates into container"
local found=${#host_certs[@]}
local copied=0
local skipped=0
local cert_name
pct exec "$CTID" -- mkdir -p /usr/local/share/ca-certificates >/dev/null 2>&1 || {
msg_warn "Failed to create CA certificate directory in container"
return 0
}
for cert in "${host_certs[@]}"; do
cert_name="$(basename "$cert")"
if [[ ! -r "$cert" || "$cert_name" != *.crt ]]; then
msg_warn "Skipping invalid or unreadable host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
continue
fi
if pct push "$CTID" "$cert" "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1; then
pct exec "$CTID" -- chmod 644 "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1 || true
copied=$((copied + 1))
else
msg_warn "Failed to push host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
fi
done
if [[ $copied -eq 0 ]]; then
msg_warn "No host CA certificates were copied (${found} found, ${skipped} skipped)"
return 0
fi
local refresh_shell="bash"
[[ "$var_os" == "alpine" ]] && refresh_shell="ash"
if pct exec "$CTID" -- "$refresh_shell" -c 'command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates' >/dev/null 2>&1; then
msg_ok "Inherited ${copied} host CA certificate(s) and updated trust store (${skipped} skipped)"
else
msg_warn "Copied ${copied} host CA certificate(s), but trust store update failed or update-ca-certificates is unavailable (${skipped} skipped)"
fi
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# build_container() # build_container()
# #
@@ -4789,6 +4919,7 @@ EOF
local install_exit_code=0 local install_exit_code=0
_apply_http_proxy_in_container _apply_http_proxy_in_container
_apply_host_ca_certs_in_container
# Continue with standard container setup # Continue with standard container setup
if [ "$var_os" == "alpine" ]; then if [ "$var_os" == "alpine" ]; then
+78 -24
View File
@@ -2736,7 +2736,8 @@ get_latest_gl_tag() {
local repo_encoded local repo_encoded
repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g')
local api_base="https://gitlab.com/api/v4/projects/${repo_encoded}/repository/tags" local gitlab_url="${GITLAB_URL:-https://gitlab.com}"
local api_base="${gitlab_url}/api/v4/projects/${repo_encoded}/repository/tags"
local api_timeout="--connect-timeout 10 --max-time 60" local api_timeout="--connect-timeout 10 --max-time 60"
local header=() local header=()
@@ -2818,6 +2819,7 @@ get_latest_gl_tag() {
# Notes: # Notes:
# - Supports CLEAN_INSTALL=1 to wipe target before extracting # - Supports CLEAN_INSTALL=1 to wipe target before extracting
# - Supports GITLAB_TOKEN for private/rate-limited projects # - Supports GITLAB_TOKEN for private/rate-limited projects
# - Supports GITLAB_URL for self-hosted GitLab (default: https://gitlab.com)
# - For repos that only publish tags, not formal GitLab Releases # - For repos that only publish tags, not formal GitLab Releases
# (use fetch_and_deploy_gl_release for proper Releases with assets) # (use fetch_and_deploy_gl_release for proper Releases with assets)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -2861,8 +2863,9 @@ fetch_and_deploy_gl_tag() {
repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g')
# GitLab source tarball URL (no release needed, works for any tag). # GitLab source tarball URL (no release needed, works for any tag).
local gitlab_url="${GITLAB_URL:-https://gitlab.com}"
local version_safe="${resolved_tag//\//-}" local version_safe="${resolved_tag//\//-}"
local tarball_url="https://gitlab.com/${repo}/-/archive/${resolved_tag}/${app_lc}-${version_safe}.tar.gz" local tarball_url="${gitlab_url}/${repo}/-/archive/${resolved_tag}/${app_lc}-${version_safe}.tar.gz"
local tmpdir local tmpdir
tmpdir=$(mktemp -d) || return 1 tmpdir=$(mktemp -d) || return 1
@@ -6947,6 +6950,60 @@ setup_mariadb_db() {
export MARIADB_DB_PASS export MARIADB_DB_PASS
} }
# ------------------------------------------------------------------------------
# Creates a MySQL database and user (for apps that require MySQL, not MariaDB).
#
# Description:
# - Creates database, user and grants using the mysql root socket login
# - The user is created with host '%' because MySQL treats 'localhost'
# (socket) and '127.0.0.1' (TCP) as distinct hosts; apps connecting over
# TCP to 127.0.0.1 would not match a 'localhost' account. mysql-server
# binds to 127.0.0.1 by default, so '%' stays local-only.
#
# Variables:
# MYSQL_DB_NAME - Database name (required)
# MYSQL_DB_USER - Database user (required)
# MYSQL_DB_PASS - Password (optional, generated if unset)
#
# Exports:
# MYSQL_DB_NAME, MYSQL_DB_USER, MYSQL_DB_PASS
#
# Example:
# MYSQL_DB_NAME="fleet" MYSQL_DB_USER="fleet" setup_mysql_db
# ------------------------------------------------------------------------------
setup_mysql_db() {
if [[ -z "${MYSQL_DB_NAME:-}" || -z "${MYSQL_DB_USER:-}" ]]; then
msg_error "MYSQL_DB_NAME and MYSQL_DB_USER must be set before calling setup_mysql_db"
return 65
fi
if [[ -z "${MYSQL_DB_PASS:-}" ]]; then
MYSQL_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
fi
msg_info "Setting up MySQL Database"
$STD mysql -u root -e "CREATE DATABASE \`${MYSQL_DB_NAME//\`/\`\`}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
$STD mysql -u root -e "CREATE USER '${MYSQL_DB_USER//\'/\'\'}'@'%' IDENTIFIED BY '${MYSQL_DB_PASS//\'/\'\'}';"
$STD mysql -u root -e "GRANT ALL ON \`${MYSQL_DB_NAME//\`/\`\`}\`.* TO '${MYSQL_DB_USER//\'/\'\'}'@'%';"
$STD mysql -u root -e "FLUSH PRIVILEGES;"
local app_name="${APPLICATION,,}"
local CREDS_FILE="${MYSQL_DB_CREDS_FILE:-${HOME}/${app_name}.creds}"
{
echo "MySQL Credentials"
echo "Database: $MYSQL_DB_NAME"
echo "User: $MYSQL_DB_USER"
echo "Password: $MYSQL_DB_PASS"
} >>"$CREDS_FILE"
msg_ok "Set up MySQL Database"
export MYSQL_DB_NAME
export MYSQL_DB_USER
export MYSQL_DB_PASS
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Installs or updates MeiliSearch search engine. # Installs or updates MeiliSearch search engine.
# #
@@ -8195,22 +8252,17 @@ EOF
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
get_php_fpm_socket() { get_php_fpm_socket() {
local sock local sock
if [[ -n "${PHP_VERSION:-}" && -S "/run/php/php${PHP_VERSION}-fpm.sock" ]]; then if [[ -n "${PHP_VERSION:-}" && -S "/run/php/php${PHP_VERSION}-fpm.sock" ]]; then
echo "/run/php/php${PHP_VERSION}-fpm.sock" echo "/run/php/php${PHP_VERSION}-fpm.sock"
return 0 return 0
fi fi
sock=$(find /run/php -maxdepth 1 -name "php*-fpm.sock" -type s 2>/dev/null | sort -V | tail -1) sock=$(find /run/php -maxdepth 1 -name "php*-fpm.sock" -type s 2>/dev/null | sort -V | tail -1)
if [[ -z "$sock" ]]; then if [[ -z "$sock" ]]; then
msg_error "No active PHP-FPM socket found under /run/php" msg_error "No active PHP-FPM socket found under /run/php"
return 1 return 1
fi fi
echo "$sock" echo "$sock"
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Enables an nginx site, validates the configuration and reloads the service. # Enables an nginx site, validates the configuration and reloads the service.
# #
@@ -8232,25 +8284,20 @@ get_php_fpm_socket() {
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
nginx_enable_site() { nginx_enable_site() {
local site="${1:-}" local site="${1:-}"
if [[ -z "$site" ]]; then if [[ -z "$site" ]]; then
msg_error "nginx_enable_site: no site name given" msg_error "nginx_enable_site: no site name given"
return 1 return 1
fi fi
if [[ ! -f "/etc/nginx/sites-available/${site}" ]]; then if [[ ! -f "/etc/nginx/sites-available/${site}" ]]; then
msg_error "nginx site config not found: /etc/nginx/sites-available/${site}" msg_error "nginx site config not found: /etc/nginx/sites-available/${site}"
return 1 return 1
fi fi
rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default
ln -sf "/etc/nginx/sites-available/${site}" "/etc/nginx/sites-enabled/${site}" ln -sf "/etc/nginx/sites-available/${site}" "/etc/nginx/sites-enabled/${site}"
if ! $STD nginx -t; then if ! $STD nginx -t; then
msg_error "nginx configuration test failed for site '${site}'" msg_error "nginx configuration test failed for site '${site}'"
return 1 return 1
fi fi
$STD systemctl enable -q nginx $STD systemctl enable -q nginx
safe_service_restart nginx safe_service_restart nginx
} }
@@ -9447,11 +9494,12 @@ get_latest_gitlab_release() {
local temp_file local temp_file
temp_file=$(mktemp) temp_file=$(mktemp)
local gitlab_url="${GITLAB_URL:-https://gitlab.com}"
local http_code local http_code
http_code=$(curl --connect-timeout 10 --max-time 30 -sSL \ http_code=$(curl --connect-timeout 10 --max-time 30 -sSL \
-w "%{http_code}" -o "$temp_file" \ -w "%{http_code}" -o "$temp_file" \
"${header[@]}" \ "${header[@]}" \
"https://gitlab.com/api/v4/projects/$repo_encoded/releases?per_page=1&order_by=released_at&sort=desc" 2>/dev/null) || true "${gitlab_url}/api/v4/projects/$repo_encoded/releases?per_page=1&order_by=released_at&sort=desc" 2>/dev/null) || true
if [[ "$http_code" != "200" ]]; then if [[ "$http_code" != "200" ]]; then
rm -f "$temp_file" rm -f "$temp_file"
@@ -9493,6 +9541,7 @@ get_latest_gitlab_release() {
# Notes: # Notes:
# - Requires `jq` (auto-installed if missing) # - Requires `jq` (auto-installed if missing)
# - Supports GITLAB_TOKEN env var for private/rate-limited repos # - Supports GITLAB_TOKEN env var for private/rate-limited repos
# - Supports GITLAB_URL for self-hosted GitLab (default: https://gitlab.com)
# - Does not modify anything, only checks version state # - Does not modify anything, only checks version state
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
check_for_gl_release() { check_for_gl_release() {
@@ -9504,11 +9553,15 @@ check_for_gl_release() {
local app_lc="${app,,}" local app_lc="${app,,}"
local current_file="$HOME/.${app_lc}" local current_file="$HOME/.${app_lc}"
local gitlab_url="${GITLAB_URL:-https://gitlab.com}"
local gitlab_hostname="${gitlab_url#*://}"
gitlab_hostname="${gitlab_hostname%%/*}"
msg_info "Checking for update: ${app}" msg_info "Checking for update: ${app}"
# DNS check # DNS check
if ! getent hosts gitlab.com >/dev/null 2>&1; then if ! getent hosts "$gitlab_hostname" >/dev/null 2>&1; then
msg_error "Network error: cannot resolve gitlab.com" msg_error "Network error: cannot resolve $gitlab_hostname"
return 6 return 6
fi fi
@@ -9531,7 +9584,7 @@ check_for_gl_release() {
local pinned_encoded="${pinned_version_in//\//%2F}" local pinned_encoded="${pinned_version_in//\//%2F}"
http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gl_check_json" \ http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gl_check_json" \
"${header[@]}" \ "${header[@]}" \
"https://gitlab.com/api/v4/projects/$repo_encoded/releases/$pinned_encoded" 2>/dev/null) || true "${gitlab_url}/api/v4/projects/$repo_encoded/releases/$pinned_encoded" 2>/dev/null) || true
if [[ "$http_code" == "200" ]] && [[ -s "$gl_check_json" ]]; then if [[ "$http_code" == "200" ]] && [[ -s "$gl_check_json" ]]; then
releases_json="[$(<"$gl_check_json")]" releases_json="[$(<"$gl_check_json")]"
fi fi
@@ -9542,7 +9595,7 @@ check_for_gl_release() {
if [[ -z "$releases_json" ]]; then if [[ -z "$releases_json" ]]; then
http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gl_check_json" \ http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gl_check_json" \
"${header[@]}" \ "${header[@]}" \
"https://gitlab.com/api/v4/projects/$repo_encoded/releases?per_page=100&order_by=released_at&sort=desc" 2>/dev/null) || true "${gitlab_url}/api/v4/projects/$repo_encoded/releases?per_page=100&order_by=released_at&sort=desc" 2>/dev/null) || true
if [[ "$http_code" == "200" ]] && [[ -s "$gl_check_json" ]]; then if [[ "$http_code" == "200" ]] && [[ -s "$gl_check_json" ]]; then
releases_json=$(<"$gl_check_json") releases_json=$(<"$gl_check_json")
@@ -9566,7 +9619,7 @@ check_for_gl_release() {
return 22 return 22
elif [[ "$http_code" == "000" || -z "$http_code" ]]; then elif [[ "$http_code" == "000" || -z "$http_code" ]]; then
msg_error "GitLab API connection failed (no response)." msg_error "GitLab API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://gitlab.com/api/v4/version" msg_error "Check your network/DNS: curl -sSL ${gitlab_url}/api/v4/version"
rm -f "$gl_check_json" rm -f "$gl_check_json"
return 7 return 7
else else
@@ -9811,7 +9864,8 @@ fetch_and_deploy_gl_release() {
local repo_encoded local repo_encoded
repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g')
local api_base="https://gitlab.com/api/v4/projects/$repo_encoded/releases" local gitlab_url="${GITLAB_URL:-https://gitlab.com}"
local api_base="${gitlab_url}/api/v4/projects/$repo_encoded/releases"
local api_url local api_url
if [[ "$version" != "latest" ]]; then if [[ "$version" != "latest" ]]; then
api_url="$api_base/$version" api_url="$api_base/$version"
@@ -9860,7 +9914,7 @@ fetch_and_deploy_gl_release() {
msg_error " export GITLAB_TOKEN=\"glpat-your_token_here\"" msg_error " export GITLAB_TOKEN=\"glpat-your_token_here\""
elif [[ "$http_code" == "000" || -z "$http_code" ]]; then elif [[ "$http_code" == "000" || -z "$http_code" ]]; then
msg_error "GitLab API connection failed (no response)." msg_error "GitLab API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://gitlab.com/api/v4/version" msg_error "Check your network/DNS: curl -sSL ${gitlab_url}/api/v4/version"
else else
msg_error "Failed to fetch release metadata (HTTP $http_code)" msg_error "Failed to fetch release metadata (HTTP $http_code)"
fi fi
@@ -9915,7 +9969,7 @@ fetch_and_deploy_gl_release() {
### Tarball Mode ### ### Tarball Mode ###
if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then
local direct_tarball_url="https://gitlab.com/$repo/-/archive/$tag_name/${app_lc}-${version_safe}.tar.gz" local direct_tarball_url="${gitlab_url}/$repo/-/archive/$tag_name/${app_lc}-${version_safe}.tar.gz"
filename="${app_lc}-${version_safe}.tar.gz" filename="${app_lc}-${version_safe}.tar.gz"
_download_source_tarball "$direct_tarball_url" "$tmpdir/$filename" "${header[@]}" || { _download_source_tarball "$direct_tarball_url" "$tmpdir/$filename" "${header[@]}" || {
@@ -9963,7 +10017,7 @@ fetch_and_deploy_gl_release() {
if [[ -z "$url_match" ]]; then if [[ -z "$url_match" ]]; then
local fallback_json local fallback_json
if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "binary" "$asset_pattern" "$tag_name"); then if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "$gitlab_url" "binary" "$asset_pattern" "$tag_name"); then
json="$fallback_json" json="$fallback_json"
tag_name=$(echo "$json" | jq -r '.tag_name // empty') tag_name=$(echo "$json" | jq -r '.tag_name // empty')
[[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name"
@@ -10035,7 +10089,7 @@ fetch_and_deploy_gl_release() {
if [[ -z "$asset_url" ]]; then if [[ -z "$asset_url" ]]; then
local fallback_json local fallback_json
if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "prebuild" "$pattern" "$tag_name"); then if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "$gitlab_url" "prebuild" "$pattern" "$tag_name"); then
json="$fallback_json" json="$fallback_json"
tag_name=$(echo "$json" | jq -r '.tag_name // empty') tag_name=$(echo "$json" | jq -r '.tag_name // empty')
[[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name"
@@ -10088,7 +10142,7 @@ fetch_and_deploy_gl_release() {
if [[ -z "$asset_url" ]]; then if [[ -z "$asset_url" ]]; then
local fallback_json local fallback_json
if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "singlefile" "$pattern" "$tag_name"); then if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "$gitlab_url" "singlefile" "$pattern" "$tag_name"); then
json="$fallback_json" json="$fallback_json"
tag_name=$(echo "$json" | jq -r '.tag_name // empty') tag_name=$(echo "$json" | jq -r '.tag_name // empty')
[[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name"