File size: 4,580 Bytes
c95b19e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d77da37
 
 
 
 
c95b19e
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env bash
# ChatCsv&Pdf-Backend β€” HF Space entrypoint.
# Loads the single `ENV_FILE` secret (verbatim copy of `.env`) into the
# environment, then starts uvicorn. Same pattern as the reference
# huggingface-service start.sh, simplified for this app.
set -euo pipefail

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

# Track pre-existing process env (HF Space Secrets injected by the platform
# take precedence over ENV_FILE entries with the same name).
declare -A _ENV_FROM_PROCESS
for _k in $(compgen -e); do _ENV_FROM_PROCESS["$_k"]=1; done

# Parse a multi-line KEY=VALUE document (ENV_FILE secret value) and export
# each entry. Handles CRLF, blank lines, '#' comments, 'export ' prefixes,
# surrounding quotes, and values containing '=' (split on first '=' only).
# Never logs values β€” names only.
load_env_document() {
    local content="$1" line key val
    local _ok=0 _skip=0 _fail=0
    if [[ "$content" != *$'\n'* && "$content" == *'\n'* ]]; then
        log "ENV_FILE: no real newlines found β€” expanding literal \\n sequences"
        content="${content//\\n/$'\n'}"
    fi
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        key="${line#"${line%%[![:space:]]*}"}"
        [ -n "$key" ] || continue
        case "$key" in '#'*) continue ;; esac
        if [[ "$key" == 'export '* ]]; then
            key="${key#export }"
            key="${key#"${key%%[![:space:]]*}"}"
        fi
        case "$key" in
            *=*) val="${key#*=}"; key="${key%%=*}" ;;
            *) log "ENV FAIL: malformed line, no '=' found: ${line:0:40}"; _fail=$((_fail+1)); continue ;;
        esac
        key="${key%"${key##*[![:space:]]}"}"
        if ! [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
            log "ENV FAIL: invalid variable name '${key:0:40}'"; _fail=$((_fail+1)); continue
        fi
        val="${val#"${val%%[![:space:]]*}"}"
        val="${val%"${val##*[![:space:]]}"}"
        case "$val" in
            \"*\") val="${val#\"}"; val="${val%\"}" ;;
            \'*\') val="${val#\'}"; val="${val%\'}" ;;
        esac
        if [ -n "${_ENV_FROM_PROCESS[$key]:-}" ]; then
            log "ENV SKIP: $key (already set in process environment β€” keeping existing value)"
            _skip=$((_skip+1))
        elif export "$key=$val" 2>/dev/null; then
            log "ENV OK: $key"; _ok=$((_ok+1))
        else
            log "ENV FAIL: $key (could not export)"; _fail=$((_fail+1))
        fi
    done <<< "$content"
    log "ENV_FILE load summary: $_ok loaded, $_skip skipped, $_fail failed, $((_ok+_skip+_fail)) total"
}

if [ -n "${ENV_FILE:-}" ]; then
    log "ENV_FILE secret detected (${#ENV_FILE} chars) β€” loading embedded environment"
    load_env_document "$ENV_FILE"
else
    log "ENV_FILE secret not set β€” falling back to .env files (local dev) / platform env (HF Secrets)"
    [ -f ".env" ] && log ".env file present β€” pydantic-settings will load it" || log "no .env file β€” using process environment"
fi

_crit_missing=""
for _v in APPLICATION_MAIN_BACKEND_AUTH_TOKEN REDIS_URL PINECONE_API_KEYS; do
    [ -n "${!_v:-}" ] || _crit_missing="$_crit_missing $_v"
done
if [ -n "$_crit_missing" ]; then
    log "WARNING: keys not set:$_crit_missing (app still boots; /chat degrades without Pinecone/Redis)"
else
    log "Critical keys: all present"
fi
unset _crit_missing _v

log "=== Starting up ==="
log "Python: $(python --version 2>&1)"
log "Working dir: $(pwd)"

mkdir -p "${STORAGE_DIR:-/app/storage}" /app/storage
log "Storage directories: OK"

python -c "import fastapi, uvicorn, httpx, pydantic" 2>/dev/null \
    && log "Core dependencies: OK" \
    || { log "ERROR: missing core dependencies (fastapi/uvicorn/httpx/pydantic)"; exit 1; }

HOST="${HOST:-0.0.0.0}"
PORT="${PORT:-7860}"
log "Self-ping: SELF_PING_ENABLED=${SELF_PING_ENABLED:-true} SELF_PING_URL=${SELF_PING_URL:-'(derived from SPACE_ID)'} interval=${SELF_PING_INTERVAL:-600}s"
_cron_base="${FRONTEND_BASE_URL:-'(missing)'}"
_cron_secret_state="missing"; [ -n "${CRON_SECRET:-}" ] && _cron_secret_state="set"
_cron_v1_state="missing"; [ -n "${EXTERNAL_API_CRON_TOKEN:-}" ] && _cron_v1_state="set"
log "Frontend credit crons: CRON_ENABLED=${CRON_ENABLED:-true} CRON_TIMEZONE=${CRON_TIMEZONE:-'(system)'} FRONTEND_BASE_URL=${_cron_base} /api/cron(${_cron_secret_state}) /api/v1/cron(${_cron_v1_state}) daily=00:00"
unset _cron_base _cron_secret_state _cron_v1_state
log "Starting uvicorn: host=$HOST port=$PORT workers=1 app=app.main:app"
exec python -m uvicorn app.main:app --host "$HOST" --port "$PORT" --workers 1