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

more fixes

Browse files
Files changed (2) hide show
  1. backend/scripts/restore.py +93 -18
  2. backend/start.sh +74 -23
backend/scripts/restore.py CHANGED
@@ -7,13 +7,18 @@ 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)
@@ -24,8 +29,11 @@ def check_requirements():
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)]
@@ -35,10 +43,38 @@ def validate_secrets():
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"]:
@@ -50,20 +86,32 @@ def ensure_directories():
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,
@@ -83,12 +131,25 @@ def get_latest_backup_info(repo_id, hf_token):
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...")
@@ -111,8 +172,13 @@ def download_backup(repo_id, hf_token):
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")
@@ -141,8 +207,10 @@ def decrypt_database(passphrase):
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
@@ -165,8 +233,11 @@ def verify_database():
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():
@@ -181,6 +252,10 @@ def restore_db():
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
 
 
7
  from huggingface_hub import HfApi, hf_hub_download
8
 
9
  # Keep paths consistent with backup.py for better maintainability
10
+ # Using relative paths allows the script to work regardless of where the repository is cloned
11
  TIMESTAMP_FILE_PATH = "db_backup/last_backup_time.txt"
12
  DB_GPG_PATH = "db_backup/webui.db.gpg"
13
  DB_FILE_PATH = "data/webui.db"
14
 
15
  def check_requirements():
16
  """
17
+ Verify that GPG is installed and available in the system.
18
+ GPG is essential for decrypting the database backup.
19
+
20
+ Returns:
21
+ bool: True if GPG is available, False otherwise
22
  """
23
  try:
24
  subprocess.run(["gpg", "--version"], check=True, capture_output=True)
 
29
 
30
  def validate_secrets():
31
  """
32
+ Check that all required environment variables are set.
33
+ These variables are essential for accessing the backup and decrypting it.
34
+
35
+ Returns:
36
+ bool: True if all required variables are set, False otherwise
37
  """
38
  required_vars = ["BACKUP_PASSPHRASE", "HF_TOKEN", "SPACE_ID"]
39
  missing = [var for var in required_vars if not os.environ.get(var)]
 
43
  return False
44
  return True
45
 
46
+ def validate_repo_access(repo_id, token):
47
+ """
48
+ Verify that we can access the HuggingFace repository with the provided credentials.
49
+ This early check helps prevent cryptic errors later in the process.
50
+
51
+ Args:
52
+ repo_id (str): The HuggingFace Space ID
53
+ token (str): The HuggingFace API token
54
+
55
+ Returns:
56
+ bool: True if repository access is confirmed, False otherwise
57
+ """
58
+ api = HfApi()
59
+ try:
60
+ api.list_repo_files(
61
+ repo_id=repo_id,
62
+ repo_type="space",
63
+ token=token
64
+ )
65
+ return True
66
+ except Exception as e:
67
+ print(f"Error validating repository access: {e}")
68
+ print(f"Please verify your SPACE_ID ({repo_id}) and HF_TOKEN are correct")
69
+ return False
70
+
71
  def ensure_directories():
72
  """
73
+ Create the necessary directory structure for database and backup files.
74
+ This ensures we have proper permissions and all required directories exist.
75
+
76
+ Returns:
77
+ bool: True if directories are created successfully, False otherwise
78
  """
79
  try:
80
  for directory in ["data", "db_backup"]:
 
86
 
87
  def get_latest_backup_info(repo_id, hf_token):
88
  """
89
+ Check for and retrieve information about the latest backup from HuggingFace.
90
+ This function both verifies backup existence and gets its timestamp.
91
+
92
+ Args:
93
+ repo_id (str): The HuggingFace Space ID
94
+ hf_token (str): The HuggingFace API token
95
+
96
+ Returns:
97
+ tuple: (backup_exists: bool, timestamp: datetime or None)
98
  """
99
  api = HfApi()
100
  try:
101
  # First check if the backup file exists in the repository
