Xyro123 commited on
Commit
4bd38e0
·
verified ·
1 Parent(s): 7110f93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -98
app.py CHANGED
@@ -1,148 +1,238 @@
 
1
  import os
2
- import requests
3
- import gradio as gr
4
- import time
5
  import json
 
6
  import hashlib
7
- from markdown import markdown
 
 
 
 
 
8
 
9
  # ======================
10
- # 🔐 USER AUTH SYSTEM
11
  # ======================
12
-
13
  DB_PATH = "users.json"
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- def hash_pw(pw):
 
 
 
16
  return hashlib.sha256(pw.encode()).hexdigest()
17
 
18
  def load_users():
19
  if not os.path.exists(DB_PATH):
20
  return {}
21
- return json.load(open(DB_PATH, "r"))
 
 
 
 
22
 
23
- def save_users(data):
24
- json.dump(data, open(DB_PATH, "w"), indent=4)
 
25
 
26
- def signup(username, password):
27
  users = load_users()
28
  if username in users:
29
  return "❌ Хэрэглэгч байна!"
30
- users[username] = hash_pw(password)
31
  save_users(users)
32
  return "✅ Амжилттай бүртгэгдлээ!"
33
 
34
- def login(username, password):
35
  users = load_users()
36
  if username not in users:
37
  return "❌ Хэрэглэгч олдсонгүй!"
38
- if users[username] != hash_pw(password):
39
  return "❌ Нууц үг буруу!"
40
  return "SUCCESS"
41
 
42
  # ======================
43
- # 🔑 Gemini API
44
  # ======================
45
-
46
- gemini_api_key = os.getenv("GEMINI_API_KEY")
47
- if gemini_api_key is None:
48
- raise ValueError("⚠️ GEMINI_API_KEY тохируулаагүй байна!")
49
-
50
- def gemini_response(prompt):
51
  try:
52
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={gemini_api_key}"
53
  headers = {"Content-Type": "application/json"}
54
- data = {"contents":[{"parts":[{"text":prompt}]}]}
55
- res = requests.post(url, headers=headers, json=data)
56
- res_json = res.json()
57
- return res_json["candidates"][0]["content"]["parts"][0]["text"]
 
58
  except Exception as e:
59
  return f"⚠️ Gemini error: {e}"
60
 
61
  # ======================
62
- # 🌙 CHAT FUNCTION
63
  # ======================
64
-
65
  def chat_fn(message, chat_history):
66
  chat_history = chat_history or []
 
67
  chat_history.append({"role": "user", "content": message})
68
- time.sleep(0.5)
 
69
  reply = gemini_response(message)
 
70
  chat_history.append({"role": "assistant", "content": reply})
71
  return chat_history, chat_history
72
 
73
  # ======================
74
- # 🌟 UI CSS
75
  # ======================
76
-
77
- css = """
78
- .gradio-container {font-family: 'Segoe UI', sans-serif;}
79
- textarea, input {border-radius:15px; padding:10px;}
80
- button {border-radius:15px; padding:10px 20px; font-weight:bold;}
81
- """
82
-
83
- logo_path = "logo.png"
84
-
85
- # ======================
86
- # 🚀 FULL APP UI
87
- # ======================
88
-
89
- with gr.Blocks(css=css, theme=gr.themes.Soft()) as app:
90
-
91
- gr.Markdown("## 🔐 Welcome to ZeppFusion Login System")
92
-
93
- # Tabs: Login / Signup
94
- with gr.Tabs() as tabs:
95
-
96
- with gr.Tab("Login"):
97
- log_user = gr.Textbox(label="Username")
98
- log_pw = gr.Textbox(label="Password", type="password")
99
- log_status = gr.Textbox(label="Status", interactive=False)
100
- log_btn = gr.Button("Login")
101
-
102
- with gr.Tab("Signup"):
103
- reg_user = gr.Textbox(label="New Username")
104
- reg_pw = gr.Textbox(label="New Password", type="password")
105
- reg_status = gr.Textbox(label="Status", interactive=False)
106
- reg_btn = gr.Button("Signup")
107
-
108
- # Chat UI (hidden initially)
109
- with gr.Group(visible=False) as chat_ui:
110
- with gr.Row():
111
- gr.Image(value=logo_path, show_label=False)
112
- gr.Markdown("## ZeppFusion AI Chat")
113
- chatbot = gr.Chatbot(type="messages")
114
- msg = gr.Textbox(placeholder="Таны асуулт...", lines=1)
115
- clear = gr.Button("🗑️ Clear History")
116
- msg.submit(chat_fn, [msg, chatbot], [chatbot, chatbot])
117
- clear.click(lambda: [], None, chatbot)
118
-
119
- # ======================
120
- # BUTTON ACTIONS
121
- # ======================
122
-
123
- # Signup
124
- reg_btn.click(
125
- signup,
126
- inputs=[reg_user, reg_pw],
127
- outputs=reg_status
128
- )
129
-
130
- # Login
131
- def login_action(username, password):
132
- res = login(username, password)
133
- if res == "SUCCESS":
134
- return "✅ Амжилттай нэвтэрлээ!", gr.update(visible=False), gr.update(visible=True)
135
- return res, gr.update(visible=True), gr.update(visible=False)
136
-
137
- log_btn.click(
138
- login_action,
139
- inputs=[log_user, log_pw],
140
- outputs=[log_status, tabs, chat_ui]
141
- )
142
 
