Oviya commited on
Commit
707d3fc
·
1 Parent(s): 8e485f9
assets/chroma_db/88b01f33-9f9f-4ac5-b7a5-c6afecf476d2/length.bin CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d6a4c4d53359423150f4f21fa4bb976d16f34587b0e8d2925da3a6cc0505f3fa
3
  size 4000
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c467497f6e6f489256b14d0a00d70cdc3736a24e198d87e3b5abbef0a407cf99
3
  size 4000
auth/json_database.py CHANGED
@@ -2,50 +2,52 @@
2
  JSON file-based database module
3
 
4
  Handles:
5
- - JSON file storage for users, refresh tokens, and blacklisted tokens
6
  - User management and authentication
7
  - Token blacklist management
8
  - Refresh token storage
9
  - Thread-safe JSON operations with file locking
10
-
11
- Hugging Face notes:
12
- - Use AUTH_DB_DIR=/data/auth_db to store runtime JSON files.
13
- - Optionally set AUTH_SEED_DIR=/home/user/app/data (repo data folder) to seed initial users.json.
14
  """
15
 
16
  import os
17
  import json
18
  import threading
 
19
  from typing import Optional, Dict, Any, List, Tuple
20
  from pathlib import Path
21
  from datetime import datetime
22
- import tempfile
23
- import shutil
24
-
25
 
26
  # =========================================================
27
- # STORAGE LOCATION (LOCAL DEFAULT + HUGGING FACE OVERRIDE)
28
  # =========================================================
29
 
30
- # Local default: repo_root/data (auth/../data)
31
- DEFAULT_DB_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "data"))
 
32
 
33
- # Runtime DB directory (HF: /data/auth_db)
34
- DB_DIR = os.path.abspath(os.getenv("AUTH_DB_DIR", DEFAULT_DB_DIR))
35
 
36
- # Seed directory (where you keep committed JSONs in repo)
37
- # HF typical repo path is /home/user/app
38
- DEFAULT_SEED_DIR = os.path.abspath(os.getenv("AUTH_SEED_DIR", DEFAULT_DB_DIR))
 
 
 
 
 
 
 
 
 
 
39
 
40
  USERS_FILE = os.path.join(DB_DIR, "users.json")
41
  BLACKLISTED_TOKENS_FILE = os.path.join(DB_DIR, "blacklisted_tokens.json")
42
  REFRESH_TOKENS_FILE = os.path.join(DB_DIR, "refresh_tokens.json")
43
 
44
- SEED_USERS_FILE = os.path.join(DEFAULT_SEED_DIR, "users.json")
45
- SEED_BLACKLISTED_FILE = os.path.join(DEFAULT_SEED_DIR, "blacklisted_tokens.json")
46
- SEED_REFRESH_FILE = os.path.join(DEFAULT_SEED_DIR, "refresh_tokens.json")
47
-
48
- # Thread-safe file locks
49
  _file_locks = {
50
  "users": threading.Lock(),
51
  "blacklisted_tokens": threading.Lock(),
@@ -55,81 +57,106 @@ _file_locks = {
55
  _db_init_done = False
56
  _db_init_lock = threading.Lock()
57
 
 
 
 
58
 
59
- # -----------------------------
60
- # Helpers
61
- # -----------------------------
62
- def ensure_data_directory() -> None:
63
- Path(DB_DIR).mkdir(parents=True, exist_ok=True)
 
 
 
 
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
  def load_json_file(filepath: str) -> Dict[str, Any]:
67
- """Load JSON file. Returns empty dict if file doesn't exist or is invalid."""
 
 
 
68
  if not os.path.exists(filepath):
69
  return {}
 
70
  try:
71
  with open(filepath, "r", encoding="utf-8") as f:
72
- data = json.load(f)
73
- return data if isinstance(data, dict) else {}
74
- except Exception:
75
  return {}
76
 
77
-
78
- def _atomic_write_json(filepath: str, data: Dict[str, Any]) -> None:
79
  """
80
- Atomic JSON write:
81
- write to temp file in same directory, then replace.
82
  """
83
- directory = os.path.dirname(filepath)
84
- Path(directory).mkdir(parents=True, exist_ok=True)
85
 
86
- fd, tmp_path = tempfile.mkstemp(prefix=".tmp_", dir=directory)
87
  try:
88
- with os.fdopen(fd, "w", encoding="utf-8") as f:
89
  json.dump(data, f, indent=2, ensure_ascii=False)