102
+ files = api.list_repo_files(
103
+ repo_id=repo_id,
104
+ repo_type="space",
105
+ token=hf_token
106
+ )
107
+
108
+ backup_exists = DB_GPG_PATH in files
109
 
110
  if not backup_exists:
111
  print("No backup file found in the repository")
112
  return False, None
113
 
114
+ # If backup exists, try to get its timestamp
115
  try:
116
  timestamp_file = hf_hub_download(
117
  repo_id=repo_id,
 
131
 
132
  except Exception as e:
133
  print(f"Error checking repository: {e}")
134
+ print("For debugging - trying to list repository contents:")
135
+ try:
136
+ files = api.list_repo_files(repo_id=repo_id, repo_type="space", token=hf_token)
137
+ print(f"Files in repository: {files}")
138
+ except Exception as debug_e:
139
+ print(f"Debug listing failed: {debug_e}")
140
  return False, None
141
 
142
  def download_backup(repo_id, hf_token):
143
  """
144
+ Download the encrypted database backup from HuggingFace.
145
+ Handles the safe download and placement of the backup file.
146
+
147
+ Args:
148
+ repo_id (str): The HuggingFace Space ID
149
+ hf_token (str): The HuggingFace API token
150
+
151
+ Returns:
152
+ bool: True if download is successful, False otherwise
153
  """
154
  try:
155
  print("Downloading encrypted database backup...")
 
172
 
173
  def decrypt_database(passphrase):
174
  """
175
+ Decrypt the downloaded database file using GPG.
176
+
177
+ Args:
178
+ passphrase (str): The passphrase for decrypting the database
179
+
180
+ Returns:
181
+ bool: True if decryption is successful or no backup exists, False on error
182
  """
183
  if not os.path.exists(DB_GPG_PATH):
184
  print("No encrypted backup found locally")
 
207
 
208
  def verify_database():
209
  """
210
+ Verify the integrity of the restored database using SQLite's built-in checks.
211
+
212
+ Returns:
213
+ bool: True if database is valid or doesn't exist, False if corrupted
214
  """
215
  if not os.path.exists(DB_FILE_PATH):
216
  return True # Not an error, might be first run
 
233
 
234
  def restore_db():
235
  """
236
+ Main function that orchestrates the database restoration process.
237
+ Performs all necessary checks and handles the complete restore operation.
238
+
239
+ Returns:
240
+ bool: True if restore is successful or no backup needed, False on error
241
  """
242
  # Check requirements and environment
243
  if not check_requirements() or not validate_secrets():
 
252
  hf_token = os.environ["HF_TOKEN"]
253
  space_id = os.environ["SPACE_ID"]
254
 
255
+ # Validate repository access first
256
+ if not validate_repo_access(space_id, hf_token):
257
+ return False
258
+
259
  # Check if there's a backup in the repository
260
  backup_exists, timestamp = get_latest_backup_info(space_id, hf_token)
261
 
backend/start.sh CHANGED
@@ -1,7 +1,39 @@
1
  #!/usr/bin/env bash
2
 
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
@@ -11,35 +43,44 @@ for var in "BACKUP_PASSPHRASE" "HF_TOKEN" "SPACE_ID"; do
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
37
- echo "USE_OLLAMA is set to true, starting ollama serve."
38
  ollama serve &
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
 
@@ -67,27 +108,37 @@ if [ -n "$SPACE_ID" ]; then
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
  ) &
 
1
  #!/usr/bin/env bash
2
 
3
+ SCRIPT_PATH=$(readlink -f "${BASH_SOURCE[0]}")
4
+ SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
5
+ ORIGINAL_DIR=$(pwd)
6
+
7
+ log_message() {
8
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
9
+ }
10
+
11
+ validate_environment() {
12
+ # Check if we're in the expected container directory structure
13
+ if [[ "$(pwd)" != *"/app/backend" ]]; then
14
+ log_message "Warning: Unexpected working directory: $(pwd)"
15
+ log_message "Expected path to contain: /app/backend"
16
+ return 1
17
+ fi
18
+ return 0
19
+ }
20
+
21
+ log_message "Changing to script directory: $SCRIPT_DIR"
22
+ cd "$SCRIPT_DIR" || {
23
+ log_message "Error: Could not change to directory: $SCRIPT_DIR"
24
+ exit 1
25
+ }
26
+
27
+ validate_environment || {
28
+ log_message "Warning: Environment validation failed, but continuing..."
29
+ }
30
+
31
+ log_message "Working from directory: $(pwd)"
32
+
33
+ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
34
+ DATA_DIR="$SCRIPT_DIR/data"
35
+ BACKUP_DIR="$SCRIPT_DIR/db_backup"
36
+
37
 
