#!/bin/sh # SecuBox Gitea Platform Controller # Copyright (C) 2025 CyberMind.fr # # Manages Gitea in LXC container CONFIG="gitea" LXC_NAME="gitea" # Paths LXC_PATH="/srv/lxc" LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs" LXC_CONFIG="$LXC_PATH/$LXC_NAME/config" DATA_PATH="/srv/gitea" BACKUP_PATH="/srv/gitea/backups" GITEA_VERSION="1.22.6" # Logging log_info() { echo "[INFO] $*"; logger -t gitea "$*"; } log_error() { echo "[ERROR] $*" >&2; logger -t gitea -p err "$*"; } log_debug() { [ "$DEBUG" = "1" ] && echo "[DEBUG] $*"; } # Helpers require_root() { [ "$(id -u)" -eq 0 ] || { log_error "This command requires root privileges" exit 1 } } has_lxc() { command -v lxc-start >/dev/null 2>&1; } ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; } uci_get() { uci -q get ${CONFIG}.$1; } uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; } # Load configuration load_config() { http_port="$(uci_get main.http_port)" || http_port="3000" ssh_port="$(uci_get main.ssh_port)" || ssh_port="2222" http_host="$(uci_get main.http_host)" || http_host="0.0.0.0" data_path="$(uci_get main.data_path)" || data_path="/srv/gitea" memory_limit="$(uci_get main.memory_limit)" || memory_limit="512M" app_name="$(uci_get main.app_name)" || app_name="SecuBox Git" domain="$(uci_get main.domain)" || domain="git.local" # Server settings protocol="$(uci_get server.protocol)" || protocol="http" disable_registration="$(uci_get server.disable_registration)" || disable_registration="false" require_signin="$(uci_get server.require_signin)" || require_signin="false" landing_page="$(uci_get server.landing_page)" || landing_page="explore" # Database settings db_type="$(uci_get database.type)" || db_type="sqlite3" db_path="$(uci_get database.path)" || db_path="/data/gitea.db" DATA_PATH="$data_path" BACKUP_PATH="$data_path/backups" ensure_dir "$data_path" ensure_dir "$data_path/git" ensure_dir "$data_path/custom" ensure_dir "$data_path/custom/conf" ensure_dir "$BACKUP_PATH" } # Usage usage() { cat < [options] Commands: install Download Alpine rootfs and setup LXC container uninstall Remove container (preserves repositories) update Update Gitea binary to latest version start Start Gitea service (via init) stop Stop Gitea service (via init) restart Restart Gitea service status Show service status (JSON format) logs Show container logs shell Open shell in container backup Create backup of repos and database restore Restore from backup admin create-user Create admin user --username --password --email mirror-sync Sync a mirrored repository mirror-list List all mirrored repositories mirror-create Create a new mirror from URL --name --url --owner (default: first admin user) repo-list List all repositories service-run Start service (used by init) service-stop Stop service (used by init) Configuration: /etc/config/gitea Data directory: /srv/gitea EOF } # Check prerequisites lxc_check_prereqs() { if ! has_lxc; then log_error "LXC not installed. Install with: opkg install lxc lxc-common" return 1 fi return 0 } # Detect architecture for Gitea download get_gitea_arch() { local arch=$(uname -m) case "$arch" in x86_64) echo "linux-amd64" ;; aarch64) echo "linux-arm64" ;; armv7l) echo "linux-arm-6" ;; *) log_error "Unsupported architecture: $arch"; return 1 ;; esac } # Create LXC rootfs from Alpine lxc_create_rootfs() { local rootfs="$LXC_ROOTFS" local arch=$(uname -m) log_info "Creating Alpine rootfs for Gitea..." ensure_dir "$rootfs" # Use Alpine mini rootfs local alpine_version="3.21" case "$arch" in x86_64) alpine_arch="x86_64" ;; aarch64) alpine_arch="aarch64" ;; armv7l) alpine_arch="armv7" ;; *) log_error "Unsupported architecture: $arch"; return 1 ;; esac local alpine_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version}/releases/${alpine_arch}/alpine-minirootfs-${alpine_version}.0-${alpine_arch}.tar.gz" local tmpfile="/tmp/alpine-rootfs.tar.gz" log_info "Downloading Alpine ${alpine_version} rootfs..." wget -q -O "$tmpfile" "$alpine_url" || { log_error "Failed to download Alpine rootfs" return 1 } log_info "Extracting rootfs..." tar -xzf "$tmpfile" -C "$rootfs" || { log_error "Failed to extract rootfs" return 1 } rm -f "$tmpfile" # Setup resolv.conf cp /etc/resolv.conf "$rootfs/etc/resolv.conf" 2>/dev/null || \ echo "nameserver 1.1.1.1" > "$rootfs/etc/resolv.conf" # Create required directories mkdir -p "$rootfs/data" mkdir -p "$rootfs/opt" mkdir -p "$rootfs/run" log_info "Rootfs created successfully" return 0 } # Download and install Gitea binary install_gitea_binary() { local rootfs="$LXC_ROOTFS" local gitea_arch=$(get_gitea_arch) [ -z "$gitea_arch" ] && return 1 log_info "Downloading Gitea ${GITEA_VERSION}..." local gitea_url="https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-${gitea_arch}" ensure_dir "$rootfs/usr/local/bin" wget -q -O "$rootfs/usr/local/bin/gitea" "$gitea_url" || { log_error "Failed to download Gitea binary" return 1 } chmod +x "$rootfs/usr/local/bin/gitea" log_info "Gitea binary installed" return 0 } # Install Alpine packages inside container install_container_packages() { local rootfs="$LXC_ROOTFS" log_info "Installing container packages..." # Create install script cat > "$rootfs/tmp/install-deps.sh" << 'SCRIPT' #!/bin/sh apk update apk add --no-cache git git-lfs openssh sqlite bash su-exec # Create git user adduser -D -s /bin/bash -h /data git 2>/dev/null || true # Setup SSH directory mkdir -p /data/ssh chmod 700 /data/ssh touch /tmp/.deps-installed SCRIPT chmod +x "$rootfs/tmp/install-deps.sh" # Run in a temporary container lxc-execute -n "$LXC_NAME" -f "$LXC_CONFIG" -- /tmp/install-deps.sh 2>/dev/null || { # Fallback: run via start/attach lxc-start -n "$LXC_NAME" -f "$LXC_CONFIG" -d sleep 2 lxc-attach -n "$LXC_NAME" -- /tmp/install-deps.sh lxc-stop -n "$LXC_NAME" -k 2>/dev/null } rm -f "$rootfs/tmp/install-deps.sh" log_info "Container packages installed" return 0 } # Create startup script create_startup_script() { local rootfs="$LXC_ROOTFS" cat > "$rootfs/opt/start-gitea.sh" << 'STARTUP' #!/bin/sh set -e export PATH="/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin" export GITEA_WORK_DIR=/data export HOME=/data export USER=git # Install packages if needed (check if su-exec exists) if ! command -v su-exec >/dev/null 2>&1; then echo "Installing dependencies..." apk update apk add --no-cache git git-lfs openssh su-exec sqlite fi # Always ensure git user/group exists (doesn't persist between container restarts) if ! getent group git >/dev/null 2>&1; then echo "Creating git group..." addgroup -g 1000 git fi if ! id -u git >/dev/null 2>&1; then echo "Creating git user..." adduser -D -s /bin/sh -h /data -u 1000 -G git git fi # Ensure directories exist with correct ownership mkdir -p /data/git/repositories mkdir -p /data/custom/conf mkdir -p /data/log chown -R git:git /data chmod 755 /data /data/git /data/custom /data/custom/conf # Generate SSH host keys if needed if [ ! -f /data/ssh/ssh_host_rsa_key ]; then echo "Generating SSH host keys..." mkdir -p /data/ssh ssh-keygen -A mv /etc/ssh/ssh_host_* /data/ssh/ 2>/dev/null || true chown root:root /data/ssh/ssh_host_* chmod 600 /data/ssh/ssh_host_*_key chmod 644 /data/ssh/ssh_host_*_key.pub fi # Create sshd config for git cat > /data/ssh/sshd_config << 'SSHD' Port ${GITEA_SSH_PORT:-2222} ListenAddress 0.0.0.0 HostKey /data/ssh/ssh_host_rsa_key HostKey /data/ssh/ssh_host_ecdsa_key HostKey /data/ssh/ssh_host_ed25519_key PermitRootLogin no PubkeyAuthentication yes AuthorizedKeysFile /data/git/.ssh/authorized_keys PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no PrintMotd no AcceptEnv LANG LC_* Subsystem sftp /usr/lib/ssh/sftp-server SSHD # Start SSH server for git operations (optional, Gitea has built-in SSH) # /usr/sbin/sshd -f /data/ssh/sshd_config # Generate app.ini if not exists if [ ! -f /data/custom/conf/app.ini ]; then mkdir -p /data/custom/conf cat > /data/custom/conf/app.ini << EOF [server] APP_NAME = ${GITEA_APP_NAME:-SecuBox Git} DOMAIN = ${GITEA_DOMAIN:-git.local} HTTP_ADDR = ${GITEA_HTTP_HOST:-0.0.0.0} HTTP_PORT = ${GITEA_HTTP_PORT:-3000} ROOT_URL = http://${GITEA_DOMAIN:-git.local}:${GITEA_HTTP_PORT:-3000}/ DISABLE_SSH = false START_SSH_SERVER = true SSH_PORT = ${GITEA_SSH_PORT:-2222} SSH_LISTEN_HOST = 0.0.0.0 LFS_START_SERVER = true [database] DB_TYPE = sqlite3 PATH = /data/gitea.db [repository] ROOT = /data/git/repositories SCRIPT_TYPE = sh [security] INSTALL_LOCK = true SECRET_KEY = $(head -c 32 /dev/urandom | base64 | tr -d '\n') INTERNAL_TOKEN = $(head -c 64 /dev/urandom | base64 | tr -d '\n') [service] DISABLE_REGISTRATION = ${GITEA_DISABLE_REGISTRATION:-false} REQUIRE_SIGNIN_VIEW = ${GITEA_REQUIRE_SIGNIN:-false} [log] MODE = console LEVEL = Info [ui] DEFAULT_THEME = gitea-dark EOF chown git:git /data/custom/conf/app.ini fi # Start Gitea echo "Starting Gitea..." cd /data export PATH="/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin" export HOME=/data exec su-exec git /usr/local/bin/gitea web --config /data/custom/conf/app.ini STARTUP chmod +x "$rootfs/opt/start-gitea.sh" } # Create LXC config lxc_create_config() { load_config ensure_dir "$(dirname "$LXC_CONFIG")" # Ensure host data directories exist ensure_dir "$data_path" ensure_dir "$data_path/git" ensure_dir "$data_path/custom" # Convert memory limit to bytes local mem_bytes case "$memory_limit" in *G|*g) mem_bytes=$((${memory_limit%[Gg]} * 1024 * 1024 * 1024)) ;; *M|*m) mem_bytes=$((${memory_limit%[Mm]} * 1024 * 1024)) ;; *K|*k) mem_bytes=$((${memory_limit%[Kk]} * 1024)) ;; *) mem_bytes="$memory_limit" ;; esac cat > "$LXC_CONFIG" << EOF # Gitea Platform LXC Configuration lxc.uts.name = $LXC_NAME lxc.rootfs.path = dir:$LXC_ROOTFS lxc.arch = $(uname -m) # Network: use host network lxc.net.0.type = none # Mount points lxc.mount.auto = proc:mixed sys:ro cgroup:mixed lxc.mount.entry = $data_path data none bind,create=dir 0 0 # Environment lxc.environment = GITEA_HTTP_HOST=$http_host lxc.environment = GITEA_HTTP_PORT=$http_port lxc.environment = GITEA_SSH_PORT=$ssh_port lxc.environment = GITEA_APP_NAME=$app_name lxc.environment = GITEA_DOMAIN=$domain lxc.environment = GITEA_DISABLE_REGISTRATION=$disable_registration lxc.environment = GITEA_REQUIRE_SIGNIN=$require_signin # Security lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time sys_rawio # Resource limits lxc.cgroup.memory.limit_in_bytes = $mem_bytes # Init command lxc.init.cmd = /opt/start-gitea.sh EOF log_info "LXC config created" } # Container control lxc_running() { lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING" } lxc_exists() { [ -f "$LXC_CONFIG" ] && [ -d "$LXC_ROOTFS" ] } lxc_stop() { if lxc_running; then log_info "Stopping Gitea container..." lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true sleep 2 fi } lxc_run() { load_config lxc_stop if ! lxc_exists; then log_error "Container not installed. Run: giteactl install" return 1 fi # Regenerate config in case settings changed lxc_create_config log_info "Starting Gitea container..." exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG" } # Commands cmd_install() { require_root load_config log_info "Installing Gitea Platform..." lxc_check_prereqs || exit 1 # Create container if ! lxc_exists; then lxc_create_rootfs || exit 1 fi # Install Gitea binary install_gitea_binary || exit 1 # Create startup script create_startup_script # Create config lxc_create_config || exit 1 # Install container packages (do this separately as it needs a running container) # We'll let the startup script handle package installation on first run instead # Enable service uci_set main.enabled '1' /etc/init.d/gitea enable 2>/dev/null || true log_info "Installation complete!" log_info "" log_info "Start with: /etc/init.d/gitea start" log_info "Web interface: http://:$http_port" log_info "SSH Git access: ssh://git@:$ssh_port" log_info "" log_info "Create admin: giteactl admin create-user --username admin --password secret --email admin@localhost" } cmd_uninstall() { require_root log_info "Uninstalling Gitea Platform..." # Stop service /etc/init.d/gitea stop 2>/dev/null || true /etc/init.d/gitea disable 2>/dev/null || true lxc_stop # Remove container (keep data) if [ -d "$LXC_PATH/$LXC_NAME" ]; then rm -rf "$LXC_PATH/$LXC_NAME" log_info "Container removed" fi uci_set main.enabled '0' log_info "Gitea Platform uninstalled" log_info "Data preserved in: $(uci_get main.data_path)" } cmd_update() { require_root load_config if ! lxc_exists; then log_error "Container not installed. Run: giteactl install" return 1 fi log_info "Updating Gitea binary..." # Download new binary install_gitea_binary || exit 1 # Restart if running if [ "$(uci_get main.enabled)" = "1" ]; then /etc/init.d/gitea restart fi log_info "Update complete" } cmd_status() { load_config local enabled="$(uci_get main.enabled)" local running="false" local installed="false" local uptime="" if lxc_exists; then installed="true" fi if lxc_running; then running="true" uptime=$(lxc-info -n "$LXC_NAME" 2>/dev/null | grep -i "cpu use" | head -1 | awk '{print $3}') fi # Get LAN IP for URL local lan_ip lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1") # Count repositories local repo_count=0 if [ -d "$data_path/git/repositories" ]; then repo_count=$(find "$data_path/git/repositories" -name "*.git" -type d 2>/dev/null | wc -l) fi # Get disk usage local disk_usage="0" if [ -d "$data_path" ]; then disk_usage=$(du -sh "$data_path" 2>/dev/null | awk '{print $1}' || echo "0") fi cat << EOF { "enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"), "running": $running, "installed": $installed, "uptime": "$uptime", "http_port": $http_port, "ssh_port": $ssh_port, "data_path": "$data_path", "memory_limit": "$memory_limit", "app_name": "$app_name", "domain": "$domain", "repo_count": $repo_count, "disk_usage": "$disk_usage", "http_url": "http://${lan_ip}:${http_port}", "ssh_url": "ssh://git@${lan_ip}:${ssh_port}", "container_name": "$LXC_NAME", "version": "$GITEA_VERSION" } EOF } cmd_logs() { load_config local lines="${1:-100}" # Check for gitea logs if lxc_running; then log_info "Container logs (last $lines lines):" lxc-attach -n "$LXC_NAME" -- cat /data/log/gitea.log 2>/dev/null | tail -n "$lines" || \ echo "No logs available" else echo "Container not running" fi # Also check install logs for logfile in /var/log/gitea-install.log /var/log/gitea-update.log; do if [ -f "$logfile" ]; then echo "" echo "=== $logfile ===" tail -n 50 "$logfile" fi done } cmd_shell() { require_root if ! lxc_running; then log_error "Container not running" exit 1 fi lxc-attach -n "$LXC_NAME" -- /bin/sh } cmd_backup() { require_root load_config local backup_file="$BACKUP_PATH/gitea-backup-$(date +%Y%m%d-%H%M%S).tar.gz" log_info "Creating backup..." ensure_dir "$BACKUP_PATH" # Stop service for consistent backup local was_running=0 if lxc_running; then was_running=1 lxc_stop fi # Create backup tar -czf "$backup_file" -C "$data_path" \ git \ custom \ gitea.db 2>/dev/null || true if [ $was_running -eq 1 ]; then /etc/init.d/gitea start & fi local size=$(ls -lh "$backup_file" 2>/dev/null | awk '{print $5}') log_info "Backup created: $backup_file ($size)" echo "$backup_file" } cmd_restore() { require_root load_config local backup_file="$1" if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then log_error "Usage: giteactl restore " log_error "Available backups:" ls -la "$BACKUP_PATH"/*.tar.gz 2>/dev/null || echo "No backups found" return 1 fi log_info "Restoring from: $backup_file" # Stop service local was_running=0 if lxc_running; then was_running=1 lxc_stop fi # Restore backup tar -xzf "$backup_file" -C "$data_path" if [ $was_running -eq 1 ]; then /etc/init.d/gitea start & fi log_info "Restore complete" } cmd_admin_create_user() { require_root load_config local username="" local password="" local email="" # Parse arguments while [ $# -gt 0 ]; do case "$1" in --username) username="$2"; shift 2 ;; --password) password="$2"; shift 2 ;; --email) email="$2"; shift 2 ;; *) shift ;; esac done if [ -z "$username" ] || [ -z "$password" ] || [ -z "$email" ]; then log_error "Usage: giteactl admin create-user --username --password --email " return 1 fi if ! lxc_running; then log_error "Container must be running to create users" return 1 fi log_info "Creating admin user: $username" lxc-attach -n "$LXC_NAME" -- su-exec git /usr/local/bin/gitea admin user create \ --username "$username" \ --password "$password" \ --email "$email" \ --admin \ --config /data/custom/conf/app.ini if [ $? -eq 0 ]; then log_info "Admin user created successfully" else log_error "Failed to create admin user" return 1 fi } # Get Gitea API token (from admin user or config) get_api_token() { local token token="$(uci_get main.api_token)" if [ -n "$token" ]; then echo "$token" return 0 fi # Try to get token from container if lxc_running; then token=$(lxc-attach -n "$LXC_NAME" -- cat /data/api_token 2>/dev/null) if [ -n "$token" ]; then echo "$token" return 0 fi fi return 1 } # Get Gitea API URL get_api_url() { load_config echo "http://127.0.0.1:${http_port}/api/v1" } # Make Gitea API call gitea_api() { local method="$1" local endpoint="$2" local data="$3" local token token=$(get_api_token) || { log_error "No API token configured. Set with: uci set gitea.main.api_token=" log_error "Generate token in Gitea: Settings → Applications → Generate Token" return 1 } local api_url=$(get_api_url) local url="${api_url}${endpoint}" if [ "$method" = "GET" ]; then wget -q -O- --header="Authorization: token $token" "$url" 2>/dev/null elif [ "$method" = "POST" ]; then if [ -n "$data" ]; then wget -q -O- --header="Authorization: token $token" \ --header="Content-Type: application/json" \ --post-data="$data" "$url" 2>/dev/null else wget -q -O- --header="Authorization: token $token" \ --post-data="" "$url" 2>/dev/null fi fi } cmd_mirror_sync() { load_config local repo_name="$1" if [ -z "$repo_name" ]; then log_error "Usage: giteactl mirror-sync or " return 1 fi if ! lxc_running; then log_error "Gitea container is not running" return 1 fi # If no owner specified, try to find the repo if ! echo "$repo_name" | grep -q "/"; then # Search for repo in all users local found_owner found_owner=$(gitea_api GET "/repos/search?q=$repo_name" 2>/dev/null | \ jsonfilter -e '@.data[0].owner.login' 2>/dev/null) if [ -n "$found_owner" ]; then repo_name="${found_owner}/${repo_name}" else log_error "Repository not found: $repo_name" log_error "Specify full path: owner/repo" return 1 fi fi log_info "Syncing mirror: $repo_name" # Trigger mirror sync via API local result result=$(gitea_api POST "/repos/${repo_name}/mirror-sync" 2>&1) if [ $? -eq 0 ]; then log_info "Mirror sync triggered for $repo_name" log_info "Check progress in Gitea web UI" else log_error "Failed to sync mirror: $result" # Try alternative: use gitea command directly in container log_info "Trying direct sync via container..." lxc-attach -n "$LXC_NAME" -- su-exec git /usr/local/bin/gitea admin repo-sync-releases \ --config /data/custom/conf/app.ini 2>/dev/null || true return 1 fi } cmd_mirror_list() { load_config if ! lxc_running; then log_error "Gitea container is not running" return 1 fi log_info "Fetching mirror repositories..." local repos repos=$(gitea_api GET "/repos/search?mirror=true&limit=50" 2>/dev/null) if [ -z "$repos" ]; then echo "No mirrored repositories found (or API token not set)" return 1 fi echo "" echo "Mirrored Repositories:" echo "======================" echo "$repos" | jsonfilter -e '@.data[*]' 2>/dev/null | while read repo; do local name=$(echo "$repo" | jsonfilter -e '@.full_name' 2>/dev/null) local url=$(echo "$repo" | jsonfilter -e '@.original_url' 2>/dev/null) local updated=$(echo "$repo" | jsonfilter -e '@.updated_at' 2>/dev/null) echo " $name" echo " Source: $url" echo " Updated: $updated" echo "" done } cmd_mirror_create() { load_config local name="" local url="" local owner="" # Parse arguments while [ $# -gt 0 ]; do case "$1" in --name) name="$2"; shift 2 ;; --url) url="$2"; shift 2 ;; --owner) owner="$2"; shift 2 ;; *) shift ;; esac done if [ -z "$name" ] || [ -z "$url" ]; then log_error "Usage: giteactl mirror-create --name --url [--owner ]" return 1 fi if ! lxc_running; then log_error "Gitea container is not running" return 1 fi # Get default owner if not specified if [ -z "$owner" ]; then owner=$(gitea_api GET "/user" 2>/dev/null | jsonfilter -e '@.login' 2>/dev/null) if [ -z "$owner" ]; then log_error "Could not determine owner. Specify with --owner" return 1 fi fi log_info "Creating mirror repository: $owner/$name from $url" local data=$(cat <&1) if echo "$result" | grep -q '"id":'; then log_info "Mirror created successfully: $owner/$name" log_info "First sync in progress..." else log_error "Failed to create mirror: $result" return 1 fi } cmd_repo_list() { load_config if ! lxc_running; then log_error "Gitea container is not running" return 1 fi local repos repos=$(gitea_api GET "/repos/search?limit=100" 2>/dev/null) if [ -z "$repos" ]; then echo "No repositories found (or API token not set)" return 1 fi echo "" echo "Repositories:" echo "=============" echo "$repos" | jsonfilter -e '@.data[*].full_name' 2>/dev/null | while read name; do local is_mirror=$(echo "$repos" | jsonfilter -e "@.data[?(@.full_name=='$name')].mirror" 2>/dev/null) if [ "$is_mirror" = "true" ]; then echo " [mirror] $name" else echo " $name" fi done } cmd_service_run() { require_root load_config lxc_check_prereqs || exit 1 lxc_run } cmd_service_stop() { require_root lxc_stop } # Main case "${1:-}" in install) shift; cmd_install "$@" ;; uninstall) shift; cmd_uninstall "$@" ;; update) shift; cmd_update "$@" ;; start) /etc/init.d/gitea start ;; stop) /etc/init.d/gitea stop ;; restart) /etc/init.d/gitea restart ;; status) shift; cmd_status "$@" ;; logs) shift; cmd_logs "$@" ;; shell) shift; cmd_shell "$@" ;; backup) shift; cmd_backup "$@" ;; restore) shift; cmd_restore "$@" ;; admin) shift case "${1:-}" in create-user) shift; cmd_admin_create_user "$@" ;; *) echo "Usage: giteactl admin create-user --username --password --email "; exit 1 ;; esac ;; mirror-sync) shift; cmd_mirror_sync "$@" ;; mirror-list) shift; cmd_mirror_list "$@" ;; mirror-create) shift; cmd_mirror_create "$@" ;; repo-list) shift; cmd_repo_list "$@" ;; service-run) shift; cmd_service_run "$@" ;; service-stop) shift; cmd_service_stop "$@" ;; *) usage ;; esac