143
  # ======================
144
- # RUN
145
  # ======================
 
 
 
 
 
 
 
 
 
 
 
 
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  if __name__ == "__main__":
148
- app.launch()
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
  import os
 
 
 
3
  import json
4
+ import time
5
  import hashlib
6
+ import requests
7
+ from urllib.parse import urlencode
8
+ from fastapi import FastAPI
9
+ from fastapi.responses import RedirectResponse
10
+ from fastapi.middleware.wsgi import WSGIMiddleware
11
+ import gradio as gr
12
 
13
  # ======================
14
+ # CONFIG
15
  # ======================
 
16
  DB_PATH = "users.json"
17
+ LOGO = "logo.png" # upload to your Space root (optional)
18
+ # Set these environment variables in your Space secrets if you want Google OAuth:
19
+ GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
20
+ GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
21
+ # Replace <YOUR_SPACE> below with your space name OR set REDIRECT_URI env var
22
+ REDIRECT_URI = os.getenv("REDIRECT_URI") or "https://<YOUR_SPACE>.hf.space/login/callback"
23
+
24
+ # Gemini API key (required)
25
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
26
+ if not GEMINI_API_KEY:
27
+ raise ValueError("Please set GEMINI_API_KEY in environment variables (Settings → Secrets)")
28
 
29
+ # ======================
30
+ # UTIL: users DB
31
+ # ======================
32
+ def _hash_pw(pw: str) -> str:
33
  return hashlib.sha256(pw.encode()).hexdigest()
34
 
35
  def load_users():
36
  if not os.path.exists(DB_PATH):
37
  return {}
38
+ try:
39
+ with open(DB_PATH, "r") as f:
40
+ return json.load(f)
41
+ except Exception:
42
+ return {}
43
 
44
+ def save_users(d):
45
+ with open(DB_PATH, "w") as f:
46
+ json.dump(d, f, indent=4)
47
 
48
+ def signup_user(username, password):
49
  users = load_users()
50
  if username in users:
51
  return "❌ Хэрэглэгч байна!"
52
+ users[username] = _hash_pw(password)
53
  save_users(users)
54
  return "✅ Амжилттай бүртгэгдлээ!"
55
 
56
+ def check_login(username, password):
57
  users = load_users()
58
  if username not in users:
59
  return "❌ Хэрэглэгч олдсонгүй!"
60
+ if users[username] != _hash_pw(password):
61
  return "❌ Нууц үг буруу!"
62
  return "SUCCESS"
63
 
64
  # ======================
65
+ # Gemini: simple API call
66
  # ======================
67
+ def gemini_response(prompt: str) -> str:
 
 
 
 
 
68
  try:
69
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={GEMINI_API_KEY}"
70
  headers = {"Content-Type": "application/json"}