38
  # Validate required environment variables for backup/restore operations
39
  for var in "BACKUP_PASSPHRASE" "HF_TOKEN" "SPACE_ID"; do
 
43
  fi
44
  done
45
 
46
+ # Restore database from backup using absolute path
47
+ log_message "Restoring database from backup..."
48
+ python "$SCRIPTS_DIR/restore.py"
49
  restore_status=$?
50
  if [ $restore_status -ne 0 ]; then
51
+ log_message "Warning: Database restore failed. Starting with empty database."
52
  fi
53
 
54
  # Handle WebUI secret key generation/loading
55
+ KEY_FILE="$SCRIPT_DIR/.webui_secret_key"
56
  PORT="${PORT:-8080}"
57
  HOST="${HOST:-0.0.0.0}"
58
+
59
  if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
60
+ log_message "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
61
  if ! [ -e "$KEY_FILE" ]; then
62
+ log_message "Generating WEBUI_SECRET_KEY"
63
+ head -c 12 /dev/random | base64 > "$KEY_FILE" || {
64
+ log_message "Error: Could not generate secret key"
65
+ exit 1
66
+ }
67
  fi
68
+ log_message "Loading WEBUI_SECRET_KEY from $KEY_FILE"
69
+ WEBUI_SECRET_KEY=$(cat "$KEY_FILE") || {
70
+ log_message "Error: Could not read secret key"
71
+ exit 1
72
+ }
73
  fi
74
 
75
+ # Handle Ollama configuration if enabled
76
  if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
77
+ log_message "USE_OLLAMA is set to true, starting ollama serve."
78
  ollama serve &
79
  fi
80
 
81
+ # Configure CUDA environment if enabled
82
  if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
83
+ log_message "CUDA is enabled, configuring LD_LIBRARY_PATH"
84
  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"
85
  fi
86
 
 
108
  fi
109
 
110
  # Launch the main web server in the background
111
+ log_message "Starting main web server..."
112
  WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app \
113
  --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
114
  WEBUI_PID=$!
115
 
116
+ # Configure backup schedule with environment variable defaults
117
  BACKUP_INITIAL_WAIT="${BACKUP_INITIAL_WAIT:-500}" # Default to 500 seconds
118
+ BACKUP_INTERVAL="${BACKUP_INTERVAL:-21600}" # Default to 6 hours
119
 
120
  # Start the background backup job
121
  (
122
+ log_message "Starting backup scheduler"
123
+ log_message "Initial wait: ${BACKUP_INITIAL_WAIT}s, Interval: ${BACKUP_INTERVAL}s"
124
+
125
  sleep "$BACKUP_INITIAL_WAIT"
126
 
127
  while true; do
128
+ log_message "=== Starting scheduled backup ==="
129
+
130
+ # Run backup script with absolute path
131
+ BACKUP_THRESHOLD_MINUTES="${BACKUP_THRESHOLD_MINUTES:-120}" \
132
+ python "$SCRIPTS_DIR/backup.py"
133
+
134
+ backup_status=$?
135
+ if [ $backup_status -ne 0 ]; then
136
+ log_message "Warning: Backup failed with status $backup_status"
137
+ else
138
+ log_message "Backup completed successfully"
139
+ fi
140
 
141
+ log_message "Waiting ${BACKUP_INTERVAL} seconds until next backup..."
142
  sleep "$BACKUP_INTERVAL"
143
  done
144
  ) &