Merge branch 'community-scripts:main' into step-ca

This commit is contained in:
Joerg Heinemann 2026-02-04 18:36:52 +01:00 committed by GitHub
commit 0f787aacd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 419 additions and 513 deletions

View File

@ -1,6 +0,0 @@
 _ ___ __ ___ __
| | / (_)____/ /_ / (_)____/ /_
| | /| / / / ___/ __ \/ / / ___/ __/
| |/ |/ / (__ ) / / / / (__ ) /_
|__/|__/_/____/_/ /_/_/_/____/\__/

View File

@ -31,7 +31,7 @@ function update_script() {
if check_for_gh_release "wger" "wger-project/wger"; then
msg_info "Stopping Service"
systemctl stop apache2
systemctl stop redis-server nginx celery celery-beat wger
msg_ok "Stopped Service"
msg_info "Backing up Data"
@ -45,6 +45,7 @@ function update_script() {
cp -r /opt/wger_media_backup/. /opt/wger/media
cp /opt/wger_env_backup /opt/wger/.env
rm -rf /opt/wger_media_backup /opt/wger_env_backup
msg_ok "Restored Data"
msg_info "Updating wger"
@ -56,9 +57,9 @@ function update_script() {
$STD uv run python manage.py collectstatic --no-input
msg_ok "Updated wger"
msg_info "Starting Service"
systemctl start apache2
msg_ok "Started Service"
msg_info "Starting Services"
systemctl start redis-server nginx celery celery-beat wger
msg_ok "Started Services"
msg_ok "Updated Successfully"
fi
exit
@ -71,4 +72,4 @@ 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}${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"

View File

@ -1,76 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Dunky13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/cmintey/wishlist
APP="Wishlist"
var_tags="${var_tags:-sharing}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-5}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
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/wishlist ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "wishlist" "cmintey/wishlist"; then
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
msg_info "Stopping Service"
systemctl stop wishlist
msg_ok "Stopped Service"
mkdir -p /opt/wishlist-backup
cp /opt/wishlist/.env /opt/wishlist-backup/.env
cp -a /opt/wishlist/uploads /opt/wishlist-backup
cp -a /opt/wishlist/data /opt/wishlist-backup
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball"
LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false)
msg_info "Updating Wishlist"
cd /opt/wishlist
$STD pnpm install
$STD pnpm svelte-kit sync
$STD pnpm prisma generate
sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist)
export VERSION="${LATEST_APP_VERSION}"
export SHA="${LATEST_APP_VERSION}"
$STD pnpm run build
$STD pnpm prune --prod
chmod +x /opt/wishlist/entrypoint.sh
cp /opt/wishlist-backup/.env /opt/wishlist/.env
cp -a /opt/wishlist-backup/uploads /opt/wishlist
cp -a /opt/wishlist-backup/data /opt/wishlist
rm -rf /opt/wishlist-backup
msg_ok "Updated Wishlist"
msg_info "Starting Service"
systemctl start wishlist
msg_ok "Started Service"
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 "${TAB}${GATEWAY}${BGN}http://${IP}:3280${CL}"

View File

@ -1,72 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: StellaeAlis
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/writefreely/writefreely
# App Default Values
APP="WriteFreely"
var_tags="${var_tags:-writing}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-1024}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
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/writefreely ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "writefreely" "writefreely/writefreely"; then
msg_info "Stopping Services"
systemctl stop writefreely
msg_ok "Stopped Services"
msg_info "Creating Backup"
mkdir -p /tmp/writefreely_backup
cp /opt/writefreely/keys /tmp/writefreely_backup/ 2>/dev/null
cp /opt/writefreely/config.ini /tmp/writefreely_backup/ 2>/dev/null
msg_ok "Created Backup"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_amd64.tar.gz"
msg_info "Restoring Data"
cp /tmp/writefreely_backup/config.ini /opt/writefreely/ 2>/dev/null
cp /tmp/writefreely_backup/keys/* /opt/writefreely/keys/ 2>/dev/null
rm -rf /tmp/writefreely_backup
msg_ok "Restored Data"
msg_info "Running Post-Update Tasks"
cd /opt/writefreely
$STD ./writefreely db migrate
msg_ok "Ran Post-Update Tasks"
msg_info "Starting Services"
systemctl start writefreely
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 "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"

View File

@ -1,5 +1,5 @@
{
"generated": "2026-02-04T07:03:37Z",
"generated": "2026-02-04T13:17:54Z",
"versions": [
{
"slug": "affine",
@ -99,13 +99,6 @@
"pinned": true,
"date": "2026-01-28T15:08:23Z"
},
{
"slug": "piler",
"repo": "jsuto/piler",
"version": "piler-1.4.8",
"pinned": false,
"date": "2025-09-24T06:51:38Z"
},
{
"slug": "pixelfed",
"repo": "pixelfed/pixelfed",
@ -135,18 +128,11 @@
"date": "2026-01-21T19:07:21Z"
},
{
"slug": "wishlist",
"repo": "cmintey/wishlist",
"version": "v0.59.0",
"slug": "wger",
"repo": "wger-project/wger",
"version": "2.4",
"pinned": false,
"date": "2026-01-19T16:42:14Z"
},
{
"slug": "writefreely",
"repo": "writefreely/writefreely",
"version": "v0.16.0",
"pinned": false,
"date": "2025-08-29T19:30:02Z"
"date": "2026-01-18T12:12:02Z"
}
]
}

View File

@ -11,7 +11,7 @@
"interface_port": 9000,
"documentation": "https://nginxui.com/guide/",
"website": "https://nginxui.com",
"logo": "https://raw.githubusercontent.com/0xJacky/nginx-ui/master/app/public/logo.png",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nginx-ui.webp",
"config_path": "/usr/local/etc/nginx-ui/app.ini",
"description": "Nginx UI is a comprehensive web-based interface designed to simplify the management and configuration of Nginx servers. It provides features like online statistics, ChatGPT-powered config assistant, automatic Let's Encrypt certificates, and config file editing with syntax highlighting.",
"install_methods": [
@ -32,10 +32,6 @@
"password": null
},
"notes": [
{
"text": "Create your admin account on first login via the web interface.",
"type": "info"
},
{
"text": "Nginx runs on ports 80/443, Nginx UI management interface on port 9000.",
"type": "info"

View File

@ -1,35 +0,0 @@
{
"name": "Piler",
"slug": "piler",
"categories": [
7
],
"date_created": "2025-12-15",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 80,
"documentation": "https://www.mailpiler.org/",
"config_path": "",
"website": "https://www.mailpiler.org/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/piler.webp",
"description": "Piler is a feature rich open source email archiving solution with support for legal hold, deduplication, full text search, and many more features.",
"install_methods": [
{
"type": "default",
"script": "ct/piler.sh",
"resources": {
"cpu": 4,
"ram": 4096,
"hdd": 20,
"os": "Debian",
"version": "12"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@ -0,0 +1,40 @@
{
"name": "wger",
"slug": "wger",
"categories": [
24
],
"date_created": "2025-02-24",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3000,
"documentation": "https://wger.readthedocs.io/en/latest/index.html",
"website": "https://wger.de",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wger.webp",
"config_path": "/opt/wger/wger.env",
"description": "wger (ˈɡɐ) Workout Manager is a free, open source web application that helps you manage your personal workouts, weight and diet plans and can also be used as a simple gym management utility. It offers a REST API as well, for easy integration with other projects and tools.",
"install_methods": [
{
"type": "default",
"script": "ct/wger.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 8,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "admin",
"password": "adminadmin"
},
"notes": [
{
"text": "This LXC also runs Celery and Redis to synchronize workouts and ingredients",
"type": "info"
}
]
}

View File

@ -1,35 +0,0 @@
{
"name": "Wishlist",
"slug": "wishlist",
"categories": [
0
],
"date_created": "2025-12-29",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3280,
"documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started",
"config_path": "/opt/wishlist/.env",
"website": "https://github.com/cmintey/wishlist",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp",
"description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!",
"install_methods": [
{
"type": "default",
"script": "ct/wishlist.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 5,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@ -1,40 +0,0 @@
{
"name": "WriteFreely",
"slug": "writefreely",
"categories": [
12
],
"date_created": "2026-01-25",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 80,
"documentation": "https://writefreely.org/docs",
"config_path": "/opt/writefreely/config.ini",
"website": "https://writefreely.org/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/writefreely-light.webp",
"description": "WriteFreely is free and open source software for easily publishing writing on the web with support for the ActivityPub protocol. Use it to start a personal blog — or an entire community.",
"install_methods": [
{
"type": "default",
"script": "ct/writefreely.sh",
"resources": {
"cpu": 2,
"ram": 1024,
"hdd": 4,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "After installation execute `./writefreely user create --admin username:password` in the `/opt/writefreely` directory to create your user.",
"type": "info"
}
]
}

View File

@ -29,13 +29,21 @@ $STD apt install -y \
msg_ok "Installed Dependencies"
PG_VERSION="16" setup_postgresql
PG_DB_NAME="discourse" PG_DB_USER="discourse" setup_postgresql_db
NODE_VERSION="22" setup_nodejs
RUBY_VERSION="3.3" setup_ruby
RUBY_VERSION="3.3.6" setup_ruby
msg_info "Configuring PostgreSQL for Discourse"
DISCOURSE_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
# Configure pg_hba.conf for md5 authentication
PG_HBA="/etc/postgresql/16/main/pg_hba.conf"
sed -i 's/^local\s\+all\s\+all\s\+peer$/local all all md5/' "$PG_HBA"
$STD systemctl restart postgresql
# Create user with CREATEDB permission - Rails will create the database
$STD sudo -u postgres psql -c "CREATE ROLE discourse WITH LOGIN PASSWORD '$DISCOURSE_DB_PASS' CREATEDB;"
msg_ok "Configured PostgreSQL for Discourse"
msg_info "Configuring Discourse"
DISCOURSE_SECRET_KEY=$(openssl rand -hex 32)
DISCOURSE_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)
DISCOURSE_SECRET_KEY=$(openssl rand -hex 64)
git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse
cd /opt/discourse
@ -45,7 +53,7 @@ RAILS_ENV=production
RAILS_LOG_TO_STDOUT=true
RAILS_SERVE_STATIC_FILES=true
SECRET_KEY_BASE=${DISCOURSE_SECRET_KEY}
DISCOURSE_DB_HOST=localhost
DISCOURSE_DB_HOST=/var/run/postgresql
DISCOURSE_DB_PORT=5432
DISCOURSE_DB_NAME=discourse
DISCOURSE_DB_USERNAME=discourse
@ -64,26 +72,42 @@ chmod 755 /opt/discourse
msg_ok "Configured Discourse"
msg_info "Installing Discourse Dependencies"
$STD systemctl enable --now redis-server
cd /opt/discourse
$STD bundle install --deployment --without test development
$STD yarn install
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
$STD corepack enable
$STD bundle config set --local deployment true
$STD bundle config set --local without 'test development'
$STD bundle install
$STD pnpm install
msg_ok "Installed Discourse Dependencies"
msg_info "Building Discourse Assets"
cd /opt/discourse
$STD bundle exec rails assets:precompile
msg_ok "Built Discourse Assets"
msg_info "Setting Up Database"
cd /opt/discourse
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production
$STD bundle exec rails db:create
$STD bundle exec rails db:migrate
$STD systemctl enable --now redis-server
$STD systemctl enable --now postgresql
msg_ok "Set Up Database"
msg_info "Building Discourse Assets"
cd /opt/discourse
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production
$STD bundle exec rails assets:precompile
msg_ok "Built Discourse Assets"
msg_info "Creating Discourse Admin User"
cd /opt/discourse
$STD bundle exec rails runner -e production "User.create!(email: 'admin@local', username: 'admin', password: '${DISCOURSE_DB_PASS}', admin: true)" || true
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production
$STD bundle exec rails runner "User.create!(email: 'admin@local', username: 'admin', password: '${DISCOURSE_DB_PASS}', admin: true)" || true
msg_ok "Created Discourse Admin User"
msg_info "Creating Service"

View File

@ -14,18 +14,18 @@ network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y \
apache2 \
libapache2-mod-wsgi-py3 \
$STD apt install -y \
build-essential \
nginx \
redis-server \
libpq-dev
msg_ok "Installed Dependencies"
import_local_ip
NODE_VERSION="22" NODE_MODULE="sass" setup_nodejs
setup_uv
PG_VERSION="16" setup_postgresql
PG_DB_NAME="wger" PG_DB_USER="wger" setup_postgresql_db
fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger"
msg_info "Setting up wger"
@ -36,85 +36,147 @@ $STD corepack enable
$STD npm install
$STD npm run build:css:sass
$STD uv venv
$STD uv pip install .
$STD uv pip install . --group docker
SECRET_KEY=$(openssl rand -base64 40)
cat <<EOF >/opt/wger/.env
DJANGO_SETTINGS_MODULE=settings.main
PYTHONPATH=/opt/wger
DJANGO_DB_ENGINE=django.db.backends.postgresql
DJANGO_DB_DATABASE=${PG_DB_NAME}
DJANGO_DB_USER=${PG_DB_USER}
DJANGO_DB_PASSWORD=${PG_DB_PASS}
DJANGO_DB_HOST=localhost
DJANGO_DB_PORT=5432
DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}
DJANGO_MEDIA_ROOT=/opt/wger/media
DJANGO_STATIC_ROOT=/opt/wger/static
DJANGO_STATIC_URL=/static/
ALLOWED_HOSTS=${LOCAL_IP},localhost,127.0.0.1
CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:3000
USE_X_FORWARDED_HOST=True
SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,http
DJANGO_CACHE_BACKEND=django_redis.cache.RedisCache
DJANGO_CACHE_LOCATION=redis://127.0.0.1:6379/1
DJANGO_CACHE_TIMEOUT=300
DJANGO_CACHE_CLIENT_CLASS=django_redis.client.DefaultClient
AXES_CACHE_ALIAS=default
USE_CELERY=True
CELERY_BROKER=redis://127.0.0.1:6379/2
CELERY_BACKEND=redis://127.0.0.1:6379/2
SITE_URL=http://${LOCAL_IP}:3000
SECRET_KEY=${SECRET_KEY}
EOF
cat <<'WSGI' >/opt/wger/wsgi_wrapper.py
import os
from pathlib import Path
env_file = Path('/opt/wger/.env')
if env_file.exists():
for line in env_file.read_text().splitlines():
if line.strip() and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
os.environ.setdefault(key.strip(), value.strip())
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
from wger.wsgi import application
WSGI
set -a && source /opt/wger/.env && set +a
export DJANGO_SETTINGS_MODULE=settings.main
$STD uv run python manage.py migrate
$STD uv run python manage.py loaddata languages
$STD uv run python manage.py loaddata gym_config
$STD uv run python manage.py loaddata groups
$STD uv run python manage.py loaddata site
$STD uv run wger bootstrap
$STD uv run python manage.py collectstatic --no-input
cat <<EOF | uv run python manage.py shell
from django.contrib.auth import get_user_model
UserModel = get_user_model()
user = UserModel.objects.create_user('admin', email='admin@localhost', password='${PG_DB_PASS}')
user.is_superuser = True
user.is_staff = True
user.save()
User = get_user_model()
user, created = User.objects.get_or_create(
username="admin",
defaults={"email": "admin@localhost"},
)
if created:
user.set_password("${PG_DB_PASS}")
user.is_superuser = True
user.is_staff = True
user.save()
EOF
msg_ok "Set up wger"
msg_info "Creating Config and Services"
cat <<EOF >/etc/systemd/system/wger.service
[Unit]
Description=wger Gunicorn
After=network.target
msg_info "Creating Service"
cat <<EOF >/etc/apache2/sites-available/wger.conf
<Directory /opt/wger>
<Files wsgi_wrapper.py>
Require all granted
</Files>
</Directory>
[Service]
User=root
WorkingDirectory=/opt/wger
EnvironmentFile=/opt/wger/.env
ExecStart=/opt/wger/.venv/bin/gunicorn \
--bind 127.0.0.1:8000 \
--workers 3 \
--threads 2 \
--timeout 120 \
wger.wsgi:application
Restart=always
<VirtualHost *:80>
WSGIApplicationGroup %{GLOBAL}
WSGIDaemonProcess wger python-path=/opt/wger python-home=/opt/wger/.venv
WSGIProcessGroup wger
WSGIScriptAlias / /opt/wger/wsgi_wrapper.py
WSGIPassAuthorization On
Alias /static/ /opt/wger/static/
<Directory /opt/wger/static>
Require all granted
</Directory>
Alias /media/ /opt/wger/media/
<Directory /opt/wger/media>
Require all granted
</Directory>
ErrorLog /var/log/apache2/wger-error.log
CustomLog /var/log/apache2/wger-access.log combined
</VirtualHost>
[Install]
WantedBy=multi-user.target
EOF
$STD a2dissite 000-default.conf
$STD a2ensite wger
systemctl restart apache2
msg_ok "Created Service"
cat <<EOF >/etc/systemd/system/celery.service
[Unit]
Description=wger Celery Worker
After=network.target redis-server.service
Requires=redis-server.service
[Service]
WorkingDirectory=/opt/wger
EnvironmentFile=/opt/wger/.env
ExecStart=/opt/wger/.venv/bin/celery -A wger worker -l info
Restart=always
[Install]
WantedBy=multi-user.target
EOF
mkdir -p /var/lib/wger/celery
chmod 700 /var/lib/wger/celery
cat <<EOF >/etc/systemd/system/celery-beat.service
[Unit]
Description=wger Celery Beat
After=network.target redis-server.service
Requires=redis-server.service
[Service]
WorkingDirectory=/opt/wger
EnvironmentFile=/opt/wger/.env
ExecStart=/opt/wger/.venv/bin/celery -A wger beat -l info \
--schedule /var/lib/wger/celery/celerybeat-schedule
Restart=always
[Install]
WantedBy=multi-user.target
EOF
cat <<'EOF' >/etc/nginx/sites-available/wger
server {
listen 3000;
server_name _;
client_max_body_size 20M;
location /static/ {
alias /opt/wger/static/;
expires 30d;
}
location /media/ {
alias /opt/wger/media/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
EOF
$STD rm -f /etc/nginx/sites-enabled/default
$STD ln -sf /etc/nginx/sites-available/wger /etc/nginx/sites-enabled/wger
systemctl enable -q --now redis-server nginx wger celery celery-beat
systemctl restart nginx
msg_ok "Created Config and Services"
motd_ssh
customize

View File

@ -1,67 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Dunky13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/cmintey/wishlist
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 \
build-essential \
python3 \
openssl \
caddy
msg_ok "Installed dependencies"
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball"
LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false)
msg_info "Installing Wishlist"
cd /opt/wishlist
cp .env.example .env
sed -i "s|^ORIGIN=.*|ORIGIN=http://${LOCAL_IP}:3000|" /opt/wishlist/.env
echo "NODE_ENV=production" >>/opt/wishlist/.env
$STD pnpm install
$STD pnpm svelte-kit sync
$STD pnpm prisma generate
sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist)
export VERSION="v${LATEST_APP_VERSION}"
export SHA="v${LATEST_APP_VERSION}"
$STD pnpm run build
$STD pnpm prune --prod
chmod +x /opt/wishlist/entrypoint.sh
mkdir -p /opt/wishlist/uploads
mkdir -p /opt/wishlist/data
msg_ok "Installed Wishlist"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/wishlist.service
[Unit]
Description=Wishlist Service
After=network.target
[Service]
WorkingDirectory=/opt/wishlist
EnvironmentFile=/opt/wishlist/.env
ExecStart=/usr/bin/env sh -c './entrypoint.sh'
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now wishlist
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@ -1,71 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: StellaeAlis
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/writefreely/writefreely
# Import Functions and Setup
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 crudini
msg_ok "Installed Dependencies"
setup_mariadb
MARIADB_DB_NAME="writefreely" MARIADB_DB_USER="writefreely" setup_mariadb_db
get_lxc_ip
fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_amd64.tar.gz"
msg_info "Setting up WriteFreely"
cd /opt/writefreely
$STD ./writefreely config generate
$STD ./writefreely keys generate
msg_ok "Setup WriteFreely"
msg_info "Configuring WriteFreely"
$STD crudini --set config.ini server port 80
$STD crudini --set config.ini server bind $LOCAL_IP
$STD crudini --set config.ini database username $MARIADB_DB_USER
$STD crudini --set config.ini database password $MARIADB_DB_PASS
$STD crudini --set config.ini database database $MARIADB_DB_NAME
$STD crudini --set config.ini app host http://$LOCAL_IP:80
$STD ./writefreely db init
msg_ok "Configured WriteFreely"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/writefreely.service
[Unit]
Description=WriteFreely Service
After=syslog.target network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/writefreely
ExecStart=/opt/writefreely/writefreely
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now writefreely
msg_ok "Created Service"
msg_info "Cleaning up"
$STD rm ~/writefreely.creds
msg_ok "Cleaned up"
motd_ssh
customize
cleanup_lxc

View File

@ -35,10 +35,203 @@ set +u
CLOUDINIT_DEFAULT_USER="${CLOUDINIT_DEFAULT_USER:-root}"
CLOUDINIT_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}"
CLOUDINIT_SEARCH_DOMAIN="${CLOUDINIT_SEARCH_DOMAIN:-local}"
CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-/root/.ssh/authorized_keys}"
CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-}" # Empty by default - user must explicitly provide keys
# ==============================================================================
# SECTION 2: HELPER FUNCTIONS
# SECTION 2: SSH KEY DISCOVERY AND SELECTION
# ==============================================================================
# ------------------------------------------------------------------------------
# _ci_ssh_extract_keys_from_file - Extracts valid SSH public keys from a file
# ------------------------------------------------------------------------------
function _ci_ssh_extract_keys_from_file() {
local file="$1"
[[ -f "$file" && -r "$file" ]] || return 0
grep -E '^(ssh-(rsa|ed25519|dss|ecdsa)|ecdsa-sha2-)' "$file" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
# _ci_ssh_discover_files - Scans standard paths for SSH keys
# ------------------------------------------------------------------------------
function _ci_ssh_discover_files() {
local -a cand=()
shopt -s nullglob
cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2)
cand+=(/root/.ssh/*.pub)
cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
shopt -u nullglob
printf '%s\0' "${cand[@]}"
}
# ------------------------------------------------------------------------------
# _ci_ssh_build_choices - Builds whiptail checklist from SSH key files
#
# Sets: CI_SSH_CHOICES (array), CI_SSH_COUNT (int), CI_SSH_MAPFILE (path)
# ------------------------------------------------------------------------------
function _ci_ssh_build_choices() {
local -a files=("$@")
CI_SSH_CHOICES=()
CI_SSH_COUNT=0
CI_SSH_MAPFILE="$(mktemp)"
local id key typ fp cmt base
for f in "${files[@]}"; do
[[ -f "$f" && -r "$f" ]] || continue
base="$(basename -- "$f")"
# Skip known_hosts and private keys
case "$base" in
known_hosts | known_hosts.* | config) continue ;;
id_*) [[ "$f" != *.pub ]] && continue ;;
esac
while IFS= read -r key; do
[[ -n "$key" ]] || continue
typ=""
fp=""
cmt=""
read -r _typ _b64 _cmt <<<"$key"
typ="${_typ:-key}"
cmt="${_cmt:-}"
# Get fingerprint via ssh-keygen if available
if command -v ssh-keygen >/dev/null 2>&1; then
fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')"
fi
# Shorten long comments
[[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..."
CI_SSH_COUNT=$((CI_SSH_COUNT + 1))
id="K${CI_SSH_COUNT}"
echo "${id}|${key}" >>"$CI_SSH_MAPFILE"
CI_SSH_CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }${base}" "OFF")
done < <(_ci_ssh_extract_keys_from_file "$f")
done
}
# ------------------------------------------------------------------------------
# configure_cloudinit_ssh_keys - Interactive SSH key selection for Cloud-Init
#
# Usage: configure_cloudinit_ssh_keys
# Sets: CLOUDINIT_SSH_KEYS (path to temporary file with selected keys)
# ------------------------------------------------------------------------------
function configure_cloudinit_ssh_keys() {
local backtitle="Proxmox VE Helper Scripts"
local ssh_key_mode
# Create temp file for selected keys
CLOUDINIT_SSH_KEYS_TEMP="$(mktemp)"
: >"$CLOUDINIT_SSH_KEYS_TEMP"
# Discover keys and build choices
IFS=$'\0' read -r -d '' -a _def_files < <(_ci_ssh_discover_files && printf '\0')
_ci_ssh_build_choices "${_def_files[@]}"
local default_key_count="$CI_SSH_COUNT"
if [[ "$default_key_count" -gt 0 ]]; then
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"Provision SSH keys for Cloud-Init VM:" 14 72 4 \
"found" "Select from detected keys (${default_key_count})" \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
else
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"No host keys detected. Choose:" 12 72 3 \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
fi
case "$ssh_key_mode" in
found)
# Show checklist with individual keys
local selection
selection=$(whiptail --backtitle "$backtitle" --title "SELECT SSH KEYS" \
--checklist "Select one or more keys to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) selected${CL}"
;;
manual)
local pubkey
pubkey=$(whiptail --backtitle "$backtitle" --title "PASTE SSH PUBLIC KEY" \
--inputbox "Paste your SSH public key (ssh-rsa, ssh-ed25519, etc.):" 10 76 3>&1 1>&2 2>&3) || return 1
if [[ -n "$pubkey" ]]; then
echo "$pubkey" >"$CLOUDINIT_SSH_KEYS_TEMP"
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}1 key added manually${CL}"
else
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (empty input)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
fi
;;
folder)
local glob_path
glob_path=$(whiptail --backtitle "$backtitle" --title "SCAN FOLDER/GLOB" \
--inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub):" 10 72 3>&1 1>&2 2>&3) || return 1
if [[ -n "$glob_path" ]]; then
shopt -s nullglob
local -a _scan_files=($glob_path)
shopt -u nullglob
if [[ "${#_scan_files[@]}" -gt 0 ]]; then
_ci_ssh_build_choices "${_scan_files[@]}"
if [[ "$CI_SSH_COUNT" -gt 0 ]]; then
local folder_selection
folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \
--checklist "Select key(s) to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $folder_selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) from folder${CL}"
else
whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60
fi
else
whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60
fi
fi
;;
none | *)
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (password auth only)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
;;
esac
# Cleanup mapfile
rm -f "$CI_SSH_MAPFILE" 2>/dev/null
# Set the variable for setup_cloud_init to use
if [[ -s "$CLOUDINIT_SSH_KEYS_TEMP" ]]; then
CLOUDINIT_SSH_KEYS="$CLOUDINIT_SSH_KEYS_TEMP"
else
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
fi
return 0
}
# ==============================================================================
# SECTION 3: HELPER FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
@ -148,9 +341,10 @@ function setup_cloud_init() {
local cipassword=$(openssl rand -base64 16)
qm set "$vmid" --cipassword "$cipassword" >/dev/null
# Add SSH keys if available
if [ -f "$CLOUDINIT_SSH_KEYS" ]; then
# Add SSH keys only if explicitly provided (not auto-imported from host)
if [ -n "${CLOUDINIT_SSH_KEYS:-}" ] && [ -f "$CLOUDINIT_SSH_KEYS" ]; then
qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true
_ci_msg_info "SSH keys imported from: $CLOUDINIT_SSH_KEYS"
fi
# Configure network

View File

@ -169,6 +169,11 @@ function advanced_settings() {
select_os
select_cloud_init
# SSH Key selection for Cloud-Init VMs
if [ "$USE_CLOUD_INIT" = "yes" ]; then
configure_cloudinit_ssh_keys || true
fi
METHOD="advanced"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)