71
+ data = {"contents": [{"parts": [{"text": prompt}]}]}
72
+ r = requests.post(url, headers=headers, json=data, timeout=30)
73
+ r.raise_for_status()
74
+ j = r.json()
75
+ return j["candidates"][0]["content"]["parts"][0]["text"]
76
  except Exception as e:
77
  return f"⚠️ Gemini error: {e}"
78
 
79
  # ======================
80
+ # Chat function (Gradio messages format)
81
  # ======================
 
82
  def chat_fn(message, chat_history):
83
  chat_history = chat_history or []
84
+ # append user
85
  chat_history.append({"role": "user", "content": message})
86
+ # call model
87
+ time.sleep(0.2)
88
  reply = gemini_response(message)
89
+ # append assistant
90
  chat_history.append({"role": "assistant", "content": reply})
91
  return chat_history, chat_history
92
 
93
  # ======================
94
+ # Optional Google OAuth (FastAPI endpoints)
95
  # ======================
96
+ fastapi_app = FastAPI()
97
+
98
+ if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET:
99
+ @fastapi_app.get("/login")
100
+ def start_login():
101
+ params = {
102
+ "client_id": GOOGLE_CLIENT_ID,
103
+ "redirect_uri": REDIRECT_URI,
104
+ "response_type": "code",
105
+ "scope": "openid email profile",
106
+ "access_type": "offline",
107
+ "prompt": "consent"
108
+ }
109
+ url = f"https://accounts.google.com/o/oauth2/v2/auth?{urlencode(params)}"
110
+ return RedirectResponse(url)
111
+
112
+ @fastapi_app.get("/login/callback")
113
+ def callback(code: str = None):
114
+ if not code:
115
+ return RedirectResponse("/")
116
+ data = {
117
+ "code": code,
118
+ "client_id": GOOGLE_CLIENT_ID,
119
+ "client_secret": GOOGLE_CLIENT_SECRET,
120
+ "redirect_uri": REDIRECT_URI,
121
+ "grant_type": "authorization_code"
122
+ }
123
+ token_res = requests.post("https://oauth2.googleapis.com/token", data=data).json()
124
+ id_token = token_res.get("id_token")
125
+ # NOTE: for simplicity we don't verify id_token here.
126
+ # You can decode/verify id_token to extract user's email and save session as needed.
127
+ return RedirectResponse("/") # After login redirect to main app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  # ======================
130
+ # Gradio UI (ChatGPT-like: sidebar + main)
131
  # ======================
132
+ css = """
133
+ /* Minimal ChatGPT-like style */
134
+ :root { --bg:#0b0b0c; --panel:#0f1112; --muted:#9aa0a6; --accent:#10A37F; --user:#1e293b; }
135
+ body { background: var(--bg); color: #e6eef3; }
136
+ .gradio-container { max-width:1200px; margin:auto; padding:18px; }
137
+ .sidebar { background: #0f1112; border-radius:8px; padding:14px; width:260px; min-height:600px; }
138
+ .main { background: transparent; margin-left:20px; flex:1; }
139
+ .card { background: var(--panel); border-radius:10px; padding:14px; }
140
+ .msg-user { background: #0b1220; color:#e6eef3; border-radius:12px; padding:10px; display:inline-block; }
141
+ .msg-assistant { background: rgba(16,163,127,0.12); color:#d2fff0; border-radius:12px; padding:10px; display:inline-block; }
142
+ .input-bar { display:flex; gap:8px; align-items:center; }
143
+ """
144
 
