diff --git a/app/migrate b/app/migrate index bf5a962..744adb5 100755 --- a/app/migrate +++ b/app/migrate @@ -4,5 +4,5 @@ rex "doas sqlite3 /4server/data/contracts.db <<'EOF' ALTER TABLE containers -ADD COLUMN secret TEXT NOT NULL DEFAULT ''; +ADD COLUMN config TEXT NOT NULL DEFAULT ''; EOF" diff --git a/app/sbin/ODOO_19/restore b/app/sbin/ODOO_19/restore index 1bc6607..37e6d1a 100755 --- a/app/sbin/ODOO_19/restore +++ b/app/sbin/ODOO_19/restore @@ -17,17 +17,17 @@ export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') echo "PASSWORD $ODOO_DB_PASSWORD" -echo "Restoring $FILENAME to $1" +echo "Restoring $FILENAME to $UUID" 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" -doas docker exec "${1%}" /bin/bash -c "[ -f $TEMPLATE ]" +doas docker exec "$UUID" /bin/bash -c "[ -f $TEMPLATE ]" -if doas docker exec "${1%}" /bin/bash -c "[ -f $BACKUP ]"; then +if doas docker exec "$UUID" /bin/bash -c "[ -f $BACKUP ]"; then FILENAME="$BACKUP" -elif doas docker exec "${1%}" /bin/bash -c "[ -f $TEMPLATE ]"; then +elif doas docker exec "$UUID" /bin/bash -c "[ -f $TEMPLATE ]"; then FILENAME="$TEMPLATE" else echo "File not exists" @@ -46,19 +46,19 @@ DROP DATABASE IF EXISTS \"$UUID\"; PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql \ -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -p "$POSTGRES_PORT" -d postgres \ - -c "ALTER ROLE \"$1\" CREATEDB;" + -c "ALTER ROLE \"$UUID\" CREATEDB;" -doas docker exec "${1%}" rm -rf /home/odoo/.local/share/Odoo/filestore +doas docker exec "$UUID" rm -rf /home/odoo/.local/share/Odoo/filestore -doas docker exec "${1%}" odoo db --db_host beedb -w "$ODOO_DB_PASSWORD" -r "$1" load "$1" $FILENAME -f +doas docker exec "$UUID" odoo db --db_host beedb -w "$ODOO_DB_PASSWORD" -r "$UUID" load "$UUID" $FILENAME -f PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" psql \ -h "$POSTGRES_HOST" -U "$POSTGRES_ADMIN_USER" -p "$POSTGRES_PORT" -d postgres \ - -c "ALTER ROLE \"$1\" NOCREATEDB;" + -c "ALTER ROLE \"$UUID\" 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 +doas docker exec "$UUID" cp -r /root/.local/share/Odoo/filestore /home/odoo/.local/share/Odoo/filestore +doas docker exec "$UUID" chown -R odoo:odoo /home/odoo/.local -docker restart "${1%}" +docker restart "$UUID" diff --git a/app/sbin/api b/app/sbin/api index 89f8eed..9547532 100755 --- a/app/sbin/api +++ b/app/sbin/api @@ -108,6 +108,36 @@ class UUIDRequest(BaseModel): UUID: str +# ---------------------- Routes ---------------------- +@app.get("/", include_in_schema=False) +def redirect_to_odoo(): + return RedirectResponse(url="https://ODOO4PROJECTS.com") + +from fastapi import FastAPI, Depends +from fastapi.responses import RedirectResponse +from pydantic import BaseModel +from typing import Optional, Dict, Any +import json + +app = FastAPI() + +# ---------------------- Models ---------------------- +class ContainerModel(BaseModel): + UUID: str + email: Optional[str] = None + expires: Optional[str] = None + tags: Optional[str] = None + env: Optional[Dict[str, Any]] = None + affiliate: Optional[str] = None + image: Optional[str] = None + history: Optional[str] = None + comment: Optional[str] = None + domains: Optional[str] = None + status: Optional[str] = None + created: Optional[str] = None + bump: Optional[str] = None + secret: Optional[Dict[str, Any]] = None + # ---------------------- Routes ---------------------- @app.get("/", include_in_schema=False) def redirect_to_odoo(): @@ -116,20 +146,14 @@ def redirect_to_odoo(): @app.post("/container/update", dependencies=[Depends(verify_api_key)]) def update_container(request: ContainerModel): - env_str = json.dumps(request.env) if isinstance(request.env, dict) else request.env - secret_str = json.dumps(request.secret) if isinstance(request.secret, dict) else request.secret + # Convert dict fields to JSON strings + env_str = json.dumps(request.env) if isinstance(request.env, dict) else None + secret_str = json.dumps(request.secret) if isinstance(request.secret, dict) else None + + # Fetch existing record existing = execute_db("SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True) - if existing: - execute_db(""" - UPDATE containers SET email=?, expires=?, tags=?, env=?, affiliate=?, image=?, - history=?, comment=?, domains=?, status=?, created=?, bump=?, secret=? - WHERE UUID=? - """, ( - request.email, request.expires, request.tags, env_str, request.affiliate, - request.image, request.history, request.comment, request.domains, request.status, - request.created, request.bump, secret_str, request.UUID - )) - else: + if not existing: + # If record does not exist, insert a new one with all given fields execute_db(""" INSERT INTO containers (UUID, email, expires, tags, env, affiliate, image, history, comment, domains, status, created, bump, secret) @@ -139,7 +163,32 @@ def update_container(request: ContainerModel): request.affiliate, request.image, request.history, request.comment, request.domains, request.status, request.created, request.bump, secret_str )) - return {"message": "Container updated or created"} + return {"UUID": request.UUID, "status": "created"} + + # Existing record found, do partial update + existing = existing[0] # Assuming fetch returns list of dicts + updates = {} + params = [] + + # Only add fields that are not None in the request + for field in ContainerModel.__fields__: + if field == "UUID": + continue + value = getattr(request, field) + if value is not None: + if field in ["env", "secret"]: + value = json.dumps(value) + updates[field] = value + params.append(value) + + if updates: + # Build SQL dynamically + set_clause = ", ".join(f"{k}=?" for k in updates.keys()) + params.append(request.UUID) # UUID for WHERE clause + execute_db(f"UPDATE containers SET {set_clause} WHERE UUID=?", tuple(params)) + return {"UUID": request.UUID, "status": "updated", "fields_updated": list(updates.keys())} + + return {"UUID": request.UUID, "status": "no_change"} @app.post("/container/start", dependencies=[Depends(verify_api_key)]) diff --git a/app/sbin/backup/ODOO_19 b/app/sbin/backup/ODOO_19 index a00680f..3dad749 100755 --- a/app/sbin/backup/ODOO_19 +++ b/app/sbin/backup/ODOO_19 @@ -1,15 +1,48 @@ #!/bin/bash +# Backup Odoo database script +# Author: Your Name +# Description: Dumps Odoo DB, manages backups, and sets permissions +set -euo pipefail # Fail on error, undefined variables, and pipe errors + +# Load helper functions source /4server/sbin/helpers + +# Get contract info get_contract_info -export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') -echo "PASSWORD $ODOO_DB_PASSWORD UUID $UUID" + +# Export Odoo database password +export ODOO_DB_PASSWORD +ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') + +# Display basic info +echo "UUID: $UUID" +echo "Backup slots: $BACKUP_SLOTS" + +# Create backup filename +FILENAME="$(date +"%Y%m%d_%H%M").zip" +BACKUP_DIR="/BACKUP/$UUID" + +# Ensure backup directory exists +mkdir -p "$BACKUP_DIR" + +# Perform database dump using docker +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" + +# Set permissions for backup files +doas chmod 600 "$BACKUP_DIR"/* +doas docker exec "$UUID" chown odoo:odoo -R /mnt/backup -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/* +# Remove old backups beyond the configured slots +ls -t "$BACKUP_DIR"/[0-9]*.zip 2>/dev/null | tail -n +$((BACKUP_SLOTS + 1)) | while read -r file; do + echo "Deleting old backup: $file" + rm -f "$file" +done diff --git a/app/sbin/contractInfo b/app/sbin/contractInfo new file mode 100755 index 0000000..0913191 --- /dev/null +++ b/app/sbin/contractInfo @@ -0,0 +1,14 @@ +export PATH=/4PROJECTS/bin:$PATH +if [ ! -n "$1" ]; then + echo "Missing Parameters " + exit 0 +fi + +UUID=$1 +echo "UUID: $UUID" + +source /4server/sbin/helpers + +get_contract_info + +env diff --git a/app/sbin/helpers b/app/sbin/helpers index a18390a..db6d33b 100755 --- a/app/sbin/helpers +++ b/app/sbin/helpers @@ -31,12 +31,13 @@ done < <(sqlite3 "$DB_PATH" " UNION ALL SELECT 'STATUS=' || status FROM containers WHERE UUID='$UUID' UNION ALL SELECT 'CREATED=' || created FROM containers WHERE UUID='$UUID' UNION ALL SELECT 'SECRET=' || secret FROM containers WHERE UUID='$UUID' + UNION ALL SELECT 'CONTAINERDBID=' || id FROM containers WHERE UUID='$UUID' UNION ALL SELECT 'BUMP=' || bump FROM containers WHERE UUID='$UUID'; ") # Debug: print loaded environment variables env | grep -E 'UUID|EMAIL|EXPIRES|TAGS|ENV|AFFILIATE|IMAGE|HISTORY|COMMENT|DOMAINS|STATUS|CREATED|BUMP|SECRET' +eval $(echo "$ENV" | jq -r 'to_entries | .[] | "export \(.key | ascii_upcase)=\(.value)"') -eval $(echo "$ENV" | jq -r 'to_entries | .[] | "export \(.key)=\(.value)"') } diff --git a/app/sbin/start/ODOO_19 b/app/sbin/start/ODOO_19 index 8270d42..0c48138 100755 --- a/app/sbin/start/ODOO_19 +++ b/app/sbin/start/ODOO_19 @@ -11,6 +11,8 @@ BRANCH="${BRANCH:-release}" ODOO_DB_USER="${UUID}" export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') +echo "ENV: $HDD $DOMAIN_COUNT $BACKUP_SLOTS $CONTAINERDBID" + BASEURL="${BASEURL:-/4server/data/$UUID}" DATA_DIR="$BASEURL/odoo/" @@ -23,6 +25,7 @@ BACKUP_DIR="/BACKUP/$UUID" GIT_DIR="$BASEURL/git-server/" INSTALL_DIR="$BASEURL/install/" SSH_DIR="$BASEURL/.ssh/" +ETC_DIR="$BASEURL/etc/" SERVER_IP=$(ip -4 addr show eth0 | awk '/inet/ {print $2}' | cut -d/ -f1) @@ -31,9 +34,11 @@ 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)) +PORT=$($CONTAINERDBID+2200) +echo "PORT $PORT" +echo "git clone \"ssh://git@${UUID}.odoo4projects.com:${PORT}/git-server/repos/odoo.git\"" > "${ETC_DIR}/gitpath" + doas docker stop "$UUID" 2>/dev/null doas docker rm "$UUID" 2>/dev/null @@ -53,12 +58,18 @@ doas docker run -d --name "$UUID" \ -v "$GIT_DIR:/git-server" \ -v "$INSTALL_DIR:/mnt/install" \ -v "$SSH_DIR:/etc/sshkey" \ + -v "$ETC_DIR:/mnt/etc" \ -p "$PORT:22" \ -e HOST="beedb" \ -e USER="$ODOO_DB_USER" \ -e PASSWORD="$ODOO_DB_PASSWORD" \ -e UUID="$UUID" \ - --label "$DOMAIN_LABEL" \ + -e HDD="$HDD" \ + -e DOMAIN_COUNT="$DOMAIN_COUNT" \ + -e BACKUP_SLOTS="$BACKUP_SLOTS" \ + -e WORKER="$WORKER" \ + -e GIT="$GIT" \ + --label "$DOMAIN_LABEL" \ --label "traefik.http.services.$UUID.loadbalancer.server.port=8069" \ --label "traefic.http.routers.$UUID.entrypoints=web, websecure" \ --label "traefik.http.routers.$UUID.tls.certresolver=production" \ @@ -67,4 +78,9 @@ doas docker run -d --name "$UUID" \ docker.odoo4projects.com/4projects/odoo_19:$BRANCH +doas docker exec $UUID chown -R odoo:odoo /home/odoo/.local +doas docker exec $UUID chown -R odoo:odoo /mnt/* + + + check_and_create_db diff --git a/app/sbin/start/n8n b/app/sbin/start/n8n index 91c8eb6..64d0cc9 100755 --- a/app/sbin/start/n8n +++ b/app/sbin/start/n8n @@ -1,6 +1,5 @@ #!/usr/bin/env bash - - +#--label "traefik.http.routers.${UUID}.middlewares=cors-headers@file" \ echo "Start N8N container ${UUID}" # Get the hostname of the machine diff --git a/app/templates/traefik.yaml b/app/templates/traefik.yaml index fb24114..7519104 100644 --- a/app/templates/traefik.yaml +++ b/app/templates/traefik.yaml @@ -80,6 +80,10 @@ http: address: http://bouncer-traefik:8080/api/v1/forwardAuth trustForwardHeader: true + cors-headers: + headers: + accessControlAllowCredentials: true + routers: api-router: diff --git a/app/update_sbin b/app/update_sbin new file mode 100755 index 0000000..26ffcd5 --- /dev/null +++ b/app/update_sbin @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Running prsync ./sbin" +prsync -h "/app/host_vars/hosts" -avz ./sbin/ /4server/sbin/ + +rex doas rc-service api restart + +rex doas rc-service checkCalls restart + diff --git a/app/vault/host_vars.img b/app/vault/host_vars.img index 367334f..e2bc3e0 100644 Binary files a/app/vault/host_vars.img and b/app/vault/host_vars.img differ