thryyyyy commited on
Commit
a827de0
·
1 Parent(s): 5c56478

more fixes

Browse files
Files changed (1) hide show
  1. backend/scripts/restore.py +61 -42
backend/scripts/restore.py CHANGED
@@ -8,21 +8,28 @@ from pathlib import Path
8
 
9
  from huggingface_hub import HfApi, hf_hub_download
10
 
11
- # ------------------------------------------------------------------------------
12
- # 1. Define directories outside of /app to avoid read-only file system issues
13
- # ------------------------------------------------------------------------------
14
- RESTORE_BACKUP_DIR = "/tmp/open_webui/db_backup"
15
- RESTORE_DATA_DIR = "/tmp/open_webui/data"
 
 
 
16
 
17
- # Actual paths on the local (runtime) filesystem
18
- TIMESTAMP_FILE_PATH = os.path.join(RESTORE_BACKUP_DIR, "last_backup_time.txt")
 
 
 
19
  DB_GPG_PATH = os.path.join(RESTORE_BACKUP_DIR, "webui.db.gpg")
20
- DB_FILE_PATH = os.path.join(RESTORE_DATA_DIR, "webui.db")
21
 
22
- # Paths used in the Hugging Face Space repository
23
  REPO_TIMESTAMP_FILE = "db_backup/last_backup_time.txt"
24
  REPO_DB_GPG_FILE = "db_backup/webui.db.gpg"
25
 
 
26
  def check_requirements():
27
  """
28
  Verify that GPG is installed and available in the system.
@@ -31,9 +38,10 @@ def check_requirements():
31
  subprocess.run(["gpg", "--version"], check=True, capture_output=True)
32
  return True
33
  except (subprocess.CalledProcessError, FileNotFoundError):
34
- print("Error: gpg is not installed")
35
  return False
36
 
 
37
  def validate_secrets():
38
  """
39
  Ensure all required environment variables are set: BACKUP_PASSPHRASE, HF_TOKEN, SPACE_ID.
@@ -46,21 +54,27 @@ def validate_secrets():
46
  return False
47
  return True
48
 
 
49
  def ensure_directories():
50
  """
51
- Create necessary directories for database and backup files (in /tmp).
 
52
  """
53
  try:
54
- for directory in [RESTORE_DATA_DIR, RESTORE_BACKUP_DIR]:
55
- os.makedirs(directory, mode=0o755, exist_ok=True)
 
 
 
56
  return True
57
  except Exception as e:
58
  print(f"Error creating directories: {e}")
59
  return False
60
 
 
61
  def get_latest_backup_info(repo_id, hf_token):
62
  """
63
- Check if a backup exists in the HF Space. If so, return True and a timestamp (or None).
64
  """
65
  api = HfApi()
66
  try:
@@ -69,12 +83,12 @@ def get_latest_backup_info(repo_id, hf_token):
69
  # Check if the encrypted DB is present
70
  backup_exists = (REPO_DB_GPG_FILE in files)
71
  if not backup_exists:
72
- print("No backup file found in the repository")
73
  return False, None
74
 
75
  # Attempt to fetch the timestamp file
76
- try:
77
- if REPO_TIMESTAMP_FILE in files:
78
  timestamp_file = hf_hub_download(
79
  repo_id=repo_id,
80
  repo_type="space",
@@ -82,22 +96,24 @@ def get_latest_backup_info(repo_id, hf_token):
82
  token=hf_token
83
  )
84
  with open(timestamp_file, "r", encoding="utf-8") as f:
85
- timestamp = datetime.datetime.fromisoformat(f.read().strip())
 
86
  print(f"Found backup from: {timestamp} UTC")
87
  return True, timestamp
88
- else:
89
- print("No timestamp file found, but backup file exists")
90
  return True, None
91
- except Exception as e:
92
- print(f"Could not read timestamp (possibly first run): {e}")
93
  return True, None
94
  except Exception as e:
95
  print(f"Error checking repository: {e}")
96
  return False, None
97
 
 
98
  def download_backup(repo_id, hf_token):
99
  """
100
- Download the encrypted database backup from Hugging Face into /tmp/open_webui/db_backup.
101
  """
102
  try:
103
  print("Downloading encrypted database backup...")
@@ -107,19 +123,19 @@ def download_backup(repo_id, hf_token):
107
  filename=REPO_DB_GPG_FILE,
108
  token=hf_token
109
  )
110
-
111
  # Move the downloaded file to DB_GPG_PATH
112
- os.makedirs(os.path.dirname(DB_GPG_PATH), exist_ok=True)
113
  os.replace(temp_file, DB_GPG_PATH)
114
- print("Backup downloaded successfully")
115
  return True
116
  except Exception as e:
117
  print(f"Error downloading backup: {e}")
118
  return False
119
 
 
120
  def decrypt_database(passphrase):
121
  """
122
- Decrypt the database file using GPG, writing the decrypted DB into /tmp/open_webui/data.
 
123
  """
