thryyyyy commited on
Commit
5b9cbe5
·
1 Parent(s): e0c0a15

better backup restore

Browse files
backend/scripts/backup.py CHANGED
@@ -18,15 +18,13 @@ def get_last_backup_time(repo_id, hf_token):
18
  temp_file = hf_hub_download(
19
  repo_id=repo_id,
20
  repo_type="space",
21
- filename=TIMESTAMP_FILE_PATH, # "db_backup/last_backup_time.txt"
22
  token=hf_token
23
  )
24
  except Exception as e:
25
- # File might not exist or there's some other error
26
  print(f"Could not download {TIMESTAMP_FILE_PATH}: {e}")
27
  return None
28
 
29
- # If we downloaded successfully, read the timestamp
30
  try:
31
  with open(temp_file, "r", encoding="utf-8") as f:
32
  timestamp_str = f.read().strip()
@@ -37,18 +35,22 @@ def get_last_backup_time(repo_id, hf_token):
37
  return None
38
 
39
  def save_timestamp_locally():
40
- """
41
- Save the current UTC time to db_backup/last_backup_time.txt (locally).
42
- """
43
  now = datetime.datetime.utcnow()
44
  os.makedirs("db_backup", exist_ok=True)
45
  with open(TIMESTAMP_FILE_PATH, "w", encoding="utf-8") as f:
46
  f.write(now.isoformat())
47
 
48
  def backup_db():
 
49
  passphrase = os.environ.get("BACKUP_PASSPHRASE")
50
  hf_token = os.environ.get("HF_TOKEN")
51
  space_id = os.environ.get("SPACE_ID")
 
 
 
 
 
52
  if not passphrase:
53
  raise ValueError("BACKUP_PASSPHRASE is not set.")
54
  if not hf_token:
@@ -56,37 +58,40 @@ def backup_db():
56
  if not space_id:
57
  raise ValueError("SPACE_ID is not set (or define repo_id manually).")
58
 
59
- # 1) Check if last backup was too recent
60
- threshold_hours = 2
61
- last_backup_dt = get_last_backup_time(space_id, hf_token)
62
- if last_backup_dt is not None:
63
- now = datetime.datetime.utcnow()
64
- elapsed = now - last_backup_dt
65
- if elapsed.total_seconds() < threshold_hours * 3600:
66
- print(f"Last backup was only {elapsed.total_seconds()/3600:.2f} hours ago.")
67
- print("Skipping backup to avoid rebuild loop.")
68
- return
 
 
 
69
 
70
- # 2) Encrypt the DB using GPG
71
  print("Encrypting database with GPG...")
72
  encrypt_cmd = [
73
  "gpg", "--batch", "--yes", "--passphrase", passphrase,
74
  "-c", "--cipher-algo", "AES256",
75
- "-o", DB_GPG_PATH, # "db_backup/webui.db.gpg"
76
- DB_FILE_PATH # "data/webui.db"
77
  ]
78
  subprocess.run(encrypt_cmd, check=True)
79
  print(f"Database encrypted successfully to {DB_GPG_PATH}")
80
 
81
- # 3) Update the timestamp file locally
82
  save_timestamp_locally()
83
 
84
- # 4) Upload to Hugging Face Spaces via huggingface_hub
85
- print("Uploading to Hugging Face Spaces via huggingface_hub...")
86
  api = HfApi()
87
  repo_id = space_id
88
 
