Mark-Lasfar commited on
Commit
0e85bcd
·
1 Parent(s): 766ef88

Fix ChunkedIteratorResult in SQLAlchemyUserDatabase and toggleBtn null error

Browse files
Files changed (5) hide show
  1. api/auth.py +77 -8
  2. api/database.py +8 -1
  3. main.py +8 -1
  4. requirements.txt +6 -5
  5. static/js/scripts.js +8 -3
api/auth.py CHANGED
@@ -4,12 +4,13 @@ from fastapi_users.db import SQLAlchemyUserDatabase
4
  from httpx_oauth.clients.google import GoogleOAuth2
5
  from httpx_oauth.clients.github import GitHubOAuth2
6
  from api.database import User, OAuthAccount, get_user_db
7
- from api.models import UserRead, UserCreate, UserUpdate # إضافة الاستيرادات
8
  from fastapi_users.manager import BaseUserManager, IntegerIDMixin
9
  from fastapi import Depends, Request, FastAPI
10
  from sqlalchemy.ext.asyncio import AsyncSession
 
11
  from fastapi_users.models import UP
12
- from typing import Optional
13
  import os
14
  import logging
15
 
@@ -51,10 +52,73 @@ if not all([GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLI
51
  google_oauth_client = GoogleOAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
52
  github_oauth_client = GitHubOAuth2(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
55
  reset_password_token_secret = SECRET
56
  verification_token_secret = SECRET
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  async def oauth_callback(
59
  self,
60
  oauth_name: str,
@@ -68,6 +132,7 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
68
  associate_by_email: bool = False,
69
  is_verified_by_default: bool = False,
70
  ) -> UP:
 
71
  oauth_account_dict = {
72
  "oauth_name": oauth_name,
73
  "access_token": access_token,
@@ -77,15 +142,17 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
77
  "refresh_token": refresh_token,
78
  }
79
  oauth_account = OAuthAccount(**oauth_account_dict)
80
- existing_oauth_account = await self.user_db.get_by_oauth_account(oauth_name, account_id)
81
  if existing_oauth_account is not None:
 
82
  return await self.on_after_login(existing_oauth_account.user, request)
83
 
84
  if associate_by_email:
85
  user = await self.user_db.get_by_email(account_email)
86
  if user is not None:
87
  oauth_account.user_id = user.id
88
- await self.user_db.add_oauth_account(oauth_account)
 
89
  return await self.on_after_login(user, request)
90
 
91
  user_dict = {
@@ -96,13 +163,15 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
96
  }
97
  user = await self.user_db.create(user_dict)
98
  oauth_account.user_id = user.id
99
- await self.user_db.add_oauth_account(oauth_account)
 
100
  return await self.on_after_login(user, request)
101
 
102
- async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
103
- yield UserManager(user_db)
104
 
105
- from fastapi_users.router.oauth import get_oauth_router
 
106
 
107
  google_oauth_router = get_oauth_router(
108
  google_oauth_client,
 
4
  from httpx_oauth.clients.google import GoogleOAuth2
5
  from httpx_oauth.clients.github import GitHubOAuth2
6
  from api.database import User, OAuthAccount, get_user_db
7
+ from api.models import UserRead, UserCreate, UserUpdate
8
  from fastapi_users.manager import BaseUserManager, IntegerIDMixin
9
  from fastapi import Depends, Request, FastAPI
10
  from sqlalchemy.ext.asyncio import AsyncSession
11
+ from sqlalchemy import select
12
  from fastapi_users.models import UP
13
+ from typing import Optional, Dict, Any
14
  import os
15
  import logging
16
 
 
52
  google_oauth_client = GoogleOAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
53
  github_oauth_client = GitHubOAuth2(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)
54
 
55
+ class CustomSQLAlchemyUserDatabase(SQLAlchemyUserDatabase):
56
+ async def get_by_email(self, email: str) -> Optional[User]:
57
+ """Override to fix ChunkedIteratorResult issue for get_by_email"""
58
+ logger.info(f"Checking for user with email: {email}")
59
+ try:
60
+ statement = select(self.user_table).where(self.user_table.email == email)
61
+ result = await self.session.execute(statement)
62
+ user = result.scalar_one_or_none()
63
+ if user:
64
+ logger.info(f"Found user with email: {email}")
65
+ else:
66
+ logger.info(f"No user found with email: {email}")
67
+ return user
68
+ except Exception as e:
69
+ logger.error(f"Error in get_by_email: {e}")
70
+ raise
71
+
72
+ async def create(self, create_dict: Dict[str, Any]) -> User:
73
+ """Override to fix potential async issues in create"""
74
+ logger.info(f"Creating user with email: {create_dict.get('email')}")
75
+ try:
76
+ user = self.user_table(**create_dict)
77
+ self.session.add(user)
78
+ await self.session.commit()
79
+ await self.session.refresh(user)
80
+ logger.info(f"Created user with email: {create_dict.get('email')}")
81
+ return user
82
+ except Exception as e:
83
+ logger.error(f"Error creating user: {e}")
84
+ await self.session.rollback()
85
+ raise
86
+
87
  class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
88
  reset_password_token_secret = SECRET
89
  verification_token_secret = SECRET
90
 
91
+ async def get_by_oauth_account(self, oauth_name: str, account_id: str):
92
+ """Override to fix ChunkedIteratorResult issue in SQLAlchemy 2.0+"""
93
+ logger.info(f"Checking for existing OAuth account: {oauth_name}/{account_id}")
94
+ try:
95
+ statement = select(OAuthAccount).where(
96
+ OAuthAccount.oauth_name == oauth_name, OAuthAccount.account_id == account_id
97
+ )
98
+ result = await self.session.execute(statement)
99
+ oauth_account = result.scalar_one_or_none()
100
+ if oauth_account:
101
+ logger.info(f"Found existing OAuth account for {account_id}")
102
+ else:
103
+ logger.info(f"No existing OAuth account found for {account_id}")
104
+ return oauth_account
105
+ except Exception as e:
106
+ logger.error(f"Error in get_by_oauth_account: {e}")
107
+ raise
108
+
109
+ async def add_oauth_account(self, oauth_account: OAuthAccount):
110
+ """Override to fix potential async issues"""
111
+ logger.info(f"Adding OAuth account for user {oauth_account.user_id}")
112
+ try:
113
+ self.session.add(oauth_account)
114
+ await self.session.commit()
115
+ await self.session.refresh(oauth_account)
116
+ logger.info(f"Successfully added OAuth account for user {oauth_account.user_id}")
117
+ except Exception as e:
118
+ logger.error(f"Error adding OAuth account: {e}")
119
+ await self.session.rollback()
120
+ raise
121
+
122
  async def oauth_callback(
123
  self,
124
  oauth_name: str,
 
132
  associate_by_email: bool = False,
133
  is_verified_by_default: bool = False,
134
  ) -> UP:
135
+ logger.info(f"OAuth callback for {oauth_name} with account_id {account_id}")
136
  oauth_account_dict = {
137
  "oauth_name": oauth_name,
138
  "access_token": access_token,
 
142
  "refresh_token": refresh_token,
143
  }
144
  oauth_account = OAuthAccount(**oauth_account_dict)
145
+ existing_oauth_account = await self.get_by_oauth_account(oauth_name, account_id)
146
  if existing_oauth_account is not None:
147
+ logger.info(f"Existing account found, logging in user {existing_oauth_account.user.email}")
148
  return await self.on_after_login(existing_oauth_account.user, request)
149
 
150
  if associate_by_email:
151
  user = await self.user_db.get_by_email(account_email)
152
  if user is not None:
153
  oauth_account.user_id = user.id
154
+ await self.add_oauth_account(oauth_account)
155
+ logger.info(f"Associated with existing user {user.email}")
156
  return await self.on_after_login(user, request)
157
 
158
  user_dict = {
 
163
  }
164
  user = await self.user_db.create(user_dict)
165
  oauth_account.user_id = user.id
166
+ await self.add_oauth_account(oauth_account)
167
+ logger.info(f"Created new user {user.email}")
168
  return await self.on_after_login(user, request)
169
 
170
+ async def get_user_db(session: AsyncSession = Depends(get_db)):
171
+ yield CustomSQLAlchemyUserDatabase(session, User, OAuthAccount)
172
 
173
+ async def get_user_manager(user_db: CustomSQLAlchemyUserDatabase = Depends(get_user_db)):
174
+ yield UserManager(user_db)
175
 
176
  google_oauth_router = get_oauth_router(
177
  google_oauth_client,
api/database.py CHANGED
@@ -8,7 +8,9 @@ from sqlalchemy.orm import Session
8
  from typing import AsyncGenerator
9
  from fastapi import Depends
10
  from datetime import datetime
 
11
 
 
12
  # جلب URL قاعدة البيانات من المتغيرات البيئية
13
  SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL")
14
  if not SQLALCHEMY_DATABASE_URL:
@@ -84,4 +86,9 @@ async def get_user_db(session: Session = Depends(get_db)):
84
  yield SQLAlchemyUserDatabase(session, User, OAuthAccount)
85
 
86
  # إنشاء الجداول
87
- Base.metadata.create_all(bind=engine)
 
 
 
 
 
 
8
  from typing import AsyncGenerator
9
  from fastapi import Depends
10
  from datetime import datetime
11
+ import logging
12
 
13
+ logger = logging.getLogger(__name__)
14
  # جلب URL قاعدة البيانات من المتغيرات البيئية
15
  SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL")
16
  if not SQLALCHEMY_DATABASE_URL:
 
86
  yield SQLAlchemyUserDatabase(session, User, OAuthAccount)
87
 
88
  # إنشاء الجداول
89
+ try:
90
+ Base.metadata.create_all(bind=engine)
91
+ logger.info("Database tables created successfully")
92
+ except Exception as e:
93
+ logger.error(f"Error creating database tables: {e}")
94
+ raise
main.py CHANGED
@@ -127,6 +127,7 @@ async def debug_routes():
127
  return "\n".join(sorted(routes))
128
 
129
  # Custom middleware for 404 and 500 errors
 
130
  class NotFoundMiddleware(BaseHTTPMiddleware):
131
  async def dispatch(self, request: Request, call_next):
132
  try:
@@ -137,7 +138,13 @@ class NotFoundMiddleware(BaseHTTPMiddleware):
137
  return response
138
  except Exception as e:
139
  logger.exception(f"Error processing request {request.url}: {e}")
140
- return templates.TemplateResponse("500.html", {"request": request, "error": str(e)}, status_code=500)
 
 
 
 
 
 
141
 
142
  app.add_middleware(NotFoundMiddleware)
143
 
 
127
  return "\n".join(sorted(routes))
128
 
129
  # Custom middleware for 404 and 500 errors
130
+ # في main.py، استبدل NotFoundMiddleware ب:
131
  class NotFoundMiddleware(BaseHTTPMiddleware):
132
  async def dispatch(self, request: Request, call_next):
133
  try:
 
138
  return response
139
  except Exception as e:
140
  logger.exception(f"Error processing request {request.url}: {e}")
141
+ if "ChunkedIteratorResult" in str(e):
142
+ logger.error("ChunkedIteratorResult error detected - check SQLAlchemy async execution")
143
+ return templates.TemplateResponse(
144
+ "500.html",
145
+ {"request": request, "error": str(e)},
146
+ status_code=500
147
+ )
148
 
149
  app.add_middleware(NotFoundMiddleware)
150
 
requirements.txt CHANGED
@@ -2,13 +2,13 @@ fastapi==0.115.2
2
  packaging>=23.0
3
  uvicorn==0.30.6
4
  gradio>=4.44.1
5
- openai==1.42.0
6
  httpx==0.27.0
7
  python-dotenv==1.0.1
8
  pydocstyle==6.3.0
9
  requests==2.32.5
10
  beautifulsoup4==4.12.3
11
- tenacity==8.5.0
12
  selenium==4.25.0
13
  webdriver-manager==4.0.2
14
  jinja2==3.1.4
@@ -25,15 +25,16 @@ Pillow==10.4.0
25
  urllib3==2.0.7
26
  itsdangerous
27
  protobuf==3.19.6
28
- fastapi-users[sqlalchemy,oauth]>=13.0.0
29
 
 
30
  sqlalchemy>=2.0.0
31
  python-jose[cryptography]>=3.3.0
32
  passlib[bcrypt]>=1.7.4
33
- httpx-oauth
34
- python-multipart
35
  aiofiles
36
  motor
37
  redis
38
  markdown2
 
39
 
 
2
  packaging>=23.0
3
  uvicorn==0.30.6
4
  gradio>=4.44.1
5
+ openai==1.51.2
6
  httpx==0.27.0
7
  python-dotenv==1.0.1
8
  pydocstyle==6.3.0
9
  requests==2.32.5
10
  beautifulsoup4==4.12.3
11
+ tenacity==9.0.0
12
  selenium==4.25.0
13
  webdriver-manager==4.0.2
14
  jinja2==3.1.4
 
25
  urllib3==2.0.7
26
  itsdangerous
27
  protobuf==3.19.6
 
28
 
29
+ fastapi-users[sqlalchemy,oauth2]==14.0.0
30
  sqlalchemy>=2.0.0
31
  python-jose[cryptography]>=3.3.0
32
  passlib[bcrypt]>=1.7.4
33
+ httpx-oauth==0.15.3
34
+ python-multipart==0.0.12
35
  aiofiles
36
  motor
37
  redis
38
  markdown2
39
+ pymongo==4.10.1
40
 
static/js/scripts.js CHANGED
@@ -33,7 +33,12 @@ document.addEventListener('DOMContentLoaded', () => {
33
  const sidebar = document.querySelector('.sidebar');
34
  const toggleBtn = document.querySelector('.sidebar-toggle');
35
 
36
- toggleBtn.addEventListener('click', () => {
37
- sidebar.classList.toggle('active');
38
- });
 
 
 
 
 
39
  });
 
33
  const sidebar = document.querySelector('.sidebar');
34
  const toggleBtn = document.querySelector('.sidebar-toggle');
35
 
36
+ if (toggleBtn && sidebar) {
37
+ toggleBtn.addEventListener('click', () => {
38
+ sidebar.classList.toggle('active');
39
+ console.log('Sidebar toggled'); // Debugging
40
+ });
41
+ } else {
42
+ console.warn('Sidebar or toggle button not found');
43
+ }
44
  });