JakeFake222 commited on
Commit
4f17d4f
·
verified ·
1 Parent(s): 3de6c41

Add github_storage.py

Browse files
Files changed (1) hide show
  1. github_storage.py +100 -0
github_storage.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ GitHub Storage Module
3
+ Handles encrypted read/write operations to a GitHub repository.
4
+ """
5
+
6
+ import os
7
+ import json
8
+ import base64
9
+ import requests
10
+ from cryptography.fernet import Fernet
11
+
12
+ # Configuration from environment variables
13
+ GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
14
+ ENCRYPTION_KEY = os.environ.get("ENCRYPTION_KEY", "")
15
+ GITHUB_REPO = os.environ.get("GITHUB_REPO", "")
16
+ DATA_PATH = "data/users.enc"
17
+
18
+ def get_cipher():
19
+ """Get Fernet cipher instance."""
20
+ if not ENCRYPTION_KEY:
21
+ raise ValueError("ENCRYPTION_KEY environment variable not set")
22
+ return Fernet(ENCRYPTION_KEY.encode())
23
+
24
+ def read_users() -> dict:
25
+ """Read and decrypt user data from GitHub."""
26
+ if not GITHUB_TOKEN or not GITHUB_REPO:
27
+ raise ValueError("GITHUB_TOKEN and GITHUB_REPO must be set")
28
+
29
+ url = f"https://api.github.com/repos/{GITHUB_REPO}/contents/{DATA_PATH}"
30
+ headers = {
31
+ "Authorization": f"token {GITHUB_TOKEN}",
32
+ "Accept": "application/vnd.github.v3+json"
33
+ }
34
+
35
+ response = requests.get(url, headers=headers)
36
+
37
+ if response.status_code == 404:
38
+ # No data file yet, return empty users dict
39
+ return {"users": {}}
40
+
41
+ if response.status_code != 200:
42
+ raise Exception(f"GitHub API error: {response.status_code} - {response.text}")
43
+
44
+ content = response.json()
45
+ encrypted_data = base64.b64decode(content["content"])
46
+
47
+ # Handle initial placeholder
48
+ try:
49
+ cipher = get_cipher()
50
+ decrypted = cipher.decrypt(encrypted_data)
51
+ return json.loads(decrypted)
52
+ except Exception:
53
+ # If decryption fails (e.g., placeholder data), return empty
54
+ return {"users": {}}
55
+
56
+ def write_users(data: dict) -> bool:
57
+ """Encrypt and write user data to GitHub."""
58
+ if not GITHUB_TOKEN or not GITHUB_REPO:
59
+ raise ValueError("GITHUB_TOKEN and GITHUB_REPO must be set")
60
+
61
+ url = f"https://api.github.com/repos/{GITHUB_REPO}/contents/{DATA_PATH}"
62
+ headers = {
63
+ "Authorization": f"token {GITHUB_TOKEN}",
64
+ "Accept": "application/vnd.github.v3+json"
65
+ }
66
+
67
+ # Encrypt the data
68
+ cipher = get_cipher()
69
+ encrypted = cipher.encrypt(json.dumps(data).encode())
70
+ content_b64 = base64.b64encode(encrypted).decode()
71
+
72
+ # Get current file SHA (required for updates)
73
+ get_response = requests.get(url, headers=headers)
74
+ sha = None
75
+ if get_response.status_code == 200:
76
+ sha = get_response.json().get("sha")
77
+
78
+ # Create or update file
79
+ payload = {
80
+ "message": "Update user data",
81
+ "content": content_b64,
82
+ "branch": "main"
83
+ }
84
+ if sha:
85
+ payload["sha"] = sha
86
+
87
+ response = requests.put(url, headers=headers, json=payload)
88
+ return response.status_code in [200, 201]
89
+
90
+ def get_file_sha(path: str) -> str | None:
91
+ """Get the SHA of a file in the repo."""
92
+ url = f"https://api.github.com/repos/{GITHUB_REPO}/contents/{path}"
93
+ headers = {
94
+ "Authorization": f"token {GITHUB_TOKEN}",
95
+ "Accept": "application/vnd.github.v3+json"
96
+ }
97
+ response = requests.get(url, headers=headers)
98
+ if response.status_code == 200:
99
+ return response.json().get("sha")
100
+ return None