Mark-Lasfar commited on
Commit
873e9c2
·
1 Parent(s): e7ce5ff

update main.py chat.js auth.py login.html

Browse files
Files changed (5) hide show
  1. api/auth.py +10 -6
  2. main.py +2 -2
  3. static/js/chat.js +87 -119
  4. templates/chat.html +128 -84
  5. templates/login.html +226 -185
api/auth.py CHANGED
@@ -12,12 +12,13 @@ from fastapi import Depends, Request, FastAPI
12
  from sqlalchemy.ext.asyncio import AsyncSession
13
  from sqlalchemy import select
14
  from fastapi_users.models import UP
15
- from typing import Optional, Dict
16
  import os
17
  import logging
18
  import secrets
 
19
 
20
- from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db # استيراد من database.py
21
  from api.models import UserRead, UserCreate, UserUpdate
22
 
23
  # إعداد اللوقينج
@@ -110,7 +111,8 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
110
  if user:
111
  logger.info(f"User found: {user.email}, proceeding with on_after_login")
112
  await self.on_after_login(user, request)
113
- return user
 
114
  else:
115
  logger.error(f"No user found for OAuth account with user_id: {existing_oauth_account.user_id}")
116
  raise ValueError("User not found for existing OAuth account")
@@ -123,7 +125,8 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
123
  await self.add_oauth_account(oauth_account)
124
  logger.info(f"User associated: {user.email}, proceeding with on_after_login")
125
  await self.on_after_login(user, request)
126
- return user
 
127
 
128
  logger.info(f"Creating new user for email: {account_email}")
129
  user_dict = {
@@ -138,7 +141,8 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
138
  await self.add_oauth_account(oauth_account)
139
  logger.info(f"New user created: {user.email}, proceeding with on_after_login")
140
  await self.on_after_login(user, request)
141
- return user
 
142
 
143
  # استدعاء user manager من get_user_db
144
  async def get_user_manager(user_db: CustomSQLAlchemyUserDatabase = Depends(get_user_db)):
@@ -180,4 +184,4 @@ def get_auth_router(app: FastAPI):
180
  app.include_router(fastapi_users.get_register_router(UserRead, UserCreate), prefix="/auth", tags=["auth"])
181
  app.include_router(fastapi_users.get_reset_password_router(), prefix="/auth", tags=["auth"])
182
  app.include_router(fastapi_users.get_verify_router(UserRead), prefix="/auth", tags=["auth"])
183
- app.include_router(fastapi_users.get_users_router(UserRead, UserUpdate), prefix="/users", tags=["users"])
 
12
  from sqlalchemy.ext.asyncio import AsyncSession
13
  from sqlalchemy import select
14
  from fastapi_users.models import UP
15
+ from typing import Optional
16
  import os
17
  import logging
18
  import secrets
19
+ from fastapi.responses import RedirectResponse
20
 
21
+ from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db
22
  from api.models import UserRead, UserCreate, UserUpdate
23
 
24
  # إعداد اللوقينج
 
111
  if user:
112
  logger.info(f"User found: {user.email}, proceeding with on_after_login")
113
  await self.on_after_login(user, request)
114
+ logger.info(f"Redirecting user {user.email} to /chat")
115
+ return RedirectResponse(url="/chat", status_code=302)
116
  else:
117
  logger.error(f"No user found for OAuth account with user_id: {existing_oauth_account.user_id}")
118
  raise ValueError("User not found for existing OAuth account")
 
125
  await self.add_oauth_account(oauth_account)
126
  logger.info(f"User associated: {user.email}, proceeding with on_after_login")
127
  await self.on_after_login(user, request)
128
+ logger.info(f"Redirecting user {user.email} to /chat")
129
+ return RedirectResponse(url="/chat", status_code=302)
130
 
131
  logger.info(f"Creating new user for email: {account_email}")
132
  user_dict = {
 
141
  await self.add_oauth_account(oauth_account)
142
  logger.info(f"New user created: {user.email}, proceeding with on_after_login")
143
  await self.on_after_login(user, request)
144
+ logger.info(f"Redirecting new user {user.email} to /chat")
145
+ return RedirectResponse(url="/chat", status_code=302)
146
 
147
  # استدعاء user manager من get_user_db
148
  async def get_user_manager(user_db: CustomSQLAlchemyUserDatabase = Depends(get_user_db)):
 
184
  app.include_router(fastapi_users.get_register_router(UserRead, UserCreate), prefix="/auth", tags=["auth"])
185
  app.include_router(fastapi_users.get_reset_password_router(), prefix="/auth", tags=["auth"])
186
  app.include_router(fastapi_users.get_verify_router(UserRead), prefix="/auth", tags=["auth"])
187
+ app.include_router(fastapi_users.get_users_router(UserRead, UserUpdate), prefix="/users", tags=["users"])
main.py CHANGED
@@ -134,13 +134,13 @@ get_auth_router(app)
134
  logger.debug("API and auth routers included")
135
 
136
  # Add logout endpoint
137
- @app.get("/logout")
138
  async def logout(request: Request):
139
  logger.info("User logout requested")
140
  session_data = request.session.copy()
141
  request.session.clear()
142
  logger.debug(f"Cleared session data: {session_data}")
143
- response = RedirectResponse("/login")
144
  response.delete_cookie("access_token")
145
  response.delete_cookie("session")
146
  logger.debug("Session and access_token cookies deleted")
 
134
  logger.debug("API and auth routers included")
135
 
136
  # Add logout endpoint
137
+ @app.post("/logout")
138
  async def logout(request: Request):
139
  logger.info("User logout requested")
140
  session_data = request.session.copy()
141
  request.session.clear()
142
  logger.debug(f"Cleared session data: {session_data}")
143
+ response = RedirectResponse("/login", status_code=302)
144
  response.delete_cookie("access_token")
145
  response.delete_cookie("session")
146
  logger.debug("Session and access_token cookies deleted")
static/js/chat.js CHANGED
@@ -39,27 +39,27 @@ const uiElements = {
39
  once: true,
40
  offset: 50,
41
  });
42
- if (currentConversationId && checkAuth()) {
43
  console.log('Loading conversation with ID:', currentConversationId);
44
- await loadConversation(currentConversationId);
45
  } else if (conversationHistory.length > 0) {
46
- console.log('Restoring conversation history from sessionStorage:', conversationHistory);
47
- enterChatView();
48
- conversationHistory.forEach(msg => {
49
- console.log('Adding message from history:', msg);
50
- addMsg(msg.role, msg.content);
51
- });
52
- } else {
53
- console.log('No conversation history or ID, starting fresh');
54
- }
55
- autoResizeTextarea();
56
- updateSendButtonState();
57
- if (uiElements.swipeHint) {
58
- setTimeout(() => {
59
- uiElements.swipeHint.style.display = 'none';
60
- }, 3000);
61
- }
62
- setupTouchGestures();
63
  });
64
 
65
  // Check authentication token
@@ -526,6 +526,7 @@ async function createNewConversation() {
526
  console.error('Error creating conversation:', error);
527
  alert('Failed to create new conversation. Please try again.');
528
  }
 
529
  }
530
 
531
  // Update conversation title