90
- os.replace(tmp_path, filepath) # atomic on POSIX
91
- finally:
 
 
92
  try:
93
  if os.path.exists(tmp_path):
94
  os.remove(tmp_path)
95
  except Exception:
96
  pass
 
97
 
98
-
99
- def save_json_file(filepath: str, data: Dict[str, Any]) -> None:
100
- _atomic_write_json(filepath, data)
101
-
102
-
103
- def _is_empty_or_missing(filepath: str) -> bool:
104
  if not os.path.exists(filepath):
105
  return True
106
  try:
107
- d = load_json_file(filepath)
108
- return len(d) == 0
109
  except Exception:
110
  return True
111
 
112
-
113
- def _copy_seed_file_if_needed(seed_path: str, target_path: str, *, only_if_missing_or_empty: bool = True) -> bool:
114
  """
115
- Copy seed json into runtime DB.
116
- Returns True if copied.
 
117
  """
118
- if not os.path.exists(seed_path):
119
- return False
120
 
121
- if only_if_missing_or_empty and not _is_empty_or_missing(target_path):
122
- return False
123
 
124
- ensure_data_directory()
125
- Path(os.path.dirname(target_path)).mkdir(parents=True, exist_ok=True)
126
- shutil.copy2(seed_path, target_path)
127
- return True
128
 
 
 
 
129
 
130
- # -----------------------------
131
- # Data Access Classes
132
- # -----------------------------
133
  class JSONUsers:
134
  @staticmethod
135
  def load_all() -> Dict[str, Any]:
@@ -152,7 +179,7 @@ class JSONUsers:
152
  return False
153
 