89
- # Create a list of operations as CommitOperationAdd objects
90
  operations = [
91
  CommitOperationAdd(path_in_repo=DB_GPG_PATH, path_or_fileobj=DB_GPG_PATH),
92
  CommitOperationAdd(path_in_repo=TIMESTAMP_FILE_PATH, path_or_fileobj=TIMESTAMP_FILE_PATH)
 
18
  temp_file = hf_hub_download(
19
  repo_id=repo_id,
20
  repo_type="space",
21
+ filename=TIMESTAMP_FILE_PATH,
22
  token=hf_token
23
  )
24
  except Exception as e:
 
25
  print(f"Could not download {TIMESTAMP_FILE_PATH}: {e}")
26
  return None
27
 
 
28
  try:
29
  with open(temp_file, "r", encoding="utf-8") as f:
30
  timestamp_str = f.read().strip()
 
35
  return None
36
 
37
  def save_timestamp_locally():
38
+ """Save the current UTC time to db_backup/last_backup_time.txt (locally)."""
 
 
39
  now = datetime.datetime.utcnow()
40
  os.makedirs("db_backup", exist_ok=True)
41
  with open(TIMESTAMP_FILE_PATH, "w", encoding="utf-8") as f:
42
  f.write(now.isoformat())
43
 
44
  def backup_db():
45
+ # Get environment variables
46
  passphrase = os.environ.get("BACKUP_PASSPHRASE")
47
  hf_token = os.environ.get("HF_TOKEN")
48
  space_id = os.environ.get("SPACE_ID")
49
+
50
+ # Get threshold from environment variable, defaulting to 2 hours if not set
51
+ # For testing, set BACKUP_THRESHOLD_MINUTES=0 to disable the threshold check
52
+ threshold_minutes = int(os.environ.get("BACKUP_THRESHOLD_MINUTES", 120))
53
+
54
  if not passphrase:
55
  raise ValueError("BACKUP_PASSPHRASE is not set.")
56
  if not hf_token:
 
58
  if not space_id:
59
  raise ValueError("SPACE_ID is not set (or define repo_id manually).")
60
 
61
+ # Check if backup is needed based on threshold
62
+ if threshold_minutes > 0: # Only check if threshold is positive
63
+ last_backup_dt = get_last_backup_time(space_id, hf_token)
64
+ if last_backup_dt is not None:
65
+ now = datetime.datetime.utcnow()
66
+ elapsed = now - last_backup_dt
67
+ if elapsed.total_seconds() < threshold_minutes * 60:
68
+ print(f"Last backup was only {elapsed.total_seconds()/3600:.2f} hours ago.")
69
+ print(f"Threshold is {threshold_minutes} minutes.")
70
+ print("Skipping backup to avoid rebuild loop.")
71
+ return
72
+ else:
73
+ print("Backup threshold check disabled for testing")
74
 
75
+ # Proceed with backup...
76
  print("Encrypting database with GPG...")
77
  encrypt_cmd = [
78
  "gpg", "--batch", "--yes", "--passphrase", passphrase,
79
  "-c", "--cipher-algo", "AES256",
80
+ "-o", DB_GPG_PATH,
81
+ DB_FILE_PATH
82
  ]
83
  subprocess.run(encrypt_cmd, check=True)
84
  print(f"Database encrypted successfully to {DB_GPG_PATH}")
85
 
86
+ # Update the timestamp file locally
87
  save_timestamp_locally()
88
 
89
+ # Upload to Hugging Face Spaces
90
+ print("Uploading to Hugging Face Spaces...")
91
  api = HfApi()
92
  repo_id = space_id
93
 
94
+ # Create operations list for both files
95
  operations = [
96
  CommitOperationAdd(path_in_repo=DB_GPG_PATH, path_or_fileobj=DB_GPG_PATH),
97
  CommitOperationAdd(path_in_repo=TIMESTAMP_FILE_PATH, path_or_fileobj=TIMESTAMP_FILE_PATH)
backend/scripts/restore.py CHANGED
@@ -6,93 +6,103 @@ import datetime
6
  from pathlib import Path
7
  from huggingface_hub import HfApi, hf_hub_download
8
 
9
- # Get the backend directory (equivalent to BACKEND_DIR in bash)
10
- BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11
-
12
- # Define paths relative to BACKEND_DIR
13
- DB_GPG_PATH = os.path.join(BACKEND_DIR, "db_backup/webui.db.gpg")
14
- DB_FILE_PATH = os.path.join(BACKEND_DIR, "data/webui.db")
15
- TIMESTAMP_FILE_PATH = os.path.join(BACKEND_DIR, "db_backup/last_backup_time.txt")
16
 
17
  def check_requirements():
18
  """
19
  Check if GPG is installed and available.
20
- Equivalent to check_requirements() in db_crypt.sh
21
  """
22
  try:
23
  subprocess.run(["gpg", "--version"], check=True, capture_output=True)
24
  return True
25
- except subprocess.CalledProcessError:
26
- print("Error: gpg is not installed")
27
- return False
28
- except FileNotFoundError:
29
  print("Error: gpg is not installed")
30
  return False
31
 
32
  def validate_secrets():
33
  """
34
- Validate that required environment variables are set.
35
- Equivalent to validate_secrets() in db_crypt.sh
36
  """
37
- missing = []
38
- for var in ["BACKUP_PASSPHRASE", "HF_TOKEN"]:
39
- if not os.environ.get(var):
40
- missing.append(var)
41
 
42
  if missing:
43
- print(f"Error: {', '.join(missing)} secret(s) not set")
44
  return False
45
  return True
46
 
47
  def ensure_directories():
48
  """
49
- Create necessary directories with proper permissions.
50
- Equivalent to mkdir -p commands in db_crypt.sh
51
  """
52
  try:
53
- # Create data directory
54
- data_dir = os.path.join(BACKEND_DIR, "data")
55
- os.makedirs(data_dir, mode=0o755, exist_ok=True)
56
-
57
- # Create backup directory
58
- backup_dir = os.path.join(BACKEND_DIR, "db_backup")
59
- os.makedirs(backup_dir, mode=0o755, exist_ok=True)
60
-
61
  return True
62
  except Exception as e:
63
  print(f"Error creating directories: {e}")
64
  return False
65
 
66
- def download_latest_backup(repo_id, hf_token):
67
  """
68
- Download the latest encrypted database backup from HuggingFace Spaces.
 
69
  """
 
70
  try:
71
- print("Downloading latest encrypted database backup...")
72
- temp_file = hf_hub_download(
73
- repo_id=repo_id,
74
- repo_type="space",
75
- filename="db_backup/webui.db.gpg", # Use relative path for HF download
76
- token=hf_token,
77
- local_dir=BACKEND_DIR # Download directly to backend directory
78
- )
79
 
80
- # Also download timestamp file for verification
 
 
 
 
81
  try:
82
  timestamp_file = hf_hub_download(
83
  repo_id=repo_id,
84
  repo_type="space",
85
- filename="db_backup/last_backup_time.txt",
86
- token=hf_token,
87
- local_dir=BACKEND_DIR
88
  )
89
 
90
  with open(timestamp_file, "r", encoding="utf-8") as f:
91
  timestamp = datetime.datetime.fromisoformat(f.read().strip())
92
  print(f"Found backup from: {timestamp} UTC")
 
 
93
  except Exception as e:
94
- print(f"Note: Could not download timestamp file (this is okay for first run): {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
 
 
 
 
96
  return True
97
 
98
  except Exception as e:
@@ -101,11 +111,11 @@ def download_latest_backup(repo_id, hf_token):
101
 
102
  def decrypt_database(passphrase):
103
  """
104
- Decrypt the downloaded database file using GPG.
105
- Equivalent to decrypt_database() in db_crypt.sh
106
  """
107
  if not os.path.exists(DB_GPG_PATH):
108
- print("No encrypted backup found at db_backup/webui.db.gpg")
109
  return True # Not an error, might be first run
110
 
111
  try:
@@ -120,7 +130,6 @@ def decrypt_database(passphrase):
120
  DB_GPG_PATH
121
  ]
122
 
123
- # Run GPG decryption
124
  subprocess.run(decrypt_cmd, check=True, stderr=subprocess.PIPE)
125
  print(f"Database decrypted successfully to {DB_FILE_PATH}")
126
  return True
@@ -130,42 +139,72 @@ def decrypt_database(passphrase):
130
  print(f"GPG error output: {e.stderr.decode()}")
131
  return False
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  def restore_db():
134
  """
135
  Main function to handle the database restoration process.
 
136
  """
137
- os.chdir(BACKEND_DIR) # Ensure we're in the backend directory
138
-
139
- # Check requirements first
140
- if not check_requirements():
141
- return False
142
-
143
- # Validate environment variables
144
- if not validate_secrets():
145
  return False
146
 
147
  # Ensure required directories exist
148
  if not ensure_directories():
149
  return False
150
 
151
- passphrase = os.environ.get("BACKUP_PASSPHRASE")
152
- hf_token = os.environ.get("HF_TOKEN")
153
- space_id = os.environ.get("SPACE_ID")
 
154
 
155
- # Download latest backup if space_id is provided
156
- if space_id:
157
- if not download_latest_backup(space_id, hf_token):
158
- print("Warning: Failed to download latest backup")
159
- # Continue anyway - we might have a local backup
160
 
161
- # Decrypt the database
162
- if not decrypt_database(passphrase):
163
- print("Failed to decrypt database")
164
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  print("Database restore completed successfully!")
167
  return True
168
 
169
  if __name__ == "__main__":
170
  success = restore_db()
171
- sys.exit(0 if success else 1)
 
6
  from pathlib import Path
7
  from huggingface_hub import HfApi, hf_hub_download
8
 
9
+ # Keep paths consistent with backup.py for better maintainability
10
+ TIMESTAMP_FILE_PATH = "db_backup/last_backup_time.txt"
11
+ DB_GPG_PATH = "db_backup/webui.db.gpg"
12
+ DB_FILE_PATH = "data/webui.db"
 
 
 
13
 
14
  def check_requirements():
15
  """
16
  Check if GPG is installed and available.
 
17
  """
18
  try:
19
  subprocess.run(["gpg", "--version"], check=True, capture_output=True)
20
  return True
21
+ except (subprocess.CalledProcessError, FileNotFoundError):
 
 
 
22
  print("Error: gpg is not installed")
23
  return False
24
 
25
  def validate_secrets():
26
  """
27
+ Ensure all required environment variables are set.
28
+ Returns False if any required variable is missing.
29
  """
30
+ required_vars = ["BACKUP_PASSPHRASE", "HF_TOKEN", "SPACE_ID"]
31
+ missing = [var for var in required_vars if not os.environ.get(var)]
 
 
32
 
33
  if missing:
34
+ print(f"Error: Missing required environment variables: {', '.join(missing)}")
35
  return False
36
  return True
37
 
38
  def ensure_directories():
39
  """
40
+ Create necessary directories for database and backup files.
41
+ Uses relative paths consistent with backup.py.
42
  """
43
  try:
44
+ for directory in ["data", "db_backup"]:
45
+ os.makedirs(directory, mode=0o755, exist_ok=True)
 
 
 
 
 
 
46
  return True
47
  except Exception as e:
48
  print(f"Error creating directories: {e}")
49
  return False
50
 
51
+ def get_latest_backup_info(repo_id, hf_token):
52
  """
53
+ Retrieve information about the latest backup from Hugging Face.
54
+ Returns a tuple of (backup_exists, timestamp) where timestamp might be None.
55
  """
56
+ api = HfApi()
57
  try:
58
+ # First check if the backup file exists in the repository
59
+ files = api.list_files_info(repo_id=repo_id, repo_type="space", token=hf_token)
60
+ backup_exists = any(file.rfilename == DB_GPG_PATH for file in files)
 
 
 
 
 
61
 
62
+ if not backup_exists:
63
+ print("No backup file found in the repository")
64
+ return False, None
65
+
66
+ # Try to get the timestamp information
67
  try:
68
  timestamp_file = hf_hub_download(
69
  repo_id=repo_id,
70
  repo_type="space",
71
+ filename=TIMESTAMP_FILE_PATH,
72
+ token=hf_token
 
73
  )
74
 
75
  with open(timestamp_file, "r", encoding="utf-8") as f:
76
  timestamp = datetime.datetime.fromisoformat(f.read().strip())
77
  print(f"Found backup from: {timestamp} UTC")
78
+ return True, timestamp
79
+
80
  except Exception as e:
81
+ print(f"Note: Could not read timestamp (this is okay for first run): {e}")
82
+ return True, None
83
+
84
+ except Exception as e:
85
+ print(f"Error checking repository: {e}")
86
+ return False, None
87
+
88
+ def download_backup(repo_id, hf_token):
89
+ """
90
+ Download the encrypted database backup from Hugging Face.
91
+ Returns True if successful, False otherwise.
92
+ """
93
+ try:
94
+ print("Downloading encrypted database backup...")
95
+ temp_file = hf_hub_download(
96
+ repo_id=repo_id,
97
+ repo_type="space",
98
+ filename=DB_GPG_PATH,
99
+ token=hf_token
100
+ )
101
 
102
+ # Move the downloaded file to the correct location
103
+ os.makedirs(os.path.dirname(DB_GPG_PATH), exist_ok=True)
104
+ os.replace(temp_file, DB_GPG_PATH)
105
+ print("Backup downloaded successfully")
106
  return True
107
 
108
  except Exception as e:
 
111
 
112
  def decrypt_database(passphrase):
113
  """
114
+ Decrypt the database file using GPG.
115
+ Returns True if successful or if no backup exists (first run).
116
  """
117
  if not os.path.exists(DB_GPG_PATH):
118
+ print("No encrypted backup found locally")
119
  return True # Not an error, might be first run
120
 
121
  try:
 
130
  DB_GPG_PATH
131
  ]
132
 
 
133
  subprocess.run(decrypt_cmd, check=True, stderr=subprocess.PIPE)
134
  print(f"Database decrypted successfully to {DB_FILE_PATH}")
135
  return True
 
139
  print(f"GPG error output: {e.stderr.decode()}")
140
  return False
141
 
142
+ def verify_database():
143
+ """
144
+ Verify the integrity of the restored database.
145
+ Returns True if the database is valid or doesn't exist.
146
+ """
147
+ if not os.path.exists(DB_FILE_PATH):
148
+ return True # Not an error, might be first run
149
+
150
+ try:
151
+ print("Verifying database integrity...")
152
+ verify_cmd = ["sqlite3", DB_FILE_PATH, "PRAGMA integrity_check;"]
153
+ result = subprocess.run(verify_cmd, capture_output=True, text=True, check=True)
154
+
155
+ if "ok" in result.stdout.lower():
156
+ print("Database integrity verified")
157
+ return True
158
+ else:
159
+ print("Database integrity check failed")
160
+ return False
161
+
162
+ except subprocess.CalledProcessError as e:
163
+ print(f"Database verification failed: {e}")
164
+ return False
165
+
166
  def restore_db():
167
  """
168
  Main function to handle the database restoration process.
169
+ Downloads the latest backup from Hugging Face and restores it.
170
  """
171
+ # Check requirements and environment
172
+ if not check_requirements() or not validate_secrets():
 
 
 
 
 
 
173
  return False
174
 
175
  # Ensure required directories exist
176
  if not ensure_directories():
177
  return False
178
 
179
+ # Get environment variables
180
+ passphrase = os.environ["BACKUP_PASSPHRASE"]
181
+ hf_token = os.environ["HF_TOKEN"]
182
+ space_id = os.environ["SPACE_ID"]
183
 
184
+ # Check if there's a backup in the repository
185
+ backup_exists, timestamp = get_latest_backup_info(space_id, hf_token)
 
 
 
186
 
187
+ if backup_exists:
188
+ if not download_backup(space_id, hf_token):
189
+ print("Failed to download backup")
190
+ return False
191
+
192
+ if not decrypt_database(passphrase):
193
+ print("Failed to decrypt database")
194
+ return False
195
+
196
+ if not verify_database():
197
+ print("Failed to verify database integrity")
198
+ # Remove potentially corrupted database
199
+ if os.path.exists(DB_FILE_PATH):
200
+ os.unlink(DB_FILE_PATH)
201
+ return False
202
+ else:
203
+ print("No backup found - starting with fresh database")
204
 
205
  print("Database restore completed successfully!")
206
  return True
207
 
208
  if __name__ == "__main__":
209
  success = restore_db()
210
+ sys.exit(0 if success else 1)
backend/start.sh CHANGED
@@ -3,30 +3,34 @@
3
  SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
4
  cd "$SCRIPT_DIR" || exit
5
 
 
 
 
 
 
 
 
 
6
  # Restore database from backup
7
  echo "Restoring database from backup..."
8
  python scripts/restore.py
9
  restore_status=$?
10
-
11
  if [ $restore_status -ne 0 ]; then
12
  echo "Warning: Database restore failed. Starting with empty database."
13
  fi
14
 
 
15
  KEY_FILE=.webui_secret_key
16
-
17
  PORT="${PORT:-8080}"
18
  HOST="${HOST:-0.0.0.0}"
19
  if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
20
- echo "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
21
-
22
- if ! [ -e "$KEY_FILE" ]; then
23
- echo "Generating WEBUI_SECRET_KEY"
24
- # Generate a random value to use as a WEBUI_SECRET_KEY in case the user didn't provide one.
25
- echo $(head -c 12 /dev/random | base64) > "$KEY_FILE"
26
- fi
27
-
28
- echo "Loading WEBUI_SECRET_KEY from $KEY_FILE"
29
- WEBUI_SECRET_KEY=$(cat "$KEY_FILE")
30
  fi
31
 
32
  if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
@@ -35,53 +39,58 @@ if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
35
  fi
36
 
37
  if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
38
- echo "CUDA is enabled, appending LD_LIBRARY_PATH to include torch/cudnn & cublas libraries."
39
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib"
40
  fi
41
 
42
- # Check if SPACE_ID is set, if so, configure for space
43
  if [ -n "$SPACE_ID" ]; then
44
- echo "Configuring for HuggingFace Space deployment"
45
- if [ -n "$ADMIN_USER_EMAIL" ] && [ -n "$ADMIN_USER_PASSWORD" ]; then
46
- echo "Admin user configured, creating"
47
- WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
48
- webui_pid=$!
49
- echo "Waiting for webui to start..."
50
- while ! curl -s http://localhost:8080/health > /dev/null; do
51
- sleep 1
52
- done
53
- echo "Creating admin user..."
54
- curl \
55
- -X POST "http://localhost:8080/api/v1/auths/signup" \
56
- -H "accept: application/json" \
57
- -H "Content-Type: application/json" \
58
- -d "{ \"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\" }"
59
- echo "Shutting down webui..."
60
- kill $webui_pid
61
- fi
62
-
63
- export WEBUI_URL=${SPACE_HOST}
64
  fi
65
 
66
- # 6. Launch the main web server in the background (DO NOT use 'exec' here)
67
  WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app \
68
- --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
69
  WEBUI_PID=$!
70
 
71
- # 7. Background job for daily backup
 
 
 
 
72
  (
73
- # Optional: wait a few minutes before the first backup, so the DB is stable
74
- sleep 500
75
 
76
- while true; do
77
- echo "==============================================="
78
- echo "Daily backup job: encrypting and pushing the DB"
79
- echo "==============================================="
80
- python "$SCRIPT_DIR/backup.py"
81
- # Sleep for 8 hours
82
- sleep 21600
83
- done
 
 
84
  ) &
85
 
86
- # 8. Wait on the main web server (keep container alive)
87
  wait $WEBUI_PID
 
3
  SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
4
  cd "$SCRIPT_DIR" || exit
5
 
6
+ # Validate required environment variables for backup/restore operations
7
+ for var in "BACKUP_PASSPHRASE" "HF_TOKEN" "SPACE_ID"; do
8
+ if [ -z "${!var}" ]; then
9
+ echo "Error: $var is not set. This is required for database backup/restore operations."
10
+ exit 1
11
+ fi
12
+ done
13
+
14
  # Restore database from backup
15
  echo "Restoring database from backup..."
16
  python scripts/restore.py
17
  restore_status=$?
 
18
  if [ $restore_status -ne 0 ]; then
19
  echo "Warning: Database restore failed. Starting with empty database."
20
  fi
21
 
22
+ # Handle WebUI secret key generation/loading
23
  KEY_FILE=.webui_secret_key
 
24
  PORT="${PORT:-8080}"
25
  HOST="${HOST:-0.0.0.0}"
26
  if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
27
+ echo "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
28
+ if ! [ -e "$KEY_FILE" ]; then
29
+ echo "Generating WEBUI_SECRET_KEY"
30
+ echo $(head -c 12 /dev/random | base64) > "$KEY_FILE"
31
+ fi
32
+ echo "Loading WEBUI_SECRET_KEY from $KEY_FILE"
33
+ WEBUI_SECRET_KEY=$(cat "$KEY_FILE")
 
 
 
34
  fi
35
 
36
  if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
 
39
  fi
40
 
41
  if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
42
+ echo "CUDA is enabled, appending LD_LIBRARY_PATH to include torch/cudnn & cublas libraries."
43
+ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib"
44
  fi
45
 
46
+ # Handle HuggingFace Space deployment configuration
47
  if [ -n "$SPACE_ID" ]; then
48
+ echo "Configuring for HuggingFace Space deployment"
49
+ if [ -n "$ADMIN_USER_EMAIL" ] && [ -n "$ADMIN_USER_PASSWORD" ]; then
50
+ echo "Admin user configured, creating"
51
+ WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
52
+ webui_pid=$!
53
+ echo "Waiting for webui to start..."
54
+ while ! curl -s http://localhost:8080/health > /dev/null; do
55
+ sleep 1
56
+ done
57
+ echo "Creating admin user..."
58
+ curl \
59
+ -X POST "http://localhost:8080/api/v1/auths/signup" \
60
+ -H "accept: application/json" \
61
+ -H "Content-Type: application/json" \
62
+ -d "{ \"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\" }"
63
+ echo "Shutting down temporary webui instance..."
64
+ kill $webui_pid
65
+ fi
66
+ export WEBUI_URL=${SPACE_HOST}
 
67
  fi
68
 
69
+ # Launch the main web server in the background
70
  WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app \
71
+ --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
72
  WEBUI_PID=$!
73
 
74
+ # Configure backup schedule
75
+ BACKUP_INITIAL_WAIT="${BACKUP_INITIAL_WAIT:-500}" # Default to 500 seconds
76
+ BACKUP_INTERVAL="${BACKUP_INTERVAL:-21600}" # Default to 6 hours (21600 seconds)
77
+
78
+ # Start the background backup job
79
  (
80
+ echo "Waiting ${BACKUP_INITIAL_WAIT} seconds before starting backup schedule..."
81
+ sleep "$BACKUP_INITIAL_WAIT"
82
 
83
+ while true; do
84
+ echo "==============================================="
85
+ echo "Running scheduled backup job"
86
+ echo "==============================================="
87
+ # Allow threshold override for testing
88
+ BACKUP_THRESHOLD_MINUTES="${BACKUP_THRESHOLD_MINUTES:-120}" python "$SCRIPT_DIR/backup.py"
89
+
90
+ echo "Waiting ${BACKUP_INTERVAL} seconds until next backup..."
91
+ sleep "$BACKUP_INTERVAL"
92
+ done
93
  ) &
94
 
95
+ # Wait for the main web server process to keep container alive
96
  wait $WEBUI_PID