feat(streamlit): Multi-instance support for compartmentalized apps
- Add multi-instance mode: run multiple apps on different ports
- New UCI config structure with 'instance' sections
- Container starts multiple streamlit processes via STREAMLIT_INSTANCES env
- CLI commands: instance list/add/remove/enable/disable
- Each instance has its own port, requirements auto-install
- Backward compatible: single-app mode still works
- Bumped to 1.0.0-r4
Example config:
config instance 'dashboard'
option app 'dashboard.py'
option port '8502'
option enabled '1'
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
24dc62cb79
commit
a596eb64d8
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=secubox-app-streamlit
|
PKG_NAME:=secubox-app-streamlit
|
||||||
PKG_VERSION:=1.0.0
|
PKG_VERSION:=1.0.0
|
||||||
PKG_RELEASE:=3
|
PKG_RELEASE:=4
|
||||||
PKG_ARCH:=all
|
PKG_ARCH:=all
|
||||||
|
|
||||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||||
@ -30,9 +30,10 @@ Streamlit App Platform - Self-hosted Python data app platform
|
|||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Run Streamlit apps in LXC container
|
- Run Streamlit apps in LXC container
|
||||||
|
- Multi-instance support (multiple apps on different ports)
|
||||||
- Python 3.12 with Streamlit 1.53.x
|
- Python 3.12 with Streamlit 1.53.x
|
||||||
- App management (add, remove, switch)
|
|
||||||
- Auto-install requirements.txt dependencies
|
- Auto-install requirements.txt dependencies
|
||||||
|
- HAProxy publish wizard for vhost routing
|
||||||
- Web dashboard integration
|
- Web dashboard integration
|
||||||
- Configurable port and memory limits
|
- Configurable port and memory limits
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
config streamlit 'main'
|
config streamlit 'main'
|
||||||
option enabled '0'
|
option enabled '0'
|
||||||
option http_port '8501'
|
|
||||||
option http_host '0.0.0.0'
|
|
||||||
option data_path '/srv/streamlit'
|
option data_path '/srv/streamlit'
|
||||||
option memory_limit '512M'
|
option memory_limit '1024M'
|
||||||
option active_app 'hello'
|
# Base port - instances use 8501, 8502, 8503...
|
||||||
|
option base_port '8501'
|
||||||
|
|
||||||
config server 'server'
|
config server 'server'
|
||||||
option headless 'true'
|
option headless 'true'
|
||||||
@ -12,7 +11,19 @@ config server 'server'
|
|||||||
option theme_base 'dark'
|
option theme_base 'dark'
|
||||||
option theme_primary_color '#0ff'
|
option theme_primary_color '#0ff'
|
||||||
|
|
||||||
config app 'hello'
|
# Default hello app on port 8501
|
||||||
|
config instance 'hello'
|
||||||
option name 'Hello World'
|
option name 'Hello World'
|
||||||
option path 'hello.py'
|
option app 'hello.py'
|
||||||
|
option port '8501'
|
||||||
option enabled '1'
|
option enabled '1'
|
||||||
|
option autostart '1'
|
||||||
|
|
||||||
|
# Example: Add more instances
|
||||||
|
# config instance 'dashboard'
|
||||||
|
# option name 'Dashboard App'
|
||||||
|
# option app 'dashboard.py'
|
||||||
|
# option port '8502'
|
||||||
|
# option enabled '1'
|
||||||
|
# option autostart '1'
|
||||||
|
# option domain 'dashboard.example.com'
|
||||||
|
|||||||
@ -72,7 +72,12 @@ Commands:
|
|||||||
app list List deployed apps
|
app list List deployed apps
|
||||||
app add <name> <path> Deploy new app
|
app add <name> <path> Deploy new app
|
||||||
app remove <name> Remove app
|
app remove <name> Remove app
|
||||||
app run <name> Switch active app
|
|
||||||
|
instance list List running instances
|
||||||
|
instance add <name> <app> <port> Add instance config
|
||||||
|
instance remove <name> Remove instance config
|
||||||
|
instance enable <name> Enable instance
|
||||||
|
instance disable <name> Disable instance
|
||||||
|
|
||||||
service-run Start service (used by init)
|
service-run Start service (used by init)
|
||||||
service-stop Stop service (used by init)
|
service-stop Stop service (used by init)
|
||||||
@ -83,12 +88,18 @@ Configuration:
|
|||||||
Data directory:
|
Data directory:
|
||||||
/srv/streamlit
|
/srv/streamlit
|
||||||
|
|
||||||
|
Multi-Instance Mode:
|
||||||
|
Add instances in /etc/config/streamlit:
|
||||||
|
config instance 'myapp'
|
||||||
|
option app 'myapp.py'
|
||||||
|
option port '8502'
|
||||||
|
option enabled '1'
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
Place a requirements.txt file in /srv/streamlit/apps/ to auto-install
|
Place requirements.txt in /srv/streamlit/apps/
|
||||||
Python dependencies. Supported naming conventions:
|
- <appname>.requirements.txt
|
||||||
- <appname>.requirements.txt (e.g., sappix.requirements.txt)
|
- <appname>_requirements.txt
|
||||||
- <appname>_requirements.txt (e.g., sappix_requirements.txt)
|
- requirements.txt (global)
|
||||||
- requirements.txt (global fallback)
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@ -140,16 +151,15 @@ lxc_create_rootfs() {
|
|||||||
cp /etc/resolv.conf "$rootfs/etc/resolv.conf" 2>/dev/null || \
|
cp /etc/resolv.conf "$rootfs/etc/resolv.conf" 2>/dev/null || \
|
||||||
echo "nameserver 1.1.1.1" > "$rootfs/etc/resolv.conf"
|
echo "nameserver 1.1.1.1" > "$rootfs/etc/resolv.conf"
|
||||||
|
|
||||||
# Create startup script
|
# Create startup script - Multi-instance support
|
||||||
cat > "$rootfs/opt/start-streamlit.sh" << 'STARTUP'
|
cat > "$rootfs/opt/start-streamlit.sh" << 'STARTUP'
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
|
||||||
|
|
||||||
# Install Python and Streamlit on first run
|
# Install Python and Streamlit on first run
|
||||||
if [ ! -f /opt/.installed ]; then
|
if [ ! -f /opt/.installed ]; then
|
||||||
echo "Installing Python 3.12 and dependencies..."
|
echo "Installing Python 3.12 and dependencies..."
|
||||||
apk update
|
apk update
|
||||||
apk add --no-cache python3 py3-pip
|
apk add --no-cache python3 py3-pip procps
|
||||||
|
|
||||||
echo "Installing Streamlit..."
|
echo "Installing Streamlit..."
|
||||||
pip3 install --break-system-packages streamlit 2>/dev/null || \
|
pip3 install --break-system-packages streamlit 2>/dev/null || \
|
||||||
@ -159,18 +169,10 @@ if [ ! -f /opt/.installed ]; then
|
|||||||
echo "Installation complete!"
|
echo "Installation complete!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Find active app
|
# Create default hello app if missing
|
||||||
ACTIVE_APP="${STREAMLIT_APP:-hello.py}"
|
mkdir -p /srv/apps
|
||||||
APP_PATH="/srv/apps/${ACTIVE_APP}"
|
if [ ! -f "/srv/apps/hello.py" ]; then
|
||||||
|
cat > /srv/apps/hello.py << 'HELLO'
|
||||||
# Fallback to hello.py if app not found
|
|
||||||
if [ ! -f "$APP_PATH" ]; then
|
|
||||||
if [ -f "/srv/apps/hello.py" ]; then
|
|
||||||
APP_PATH="/srv/apps/hello.py"
|
|
||||||
else
|
|
||||||
echo "No app found, creating default..."
|
|
||||||
mkdir -p /srv/apps
|
|
||||||
cat > /srv/apps/hello.py << 'HELLO'
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
st.set_page_config(page_title="SecuBox Streamlit", page_icon="⚡", layout="wide")
|
st.set_page_config(page_title="SecuBox Streamlit", page_icon="⚡", layout="wide")
|
||||||
st.title("⚡ SECUBOX STREAMLIT ⚡")
|
st.title("⚡ SECUBOX STREAMLIT ⚡")
|
||||||
@ -184,42 +186,80 @@ with col3:
|
|||||||
st.metric("Platform", "SecuBox")
|
st.metric("Platform", "SecuBox")
|
||||||
st.info("Deploy your Streamlit apps via LuCI dashboard")
|
st.info("Deploy your Streamlit apps via LuCI dashboard")
|
||||||
HELLO
|
HELLO
|
||||||
APP_PATH="/srv/apps/hello.py"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get app name without .py extension
|
# Function to install requirements for an app
|
||||||
APP_NAME=$(basename "$APP_PATH" .py)
|
install_requirements() {
|
||||||
|
local app_name="$1"
|
||||||
# Install app-specific requirements if present
|
for req_file in "/srv/apps/${app_name}.requirements.txt" \
|
||||||
# Check for: <app>.requirements.txt, <app>_requirements.txt, or global requirements.txt
|
"/srv/apps/${app_name}_requirements.txt" \
|
||||||
for req_file in "/srv/apps/${APP_NAME}.requirements.txt" \
|
"/srv/apps/requirements.txt"; do
|
||||||
"/srv/apps/${APP_NAME}_requirements.txt" \
|
if [ -f "$req_file" ]; then
|
||||||
"/srv/apps/requirements.txt"; do
|
REQ_HASH=$(md5sum "$req_file" 2>/dev/null | cut -d' ' -f1)
|
||||||
if [ -f "$req_file" ]; then
|
REQ_MARKER="/opt/.req_${app_name}_${REQ_HASH}"
|
||||||
REQ_HASH=$(md5sum "$req_file" 2>/dev/null | cut -d' ' -f1)
|
if [ ! -f "$REQ_MARKER" ]; then
|
||||||
REQ_MARKER="/opt/.req_${APP_NAME}_${REQ_HASH}"
|
echo "Installing requirements for $app_name from: $req_file"
|
||||||
if [ ! -f "$REQ_MARKER" ]; then
|
pip3 install --break-system-packages -r "$req_file" 2>/dev/null || \
|
||||||
echo "Installing requirements from: $req_file"
|
pip3 install -r "$req_file" 2>/dev/null || true
|
||||||
pip3 install --break-system-packages -r "$req_file" 2>/dev/null || \
|
touch "$REQ_MARKER"
|
||||||
pip3 install -r "$req_file" 2>/dev/null || \
|
fi
|
||||||
echo "Warning: Some requirements may have failed to install"
|
break
|
||||||
touch "$REQ_MARKER"
|
|
||||||
fi
|
fi
|
||||||
break
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to start a single Streamlit instance
|
||||||
|
start_instance() {
|
||||||
|
local app_file="$1"
|
||||||
|
local port="$2"
|
||||||
|
local app_name=$(basename "$app_file" .py)
|
||||||
|
|
||||||
|
if [ ! -f "/srv/apps/$app_file" ]; then
|
||||||
|
echo "App not found: $app_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_requirements "$app_name"
|
||||||
|
|
||||||
|
echo "Starting instance: $app_name on port $port"
|
||||||
|
cd /srv/apps
|
||||||
|
streamlit run "$app_file" \
|
||||||
|
--server.address="0.0.0.0" \
|
||||||
|
--server.port="$port" \
|
||||||
|
--server.headless=true \
|
||||||
|
--browser.gatherUsageStats=false \
|
||||||
|
--theme.base="${STREAMLIT_THEME_BASE:-dark}" \
|
||||||
|
--theme.primaryColor="${STREAMLIT_THEME_PRIMARY:-#0ff}" &
|
||||||
|
echo $! > "/tmp/streamlit_${app_name}.pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse STREAMLIT_INSTANCES env var (format: "app1.py:8501,app2.py:8502")
|
||||||
|
if [ -n "$STREAMLIT_INSTANCES" ]; then
|
||||||
|
echo "Multi-instance mode: $STREAMLIT_INSTANCES"
|
||||||
|
IFS=','
|
||||||
|
for instance in $STREAMLIT_INSTANCES; do
|
||||||
|
app_file=$(echo "$instance" | cut -d: -f1)
|
||||||
|
port=$(echo "$instance" | cut -d: -f2)
|
||||||
|
start_instance "$app_file" "$port"
|
||||||
|
done
|
||||||
|
unset IFS
|
||||||
|
else
|
||||||
|
# Single instance mode (backward compatible)
|
||||||
|
ACTIVE_APP="${STREAMLIT_APP:-hello.py}"
|
||||||
|
PORT="${STREAMLIT_PORT:-8501}"
|
||||||
|
start_instance "$ACTIVE_APP" "$PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep container running and monitor processes
|
||||||
|
echo "Streamlit instances started. Monitoring..."
|
||||||
|
while true; do
|
||||||
|
sleep 30
|
||||||
|
# Check if any streamlit process is running
|
||||||
|
if ! pgrep -f "streamlit" >/dev/null; then
|
||||||
|
echo "No streamlit processes running, exiting..."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Starting Streamlit with app: $APP_PATH"
|
|
||||||
cd /srv/apps
|
|
||||||
|
|
||||||
exec streamlit run "$APP_PATH" \
|
|
||||||
--server.address="${STREAMLIT_HOST:-0.0.0.0}" \
|
|
||||||
--server.port="${STREAMLIT_PORT:-8501}" \
|
|
||||||
--server.headless=true \
|
|
||||||
--browser.gatherUsageStats="${STREAMLIT_STATS:-false}" \
|
|
||||||
--theme.base="${STREAMLIT_THEME_BASE:-dark}" \
|
|
||||||
--theme.primaryColor="${STREAMLIT_THEME_PRIMARY:-#0ff}"
|
|
||||||
STARTUP
|
STARTUP
|
||||||
chmod +x "$rootfs/opt/start-streamlit.sh"
|
chmod +x "$rootfs/opt/start-streamlit.sh"
|
||||||
|
|
||||||
@ -227,6 +267,26 @@ STARTUP
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Build instances string from UCI config
|
||||||
|
build_instances_string() {
|
||||||
|
local instances=""
|
||||||
|
local _add_instance() {
|
||||||
|
local section="$1"
|
||||||
|
local inst_enabled inst_app inst_port
|
||||||
|
config_get inst_enabled "$section" enabled "0"
|
||||||
|
config_get inst_app "$section" app ""
|
||||||
|
config_get inst_port "$section" port ""
|
||||||
|
|
||||||
|
if [ "$inst_enabled" = "1" ] && [ -n "$inst_app" ] && [ -n "$inst_port" ]; then
|
||||||
|
[ -n "$instances" ] && instances="${instances},"
|
||||||
|
instances="${instances}${inst_app}:${inst_port}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
config_load "$CONFIG"
|
||||||
|
config_foreach _add_instance instance
|
||||||
|
echo "$instances"
|
||||||
|
}
|
||||||
|
|
||||||
# Create LXC config
|
# Create LXC config
|
||||||
lxc_create_config() {
|
lxc_create_config() {
|
||||||
load_config
|
load_config
|
||||||
@ -242,14 +302,20 @@ lxc_create_config() {
|
|||||||
*) mem_bytes="$memory_limit" ;;
|
*) mem_bytes="$memory_limit" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Determine active app file
|
# Build multi-instance string or fallback to single app
|
||||||
local app_file
|
local instances_str
|
||||||
if [ -f "$APPS_PATH/${active_app}.py" ]; then
|
instances_str=$(build_instances_string)
|
||||||
app_file="${active_app}.py"
|
|
||||||
elif [ -f "$APPS_PATH/${active_app}" ]; then
|
# Fallback: if no instances defined, use active_app
|
||||||
app_file="${active_app}"
|
local app_file=""
|
||||||
else
|
if [ -z "$instances_str" ]; then
|
||||||
app_file="hello.py"
|
if [ -f "$APPS_PATH/${active_app}.py" ]; then
|
||||||
|
app_file="${active_app}.py"
|
||||||
|
elif [ -f "$APPS_PATH/${active_app}" ]; then
|
||||||
|
app_file="${active_app}"
|
||||||
|
else
|
||||||
|
app_file="hello.py"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat > "$LXC_CONFIG" << EOF
|
cat > "$LXC_CONFIG" << EOF
|
||||||
@ -267,13 +333,21 @@ lxc.mount.entry = $APPS_PATH srv/apps none bind,create=dir 0 0
|
|||||||
lxc.mount.entry = $data_path/logs srv/logs none bind,create=dir 0 0
|
lxc.mount.entry = $data_path/logs srv/logs none bind,create=dir 0 0
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
lxc.environment = STREAMLIT_HOST=$http_host
|
|
||||||
lxc.environment = STREAMLIT_PORT=$http_port
|
|
||||||
lxc.environment = STREAMLIT_APP=$app_file
|
|
||||||
lxc.environment = STREAMLIT_HEADLESS=$headless
|
|
||||||
lxc.environment = STREAMLIT_STATS=$gather_stats
|
|
||||||
lxc.environment = STREAMLIT_THEME_BASE=$theme_base
|
lxc.environment = STREAMLIT_THEME_BASE=$theme_base
|
||||||
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary
|
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Add multi-instance or single-instance env vars
|
||||||
|
if [ -n "$instances_str" ]; then
|
||||||
|
echo "lxc.environment = STREAMLIT_INSTANCES=$instances_str" >> "$LXC_CONFIG"
|
||||||
|
else
|
||||||
|
cat >> "$LXC_CONFIG" << EOF
|
||||||
|
lxc.environment = STREAMLIT_APP=$app_file
|
||||||
|
lxc.environment = STREAMLIT_PORT=$http_port
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$LXC_CONFIG" << EOF
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time sys_rawio
|
lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time sys_rawio
|
||||||
@ -665,6 +739,112 @@ cmd_service_stop() {
|
|||||||
lxc_stop
|
lxc_stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Instance management
|
||||||
|
cmd_instance_list() {
|
||||||
|
load_config
|
||||||
|
echo "{"
|
||||||
|
echo ' "instances": ['
|
||||||
|
|
||||||
|
local first=1
|
||||||
|
_list_instance() {
|
||||||
|
local section="$1"
|
||||||
|
local name app port enabled
|
||||||
|
config_get name "$section" name "$section"
|
||||||
|
config_get app "$section" app ""
|
||||||
|
config_get port "$section" port ""
|
||||||
|
config_get enabled "$section" enabled "0"
|
||||||
|
|
||||||
|
[ $first -eq 0 ] && echo ","
|
||||||
|
first=0
|
||||||
|
printf ' {"id": "%s", "name": "%s", "app": "%s", "port": "%s", "enabled": %s}' \
|
||||||
|
"$section" "$name" "$app" "$port" "$([ "$enabled" = "1" ] && echo "true" || echo "false")"
|
||||||
|
}
|
||||||
|
config_load "$CONFIG"
|
||||||
|
config_foreach _list_instance instance
|
||||||
|
echo ""
|
||||||
|
echo " ]"
|
||||||
|
echo "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_instance_add() {
|
||||||
|
local name="$1"
|
||||||
|
local app="$2"
|
||||||
|
local port="$3"
|
||||||
|
|
||||||
|
if [ -z "$name" ] || [ -z "$app" ] || [ -z "$port" ]; then
|
||||||
|
log_error "Usage: streamlitctl instance add <name> <app.py> <port>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate port is numeric
|
||||||
|
if ! echo "$port" | grep -qE '^[0-9]+$'; then
|
||||||
|
log_error "Port must be a number"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uci set "${CONFIG}.${name}=instance"
|
||||||
|
uci set "${CONFIG}.${name}.name=$name"
|
||||||
|
uci set "${CONFIG}.${name}.app=$app"
|
||||||
|
uci set "${CONFIG}.${name}.port=$port"
|
||||||
|
uci set "${CONFIG}.${name}.enabled=1"
|
||||||
|
uci commit "$CONFIG"
|
||||||
|
|
||||||
|
log_info "Instance '$name' added (app: $app, port: $port)"
|
||||||
|
echo '{"success": true, "message": "Instance added", "name": "'"$name"'", "port": '"$port"'}'
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_instance_remove() {
|
||||||
|
local name="$1"
|
||||||
|
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
log_error "Usage: streamlitctl instance remove <name>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uci -q delete "${CONFIG}.${name}" 2>/dev/null || {
|
||||||
|
log_error "Instance not found: $name"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uci commit "$CONFIG"
|
||||||
|
|
||||||
|
log_info "Instance '$name' removed"
|
||||||
|
echo '{"success": true, "message": "Instance removed", "name": "'"$name"'"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_instance_enable() {
|
||||||
|
local name="$1"
|
||||||
|
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
log_error "Usage: streamlitctl instance enable <name>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uci set "${CONFIG}.${name}.enabled=1" && uci commit "$CONFIG" || {
|
||||||
|
log_error "Instance not found: $name"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "Instance '$name' enabled"
|
||||||
|
echo '{"success": true, "message": "Instance enabled", "name": "'"$name"'"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_instance_disable() {
|
||||||
|
local name="$1"
|
||||||
|
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
log_error "Usage: streamlitctl instance disable <name>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uci set "${CONFIG}.${name}.enabled=0" && uci commit "$CONFIG" || {
|
||||||
|
log_error "Instance not found: $name"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "Instance '$name' disabled"
|
||||||
|
echo '{"success": true, "message": "Instance disabled", "name": "'"$name"'"}'
|
||||||
|
}
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
install) shift; cmd_install "$@" ;;
|
install) shift; cmd_install "$@" ;;
|
||||||
@ -686,8 +866,18 @@ case "${1:-}" in
|
|||||||
list) shift; cmd_app_list "$@" ;;
|
list) shift; cmd_app_list "$@" ;;
|
||||||
add) shift; cmd_app_add "$@" ;;
|
add) shift; cmd_app_add "$@" ;;
|
||||||
remove) shift; cmd_app_remove "$@" ;;
|
remove) shift; cmd_app_remove "$@" ;;
|
||||||
run) shift; cmd_app_run "$@" ;;
|
*) echo "Usage: streamlitctl app {list|add|remove}"; exit 1 ;;
|
||||||
*) echo "Usage: streamlitctl app {list|add|remove|run}"; exit 1 ;;
|
esac
|
||||||
|
;;
|
||||||
|
instance)
|
||||||
|
shift
|
||||||
|
case "${1:-}" in
|
||||||
|
list) shift; cmd_instance_list "$@" ;;
|
||||||
|
add) shift; cmd_instance_add "$@" ;;
|
||||||
|
remove) shift; cmd_instance_remove "$@" ;;
|
||||||
|
enable) shift; cmd_instance_enable "$@" ;;
|
||||||
|
disable) shift; cmd_instance_disable "$@" ;;
|
||||||
|
*) echo "Usage: streamlitctl instance {list|add|remove|enable|disable}"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
service-run) shift; cmd_service_run "$@" ;;
|
service-run) shift; cmd_service_run "$@" ;;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user