154
  users[username_lower] = {
155
- "id": (max([u.get("id", 0) for u in users.values()], default=0) + 1),
156
  "username": username_lower,
157
  "password_hash": password_hash,
158
  "role": role,
@@ -165,17 +192,18 @@ class JSONUsers:
165
  @staticmethod
166
  def get_all_users() -> List[Dict[str, Any]]:
167
  users = JSONUsers.load_all()
168
- return [{"id": u["id"], "username": u["username"], "role": u["role"]} for u in users.values()]
 
 
 
169
 
170
  @staticmethod
171
  def promote_to_admin(username: str) -> bool:
172
  username_lower = username.lower()
173
-
174
  with _file_locks["users"]:
175
  users = load_json_file(USERS_FILE)
176
  if username_lower not in users:
177
  return False
178
-
179
  users[username_lower]["role"] = "admin"
180
  save_json_file(USERS_FILE, users)
181
  return True
@@ -184,7 +212,6 @@ class JSONUsers:
184
  def user_count() -> int:
185
  return len(JSONUsers.load_all())
186
 
187
-
188
  class JSONBlacklistedTokens:
189
  @staticmethod
190
  def load_all() -> Dict[str, Any]:
@@ -207,7 +234,6 @@ class JSONBlacklistedTokens:
207
  save_json_file(BLACKLISTED_TOKENS_FILE, tokens)
208
  return True
209
 
210
-
211
  class JSONRefreshTokens:
212
  @staticmethod
213
  def load_all() -> Dict[str, Any]:
@@ -240,106 +266,110 @@ class JSONRefreshTokens:
240
  username_lower = username.lower()
241
  with _file_locks["refresh_tokens"]:
242
  tokens = load_json_file(REFRESH_TOKENS_FILE)
243
- keys = [k for k, v in tokens.items() if v.get("username") == username_lower]
244
- for k in keys:
245
- tokens.pop(k, None)
246
  save_json_file(REFRESH_TOKENS_FILE, tokens)
247
  return True
248
 
 
 
 
249
 
250
- # -----------------------------
251
- # Init / Seed
252
- # -----------------------------
253
- def init_db_files() -> None:
254
  """
255
- Create runtime json files if missing.
256
- Also seeds users.json from repo data folder into runtime DB if runtime is empty.
257
  """
258
  ensure_data_directory()
259
 
260
- # Seed users first if runtime missing/empty
261
- _copy_seed_file_if_needed(SEED_USERS_FILE, USERS_FILE, only_if_missing_or_empty=True)
 
 
262
 
263
- # For tokens: usually you want them empty on first boot. If you want to seed them, set SEED_TOKENS=1
264
- seed_tokens = os.getenv("SEED_TOKENS", "0") == "1"
265
- if seed_tokens:
266
- _copy_seed_file_if_needed(SEED_BLACKLISTED_FILE, BLACKLISTED_TOKENS_FILE, only_if_missing_or_empty=True)
267
- _copy_seed_file_if_needed(SEED_REFRESH_FILE, REFRESH_TOKENS_FILE, only_if_missing_or_empty=True)
268
 
269
- # Create missing files (keep existing)
270
  if not os.path.exists(USERS_FILE):
271
  save_json_file(USERS_FILE, {})
 
272
 
273
  if not os.path.exists(BLACKLISTED_TOKENS_FILE):
274
  save_json_file(BLACKLISTED_TOKENS_FILE, {})
 
275
 
276
  if not os.path.exists(REFRESH_TOKENS_FILE):
277
  save_json_file(REFRESH_TOKENS_FILE, {})
 
278
 
 
 
 
 
 
 
279
 
280
  def ensure_database_initialized() -> bool:
281
  """
282
- Ensure JSON database is initialized (thread-safe).
283
- Controlled by RUN_INIT_DB (default 1).
284
  """
285
  global _db_init_done
286
- should_init = os.getenv("RUN_INIT_DB", "1") == "1"
287
 
288
- if not should_init:
289
  return False
290
 
291
- if _db_init_done:
292
- return True
293
-
294
- with _db_init_lock:
295
- if _db_init_done:
296
- return True
297
- init_db_files()
298
- _db_init_done = True
299
- return True
300
-
301
 
302
  def get_database_info() -> Dict[str, Any]:
303
- """
304
- Return non-sensitive diagnostics.
305
- """
306
- def _file_info(path: str) -> Dict[str, Any]:
 
 
 
 
307
  if not os.path.exists(path):
308
- return {"exists": False}
309
- return {
 
 
310
  "exists": True,
311
  "path": path,
312
  "size_bytes": os.path.getsize(path),
313
- "count": len(load_json_file(path)),
314
  }
315
 
316
- info = {
317
- "database_type": "JSON",
318
- "db_dir": DB_DIR,
319
- "seed_dir": DEFAULT_SEED_DIR,
320
- "files": {
321
- "users": _file_info(USERS_FILE),
322
- "blacklisted_tokens": _file_info(BLACKLISTED_TOKENS_FILE),
323
- "refresh_tokens": _file_info(REFRESH_TOKENS_FILE),
324
- },
325
- }
326
- return info
327
 
 
328
 
329
  def test_database_connection() -> Tuple[bool, str]:
330
- """
331
- Test read/write access to DB_DIR.
332
- """
333
  try:
334
  ensure_data_directory()
335
- # Read users
336
  _ = JSONUsers.load_all()
337
 
338
- # Write temp file
339
- test_file = os.path.join(DB_DIR, ".test_write")
340
- save_json_file(test_file, {"ok": True})
341
  os.remove(test_file)
342
 
343
  return True, "JSON database connection successful"
344
  except Exception as e:
345
- return False, f"JSON database connection failed: {e}"
 
2
  JSON file-based database module
3
 
4
  Handles:
5
+ - JSON file storage for users, tokens, and blacklisted tokens
6
  - User management and authentication
7
  - Token blacklist management
8
  - Refresh token storage
9
  - Thread-safe JSON operations with file locking
10
+ - Hugging Face friendly seeding + writable fallback
 
 
 
11
  """
12
 
13
  import os
14
  import json
15
  import threading
16
+ import shutil
17
  from typing import Optional, Dict, Any, List, Tuple
18
  from pathlib import Path
19
  from datetime import datetime
 
 
 
20
 
21
  # =========================================================
22
+ # PATHS
23
  # =========================================================
24
 
25
+ def _project_root() -> Path:
26
+ # auth/json_database.py -> auth (parent) -> project root (parent)
27
+ return Path(__file__).resolve().parent.parent
28
 
29
+ DEFAULT_DB_DIR = str((_project_root() / "data").resolve()) # <project>/data
30
+ DB_DIR_ENV = os.getenv("AUTH_DB_DIR", DEFAULT_DB_DIR)
31
 
32
+ # Seed dir (where your repo JSON files exist)
33
+ SEED_DIR_ENV = os.getenv("AUTH_SEED_DIR", DEFAULT_DB_DIR)
34
+
35
+ # Seeding controls
36
+ SEED_ON_START = os.getenv("AUTH_SEED_ON_START", "1") == "1"
37
+ SEED_IF_EMPTY = os.getenv("AUTH_SEED_IF_EMPTY", "1") == "1"
38
+
39
+ # Initialize DB files on startup
40
+ RUN_INIT_DB = os.getenv("RUN_INIT_DB", "1") == "1"
41
+
42
+ # Resolve target db dir
43
+ DB_DIR = str(Path(DB_DIR_ENV).expanduser().resolve())
44
+ SEED_DIR = str(Path(SEED_DIR_ENV).expanduser().resolve())
45
 
46
  USERS_FILE = os.path.join(DB_DIR, "users.json")
47
  BLACKLISTED_TOKENS_FILE = os.path.join(DB_DIR, "blacklisted_tokens.json")
48
  REFRESH_TOKENS_FILE = os.path.join(DB_DIR, "refresh_tokens.json")
49
 
50
+ # Thread locks
 
 
 
 
51
  _file_locks = {
52
  "users": threading.Lock(),
53
  "blacklisted_tokens": threading.Lock(),
 
57
  _db_init_done = False
58
  _db_init_lock = threading.Lock()
59
 
60
+ # =========================================================
61
+ # HELPERS
62
+ # =========================================================
63
 
64
+ def _log(msg: str):
65
+ # Simple stdout logging (Hugging Face logs show this)
66
+ print(msg, flush=True)
67
+
68
+ def ensure_data_directory():
69
+ """
70
+ Create DB directory. If not writable, fallback to /tmp/auth_db.
71
+ """
72
+ global DB_DIR, USERS_FILE, BLACKLISTED_TOKENS_FILE, REFRESH_TOKENS_FILE
73
 
74
+ try:
75
+ Path(DB_DIR).mkdir(parents=True, exist_ok=True)
76
+ # write test
77
+ test_file = os.path.join(DB_DIR, ".write_test")
78
+ with open(test_file, "w", encoding="utf-8") as f:
79
+ f.write("ok")
80
+ os.remove(test_file)
81
+ return
82
+ except Exception as e:
83
+ _log(f"[AUTH][WARN] DB_DIR not writable: {DB_DIR}. Error: {e}")
84
+ fallback = "/tmp/auth_db"
85
+ _log(f"[AUTH][WARN] Falling back to: {fallback}")
86
+ DB_DIR = fallback
87
+ Path(DB_DIR).mkdir(parents=True, exist_ok=True)
88
+
89
+ USERS_FILE = os.path.join(DB_DIR, "users.json")
90
+ BLACKLISTED_TOKENS_FILE = os.path.join(DB_DIR, "blacklisted_tokens.json")
91
+ REFRESH_TOKENS_FILE = os.path.join(DB_DIR, "refresh_tokens.json")
92
 
93
  def load_json_file(filepath: str) -> Dict[str, Any]:
94
+ """
95
+ Load JSON file with error handling.
96
+ Returns {} if file does not exist.
97
+ """
98
  if not os.path.exists(filepath):
99
  return {}
100
+
101
  try:
102
  with open(filepath, "r", encoding="utf-8") as f:
103
+ return json.load(f)
104
+ except (json.JSONDecodeError, IOError) as e:
105
+ _log(f"[AUTH][ERROR] Error loading {filepath}: {e}")
106
  return {}
107
 
108
+ def save_json_file(filepath: str, data: Dict[str, Any]):
 
109
  """
110
+ Atomic save JSON file (prevents partial writes).
 
111
  """
112
+ parent = os.path.dirname(filepath)
113
+ os.makedirs(parent, exist_ok=True)
114
 
115
+ tmp_path = filepath + ".tmp"
116
  try:
117
+ with open(tmp_path, "w", encoding="utf-8") as f:
118
  json.dump(data, f, indent=2, ensure_ascii=False)
119
+ os.replace(tmp_path, filepath)
120
+ except Exception as e:
121
+ _log(f"[AUTH][ERROR] Error saving {filepath}: {e}")
122
+ # cleanup temp if exists
123
  try:
124
  if os.path.exists(tmp_path):
125
  os.remove(tmp_path)
126
  except Exception:
127
  pass
128
+ raise
129
 
130
+ def _file_is_empty_json(filepath: str) -> bool:
 
 
 
 
 
131
  if not os.path.exists(filepath):
132
  return True
133
  try:
134
+ data = load_json_file(filepath)
135
+ return not bool(data)
136
  except Exception:
137
  return True
138
 
139
+ def _seed_file_if_needed(target_path: str, seed_path: str):
 
140
  """
141
+ Copy seed file -> target if:
142
+ - target missing, OR
143
+ - target exists but empty AND SEED_IF_EMPTY=1
144
  """
145
+ if not SEED_ON_START:
146
+ return
147
 
148
+ seed_exists = os.path.exists(seed_path)
149
+ target_exists = os.path.exists(target_path)
150
 
151
+ if (not target_exists and seed_exists) or (target_exists and SEED_IF_EMPTY and seed_exists and _file_is_empty_json(target_path)):
152
+ os.makedirs(os.path.dirname(target_path), exist_ok=True)
153
+ shutil.copy2(seed_path, target_path)
154
+ _log(f"[AUTH][SEED] Copied seed: {seed_path} -> {target_path}")
155
 
156
+ # =========================================================
157
+ # STORAGE CLASSES
158
+ # =========================================================
159
 
 
 
 
160
  class JSONUsers:
161
  @staticmethod
162
  def load_all() -> Dict[str, Any]:
 
179
  return False
180
 
181
  users[username_lower] = {
182
+ "id": len(users) + 1,
183
  "username": username_lower,
184
  "password_hash": password_hash,
185
  "role": role,
 
192
  @staticmethod
193
  def get_all_users() -> List[Dict[str, Any]]:
194
  users = JSONUsers.load_all()
195
+ return [
196
+ {"id": u.get("id"), "username": u.get("username"), "role": u.get("role")}
197
+ for u in users.values()
198
+ ]
199
 
200
  @staticmethod
201
  def promote_to_admin(username: str) -> bool:
202
  username_lower = username.lower()
 
203
  with _file_locks["users"]:
204
  users = load_json_file(USERS_FILE)
205
  if username_lower not in users:
206
  return False
 
207
  users[username_lower]["role"] = "admin"
208
  save_json_file(USERS_FILE, users)
209
  return True
 
212
  def user_count() -> int:
213
  return len(JSONUsers.load_all())
214
 
 
215
  class JSONBlacklistedTokens:
216
  @staticmethod
217
  def load_all() -> Dict[str, Any]:
 
234
  save_json_file(BLACKLISTED_TOKENS_FILE, tokens)
235
  return True
236
 
 
237
  class JSONRefreshTokens:
238
  @staticmethod
239
  def load_all() -> Dict[str, Any]:
 
266
  username_lower = username.lower()
267
  with _file_locks["refresh_tokens"]:
268
  tokens = load_json_file(REFRESH_TOKENS_FILE)
269
+ to_remove = [k for k, v in tokens.items() if v.get("username") == username_lower]
270
+ for k in to_remove:
271
+ del tokens[k]
272
  save_json_file(REFRESH_TOKENS_FILE, tokens)
273
  return True
274
 
275
+ # =========================================================
276
+ # INIT / DIAG
277
+ # =========================================================
278
 
279
+ def init_db():
 
 
 
280
  """
281
+ Initialize JSON database files.
282
+ If AUTH_SEED_ON_START=1, seed from AUTH_SEED_DIR when missing or empty.
283
  """
284
  ensure_data_directory()
285
 
286
+ # seed paths
287
+ seed_users = os.path.join(SEED_DIR, "users.json")
288
+ seed_blacklist = os.path.join(SEED_DIR, "blacklisted_tokens.json")
289
+ seed_refresh = os.path.join(SEED_DIR, "refresh_tokens.json")
290
 
291
+ # Seed (if needed)
292
+ _seed_file_if_needed(USERS_FILE, seed_users)
293
+ _seed_file_if_needed(BLACKLISTED_TOKENS_FILE, seed_blacklist)
294
+ _seed_file_if_needed(REFRESH_TOKENS_FILE, seed_refresh)
 
295
 
296
+ # Ensure files exist (create empty if still missing)
297
  if not os.path.exists(USERS_FILE):
298
  save_json_file(USERS_FILE, {})
299
+ _log(f"[AUTH][INIT] Created empty {USERS_FILE}")
300
 
301
  if not os.path.exists(BLACKLISTED_TOKENS_FILE):
302
  save_json_file(BLACKLISTED_TOKENS_FILE, {})
303
+ _log(f"[AUTH][INIT] Created empty {BLACKLISTED_TOKENS_FILE}")
304
 
305
  if not os.path.exists(REFRESH_TOKENS_FILE):
306
  save_json_file(REFRESH_TOKENS_FILE, {})
307
+ _log(f"[AUTH][INIT] Created empty {REFRESH_TOKENS_FILE}")
308
 
309
+ # Startup log (very useful in HF logs)
310
+ try:
311
+ users_count = len(load_json_file(USERS_FILE))
312
+ _log(f"[AUTH][READY] DB_DIR={DB_DIR} | users={users_count}")
313
+ except Exception as e:
314
+ _log(f"[AUTH][WARN] Could not read users count: {e}")
315
 
316
  def ensure_database_initialized() -> bool:
317
  """
318
+ Ensure JSON DB is initialized once (thread-safe).
319
+ Controlled by RUN_INIT_DB.
320
  """
321
  global _db_init_done
 
322
 
323
+ if not RUN_INIT_DB:
324
  return False
325
 
326
+ if not _db_init_done:
327
+ with _db_init_lock:
328
+ if not _db_init_done:
329
+ init_db()
330
+ _db_init_done = True
331
+ return True
 
 
 
 
332
 
333
  def get_database_info() -> Dict[str, Any]:
334
+ info = {
335
+ "database_type": "JSON",
336
+ "storage_location": DB_DIR,
337
+ "seed_location": SEED_DIR,
338
+ "files": {},
339
+ }
340
+
341
+ def _file_info(path: str, label: str):
342
  if not os.path.exists(path):
343
+ info["files"][label] = {"exists": False}
344
+ return
345
+ data = load_json_file(path)
346
+ info["files"][label] = {
347
  "exists": True,
348
  "path": path,
349
  "size_bytes": os.path.getsize(path),
350
+ "count": len(data) if isinstance(data, dict) else 0,
351
  }
352
 
353
+ try:
354
+ _file_info(USERS_FILE, "users")
355
+ _file_info(BLACKLISTED_TOKENS_FILE, "blacklisted_tokens")
356
+ _file_info(REFRESH_TOKENS_FILE, "refresh_tokens")
357
+ info["connection_status"] = "ok"
358
+ except Exception as e:
359
+ info["connection_status"] = "error"
360
+ info["error"] = str(e)
 
 
 
361
 
362
+ return info
363
 
364
  def test_database_connection() -> Tuple[bool, str]:
 
 
 
365
  try:
366
  ensure_data_directory()
 
367
  _ = JSONUsers.load_all()
368
 
369
+ test_file = os.path.join(DB_DIR, ".test")
370
+ save_json_file(test_file, {"test": "ok"})
 
371
  os.remove(test_file)
372
 
373
  return True, "JSON database connection successful"
374
  except Exception as e:
375
+ return False, f"JSON database connection failed: {str(e)}"
data/blacklisted_tokens.json CHANGED
@@ -58,5 +58,9 @@
58
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgwNTIwfQ.xaovVHywd_uCUPkkEN0C9juXhZuBzEwvnotk4LgMTa8": {
59
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgwNTIwfQ.xaovVHywd_uCUPkkEN0C9juXhZuBzEwvnotk4LgMTa8",
60
  "created_at": "2026-01-28T11:23:48.110513"
 
 
 
 
61
  }
62
  }
 
58
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgwNTIwfQ.xaovVHywd_uCUPkkEN0C9juXhZuBzEwvnotk4LgMTa8": {
59
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgwNTIwfQ.xaovVHywd_uCUPkkEN0C9juXhZuBzEwvnotk4LgMTa8",
60
  "created_at": "2026-01-28T11:23:48.110513"
61
+ },
62
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgyMDQ3fQ.SjH67H5hTvAr0A2X9UeFy9BTzkMoeRAv2AQideORr2w": {
63
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzY5NTgyMDQ3fQ.SjH67H5hTvAr0A2X9UeFy9BTzkMoeRAv2AQideORr2w",
64
+ "created_at": "2026-01-28T11:49:25.157795"
65
  }
66
  }
data/refresh_tokens.json CHANGED
@@ -1 +1,7 @@
1
- {}
 
 
 
 
 
 
 
1
+ {
2
+ "admin_1": {
3
+ "username": "admin",
4
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzcwMTg1OTgwfQ.meq8kajfaFjc4ne6xfR-Pf1WzUvxybUvyseI16N7ztM",
5
+ "created_at": "2026-01-28T11:49:40.529887"
6
+ }
7
+ }