@@ -689,63 +690,20 @@ async function submitMessage() {
689
 
690
  try {
691
  const response = await sendRequest(endpoint, payload ? JSON.stringify(payload) : formData, headers);
 
692
  if (endpoint === '/api/audio-transcription') {
693
  const data = await response.json();
694
  if (!data.transcription) throw new Error('No transcription received from server');
695
- const transcription = data.transcription || 'Error: No transcription generated.';
696
- if (streamMsg) {
697
- streamMsg.dataset.text = transcription;
698
- renderMarkdown(streamMsg);
699
- streamMsg.dataset.done = '1';
700
- }
701
- conversationHistory.push({ role: 'assistant', content: transcription });
702
- sessionStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
703
- if (checkAuth() && currentConversationId) {
704
- await saveMessageToConversation(currentConversationId, 'assistant', transcription);
705
- }
706
- if (checkAuth() && data.conversation_id) {
707
- currentConversationId = data.conversation_id;
708
- currentConversationTitle = data.conversation_title || 'Untitled Conversation';
709
- if (uiElements.conversationTitle) uiElements.conversationTitle.textContent = currentConversationTitle;
710
- history.pushState(null, '', `/chat/${currentConversationId}`);
711
- await loadConversations();
712
- }
713
  } else if (endpoint === '/api/image-analysis') {
714
  const data = await response.json();
715
- const analysis = data.image_analysis || 'Error: No analysis generated.';
716
- if (streamMsg) {
717
- streamMsg.dataset.text = analysis;
718
- renderMarkdown(streamMsg);
719
- streamMsg.dataset.done = '1';
720
- }
721
- conversationHistory.push({ role: 'assistant', content: analysis });
722
- sessionStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
723
- if (checkAuth() && currentConversationId) {
724
- await saveMessageToConversation(currentConversationId, 'assistant', analysis);
725
- }
726
- if (checkAuth() && data.conversation_id) {
727
- currentConversationId = data.conversation_id;
728
- currentConversationTitle = data.conversation_title || 'Untitled Conversation';
729
- if (uiElements.conversationTitle) uiElements.conversationTitle.textContent = currentConversationTitle;
730
- history.pushState(null, '', `/chat/${currentConversationId}`);
731
- await loadConversations();
732
- }
733
  } else {
734
  const contentType = response.headers.get('Content-Type');
735
  if (contentType?.includes('application/json')) {
736
  const data = await response.json();
737
- const responseText = data.response || 'Error: No response generated.';
738
- if (streamMsg) {
739
- streamMsg.dataset.text = responseText;
740
- renderMarkdown(streamMsg);
741
- streamMsg.dataset.done = '1';
742
- }
743
- conversationHistory.push({ role: 'assistant', content: responseText });
744
- sessionStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
745
- if (checkAuth() && currentConversationId) {
746
- await saveMessageToConversation(currentConversationId, 'assistant', responseText);
747
- }
748
- if (checkAuth() && data.conversation_id) {
749
  currentConversationId = data.conversation_id;
750
  currentConversationTitle = data.conversation_title || 'Untitled Conversation';
751
  if (uiElements.conversationTitle) uiElements.conversationTitle.textContent = currentConversationTitle;
@@ -771,14 +729,23 @@ async function submitMessage() {
771
  if (uiElements.chatBox) uiElements.chatBox.scrollTop = uiElements.chatBox.scrollHeight;
772
  }
773
  }
774
- if (streamMsg) streamMsg.dataset.done = '1';
775
- conversationHistory.push({ role: 'assistant', content: buffer });
776
- sessionStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
777
- if (checkAuth() && currentConversationId) {
778
- await saveMessageToConversation(currentConversationId, 'assistant', buffer);
779
- }
780
  }
781
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
782
  finalizeRequest();
783
  } catch (error) {
784
  handleRequestError(error);
@@ -807,13 +774,13 @@ function stopStream(forceCancel = false) {
807
  }
808
 
809
  // Logout handler
810
- const logoutBtn = document.getElementById('logoutBtn');
811
  if (logoutBtn) {
812
  logoutBtn.addEventListener('click', async () => {
813
  console.log('Logout button clicked');
814
  try {
815
  const response = await fetch('/logout', {
816
- method: 'GET',
817
  credentials: 'include'
818
  });
819
  if (response.ok) {
@@ -831,59 +798,60 @@ if (logoutBtn) {
831
  });
832
  }
833
 
 
 
834
  // Settings Modal
835
  if (uiElements.settingsBtn) {
836
- uiElements.settingsBtn.addEventListener('click', () => {
837
  if (!checkAuth()) {
838
  alert('Please log in to access settings.');
839
  window.location.href = '/login';
840
  return;
841
  }
842
- uiElements.settingsModal.classList.remove('hidden');
843
- fetch('/api/settings', {
844
- headers: { 'Authorization': `Bearer ${checkAuth()}` }
845
- })
846
- .then(res => {
847
- if (!res.ok) {
848
- if (res.status === 401) {
849
- localStorage.removeItem('token');
850
- window.location.href = '/login';
851
- }
852
- throw new Error('Failed to fetch settings');
853
  }
854
- return res.json();
855
- })
856
- .then(data => {
857
- document.getElementById('display_name').value = data.user_settings.display_name || '';
858
- document.getElementById('preferred_model').value = data.user_settings.preferred_model || 'standard';
859
- document.getElementById('job_title').value = data.user_settings.job_title || '';
860
- document.getElementById('education').value = data.user_settings.education || '';
861
- document.getElementById('interests').value = data.user_settings.interests || '';
862
- document.getElementById('additional_info').value = data.user_settings.additional_info || '';
863
- document.getElementById('conversation_style').value = data.user_settings.conversation_style || 'default';
864
 
865
- const modelSelect = document.getElementById('preferred_model');
866
- modelSelect.innerHTML = '';
867
- data.available_models.forEach(model => {
868
- const option = document.createElement('option');
869
- option.value = model.alias;
870
- option.textContent = `${model.alias} - ${model.description}`;
871
- modelSelect.appendChild(option);
872
- });
873
 
874
- const styleSelect = document.getElementById('conversation_style');
875
- styleSelect.innerHTML = '';
876
- data.conversation_styles.forEach(style => {
877
- const option = document.createElement('option');
878
- option.value = style;
879
- option.textContent = style.charAt(0).toUpperCase() + style.slice(1);
880
- styleSelect.appendChild(option);
881
- });
882
- })
883
- .catch(err => {
884
- console.error('Error fetching settings:', err);
885
- alert('Failed to load settings. Please try again.');
886
  });
 
 
 
 
 
 
 
887
  });
888
  }
889
 
@@ -905,7 +873,7 @@ if (uiElements.settingsForm) {
905
  const data = Object.fromEntries(formData);
906
  fetch('/users/me', {
907
  method: 'PUT',
908
- headers: {
909
  'Content-Type': 'application/json',
910
  'Authorization': `Bearer ${checkAuth()}`
911
  },
@@ -1062,7 +1030,7 @@ if (uiElements.newConversationBtn) {
1062
 
1063
  // Debug localStorage
1064
  const originalRemoveItem = localStorage.removeItem;
1065
- localStorage.removeItem = function(key) {
1066
  console.log('Removing from localStorage:', key);
1067
  originalRemoveItem.apply(this, arguments);
1068
  };
 
39
  once: true,
40
  offset: 50,
41
  });
42
+ if(currentConversationId && checkAuth()) {
43
  console.log('Loading conversation with ID:', currentConversationId);
44
+ await loadConversation(currentConversationId);
45
  } else if (conversationHistory.length > 0) {
46
+ console.log('Restoring conversation history from sessionStorage:', conversationHistory);
47
+ enterChatView();
48
+ conversationHistory.forEach(msg => {
49
+ console.log('Adding message from history:', msg);
50
+ addMsg(msg.role, msg.content);
51
+ });
52
+ } else {
53
+ console.log('No conversation history or ID, starting fresh');
54
+ }
55
+ autoResizeTextarea();
56
+ updateSendButtonState();
57
+ if (uiElements.swipeHint) {
58
+ setTimeout(() => {
59
+ uiElements.swipeHint.style.display = 'none';
60
+ }, 3000);
61
+ }
62
+ setupTouchGestures();
63
  });
64
 
65
  // Check authentication token
 
526
  console.error('Error creating conversation:', error);
527
  alert('Failed to create new conversation. Please try again.');
528
  }
529
+ if (uiElements.chatBox) uiElements.chatBox.scrollTop = uiElements.chatBox.scrollHeight;
530
  }
531
 
532
  // Update conversation title
 
690
 
691
  try {
692
  const response = await sendRequest(endpoint, payload ? JSON.stringify(payload) : formData, headers);
693
+ let responseText = '';
694
  if (endpoint === '/api/audio-transcription') {
695
  const data = await response.json();
696
  if (!data.transcription) throw new Error('No transcription received from server');
697
+ responseText = data.transcription || 'Error: No transcription generated.';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
698
  } else if (endpoint === '/api/image-analysis') {
699
  const data = await response.json();
700
+ responseText = data.image_analysis || 'Error: No analysis generated.';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  } else {
702
  const contentType = response.headers.get('Content-Type');
703
  if (contentType?.includes('application/json')) {
704
  const data = await response.json();
705
+ responseText = data.response || 'Error: No response generated.';
706
+ if (data.conversation_id) {
 
 
 
 
 
 
 
 
 
 
707
  currentConversationId = data.conversation_id;
708
  currentConversationTitle = data.conversation_title || 'Untitled Conversation';
709
  if (uiElements.conversationTitle) uiElements.conversationTitle.textContent = currentConversationTitle;
 
729
  if (uiElements.chatBox) uiElements.chatBox.scrollTop = uiElements.chatBox.scrollHeight;
730
  }
731
  }
732
+ responseText = buffer;
 
 
 
 
 
733
  }
734
  }
735
+
736
+ if (streamMsg) {
737
+ streamMsg.dataset.text = responseText;
738
+ renderMarkdown(streamMsg);
739
+ streamMsg.dataset.done = '1';
740
+ }
741
+ conversationHistory.push({ role: 'assistant', content: responseText });
742
+ sessionStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
743
+ if (checkAuth() && currentConversationId) {
744
+ await saveMessageToConversation(currentConversationId, 'assistant', responseText);
745
+ }
746
+ if (checkAuth()) {
747
+ await loadConversations(); // تحديث السايدبار
748
+ }
749
  finalizeRequest();
750
  } catch (error) {
751
  handleRequestError(error);
 
774
  }
775
 
776
  // Logout handler
777
+ const logoutBtn = document.querySelector('#logoutBtn');
778
  if (logoutBtn) {
779
  logoutBtn.addEventListener('click', async () => {
780
  console.log('Logout button clicked');
781
  try {
782
  const response = await fetch('/logout', {
783
+ method: 'POST',
784
  credentials: 'include'
785
  });
786
  if (response.ok) {
 
798
  });
799
  }
800
 
801
+
802
+
803
  // Settings Modal
804
  if (uiElements.settingsBtn) {
805
+ uiElements.settingsBtn.addEventListener('click', async () => {
806
  if (!checkAuth()) {
807
  alert('Please log in to access settings.');
808
  window.location.href = '/login';
809
  return;
810
  }
811
+ try {
812
+ const response = await fetch('/api/settings', {
813
+ headers: { 'Authorization': `Bearer ${checkAuth()}` }
814
+ });
815
+ if (!response.ok) {
816
+ if (response.status === 401) {
817
+ localStorage.removeItem('token');
818
+ window.location.href = '/login';
 
 
 
819
  }
820
+ throw new Error('Failed to fetch settings');
821
+ }
822
+ const data = await response.json();
823
+ document.getElementById('display_name').value = data.user_settings.display_name || '';
824
+ document.getElementById('preferred_model').value = data.user_settings.preferred_model || 'standard';
825
+ document.getElementById('job_title').value = data.user_settings.job_title || '';
826
+ document.getElementById('education').value = data.user_settings.education || '';
827
+ document.getElementById('interests').value = data.user_settings.interests || '';
828
+ document.getElementById('additional_info').value = data.user_settings.additional_info || '';
829
+ document.getElementById('conversation_style').value = data.user_settings.conversation_style || 'default';
830
 
831
+ const modelSelect = document.getElementById('preferred_model');
832
+ modelSelect.innerHTML = '';
833
+ data.available_models.forEach(model => {
834
+ const option = document.createElement('option');
835
+ option.value = model.alias;
836
+ option.textContent = `${model.alias} - ${model.description}`;
837
+ modelSelect.appendChild(option);
838
+ });
839
 
840
+ const styleSelect = document.getElementById('conversation_style');
841
+ styleSelect.innerHTML = '';
842
+ data.conversation_styles.forEach(style => {
843
+ const option = document.createElement('option');
844
+ option.value = style;
845
+ option.textContent = style.charAt(0).toUpperCase() + style.slice(1);
846
+ styleSelect.appendChild(option);
 
 
 
 
 
847
  });
848
+
849
+ uiElements.settingsModal.classList.remove('hidden');
850
+ toggleSidebar(false);
851
+ } catch (err) {
852
+ console.error('Error fetching settings:', err);
853
+ alert('Failed to load settings. Please try again.');
854
+ }
855
  });
856
  }
857
 
 
873
  const data = Object.fromEntries(formData);
874
  fetch('/users/me', {
875
  method: 'PUT',
876
+ headers: {
877
  'Content-Type': 'application/json',
878
  'Authorization': `Bearer ${checkAuth()}`
879
  },
 
1030
 
1031
  // Debug localStorage
1032
  const originalRemoveItem = localStorage.removeItem;
1033
+ localStorage.removeItem = function (key) {
1034
  console.log('Removing from localStorage:', key);
1035
  originalRemoveItem.apply(this, arguments);
1036
  };
templates/chat.html CHANGED
@@ -1,10 +1,14 @@
1
  <!DOCTYPE html>
2
  <html lang="en" class="dark-theme">
 
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6
- <meta name="description" content="Chat with MGZon Chatbot, an AI-powered tool for coding, analysis, and e-commerce queries. Supports text, image, and audio inputs." />
7
- <meta name="keywords" content="MGZon Chatbot, AI chatbot, code generation, DeepSeek, Gradio, FastAPI, e-commerce, programming, Mark Al-Asfar" />
 
 
 
8
  <meta name="author" content="Mark Al-Asfar" />
9
  <meta name="robots" content="index, follow" />
10
  <title>MGZon Chatbot – AI Assistant</title>
@@ -13,26 +17,28 @@
13
  <link rel="apple-touch-icon" href="/static/images/mg.svg" />
14
  <!-- Open Graph -->
15
  <meta property="og:title" content="MGZon Chatbot – AI Assistant" />
16
- <!-- manifest for Android/Chrome -->
17
- <link rel="manifest" href="/static/manifest.json">
18
 
19
- <!-- iOS Web App Support -->
20
- <link rel="apple-touch-icon" sizes="180x180" href="/static/images/icons/mg-180.png">
21
- <meta name="apple-mobile-web-app-capable" content="yes">
22
- <meta name="apple-mobile-web-app-title" content="MGZon">
23
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
24
 
25
- <!-- General Theme -->
26
- <meta name="theme-color" content="#2d3748">
27
 
28
- <meta property="og:description" content="Chat with MGZon Chatbot for coding, analysis, and e-commerce queries with text, image, and audio support." />
 
29
  <meta property="og:image" content="/static/images/mg.svg" />
30
  <meta property="og:url" content="https://mgzon-mgzon-app.hf.space/chat" />
31
  <meta property="og:type" content="website" />
32
  <!-- Twitter Card -->
33
  <meta name="twitter:card" content="summary_large_image" />
34
  <meta name="twitter:title" content="MGZon Chatbot – AI Assistant" />
35
- <meta name="twitter:description" content="Chat with MGZon Chatbot for coding, analysis, and e-commerce queries with text, image, and audio support." />
 
36
  <meta name="twitter:image" content="/static/images/mg.svg" />
37
  <!-- JSON-LD -->
38
  <script type="application/ld+json">
@@ -46,7 +52,8 @@
46
  </script>
47
  <!-- Fonts & Styles -->
48
  <link rel="preconnect" href="https://fonts.googleapis.com" />
49
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet" />
 
50
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
51
  <script src="https://cdn.tailwindcss.com"></script>
52
  <!-- Markdown & Syntax Highlight -->
@@ -89,9 +96,11 @@
89
  <link rel="stylesheet" href="/static/css/webkit.css" />
90
  <link rel="stylesheet" href="/static/css/sidebar.css" />
91
  </head>
 
92
  <body class="min-h-screen flex flex-col bg-gradient-to-br from-gray-900 via-teal-900 to-emerald-900">
93
  <!-- Sidebar -->
94
- <aside id="sidebar" class="fixed inset-y-0 left-0 w-64 bg-gray-800/90 backdrop-blur-md transform -translate-x-full md:translate-x-0 transition-transform duration-300 ease-in-out z-50">
 
95
  <div class="flex items-center justify-between p-4 border-b border-gray-700">
96
  <div class="flex items-center">
97
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-10 h-10 mr-2 animate-pulse" />
@@ -107,7 +116,8 @@
107
  <ul class="space-y-2">
108
  <li>
109
  <a href="/" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
110
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 
111
  <path d="M3 11.5L12 4l9 7.5M5 21V11.5h14V21"></path>
112
  </svg>
113
  Home
@@ -115,50 +125,67 @@
115
  </li>
116
  <li>
117
  <a href="/about" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
118
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
119
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
 
 
120
  </svg>
121
  About
122
  </a>
123
  </li>
124
  <li>
125
  <a href="/blog" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
126
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
127
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 006 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"></path>
 
 
 
128
  </svg>
129
  Blog
130
  </a>
131
  </li>
132
  <li>
133
  <a href="/docs" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
134
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
135
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
 
 
 
136
  </svg>
137
  Docs
138
  </a>
139
  </li>
140
  {% if user %}
141
  <li>
142
- <button id="settingsBtn" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors w-full text-left">
143
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
144
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"></path>
 
 
 
 
145
  </svg>
146
  Settings
147
  </button>
148
  </li>
149
  <li>
150
- <a href="/auth/jwt/logout" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
151
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
152
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
 
 
 
153
  </svg>
154
  Logout
155
- </a>
156
  </li>
157
  {% else %}
158
  <li>
159
  <a href="/login" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
160
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
161
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
 
 
162
  </svg>
163
  Login
164
  </a>
@@ -170,14 +197,18 @@
170
  <div class="flex justify-between items-center px-2 mb-2">
171
  <h3 class="text-sm font-semibold text-white">Conversations</h3>
172
  <button id="newConversationBtn" class="text-white hover:bg-gray-700 p-1 rounded" title="New Conversation">
173
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 
174
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
175
  </svg>
176
  </button>
177
  </div>
178
- <button id="historyToggle" class="md:hidden flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors w-full text-left">
179
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
180
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
 
 
 
181
  </svg>
182
  Show History
183
  </button>
@@ -203,7 +234,8 @@
203
  </div>
204
  <div class="chat-controls flex gap-2">
205
  <button id="clearBtn" class="icon-btn" aria-label="Clear All Messages" title="Clear All Messages">
206
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
 
207
  <path d="M3 6h18" />
208
  <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
209
  <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
@@ -228,7 +260,9 @@
228
  <div class="prompts w-full max-w-md mx-auto grid gap-2">
229
  <div class="prompt-item" data-prompt="What's the capital of France?">
230
  <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
231
- <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M18.66 18.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M18.66 5.34l1.41-1.41" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
 
 
232
  <circle cx="12" cy="12" r="4" stroke-width="2" />
233
  </svg>
234
  <span>What's the capital of France?</span>
@@ -253,7 +287,8 @@
253
  </div>
254
  </div>
255
  <div id="messageLimitWarning" class="text-red-500 text-center hidden mt-4">
256
- You have reached the message limit. Please <a href="/login" class="text-blue-300 underline">login</a> to continue.
 
257
  </div>
258
  </div>
259
  <div id="chatArea" class="flex-1 hidden" aria-live="polite">
@@ -265,22 +300,27 @@
265
  <textarea id="userInput" placeholder="Ask anything..." required></textarea>
266
  <div id="rightIconGroup" class="flex gap-2">
267
  <button type="button" id="fileBtn" class="icon-btn" aria-label="Upload File" title="Upload File">
268
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
 
269
  <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
270
  <path d="M14 2v6h6" />
271
  </svg>
272
  </button>
273
  <input type="file" id="fileInput" accept="image/*,.mp3,.wav" style="display: none;" />
274
- <button type="button" id="audioBtn" class="icon-btn" aria-label="Upload Audio File" title="Upload Audio File">
275
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
 
 
276
  <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
277
  <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
278
  <path d="M12 19v4" />
279
  </svg>
280
  </button>
281
  <input type="file" id="audioInput" accept="audio/*" style="display: none;" />
282
- <button type="submit" id="sendBtn" class="icon-btn" disabled aria-label="Send or Hold to Record" title="Click to Send or Hold to Record Voice">
283
- <svg id="sendIcon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 
 
284
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7-7 7M3 12h11" />
285
  </svg>
286
  </button>
@@ -302,7 +342,8 @@
302
  <div class="flex justify-between items-center mb-4">
303
  <h2 class="text-xl font-bold text-white">User Settings</h2>
304
  <button id="closeSettingsBtn" class="text-gray-400 hover:text-white">
305
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 
306
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
307
  </svg>
308
  </button>
@@ -335,7 +376,8 @@
335
  </div>
336
  <div>
337
  <label for="additional_info" class="block text-sm text-gray-300">Additional Info</label>
338
- <textarea id="additional_info" name="additional_info" class="w-full p-2 bg-gray-700 text-white rounded"></textarea>
 
339
  </div>
340
  <div>
341
  <label for="conversation_style" class="block text-sm text-gray-300">Conversation Style</label>
@@ -358,7 +400,8 @@
358
  <div class="text-center text-xs text-gray-400 py-2">
359
  © 2025 Mark Al-Asfar & MGZon AI. All rights reserved.
360
  </div>
361
- <button id="installAppBtn" style="display: none;" class="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded shadow-lg z-50">
 
362
  📲 Install MG Chat
363
  </button>
364
  </div>
@@ -367,52 +410,53 @@
367
  <script src="/static/js/chat.js?v=1.0"></script>
368
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
369
 
370
- <script>
371
- // تهيئة مكتبة AOS
372
- AOS.init();
373
 
374
- // تمرير conversation_id و conversation_title إذا كانا موجودين
375
- {% if conversation_id %}
376
  window.conversationId = "{{ conversation_id }}";
377
  window.conversationTitle = "{{ conversation_title }}";
378
  if (window.loadConversation) {
379
  window.loadConversation("{{ conversation_id }}");
380
  }
381
- {% endif %}
382
 
383
- // تسجيل Service Worker لتفعيل ا��ـ PWA
384
- if ('serviceWorker' in navigator) {
385
- navigator.serviceWorker.register('/static/js/sw.js')
386
- .then(function(reg) {
387
- console.log('✅ Service Worker Registered', reg);
388
- }).catch(function(err) {
389
- console.error('❌ Service Worker registration failed', err);
390
- });
391
- }
392
 
393
- // التعامل مع حدث beforeinstallprompt لتثبيت التطبيق
394
- let deferredPrompt;
395
- window.addEventListener('beforeinstallprompt', (e) => {
396
- e.preventDefault();
397
- deferredPrompt = e;
398
- const installBtn = document.getElementById('installAppBtn');
399
- if (installBtn) {
400
- installBtn.style.display = 'block';
401
 
402
- installBtn.addEventListener('click', () => {
403
- deferredPrompt.prompt();
404
- deferredPrompt.userChoice.then(choice => {
405
- if (choice.outcome === 'accepted') {
406
- console.log('✅ User accepted the install prompt');
407
- } else {
408
- console.log('❌ User dismissed the install prompt');
409
- }
410
- deferredPrompt = null;
 
411
  });
412
- });
413
- }
414
- });
415
- </script>
416
 
417
  </body>
418
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en" class="dark-theme">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
+ <meta name="viewport"
7
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
8
+ <meta name="description"
9
+ content="Chat with MGZon Chatbot, an AI-powered tool for coding, analysis, and e-commerce queries. Supports text, image, and audio inputs." />
10
+ <meta name="keywords"
11
+ content="MGZon Chatbot, AI chatbot, code generation, DeepSeek, Gradio, FastAPI, e-commerce, programming, Mark Al-Asfar" />
12
  <meta name="author" content="Mark Al-Asfar" />
13
  <meta name="robots" content="index, follow" />
14
  <title>MGZon Chatbot – AI Assistant</title>
 
17
  <link rel="apple-touch-icon" href="/static/images/mg.svg" />
18
  <!-- Open Graph -->
19
  <meta property="og:title" content="MGZon Chatbot – AI Assistant" />
20
+ <!-- manifest for Android/Chrome -->
21
+ <link rel="manifest" href="/static/manifest.json">
22
 
23
+ <!-- iOS Web App Support -->
24
+ <link rel="apple-touch-icon" sizes="180x180" href="/static/images/icons/mg-180.png">
25
+ <meta name="apple-mobile-web-app-capable" content="yes">
26
+ <meta name="apple-mobile-web-app-title" content="MGZon">
27
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
28
 
29
+ <!-- General Theme -->
30
+ <meta name="theme-color" content="#2d3748">
31
 
32
+ <meta property="og:description"
33
+ content="Chat with MGZon Chatbot for coding, analysis, and e-commerce queries with text, image, and audio support." />
34
  <meta property="og:image" content="/static/images/mg.svg" />
35
  <meta property="og:url" content="https://mgzon-mgzon-app.hf.space/chat" />
36
  <meta property="og:type" content="website" />
37
  <!-- Twitter Card -->
38
  <meta name="twitter:card" content="summary_large_image" />
39
  <meta name="twitter:title" content="MGZon Chatbot – AI Assistant" />
40
+ <meta name="twitter:description"
41
+ content="Chat with MGZon Chatbot for coding, analysis, and e-commerce queries with text, image, and audio support." />
42
  <meta name="twitter:image" content="/static/images/mg.svg" />
43
  <!-- JSON-LD -->
44
  <script type="application/ld+json">
 
52
  </script>
53
  <!-- Fonts & Styles -->
54
  <link rel="preconnect" href="https://fonts.googleapis.com" />
55
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"
56
+ rel="stylesheet" />
57
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
58
  <script src="https://cdn.tailwindcss.com"></script>
59
  <!-- Markdown & Syntax Highlight -->
 
96
  <link rel="stylesheet" href="/static/css/webkit.css" />
97
  <link rel="stylesheet" href="/static/css/sidebar.css" />
98
  </head>
99
+
100
  <body class="min-h-screen flex flex-col bg-gradient-to-br from-gray-900 via-teal-900 to-emerald-900">
101
  <!-- Sidebar -->
102
+ <aside id="sidebar"
103
+ class="fixed inset-y-0 left-0 w-64 bg-gray-800/90 backdrop-blur-md transform -translate-x-full md:translate-x-0 transition-transform duration-300 ease-in-out z-50">
104
  <div class="flex items-center justify-between p-4 border-b border-gray-700">
105
  <div class="flex items-center">
106
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-10 h-10 mr-2 animate-pulse" />
 
116
  <ul class="space-y-2">
117
  <li>
118
  <a href="/" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
119
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
120
+ xmlns="http://www.w3.org/2000/svg">
121
  <path d="M3 11.5L12 4l9 7.5M5 21V11.5h14V21"></path>
122
  </svg>
123
  Home
 
125
  </li>
126
  <li>
127
  <a href="/about" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
128
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
129
+ xmlns="http://www.w3.org/2000/svg">
130
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
131
+ d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
132
  </svg>
133
  About
134
  </a>
135
  </li>
136
  <li>
137
  <a href="/blog" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
138
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
139
+ xmlns="http://www.w3.org/2000/svg">
140
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
141
+ d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 006 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25">
142
+ </path>
143
  </svg>
144
  Blog
145
  </a>
146
  </li>
147
  <li>
148
  <a href="/docs" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
149
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
150
+ xmlns="http://www.w3.org/2000/svg">
151
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
152
+ d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
153
+ </path>
154
  </svg>
155
  Docs
156
  </a>
157
  </li>
158
  {% if user %}
159
  <li>
160
+ <button id="settingsBtn"
161
+ class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors w-full text-left">
162
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
163
+ xmlns="http://www.w3.org/2000/svg">
164
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
165
+ d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4">
166
+ </path>
167
  </svg>
168
  Settings
169
  </button>
170
  </li>
171
  <li>
172
+ <button id="logoutBtn"
173
+ class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors w-full text-left">
174
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
175
+ xmlns="http://www.w3.org/2000/svg">
176
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
177
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
178
  </svg>
179
  Logout
180
+ </button>
181
  </li>
182
  {% else %}
183
  <li>
184
  <a href="/login" class="flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors">
185
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
186
+ xmlns="http://www.w3.org/2000/svg">
187
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
188
+ d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
189
  </svg>
190
  Login
191
  </a>
 
197
  <div class="flex justify-between items-center px-2 mb-2">
198
  <h3 class="text-sm font-semibold text-white">Conversations</h3>
199
  <button id="newConversationBtn" class="text-white hover:bg-gray-700 p-1 rounded" title="New Conversation">
200
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
201
+ xmlns="http://www.w3.org/2000/svg">
202
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
203
  </svg>
204
  </button>
205
  </div>
206
+ <button id="historyToggle"
207
+ class="md:hidden flex items-center text-white hover:bg-gray-700 p-2 rounded transition-colors w-full text-left">
208
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
209
+ xmlns="http://www.w3.org/2000/svg">
210
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
211
+ d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
212
  </svg>
213
  Show History
214
  </button>
 
234
  </div>
235
  <div class="chat-controls flex gap-2">
236
  <button id="clearBtn" class="icon-btn" aria-label="Clear All Messages" title="Clear All Messages">
237
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"
238
+ stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
239
  <path d="M3 6h18" />
240
  <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
241
  <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
 
260
  <div class="prompts w-full max-w-md mx-auto grid gap-2">
261
  <div class="prompt-item" data-prompt="What's the capital of France?">
262
  <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
263
+ <path
264
+ d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M18.66 18.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M18.66 5.34l1.41-1.41"
265
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
266
  <circle cx="12" cy="12" r="4" stroke-width="2" />
267
  </svg>
268
  <span>What's the capital of France?</span>
 
287
  </div>
288
  </div>
289
  <div id="messageLimitWarning" class="text-red-500 text-center hidden mt-4">
290
+ You have reached the message limit. Please <a href="/login" class="text-blue-300 underline">login</a> to
291
+ continue.
292
  </div>
293
  </div>
294
  <div id="chatArea" class="flex-1 hidden" aria-live="polite">
 
300
  <textarea id="userInput" placeholder="Ask anything..." required></textarea>
301
  <div id="rightIconGroup" class="flex gap-2">
302
  <button type="button" id="fileBtn" class="icon-btn" aria-label="Upload File" title="Upload File">
303
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
304
+ stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
305
  <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
306
  <path d="M14 2v6h6" />
307
  </svg>
308
  </button>
309
  <input type="file" id="fileInput" accept="image/*,.mp3,.wav" style="display: none;" />
310
+ <button type="button" id="audioBtn" class="icon-btn" aria-label="Upload Audio File"
311
+ title="Upload Audio File">
312
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
313
+ stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
314
  <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
315
  <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
316
  <path d="M12 19v4" />
317
  </svg>
318
  </button>
319
  <input type="file" id="audioInput" accept="audio/*" style="display: none;" />
320
+ <button type="submit" id="sendBtn" class="icon-btn" disabled aria-label="Send or Hold to Record"
321
+ title="Click to Send or Hold to Record Voice">
322
+ <svg id="sendIcon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
323
+ stroke="currentColor">
324
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7-7 7M3 12h11" />
325
  </svg>
326
  </button>
 
342
  <div class="flex justify-between items-center mb-4">
343
  <h2 class="text-xl font-bold text-white">User Settings</h2>
344
  <button id="closeSettingsBtn" class="text-gray-400 hover:text-white">
345
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
346
+ xmlns="http://www.w3.org/2000/svg">
347
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
348
  </svg>
349
  </button>
 
376
  </div>
377
  <div>
378
  <label for="additional_info" class="block text-sm text-gray-300">Additional Info</label>
379
+ <textarea id="additional_info" name="additional_info"
380
+ class="w-full p-2 bg-gray-700 text-white rounded"></textarea>
381
  </div>
382
  <div>
383
  <label for="conversation_style" class="block text-sm text-gray-300">Conversation Style</label>
 
400
  <div class="text-center text-xs text-gray-400 py-2">
401
  © 2025 Mark Al-Asfar & MGZon AI. All rights reserved.
402
  </div>
403
+ <button id="installAppBtn" style="display: none;"
404
+ class="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded shadow-lg z-50">
405
  📲 Install MG Chat
406
  </button>
407
  </div>
 
410
  <script src="/static/js/chat.js?v=1.0"></script>
411
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
412
 
413
+ <script>
414
+ // تهيئة مكتبة AOS
415
+ AOS.init();
416
 
417
+ // تمرير conversation_id و conversation_title إذا كانا موجودين
418
+ {% if conversation_id %}
419
  window.conversationId = "{{ conversation_id }}";
420
  window.conversationTitle = "{{ conversation_title }}";
421
  if (window.loadConversation) {
422
  window.loadConversation("{{ conversation_id }}");
423
  }
424
+ {% endif %}
425
 
426
+ // تسجيل Service Worker لتفعيل الـ PWA
427
+ if ('serviceWorker' in navigator) {
428
+ navigator.serviceWorker.register('/static/js/sw.js')
429
+ .then(function (reg) {
430
+ console.log('✅ Service Worker Registered', reg);
431
+ }).catch(function (err) {
432
+ console.error('❌ Service Worker registration failed', err);
433
+ });
434
+ }
435
 
436
+ // التعامل مع حدث beforeinstallprompt لتثبيت التطبيق
437
+ let deferredPrompt;
438
+ window.addEventListener('beforeinstallprompt', (e) => {
439
+ e.preventDefault();
440
+ deferredPrompt = e;
441
+ const installBtn = document.getElementById('installAppBtn');
442
+ if (installBtn) {
443
+ installBtn.style.display = 'block';
444
 
445
+ installBtn.addEventListener('click', () => {
446
+ deferredPrompt.prompt();
447
+ deferredPrompt.userChoice.then(choice => {
448
+ if (choice.outcome === 'accepted') {
449
+ console.log('✅ User accepted the install prompt');
450
+ } else {
451
+ console.log('❌ User dismissed the install prompt');
452
+ }
453
+ deferredPrompt = null;
454
+ });
455
  });
456
+ }
457
+ });
458
+ </script>
 
459
 
460
  </body>
461
+
462
+ </html>
templates/login.html CHANGED
@@ -1,9 +1,11 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <meta name="description" content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools. Sign in with email, Google, or GitHub.">
 
7
  <meta name="keywords" content="MGZon Chatbot, login, AI assistant, code generation, e-commerce, Mark Al-Asfar">
8
  <meta name="author" content="Mark Al-Asfar">
9
  <meta name="robots" content="index, follow">
@@ -20,14 +22,16 @@
20
  <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
21
  <!-- Open Graph -->
22
  <meta property="og:title" content="Login - MGZon Chatbot">
23
- <meta property="og:description" content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools.">
 
24
  <meta property="og:image" content="/static/images/mg.svg">
25
  <meta property="og:url" content="https://mgzon-mgzon-app.hf.space/login">
26
  <meta property="og:type" content="website">
27
  <!-- Twitter Card -->
28
  <meta name="twitter:card" content="summary_large_image">
29
  <meta name="twitter:title" content="Login - MGZon Chatbot">
30
- <meta name="twitter:description" content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools.">
 
31
  <meta name="twitter:image" content="/static/images/mg.svg">
32
  <!-- JSON-LD -->
33
  <script type="application/ld+json">
@@ -51,16 +55,26 @@
51
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/css/boxicons.min.css" rel="stylesheet">
52
  <style>
53
  @keyframes gradientShift {
54
- 0% { background-position: 0% 50%; }
55
- 50% { background-position: 100% 50%; }
56
- 100% { background-position: 0% 50%; }
 
 
 
 
 
 
 
 
57
  }
 
58
  body {
59
  background: linear-gradient(135deg, #0f172a, #0e7490, #065f46, #064e3b);
60
  background-size: 400% 400%;
61
  animation: gradientShift 15s ease infinite;
62
  font-family: system-ui, sans-serif;
63
  }
 
64
  .glass {
65
  background: rgba(255, 255, 255, 0.07);
66
  border-radius: 1rem;
@@ -68,6 +82,7 @@
68
  backdrop-filter: blur(12px);
69
  -webkit-backdrop-filter: blur(12px);
70
  }
 
71
  .loading {
72
  display: inline-block;
73
  width: 1rem;
@@ -78,9 +93,13 @@
78
  animation: spin 0.8s linear infinite;
79
  margin-left: 0.5rem;
80
  }
 
81
  @keyframes spin {
82
- to { transform: rotate(360deg); }
 
 
83
  }
 
84
  .glass:hover {
85
  transform: scale(1.05);
86
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
@@ -88,6 +107,7 @@
88
  }
89
  </style>
90
  </head>
 
91
  <body class="text-white min-h-screen flex flex-col justify-center items-center">
92
  <div class="container max-w-md mx-auto text-center py-12">
93
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-32 h-32 mx-auto mb-6 animate-bounce">
@@ -96,22 +116,30 @@
96
  </h1>
97
  <div class="glass p-8">
98
  <form id="loginForm" action="/auth/jwt/login" method="POST" class="flex flex-col gap-4">
99
- <input type="email" name="username" id="email" placeholder="Email" class="p-3 rounded-lg bg-gray-800/60 text-white border border-gray-700 focus:outline-none focus:ring-2 focus:ring-emerald-500" required>
100
- <input type="password" name="password" id="password" placeholder="Password" class="p-3 rounded-lg bg-gray-800/60 text-white border border-gray-700 focus:outline-none focus:ring-2 focus:ring-emerald-500" required>
101
- <button type="submit" id="loginBtn" class="bg-gradient-to-r from-emerald-500 to-teal-600 text-white px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
 
 
 
 
 
102
  Login <i class="bx bx-log-in ml-2"></i>
103
  <span id="spinner" class="loading hidden"></span>
104
  </button>
105
  </form>
106
  <div class="flex justify-center gap-4 mt-4 flex-wrap">
107
- <button id="googleLoginBtn" class="inline-flex items-center bg-gradient-to-r from-white to-gray-200 text-gray-800 px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
 
108
  Login with Google <i class="bx bxl-google ml-2"></i>
109
  </button>
110
- <button id="githubLoginBtn" class="inline-flex items-center bg-gradient-to-r from-gray-800 to-black text-white px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
 
111
  Login with GitHub <i class="bx bxl-github ml-2"></i>
112
  </button>
113
  </div>
114
- <p class="mt-4">Don't have an account? <a href="/register" class="text-emerald-300 hover:underline">Register</a></p>
 
115
  <p id="errorMsg" class="text-red-500 mt-4 hidden"></p>
116
  </div>
117
  </div>
@@ -119,8 +147,10 @@
119
  <div class="container max-w-6xl mx-auto text-center">
120
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-24 h-24 mx-auto mb-6 animate-pulse">
121
  <p class="mb-4">
122
- Developed by <a href="https://mark-elasfar.web.app/" target="_blank" class="text-emerald-300 hover:underline">Mark Al-Asfar</a>
123
- | Powered by <a href="https://hager-zon.vercel.app/" target="_blank" class="text-emerald-300 hover:underline">MGZon AI</a>
 
 
124
  </p>
125
  <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
126
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('email')">
@@ -129,7 +159,8 @@
129
  <p><a href="mailto:[email protected]" class="text-emerald-300 hover:underline">[email protected]</a></p>
130
  <div id="email-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
131
  <p>Reach out to our support team for any inquiries or assistance.</p>
132
- <button onclick="closeCardDetails('email')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
133
  </div>
134
  </div>
135
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('phone')">
@@ -138,7 +169,8 @@
138
  <p>+1-800-123-4567</p>
139
  <div id="phone-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
140
  <p>Contact our support team via phone for immediate assistance.</p>
141
- <button onclick="closeCardDetails('phone')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
142
  </div>
143
  </div>
144
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('community')">
@@ -147,7 +179,8 @@
147
  <p><a href="https://hager-zon.vercel.app/community" class="text-emerald-300 hover:underline">Join us</a></p>
148
  <div id="community-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
149
  <p>Join our vibrant community to share ideas and collaborate.</p>
150
- <button onclick="closeCardDetails('community')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
151
  </div>
152
  </div>
153
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('api-docs')">
@@ -156,16 +189,19 @@
156
  <p><a href="/docs" class="text-emerald-300 hover:underline">Explore Docs</a></p>
157
  <div id="api-docs-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
158
  <p>Explore our API documentation for seamless integration.</p>
159
- <button onclick="closeCardDetails('api-docs')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
160
  </div>
161
  </div>
162
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('faq')">
163
  <i class="bx bx-help-circle text-3xl text-emerald-300 mb-2"></i>
164
  <h4 class="font-semibold mb-1">FAQ</h4>
165
- <p><a href="https://hager-zon.vercel.app/faq" target="_blank" class="text-emerald-300 hover:underline">Read FAQ</a></p>
 
166
  <div id="faq-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
167
  <p>Find answers to common questions in our FAQ section.</p>
168
- <button onclick="closeCardDetails('faq')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
169
  </div>
170
  </div>
171
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('docs')">
@@ -174,198 +210,203 @@
174
  <p><a href="/docs" class="text-emerald-300 hover:underline">Full Docs</a></p>
175
  <div id="docs-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
176
  <p>Access comprehensive documentation for MGZon Chatbot.</p>
177
- <button onclick="closeCardDetails('docs')" class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
 
178
  </div>
179
  </div>
180
  </div>
181
  <div class="flex justify-center gap-6 mt-6">
182
- <a href="https://github.com/Mark-Lasfar/MGZon" class="text-2xl text-white hover:text-emerald-300 transition"><i class="bx bxl-github"></i></a>
183
- <a href="https://x.com/MGZon" class="text-2xl text-white hover:text-emerald-300 transition"><i class="bx bxl-twitter"></i></a>
184
- <a href="https://www.facebook.com/people/Mark-Al-Asfar/pfbid02GMisUQ8AqWkNZjoKtWFHH1tbdHuVscN1cjcFnZWy9HkRaAsmanBfT6mhySAyqpg4l/" class="text-2xl text-white hover:text-emerald-300 transition"><i class="bx bxl-facebook"></i></a>
 
 
 
185
  </div>
186
  <p class="mt-6">© 2025 Mark Al-Asfar & MGZon AI. All rights reserved.</p>
187
  </div>
188
- <button id="installAppBtn" style="display: none;" class="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded shadow-lg z-50">
 
189
  📲 Install MG Chat
190
  </button>
191
  </footer>
192
-
193
  <script>
194
- console.log('Login page script loaded'); // Debugging: Ensure script is running
195
 
196
- const loginForm = document.getElementById('loginForm');
197
- const loginBtn = document.getElementById('loginBtn');
198
- const spinner = document.getElementById('spinner');
199
- const errorMsg = document.getElementById('errorMsg');
200
- const googleLoginBtn = document.getElementById('googleLoginBtn');
201
- const githubLoginBtn = document.getElementById('githubLoginBtn');
202
 
203
- // Check authentication status on page load
204
- async function checkAuthStatus() {
205
- try {
206
- const response = await fetch('/api/check-auth', {
207
- method: 'GET',
208
- credentials: 'include',
209
- headers: { 'Accept': 'application/json' }
210
- });
211
- const data = await response.json();
212
- if (data.is_authenticated) {
213
- console.log('User is authenticated, redirecting to /chat');
214
- window.location.href = '/chat';
 
 
 
215
  }
216
- } catch (error) {
217
- console.error('Error checking auth status:', error);
218
  }
219
- }
220
 
221
- // Handle email/password login
222
- loginForm.addEventListener('submit', async (e) => {
223
- e.preventDefault();
224
- console.log('Login form submitted'); // Debugging
225
- spinner.classList.remove('hidden');
226
- errorMsg.classList.add('hidden');
227
- const formData = new FormData(loginForm);
228
- try {
229
- const response = await fetch('/auth/jwt/login', {
230
- method: 'POST',
231
- body: formData
232
- });
233
- spinner.classList.add('hidden');
234
- if (response.ok) {
235
- console.log('Login successful, redirecting to /chat'); // Debugging
236
- window.location.href = '/chat';
237
- } else {
238
- const error = await response.json();
239
- errorMsg.textContent = error.detail || 'Login failed. Please try again.';
 
 
 
 
 
 
240
  errorMsg.classList.remove('hidden');
241
- console.error('Login failed:', error); // Debugging
242
  }
243
- } catch (error) {
244
- spinner.classList.add('hidden');
245
- errorMsg.textContent = 'An error occurred. Please try again.';
246
- errorMsg.classList.remove('hidden');
247
- console.error('Error during login:', error); // Debugging
248
- }
249
- });
250
 
251
- // Handle Google OAuth login
252
- googleLoginBtn.addEventListener('click', async (e) => {
253
- e.preventDefault();
254
- console.log('Google login button clicked'); // Debugging
255
- spinner.classList.remove('hidden');
256
- errorMsg.classList.add('hidden');
257
- try {
258
- const response = await fetch('https://mgzon-mgzon-app.hf.space/auth/google/authorize', {
259
- method: 'GET',
260
- headers: { 'Accept': 'application/json' }
261
- });
262
- console.log('Google authorize response:', response); // Debugging
263
- if (!response.ok) {
264
- throw new Error(`HTTP error! Status: ${response.status}`);
265
- }
266
- const data = await response.json();
267
- console.log('Google authorize data:', data); // Debugging
268
- if (data.authorization_url) {
269
- console.log('Redirecting to Google:', data.authorization_url); // Debugging
270
- window.location.href = data.authorization_url;
271
- } else {
272
- throw new Error('No authorization URL received');
 
 
 
 
273
  }
274
- } catch (error) {
275
- spinner.classList.add('hidden');
276
- errorMsg.textContent = 'Failed to initiate Google login. Please try again.';
277
- errorMsg.classList.remove('hidden');
278
- console.error('Error initiating Google login:', error); // Debugging
279
- }
280
- });
281
 
282
- // Handle GitHub OAuth login
283
- githubLoginBtn.addEventListener('click', async (e) => {
284
- e.preventDefault();
285
- console.log('GitHub login button clicked'); // Debugging
286
- spinner.classList.remove('hidden');
287
- errorMsg.classList.add('hidden');
288
- try {
289
- const response = await fetch('https://mgzon-mgzon-app.hf.space/auth/github/authorize', {
290
- method: 'GET',
291
- headers: { 'Accept': 'application/json' }
292
- });
293
- console.log('GitHub authorize response:', response); // Debugging
294
- if (!response.ok) {
295
- throw new Error(`HTTP error! Status: ${response.status}`);
 
 
 
 
 
 
 
 
 
 
 
 
296
  }
297
- const data = await response.json();
298
- console.log('GitHub authorize data:', data); // Debugging
299
- if (data.authorization_url) {
300
- console.log('Redirecting to GitHub:', data.authorization_url); // Debugging
301
- window.location.href = data.authorization_url;
302
- } else {
303
- throw new Error('No authorization URL received');
 
 
 
 
 
 
 
 
304
  }
305
- } catch (error) {
306
- spinner.classList.add('hidden');
307
- errorMsg.textContent = 'Failed to initiate GitHub login. Please try again.';
308
- errorMsg.classList.remove('hidden');
309
- console.error('Error initiating GitHub login:', error); // Debugging
310
- }
311
- });
312
 
313
- // Check for error query parameters on page load (for OAuth errors)
314
- window.addEventListener('load', () => {
315
- console.log('Page loaded, checking auth status');
316
- checkAuthStatus();
317
- const urlParams = new URLSearchParams(window.location.search);
318
- const error = urlParams.get('error');
319
- if (error) {
320
- errorMsg.textContent = decodeURIComponent(error);
321
- errorMsg.classList.remove('hidden');
322
- console.error('OAuth error from URL:', error); // Debugging
323
  }
324
- });
325
 
326
- // Handle card details toggle
327
- function showCardDetails(cardId) {
328
- console.log('Showing card details:', cardId); // Debugging
329
- document.getElementById(`${cardId}-details`).classList.remove('hidden');
330
- }
331
-
332
- function closeCardDetails(cardId) {
333
- console.log('Closing card details:', cardId); // Debugging
334
- document.getElementById(`${cardId}-details`).classList.add('hidden');
335
- }
336
 
337
- // Service Worker for PWA
338
- if ('serviceWorker' in navigator) {
339
- console.log('Registering service worker'); // Debugging
340
- navigator.serviceWorker.register('/static/js/sw.js')
341
- .then(reg => console.log('✅ Service Worker Registered', reg))
342
- .catch(err => console.error('❌ Service Worker registration failed', err));
343
- }
344
 
345
- // Handle PWA install prompt
346
- let deferredPrompt;
347
- window.addEventListener('beforeinstallprompt', (e) => {
348
- console.log('Before install prompt triggered'); // Debugging
349
- e.preventDefault();
350
- deferredPrompt = e;
351
- const installBtn = document.getElementById('installAppBtn');
352
- if (installBtn) {
353
- installBtn.style.display = 'block';
354
- installBtn.addEventListener('click', () => {
355
- console.log('Install button clicked'); // Debugging
356
- deferredPrompt.prompt();
357
- deferredPrompt.userChoice.then(choice => {
358
- if (choice.outcome === 'accepted') {
359
- console.log('✅ User accepted the install prompt');
360
- } else {
361
- console.log('❌ User dismissed the install prompt');
362
- }
363
- deferredPrompt = null;
 
364
  });
365
- });
366
- }
367
- });
368
- </script>
369
-
370
  </body>
 
371
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <meta name="description"
8
+ content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools. Sign in with email, Google, or GitHub.">
9
  <meta name="keywords" content="MGZon Chatbot, login, AI assistant, code generation, e-commerce, Mark Al-Asfar">
10
  <meta name="author" content="Mark Al-Asfar">
11
  <meta name="robots" content="index, follow">
 
22
  <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
23
  <!-- Open Graph -->
24
  <meta property="og:title" content="Login - MGZon Chatbot">
25
+ <meta property="og:description"
26
+ content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools.">
27
  <meta property="og:image" content="/static/images/mg.svg">
28
  <meta property="og:url" content="https://mgzon-mgzon-app.hf.space/login">
29
  <meta property="og:type" content="website">
30
  <!-- Twitter Card -->
31
  <meta name="twitter:card" content="summary_large_image">
32
  <meta name="twitter:title" content="Login - MGZon Chatbot">
33
+ <meta name="twitter:description"
34
+ content="Login to MGZon Chatbot to access AI-powered code generation and e-commerce tools.">
35
  <meta name="twitter:image" content="/static/images/mg.svg">
36
  <!-- JSON-LD -->
37
  <script type="application/ld+json">
 
55
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/css/boxicons.min.css" rel="stylesheet">
56
  <style>
57
  @keyframes gradientShift {
58
+ 0% {
59
+ background-position: 0% 50%;
60
+ }
61
+
62
+ 50% {
63
+ background-position: 100% 50%;
64
+ }
65
+
66
+ 100% {
67
+ background-position: 0% 50%;
68
+ }
69
  }
70
+
71
  body {
72
  background: linear-gradient(135deg, #0f172a, #0e7490, #065f46, #064e3b);
73
  background-size: 400% 400%;
74
  animation: gradientShift 15s ease infinite;
75
  font-family: system-ui, sans-serif;
76
  }
77
+
78
  .glass {
79
  background: rgba(255, 255, 255, 0.07);
80
  border-radius: 1rem;
 
82
  backdrop-filter: blur(12px);
83
  -webkit-backdrop-filter: blur(12px);
84
  }
85
+
86
  .loading {
87
  display: inline-block;
88
  width: 1rem;
 
93
  animation: spin 0.8s linear infinite;
94
  margin-left: 0.5rem;
95
  }
96
+
97
  @keyframes spin {
98
+ to {
99
+ transform: rotate(360deg);
100
+ }
101
  }
102
+
103
  .glass:hover {
104
  transform: scale(1.05);
105
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
 
107
  }
108
  </style>
109
  </head>
110
+
111
  <body class="text-white min-h-screen flex flex-col justify-center items-center">
112
  <div class="container max-w-md mx-auto text-center py-12">
113
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-32 h-32 mx-auto mb-6 animate-bounce">
 
116
  </h1>
117
  <div class="glass p-8">
118
  <form id="loginForm" action="/auth/jwt/login" method="POST" class="flex flex-col gap-4">
119
+ <input type="email" name="username" id="email" placeholder="Email"
120
+ class="p-3 rounded-lg bg-gray-800/60 text-white border border-gray-700 focus:outline-none focus:ring-2 focus:ring-emerald-500"
121
+ required>
122
+ <input type="password" name="password" id="password" placeholder="Password"
123
+ class="p-3 rounded-lg bg-gray-800/60 text-white border border-gray-700 focus:outline-none focus:ring-2 focus:ring-emerald-500"
124
+ required>
125
+ <button type="submit" id="loginBtn"
126
+ class="bg-gradient-to-r from-emerald-500 to-teal-600 text-white px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
127
  Login <i class="bx bx-log-in ml-2"></i>
128
  <span id="spinner" class="loading hidden"></span>
129
  </button>
130
  </form>
131
  <div class="flex justify-center gap-4 mt-4 flex-wrap">
132
+ <button id="googleLoginBtn"
133
+ class="inline-flex items-center bg-gradient-to-r from-white to-gray-200 text-gray-800 px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
134
  Login with Google <i class="bx bxl-google ml-2"></i>
135
  </button>
136
+ <button id="githubLoginBtn"
137
+ class="inline-flex items-center bg-gradient-to-r from-gray-800 to-black text-white px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
138
  Login with GitHub <i class="bx bxl-github ml-2"></i>
139
  </button>
140
  </div>
141
+ <p class="mt-4">Don't have an account? <a href="/register" class="text-emerald-300 hover:underline">Register</a>
142
+ </p>
143
  <p id="errorMsg" class="text-red-500 mt-4 hidden"></p>
144
  </div>
145
  </div>
 
147
  <div class="container max-w-6xl mx-auto text-center">
148
  <img src="/static/images/mg.svg" alt="MGZon Logo" class="w-24 h-24 mx-auto mb-6 animate-pulse">
149
  <p class="mb-4">
150
+ Developed by <a href="https://mark-elasfar.web.app/" target="_blank"
151
+ class="text-emerald-300 hover:underline">Mark Al-Asfar</a>
152
+ | Powered by <a href="https://hager-zon.vercel.app/" target="_blank"
153
+ class="text-emerald-300 hover:underline">MGZon AI</a>
154
  </p>
155
  <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
156
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('email')">
 
159
  <p><a href="mailto:[email protected]" class="text-emerald-300 hover:underline">[email protected]</a></p>
160
  <div id="email-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
161
  <p>Reach out to our support team for any inquiries or assistance.</p>
162
+ <button onclick="closeCardDetails('email')"
163
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
164
  </div>
165
  </div>
166
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('phone')">
 
169
  <p>+1-800-123-4567</p>
170
  <div id="phone-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
171
  <p>Contact our support team via phone for immediate assistance.</p>
172
+ <button onclick="closeCardDetails('phone')"
173
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
174
  </div>
175
  </div>
176
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('community')">
 
179
  <p><a href="https://hager-zon.vercel.app/community" class="text-emerald-300 hover:underline">Join us</a></p>
180
  <div id="community-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
181
  <p>Join our vibrant community to share ideas and collaborate.</p>
182
+ <button onclick="closeCardDetails('community')"
183
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
184
  </div>
185
  </div>
186
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('api-docs')">
 
189
  <p><a href="/docs" class="text-emerald-300 hover:underline">Explore Docs</a></p>
190
  <div id="api-docs-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
191
  <p>Explore our API documentation for seamless integration.</p>
192
+ <button onclick="closeCardDetails('api-docs')"
193
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
194
  </div>
195
  </div>
196
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('faq')">
197
  <i class="bx bx-help-circle text-3xl text-emerald-300 mb-2"></i>
198
  <h4 class="font-semibold mb-1">FAQ</h4>
199
+ <p><a href="https://hager-zon.vercel.app/faq" target="_blank" class="text-emerald-300 hover:underline">Read
200
+ FAQ</a></p>
201
  <div id="faq-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
202
  <p>Find answers to common questions in our FAQ section.</p>
203
+ <button onclick="closeCardDetails('faq')"
204
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
205
  </div>
206
  </div>
207
  <div class="glass p-4 cursor-pointer" onclick="showCardDetails('docs')">
 
210
  <p><a href="/docs" class="text-emerald-300 hover:underline">Full Docs</a></p>
211
  <div id="docs-details" class="hidden mt-4 p-4 bg-gray-700/80 rounded-lg">
212
  <p>Access comprehensive documentation for MGZon Chatbot.</p>
213
+ <button onclick="closeCardDetails('docs')"
214
+ class="bg-emerald-500 text-white px-4 py-2 rounded-lg mt-2">Close</button>
215
  </div>
216
  </div>
217
  </div>
218
  <div class="flex justify-center gap-6 mt-6">
219
+ <a href="https://github.com/Mark-Lasfar/MGZon" class="text-2xl text-white hover:text-emerald-300 transition"><i
220
+ class="bx bxl-github"></i></a>
221
+ <a href="https://x.com/MGZon" class="text-2xl text-white hover:text-emerald-300 transition"><i
222
+ class="bx bxl-twitter"></i></a>
223
+ <a href="https://www.facebook.com/people/Mark-Al-Asfar/pfbid02GMisUQ8AqWkNZjoKtWFHH1tbdHuVscN1cjcFnZWy9HkRaAsmanBfT6mhySAyqpg4l/"
224
+ class="text-2xl text-white hover:text-emerald-300 transition"><i class="bx bxl-facebook"></i></a>
225
  </div>
226
  <p class="mt-6">© 2025 Mark Al-Asfar & MGZon AI. All rights reserved.</p>
227
  </div>
228
+ <button id="installAppBtn" style="display: none;"
229
+ class="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded shadow-lg z-50">
230
  📲 Install MG Chat
231
  </button>
232
  </footer>
233
+
234
  <script>
235
+ console.log('Login page script loaded'); // Debugging: Ensure script is running
236
 
237
+ const loginForm = document.getElementById('loginForm');
238
+ const loginBtn = document.getElementById('loginBtn');
239
+ const spinner = document.getElementById('spinner');
240
+ const errorMsg = document.getElementById('errorMsg');
241
+ const googleLoginBtn = document.getElementById('googleLoginBtn');
242
+ const githubLoginBtn = document.getElementById('githubLoginBtn');
243
 
244
+ // Check authentication status on page load
245
+ async function checkAuthStatus() {
246
+ try {
247
+ const response = await fetch('/api/check-auth', {
248
+ method: 'GET',
249
+ credentials: 'include',
250
+ headers: { 'Accept': 'application/json' }
251
+ });
252
+ const data = await response.json();
253
+ if (data.is_authenticated) {
254
+ console.log('User is authenticated, redirecting to /chat');
255
+ window.location.href = '/chat';
256
+ }
257
+ } catch (error) {
258
+ console.error('Error checking auth status:', error);
259
  }
 
 
260
  }
 
261
 
262
+ // Handle email/password login
263
+ loginForm.addEventListener('submit', async (e) => {
264
+ e.preventDefault();
265
+ console.log('Login form submitted'); // Debugging
266
+ spinner.classList.remove('hidden');
267
+ errorMsg.classList.add('hidden');
268
+ const formData = new FormData(loginForm);
269
+ try {
270
+ const response = await fetch('/auth/jwt/login', {
271
+ method: 'POST',
272
+ body: formData
273
+ });
274
+ spinner.classList.add('hidden');
275
+ if (response.ok) {
276
+ console.log('Login successful, redirecting to /chat'); // Debugging
277
+ window.location.href = '/chat';
278
+ } else {
279
+ const error = await response.json();
280
+ errorMsg.textContent = error.detail || 'Login failed. Please try again.';
281
+ errorMsg.classList.remove('hidden');
282
+ console.error('Login failed:', error); // Debugging
283
+ }
284
+ } catch (error) {
285
+ spinner.classList.add('hidden');
286
+ errorMsg.textContent = 'An error occurred. Please try again.';
287
  errorMsg.classList.remove('hidden');
288
+ console.error('Error during login:', error); // Debugging
289
  }
290
+ });
 
 
 
 
 
 
291
 
292
+ // Handle Google OAuth login
293
+ googleLoginBtn.addEventListener('click', async (e) => {
294
+ e.preventDefault();
295
+ console.log('Google login button clicked');
296
+ spinner.classList.remove('hidden');
297
+ errorMsg.classList.add('hidden');
298
+ try {
299
+ const response = await fetch('https://mgzon-mgzon-app.hf.space/auth/google/authorize', {
300
+ method: 'GET',
301
+ headers: { 'Accept': 'application/json' }
302
+ });
303
+ if (!response.ok) {
304
+ throw new Error(`HTTP error! Status: ${response.status}`);
305
+ }
306
+ const data = await response.json();
307
+ if (data.authorization_url) {
308
+ console.log('Redirecting to Google:', data.authorization_url);
309
+ window.location.href = data.authorization_url;
310
+ } else {
311
+ throw new Error('No authorization URL received');
312
+ }
313
+ } catch (error) {
314
+ spinner.classList.add('hidden');
315
+ errorMsg.textContent = 'Failed to initiate Google login. Please try again.';
316
+ errorMsg.classList.remove('hidden');
317
+ console.error('Error initiating Google login:', error);
318
  }
319
+ });
 
 
 
 
 
 
320
 
321
+ // Handle GitHub OAuth login
322
+ githubLoginBtn.addEventListener('click', async (e) => {
323
+ e.preventDefault();
324
+ console.log('GitHub login button clicked');
325
+ spinner.classList.remove('hidden');
326
+ errorMsg.classList.add('hidden');
327
+ try {
328
+ const response = await fetch('https://mgzon-mgzon-app.hf.space/auth/github/authorize', {
329
+ method: 'GET',
330
+ headers: { 'Accept': 'application/json' }
331
+ });
332
+ if (!response.ok) {
333
+ throw new Error(`HTTP error! Status: ${response.status}`);
334
+ }
335
+ const data = await response.json();
336
+ if (data.authorization_url) {
337
+ console.log('Redirecting to GitHub:', data.authorization_url);
338
+ window.location.href = data.authorization_url;
339
+ } else {
340
+ throw new Error('No authorization URL received');
341
+ }
342
+ } catch (error) {
343
+ spinner.classList.add('hidden');
344
+ errorMsg.textContent = 'Failed to initiate GitHub login. Please try again.';
345
+ errorMsg.classList.remove('hidden');
346
+ console.error('Error initiating GitHub login:', error);
347
  }
348
+ });
349
+
350
+ // Check for error query parameters on page load (for OAuth errors)
351
+ window.addEventListener('load', () => {
352
+ console.log('Page loaded, checking for OAuth callback');
353
+ const urlParams = new URLSearchParams(window.location.search);
354
+ const error = urlParams.get('error');
355
+ if (error) {
356
+ errorMsg.textContent = decodeURIComponent(error);
357
+ errorMsg.classList.remove('hidden');
358
+ console.error('OAuth error from URL:', error);
359
+ } else if (urlParams.get('code')) {
360
+ // If there's a 'code' parameter, it means OAuth login was successful
361
+ console.log('OAuth login successful, redirecting to /chat');
362
+ window.location.href = '/chat';
363
  }
364
+ });
 
 
 
 
 
 
365
 
366
+ // Handle card details toggle
367
+ function showCardDetails(cardId) {
368
+ console.log('Showing card details:', cardId); // Debugging
369
+ document.getElementById(`${cardId}-details`).classList.remove('hidden');
 
 
 
 
 
 
370
  }
 
371
 
372
+ function closeCardDetails(cardId) {
373
+ console.log('Closing card details:', cardId); // Debugging
374
+ document.getElementById(`${cardId}-details`).classList.add('hidden');
375
+ }
 
 
 
 
 
 
376
 
377
+ // Service Worker for PWA
378
+ if ('serviceWorker' in navigator) {
379
+ console.log('Registering service worker'); // Debugging
380
+ navigator.serviceWorker.register('/static/js/sw.js')
381
+ .then(reg => console.log('✅ Service Worker Registered', reg))
382
+ .catch(err => console.error('❌ Service Worker registration failed', err));
383
+ }
384
 
385
+ // Handle PWA install prompt
386
+ let deferredPrompt;
387
+ window.addEventListener('beforeinstallprompt', (e) => {
388
+ console.log('Before install prompt triggered'); // Debugging
389
+ e.preventDefault();
390
+ deferredPrompt = e;
391
+ const installBtn = document.getElementById('installAppBtn');
392
+ if (installBtn) {
393
+ installBtn.style.display = 'block';
394
+ installBtn.addEventListener('click', () => {
395
+ console.log('Install button clicked'); // Debugging
396
+ deferredPrompt.prompt();
397
+ deferredPrompt.userChoice.then(choice => {
398
+ if (choice.outcome === 'accepted') {
399
+ console.log('✅ User accepted the install prompt');
400
+ } else {
401
+ console.log('❌ User dismissed the install prompt');
402
+ }
403
+ deferredPrompt = null;
404
+ });
405
  });
406
+ }
407
+ });
408
+ </script>
409
+
 
410
  </body>
411
+
412
  </html>