145
+ # Gradio app built as WSGI app to mount into FastAPI when running as main.
146
+ with gr.Blocks(css=css, theme=gr.themes.Base()) as gr_app:
147
+ with gr.Row():
148
+ # Sidebar
149
+ with gr.Column(elem_id="left", scale=1):
150
+ with gr.Box():
151
+ gr.Markdown("## ZeppFusion")
152
+ gr.Markdown("**Your AI assistant, ChatGPT-style UI**")
153
+ # Login / Signup controls in sidebar
154
+ with gr.TabbedInterface([]): # invisible placeholder to keep layout stable
155
+ pass
156
+ with gr.Box(elem_classes="sidebar"):
157
+ gr.Markdown("### Chats")
158
+ chat_list = gr.Textbox(value="• New chat", interactive=False)
159
+ gr.Markdown("### Account")
160
+ # show google login button only if env provided
161
+ if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET:
162
+ google_btn = gr.Button("Login with Google")
163
+ else:
164
+ google_btn = None
165
+ # Local login/signup widgets (compact)
166
+ username = gr.Textbox(label="Username", placeholder="username")
167
+ password = gr.Textbox(label="Password", placeholder="password", type="password")
168
+ login_status = gr.Textbox(label="Status", interactive=False)
169
+ btn_login = gr.Button("Login")
170
+ signup_user = gr.Textbox(label="New username")
171
+ signup_pw = gr.Textbox(label="New password", type="password")
172
+ signup_status = gr.Textbox(label="Signup status", interactive=False)
173
+ btn_signup = gr.Button("Signup")
174
+ # Dark mode toggle (just cosmetic)
175
+ dark_toggle = gr.Checkbox(label="Dark mode", value=True)
176
+
177
+ # Main content
178
+ with gr.Column(elem_classes="main", scale=3):
179
+ with gr.Box(elem_classes="card"):
180
+ with gr.Row():
181
+ gr.Image(value=LOGO if os.path.exists(LOGO) else None, show_label=False, elem_id="logo")
182
+ gr.Markdown("### ZeppFusion AI — Chat")
183
+ # Chat window
184
+ chatbot = gr.Chatbot(type="messages")
185
+ msg = gr.Textbox(placeholder="Таны асуулт...", lines=1)
186
+ clear_btn = gr.Button("Clear")
187
+ # helper text area
188
+ info = gr.Markdown("Login to start chatting (or use Google if configured).")
189
+
190
+ # Actions
191
+ def on_signup(u, p):
192
+ return signup_user, signup_user, signup_user, signup_user # placeholder to satisfy outputs (not used)
193
+
194
+ # Actually hookup signup/login
195
+ btn_signup.click(lambda u, p: signup_user_func(u,p) if False else None, inputs=[], outputs=[])
196
+ # Instead, we'll define proper functions below and rebind.
197
+
198
+ # Define proper functions
199
+ def do_signup(u, p):
200
+ if not u or not p:
201
+ return "❌ Оролт дутуу!"
202
+ res = signup_user(u, p)
203
+ return res
204
+
205
+ def do_login(u, p):
206
+ if not u or not p:
207
+ return "❌ Оролт дутуу!"
208
+ res = check_login(u, p)
209
+ return ("✅ Амжилттай нэвтэрлээ!" , gr.update(visible=False), gr.update(visible=True)) if res == "SUCCESS" else (res, gr.update(visible=True), gr.update(visible=False))
210
+
211
+ # Rewire buttons to actual functions (need to find widgets by variable)
212
+ btn_signup.click(do_signup, inputs=[signup_user, signup_pw], outputs=[signup_status])
213
+ btn_login.click(do_login, inputs=[username, password], outputs=[login_status, gr_app, chatbot])
214
+
215
+ # If Google OAuth configured -> open /login path
216
+ if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET:
217
+ def open_google():
218
+ # return a direct link that user can click to open the FastAPI /login endpoint;
219
+ # in HuggingFace space this will route to /login on same host.
220
+ return RedirectResponse("/login")
221
+ google_btn.click(lambda: "/login", None, None) # clicking the button will navigate the browser if configured by the runtime
222
+
223
+ # Chat actions
224
+ msg.submit(chat_fn, inputs=[msg, chatbot], outputs=[chatbot, chatbot])
225
+ clear_btn.click(lambda: [], None, chatbot)
226
+
227
+ # Mount Gradio into FastAPI if running as main
228
  if __name__ == "__main__":
229
+ import uvicorn
230
+ from fastapi.middleware.cors import CORSMiddleware
231
+ fastapi_app.add_middleware(
232
+ CORSMiddleware,
233
+ allow_origins=["*"],
234
+ allow_methods=["*"],
235
+ allow_headers=["*"],
236
+ )
237
+ fastapi_app.mount("/", WSGIMiddleware(gr_app))
238
+ uvicorn.run(fastapi_app, host="0.0.0.0", port=7860)