124
  if not os.path.exists(DB_GPG_PATH):
125
  print("No encrypted backup found locally. Proceeding with fresh DB.")
@@ -150,12 +166,13 @@ def decrypt_database(passphrase):
150
  print(f"GPG error output: {e.stderr.decode(errors='ignore')}")
151
  return False
152
 
 
153
  def verify_database():
154
  """
155
- Verify the integrity of the restored database using SQLite's built-in integrity check.
156
  """
157
  if not os.path.exists(DB_FILE_PATH):
158
- # If there's no DB yet, that's okay (fresh start)
159
  return True
160
 
161
  try:
@@ -168,15 +185,15 @@ def verify_database():
168
  tables = cursor.fetchall()
169
 
170
  if result.lower() == "ok" and len(tables) > 0:
171
- print("Database integrity verified successfully")
172
- print(f"Found {len(tables)} tables in database")
173
  return True
174
  else:
175
- print("Database integrity check failed")
176
  if result.lower() != "ok":
177
  print(f"Integrity check result: {result}")
178
  if len(tables) == 0:
179
- print("No tables found in database")
180
  return False
181
  except sqlite3.Error as e:
182
  print(f"Database verification failed: {e}")
@@ -185,13 +202,14 @@ def verify_database():
185
  print(f"Unexpected error during database verification: {e}")
186
  return False
187
 
 
188
  def restore_db():
189
  """
190
  Main restore function:
191
- 1. Check GPG installation & environment variables
192
- 2. Create directories
193
- 3. Check if remote backup exists, download & decrypt
194
- 4. Verify DB
195
  """
196
  if not check_requirements() or not validate_secrets():
197
  return False
@@ -206,25 +224,26 @@ def restore_db():
206
  backup_exists, timestamp = get_latest_backup_info(space_id, hf_token)
207
  if backup_exists:
208
  if not download_backup(space_id, hf_token):
209
- print("Failed to download backup")
210
  return False
211
 
212
  if not decrypt_database(passphrase):
213
- print("Failed to decrypt database")
214
  return False
215
 
216
  if not verify_database():
217
- print("Database integrity verification failed")
218
- # Remove the corrupted DB so we don't keep a broken file
219
  if os.path.exists(DB_FILE_PATH):
220
  os.unlink(DB_FILE_PATH)
221
  return False
222
  else:
223
- print("No backup found - starting with an empty/fresh database")
224
 
225
  print("Database restore completed successfully!")
226
  return True
227
 
 
228
  if __name__ == "__main__":
229
  success = restore_db()
230
  sys.exit(0 if success else 1)
 
8
 
9
  from huggingface_hub import HfApi, hf_hub_download
10
 
11
+ ###############################################################################
12
+ # 1) Determine the *final* database file location.
13
+ # - We read DATA_DIR from env, defaulting to "/app/backend/data".
14
+ # - The final DB (decrypted) should be "/app/backend/data/webui.db" so
15
+ # the app will use the restored DB on startup.
16
+ ###############################################################################
17
+ DATA_DIR = os.environ.get("DATA_DIR", "/app/backend/data")
18
+ DB_FILE_PATH = os.path.join(DATA_DIR, "webui.db")
19
 
20
+ ###############################################################################
21
+ # 2) Where we put the encrypted backup and timestamp locally.
22
+ # - A writable directory on Hugging Face is "/tmp/open_webui/db_backup".
23
+ ###############################################################################
24
+ RESTORE_BACKUP_DIR = "/tmp/open_webui/db_backup"
25
  DB_GPG_PATH = os.path.join(RESTORE_BACKUP_DIR, "webui.db.gpg")
26
+ TIMESTAMP_FILE_PATH = os.path.join(RESTORE_BACKUP_DIR, "last_backup_time.txt")
27
 
28
+ # Paths in the Hugging Face repo
29
  REPO_TIMESTAMP_FILE = "db_backup/last_backup_time.txt"
30
  REPO_DB_GPG_FILE = "db_backup/webui.db.gpg"
31
 
32
+
33
  def check_requirements():
34
  """
35
  Verify that GPG is installed and available in the system.
 
38
  subprocess.run(["gpg", "--version"], check=True, capture_output=True)
39
  return True
40
  except (subprocess.CalledProcessError, FileNotFoundError):
41
+ print("Error: gpg is not installed or not in PATH.")
42
  return False
43
 
44
+
45
  def validate_secrets():
46
  """
47
  Ensure all required environment variables are set: BACKUP_PASSPHRASE, HF_TOKEN, SPACE_ID.
 
54
  return False
55
  return True
56
 
57
+
58
  def ensure_directories():
59
  """
60
+ Create necessary directories for database (DATA_DIR) and backup files (RESTORE_BACKUP_DIR).
61
+ We must ensure the final DB directory is writable so the decrypted DB can be placed there.
62
  """
63
  try:
64
+ # Create the final DB folder (if not already existing)
65
+ os.makedirs(DATA_DIR, mode=0o755, exist_ok=True)
66
+
67
+ # Create the backup folder in /tmp
68
+ os.makedirs(RESTORE_BACKUP_DIR, mode=0o755, exist_ok=True)
69
  return True
70
  except Exception as e:
71
  print(f"Error creating directories: {e}")
72
  return False
73
 
74
+
75
  def get_latest_backup_info(repo_id, hf_token):
76
  """
77
+ Check if a backup (webui.db.gpg) exists in the HF Space. If so, return True + timestamp (or None).
78
  """
79
  api = HfApi()
80
  try:
 
83
  # Check if the encrypted DB is present
84
  backup_exists = (REPO_DB_GPG_FILE in files)
85
  if not backup_exists:
86
+ print("No backup file found in the repository.")
87
  return False, None
88
 
89
  # Attempt to fetch the timestamp file
90
+ if REPO_TIMESTAMP_FILE in files:
91
+ try:
92
  timestamp_file = hf_hub_download(
93
  repo_id=repo_id,
94
  repo_type="space",
 
96
  token=hf_token
97
  )
98
  with open(timestamp_file, "r", encoding="utf-8") as f:
99
+ timestamp_str = f.read().strip()
100
+ timestamp = datetime.datetime.fromisoformat(timestamp_str)
101
  print(f"Found backup from: {timestamp} UTC")
102
  return True, timestamp
103
+ except Exception as e:
104
+ print(f"Could not read timestamp (possibly first run): {e}")
105
  return True, None
106
+ else:
107
+ print("No timestamp file found, but backup file exists.")
108
  return True, None
109
  except Exception as e:
110
  print(f"Error checking repository: {e}")
111
  return False, None
112
 
113
+
114
  def download_backup(repo_id, hf_token):
115
  """
116
+ Download the encrypted database backup from Hugging Face to /tmp/open_webui/db_backup.
117
  """
118
  try:
119
  print("Downloading encrypted database backup...")
 
123
  filename=REPO_DB_GPG_FILE,
124
  token=hf_token
125
  )
 
126
  # Move the downloaded file to DB_GPG_PATH
 
127
  os.replace(temp_file, DB_GPG_PATH)
128
+ print("Backup downloaded successfully.")
129
  return True
130
  except Exception as e:
131
  print(f"Error downloading backup: {e}")
132
  return False
133
 
134
+
135
  def decrypt_database(passphrase):
136
  """
137
+ Decrypt the database into DATA_DIR/webui.db so the app can use it.
138
+ The encrypted file is at /tmp/open_webui/db_backup/webui.db.gpg.
139
  """
140
  if not os.path.exists(DB_GPG_PATH):
141
  print("No encrypted backup found locally. Proceeding with fresh DB.")
 
166
  print(f"GPG error output: {e.stderr.decode(errors='ignore')}")
167
  return False
168
 
169
+
170
  def verify_database():
171
  """
172
+ Verify the integrity of the restored DB at DATA_DIR/webui.db using SQLite's built-in checks.
173
  """
174
  if not os.path.exists(DB_FILE_PATH):
175
+ # If there's no DB yet, might be fresh start, not an immediate error
176
  return True
177
 
178
  try:
 
185
  tables = cursor.fetchall()
186
 
187
  if result.lower() == "ok" and len(tables) > 0:
188
+ print("Database integrity verified successfully.")
189
+ print(f"Found {len(tables)} tables in database.")
190
  return True
191
  else:
192
+ print("Database integrity check failed.")
193
  if result.lower() != "ok":
194
  print(f"Integrity check result: {result}")
195
  if len(tables) == 0:
196
+ print("No tables found in database.")
197
  return False
198
  except sqlite3.Error as e:
199
  print(f"Database verification failed: {e}")
 
202
  print(f"Unexpected error during database verification: {e}")
203
  return False
204
 
205
+
206
  def restore_db():
207
  """
208
  Main restore function:
209
+ 1. Check GPG + environment
210
+ 2. Create directories
211
+ 3. Check if remote backup exists, download + decrypt
212
+ 4. Verify DB
213
  """
214
  if not check_requirements() or not validate_secrets():
215
  return False
 
224
  backup_exists, timestamp = get_latest_backup_info(space_id, hf_token)
225
  if backup_exists:
226
  if not download_backup(space_id, hf_token):
227
+ print("Failed to download backup.")
228
  return False
229
 
230
  if not decrypt_database(passphrase):
231
+ print("Failed to decrypt database.")
232
  return False
233
 
234
  if not verify_database():
235
+ print("Database integrity verification failed.")
236
+ # Remove the corrupted DB so we dont keep a broken file
237
  if os.path.exists(DB_FILE_PATH):
238
  os.unlink(DB_FILE_PATH)
239
  return False
240
  else:
241
+ print("No backup found - starting with an empty/fresh database.")
242
 
243
  print("Database restore completed successfully!")
244
  return True
245
 
246
+
247
  if __name__ == "__main__":
248
  success = restore_db()
249
  sys.exit(0 if success else 1)