diff --git a/alpine/template b/alpine/template index f8a50bf..fc6b456 100755 --- a/alpine/template +++ b/alpine/template @@ -5,6 +5,8 @@ if [ "$#" -ne 2 ]; then exit 1 fi +host_vars_dir="/app/host_vars" + keys=(NEBULA_CA API_KEY HOSTNAME NEBULA_CRT NEBULA_KEY SSH_PRIVATE SSH_PUBLIC) NEBULA_CA=$(<"$host_vars_dir/ca.crt") @@ -14,8 +16,6 @@ remotefile="$2" remotetmp_base="/var/tmp/4server" while read -r host; do - echo "Processing host: $host" - host_env_file="$host_vars_dir/$host/$host.env" if [ ! -f "$host_env_file" ]; then diff --git a/app/migrate b/app/migrate index 8d037fe..bf5a962 100755 --- a/app/migrate +++ b/app/migrate @@ -3,5 +3,6 @@ #!/bin/bash rex "doas sqlite3 /4server/data/contracts.db <<'EOF' -ALTER TABLE containers RENAME COLUMN secrets TO secret; +ALTER TABLE containers +ADD COLUMN secret TEXT NOT NULL DEFAULT ''; EOF" diff --git a/app/sbin/ODOO_19/ODOO_19.lib b/app/sbin/ODOO_19/ODOO_19.lib index 1e3659c..7a63bba 100644 --- a/app/sbin/ODOO_19/ODOO_19.lib +++ b/app/sbin/ODOO_19/ODOO_19.lib @@ -1,5 +1,6 @@ #!/bin/bash + dump_config (){ echo "========== Odoo Container Configuration ==========" echo "UUID: $UUID" diff --git a/app/sbin/ODOO_19/restore b/app/sbin/ODOO_19/restore index 9d5d1a1..1bc6607 100755 --- a/app/sbin/ODOO_19/restore +++ b/app/sbin/ODOO_19/restore @@ -6,16 +6,23 @@ if [ ! -n "$2" ]; then fi UUID=$1 +echo "UUID: $UUID" source /4server/sbin/helpers +get_contract_info + + +export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') + +echo "PASSWORD $ODOO_DB_PASSWORD" + echo "Restoring $FILENAME to $1" echo "status of container" doas docker ps -a --filter "id=$UUID" - +echo "POSTGRES HOST: $POSTGRES_HOST" BACKUP="/mnt/backup/$2" TEMPLATE="/mnt/db_images/$2" -echo "TEMPLATE" doas docker exec "${1%}" /bin/bash -c "[ -f $TEMPLATE ]" if doas docker exec "${1%}" /bin/bash -c "[ -f $BACKUP ]"; then @@ -28,32 +35,30 @@ else fi -### DELETE DATABASE +### DELETE AND CREATE DATABASE +PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -d postgres -c " +SELECT pg_terminate_backend(pid) FROM pg_stat_activity +WHERE datname = '$UUID' AND pid <> pg_backend_pid(); +" +PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -d postgres -c " +DROP DATABASE IF EXISTS \"$UUID\"; +" + PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql \ -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -p "$POSTGRES_PORT" -d postgres \ - -c "DROP DATABASE IF EXISTS \"$UUID\";" + -c "ALTER ROLE \"$1\" CREATEDB;" +doas docker exec "${1%}" rm -rf /home/odoo/.local/share/Odoo/filestore -echo "deleting old restore artifacts" -doas docker exec "${1%}" rm -rf /root/.local/ +doas docker exec "${1%}" odoo db --db_host beedb -w "$ODOO_DB_PASSWORD" -r "$1" load "$1" $FILENAME -f -if [[ "$1" == *"_"* ]]; then - echo "Neutralize DB" - doas docker exec "${1%_}" odoo db --db_host beedb -w "${1%_}" -r "$1" load "$1" $FILENAME -f --neutralize -else - echo "No Neutralize DB" - doas docker exec "${1%}" odoo db --db_host beedb -w "${1%_}" -r "$1" load "$1" $FILENAME -f -fi - - - -doas docker exec "${1%}" cp -r /root/.local/. /var/lib/odoo/.local/ -doas docker exec "${1%}" chown -R odoo:odoo /var/lib/odoo/.local/ - -### transfer ownership PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql \ -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -p "$POSTGRES_PORT" -d postgres \ - -c "ALTER DATABASE \"$UUID\" OWNER TO \"$UUID\";" + -c "ALTER ROLE \"$1\" NOCREATEDB;" + + +doas docker exec "${1%}" cp -r /root/.local/share/Odoo/filestore /home/odoo/.local/share/Odoo/filestore +doas docker exec "${1%}" chown -R odoo:odoo /home/odoo/.local/share/Odoo/filestore docker restart "${1%}" diff --git a/app/sbin/api b/app/sbin/api index 5b25ef0..89f8eed 100755 --- a/app/sbin/api +++ b/app/sbin/api @@ -35,7 +35,14 @@ def run_command(cmd: list[str]) -> str: """Run a shell command and return stdout or raise HTTPException on error.""" result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: - raise HTTPException(status_code=500, detail=result.stderr.strip() or "Unknown error") + error_msg = ( + f"Command failed\n" + f"Command: {' '.join(cmd)}\n" + f"Return code: {result.returncode}\n" + f"Stdout: {result.stdout.strip()}\n" + f"Stderr: {result.stderr.strip() or 'None'}" + ) + raise HTTPException(status_code=500, detail=error_msg) return result.stdout.strip() @@ -155,16 +162,18 @@ def nuke_container(request: UUIDRequest): @app.post("/container/info", dependencies=[Depends(verify_api_key)]) def info_container(request: Optional[UUIDRequest] = None): + # Fields to select fields = [ "ID", "UUID", "email", "expires", "tags", "env", "affiliate", "image", "history", "comment", "domains", "status", "created" ] field_str = ", ".join(fields) + # Execute query if request: rows = execute_db( f"SELECT {field_str} FROM containers WHERE UUID=?", - (request.UUID,), + (str(request.UUID),), fetch=True ) else: @@ -173,15 +182,21 @@ def info_container(request: Optional[UUIDRequest] = None): fetch=True ) - # Map rows to dicts with field names - containers = [dict(zip(fields, row)) for row in rows] + # Map rows to dicts safely + containers = [] + for row in rows: + if isinstance(row, dict): + # Already a dict (e.g., some DB wrappers) + containers.append(row) + else: + # Tuple/list -> map with fields + containers.append(dict(zip(fields, row))) - # Wrap in N8N JSON format + # Wrap in n8n JSON format n8n_items = [{"json": container} for container in containers] return n8n_items - @app.post("/container/bump", dependencies=[Depends(verify_api_key)]) def bump_container(request: UUIDRequest): today = datetime.utcnow().strftime("%Y-%m-%d") diff --git a/app/sbin/backup/ODOO_19 b/app/sbin/backup/ODOO_19 new file mode 100755 index 0000000..a00680f --- /dev/null +++ b/app/sbin/backup/ODOO_19 @@ -0,0 +1,15 @@ +#!/bin/bash + +source /4server/sbin/helpers +get_contract_info +export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') +echo "PASSWORD $ODOO_DB_PASSWORD UUID $UUID" + + +FILENAME=$(date +"%Y%m%d_%H:%M")".zip" + +doas docker exec "$UUID" odoo db --db_host beedb -r "$UUID" -w "$ODOO_DB_PASSWORD" --data-dir /home/odoo/.local/share/Odoo/ dump "$UUID" /mnt/backup/$FILENAME + +doas chmod 666 /BACKUP/$UUID/* + + diff --git a/app/sbin/backupContainer b/app/sbin/backupContainer new file mode 100755 index 0000000..589b5bd --- /dev/null +++ b/app/sbin/backupContainer @@ -0,0 +1,36 @@ +#!/bin/bash +# Usage: ./start_by_uuid.sh +# Example: ./start_by_uuid.sh abc-001-xxxx-xxxx-xxxx +exec > /4server/data/log/backupContainer.log 2>&1 +echo "$(date '+%Y-%m-%d %H:%M') Backup container $1" + +source /4server/sbin/helpers + +BIN_PATH="/4server/sbin" + +UUID="$1" + +if [[ -z "$UUID" ]]; then + echo "Usage: $0 " + exit 1 +fi + +get_contract_info + +# Extract the second part of UUID (split by "-") +SECOND_PART=$(echo "$UUID" | cut -d'-' -f2) + +# Decide which script to run +case "$SECOND_PART" in + 001) + "$BIN_PATH/backup/n8n" + ;; + 003) + "$BIN_PATH/backup/ODOO_19" + ;; + *) + echo "Unknown UUID type: $SECOND_PART" + exit 2 + ;; +esac + diff --git a/app/sbin/checkCalls b/app/sbin/checkCalls new file mode 100755 index 0000000..012e215 --- /dev/null +++ b/app/sbin/checkCalls @@ -0,0 +1,34 @@ +#!/bin/bash + +cd /4server/data/ + +while : +do + +for dir in ???-???-*; do + if [ -d "${dir}/cc" ]; then + if [ -f "${dir}/cc/backup" ]; then + echo "BACKUP for: ${dir%/}" + /4server/sbin/backupContainer ${dir%/} 2 + rm "${dir}/cc/backup" + fi + + if [ -f "${dir}/cc/restart" ]; then + echo "Restart for: ${dir%/}" + /4server/sbin/startContainer ${dir%/} + rm "${dir}/cc/restart" + fi + + if [ -f "${dir}/cc/restore" ]; then + FILENAME=$(head -n 1 "${dir}/cc/restore") + echo "Restore for: ${dir%/} - $FILENAME" + /4server/sbin/ODOO_19/restore ${dir%/} $FILENAME + rm "${dir}/cc/restore" + fi + + fi +done + +sleep 60 + +done diff --git a/app/sbin/helpers b/app/sbin/helpers index 8ebbd9f..a18390a 100755 --- a/app/sbin/helpers +++ b/app/sbin/helpers @@ -1,5 +1,12 @@ #!/bin/bash +POSTGRES_HOST="${POSTGRES_HOST:-beedb}" +POSTGRES_PORT="${POSTGRES_PORT:-5432}" +POSTGRES_ADMIN_USER="${POSTGRES_ADMIN_USER:-1gtT0sf8klB9lDbYZD9}" +POSTGRES_ADMIN_PASSWORD="${POSTGRES_ADMIN_PASSWORD:-ZpSwWNafyy9GhY2gzHw}" + + + get_contract_info() { DB_PATH="/4server/data/contracts.db" diff --git a/app/sbin/start/ODOO_19 b/app/sbin/start/ODOO_19 index de8628e..8270d42 100755 --- a/app/sbin/start/ODOO_19 +++ b/app/sbin/start/ODOO_19 @@ -2,19 +2,16 @@ # Load functions source /4server/sbin/ODOO_19/ODOO_19.lib +source /4server/sbin/helpers # Config variables UUID="${UUID:-default}" -BRANCH="${BRANCH:-main}" -STAGING="${STAGING:-false}" +BRANCH="${BRANCH:-release}" -POSTGRES_HOST="${POSTGRES_HOST:-beedb}" -POSTGRES_PORT="${POSTGRES_PORT:-5432}" -POSTGRES_ADMIN_USER="${POSTGRES_ADMIN_USER:-1gtT0sf8klB9lDbYZD9}" -POSTGRES_ADMIN_PASSWORD="${POSTGRES_ADMIN_PASSWORD:-ZpSwWNafyy9GhY2gzHw}" ODOO_DB_USER="${UUID}" export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') + BASEURL="${BASEURL:-/4server/data/$UUID}" DATA_DIR="$BASEURL/odoo/" CUSTOM_DIR="$BASEURL/git/$UUID/custom/" @@ -24,10 +21,8 @@ CONFIG_DIR="$BASEURL/config/" CC_DIR="$BASEURL/cc/" BACKUP_DIR="/BACKUP/$UUID" GIT_DIR="$BASEURL/git-server/" -ETC_DIR="$BASEURL/etc/" INSTALL_DIR="$BASEURL/install/" SSH_DIR="$BASEURL/.ssh/" -HUGO_DIR="$BASEURL/git-server/local/hugo" SERVER_IP=$(ip -4 addr show eth0 | awk '/inet/ {print $2}' | cut -d/ -f1) @@ -35,36 +30,34 @@ DOMAIN_LABEL=$(check_domains "$UUID.odoo4projects.com" "$SERVER_IP") DOMAIN_LABEL="traefik.http.routers.$UUID.rule=Host(\`$UUID.odoo4projects.com\`)" +doas find "$BASEURL" -type d -exec chmod 777 {} \; +doas chmod 777 $BACKUP_DIR + PORT=$((RANDOM%1000+2200)) doas docker stop "$UUID" 2>/dev/null doas docker rm "$UUID" 2>/dev/null EXTRA_DOCKER_PARAMETER="" -if [ -d "$HUGO_DIR" ]; then - EXTRA_DOCKER_PARAMETER="-v $HUGO_DIR:/mnt/hugo" -fi doas docker run -d --name "$UUID" \ --network 4server_4projects \ --restart=always \ $EXTRA_DOCKER_PARAMETER \ - -v "$DATA_DIR/odoo-web-data:/var/lib/odoo" \ + -v "$DATA_DIR/odoo-web-data:/home/odoo/.local/share/Odoo" \ -v "$CUSTOM_DIR:/mnt/addons/custom" \ -v "$ENTERPRISE_DIR:/mnt/addons/enterprise" \ -v "$LOGS_DIR:/mnt/logs" \ -v "$CC_DIR:/mnt/cc" \ -v "$BACKUP_DIR:/mnt/backup" \ - -v "$CONFIG_DIR:/etc/odoo" \ -v "$GIT_DIR:/git-server" \ - -v "$ETC_DIR:/mnt/etc" \ -v "$INSTALL_DIR:/mnt/install" \ -v "$SSH_DIR:/etc/sshkey" \ -p "$PORT:22" \ - -e HOST="$POSTGRES_HOST" \ + -e HOST="beedb" \ -e USER="$ODOO_DB_USER" \ -e PASSWORD="$ODOO_DB_PASSWORD" \ - -e STAGING="$STAGING" \ + -e UUID="$UUID" \ --label "$DOMAIN_LABEL" \ --label "traefik.http.services.$UUID.loadbalancer.server.port=8069" \ --label "traefic.http.routers.$UUID.entrypoints=web, websecure" \ diff --git a/app/sbin/startContainer b/app/sbin/startContainer index 82eeae6..43d0fd2 100755 --- a/app/sbin/startContainer +++ b/app/sbin/startContainer @@ -25,7 +25,7 @@ case "$SECOND_PART" in 001) "$BIN_PATH/start/n8n" ;; - 002) + 003) "$BIN_PATH/start/ODOO_19" ;; *) diff --git a/app/templates/init.d/checkCalls b/app/templates/init.d/checkCalls new file mode 100755 index 0000000..5e4fe34 --- /dev/null +++ b/app/templates/init.d/checkCalls @@ -0,0 +1,24 @@ +#!/sbin/openrc-run + +name="checkCalls" +description="check container service calls" + +# Command uses Python inside the venv +command="/4server/sbin/checkCalls" +command_args="" +pidfile="/run/${RC_SVCNAME}.pid" +command_background="yes" + +respawn_delay=5 # seconds to wait before restart +respawn_max=0 # 0 = unlimited restarts + +# Load environment variables if needed +output_log="/4server/data/log/checkCalls.log" +error_log="/4server/data/log/checkCalls.log" + +depend() { + need net + use logger dns + after firewall +} + diff --git a/app/update b/app/update index 5298d9f..26b928d 100755 --- a/app/update +++ b/app/update @@ -46,6 +46,13 @@ rex doas chown root:root /etc/init.d/api rex doas rc-update add api default rex doas rc-service api restart +#INSTALL checkCalls SERVICE +template templates/init.d/checkCalls /etc/init.d/checkCalls +rex doas chmod 0755 /etc/init.d/checkCalls +rex doas chown root:root /etc/init.d/checkCalls +rex doas rc-update add checkCalls default +rex doas rc-service checkCalls restart + ### Infrastructure ##### Docker diff --git a/app/vault/host_vars.img b/app/vault/host_vars.img index 258ae7f..367334f 100644 Binary files a/app/vault/host_vars.img and b/app/vault/host_vars.img differ diff --git a/start b/start index 6572039..7e1225f 100755 --- a/start +++ b/start @@ -1,2 +1,15 @@ #!/bin/bash -docker compose run alpine /bin/bash + +SERVICE_NAME="alpine" + +# Check if the container is running +RUNNING=$(docker compose ps -q $SERVICE_NAME) + +if [ -z "$RUNNING" ]; then + echo "Container not running. Starting..." + docker compose up -d $SERVICE_NAME +fi + +# Connect to the running container +docker compose exec -it $SERVICE_NAME sh +