mgbam commited on
Commit
3e53907
·
verified ·
1 Parent(s): fd4c462

Update services/auth.py

Browse files
Files changed (1) hide show
  1. services/auth.py +65 -12
services/auth.py CHANGED
@@ -1,9 +1,12 @@
 
 
1
  from passlib.context import CryptContext
2
  from sqlmodel import Session, select
3
  from typing import Optional
4
 
 
5
  from models.user import User, UserCreate
6
- from models.db import get_session_context # Using context manager for direct use
7
  from services.logger import app_logger
8
 
9
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -19,28 +22,78 @@ def get_user(db: Session, username: str) -> Optional[User]:
19
  user = db.exec(statement).first()
20
  return user
21
 
22
- # In services/auth.py
23
  def create_user_in_db(user_data: UserCreate) -> Optional[User]:
24
  hashed_password = get_password_hash(user_data.password)
 
 
25
  db_user = User(
26
  username=user_data.username,
27
  hashed_password=hashed_password,
28
  email=user_data.email,
29
- full_name=user_data.full_name
30
- # The 'chat_sessions' attribute is an empty list by default here, not yet loaded from DB
31
  )
 
 
 
32
  try:
33
- with get_session_context() as db: # Session starts here
34
- existing_user = get_user(db, user_data.username)
35
- if existing_user:
 
36
  app_logger.warning(f"User {user_data.username} already exists.")
37
  return None
38
 
39
  db.add(db_user)
40
- # db.commit() # Commit is handled by context manager
41
- db.refresh(db_user) # This makes sure all attributes are up-to-date from DB *within this session*
42
- # Session closes here. db_user is now "detached".
43
- return db_user # Returning the detached db_user object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  except Exception as e:
45
  app_logger.error(f"Error creating user {user_data.username}: {e}")
46
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # services/auth.py
2
+
3
  from passlib.context import CryptContext
4
  from sqlmodel import Session, select
5
  from typing import Optional
6
 
7
+ # Import User directly to avoid potential circularity with models.__init__ if not careful
8
  from models.user import User, UserCreate
9
+ from models.db import get_session_context
10
  from services.logger import app_logger
11
 
12
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
 
22
  user = db.exec(statement).first()
23
  return user
24
 
 
25
  def create_user_in_db(user_data: UserCreate) -> Optional[User]:
26
  hashed_password = get_password_hash(user_data.password)
27
+
28
+ # Create the Python object
29
  db_user = User(
30
  username=user_data.username,
31
  hashed_password=hashed_password,
32
  email=user_data.email,
33
+ full_name=user_data.full_name,
34
+ chat_sessions=[] # Initialize relationships if they are expected to be non-None
35
  )
36
+
37
+ created_user_id: Optional[int] = None
38
+
39
  try:
40
+ with get_session_context() as db:
41
+ # Check if user already exists
42
+ existing_user_check = db.exec(select(User).where(User.username == user_data.username)).first()
43
+ if existing_user_check:
44
  app_logger.warning(f"User {user_data.username} already exists.")
45
  return None
46
 
47
  db.add(db_user)
48
+ db.commit() # Commit to save the user and generate ID
49
+ db.refresh(db_user) # Refresh to get all attributes, especially ID
50
+ created_user_id = db_user.id # Store the ID
51
+
52
+ # Explicitly load the username to be safe, though it should be loaded.
53
+ # This is more of a "just in case" step.
54
+ _ = db_user.username
55
+ _ = db_user.email # if you plan to use it immediately after
56
+
57
+ # After session closes, db_user is detached.
58
+ # If we need to return the full User object, and it might be used in a way
59
+ # that requires re-attaching or accessing lazy-loaded fields, this can be an issue.
60
+ # For simple display like username, it should be fine.
61
+ # The error suggests something is trying to "refresh" it.
62
+
63
+ # To be absolutely safe for the immediate use case (displaying username),
64
+ # we can fetch a "clean" version of the user by ID in a new, short-lived session,
65
+ # though this is usually overkill for just displaying a username.
66
+ # This ensures the returned object is "fresh" but also detached.
67
+ if created_user_id is not None:
68
+ with get_session_context() as db:
69
+ # Re-fetch the user to ensure it's a clean, detached object
70
+ # with its basic attributes loaded.
71
+ user_to_return = db.get(User, created_user_id)
72
+ if user_to_return:
73
+ # Access attributes needed immediately to ensure they are loaded
74
+ # before the session closes again.
75
+ _ = user_to_return.username
76
+ _ = user_to_return.email
77
+ return user_to_return # This user object is now detached.
78
+ return None # Should not happen if creation was successful
79
+
80
  except Exception as e:
81
  app_logger.error(f"Error creating user {user_data.username}: {e}")
82
+ return None
83
+
84
+ def authenticate_user(username: str, password: str) -> Optional[User]:
85
+ with get_session_context() as db:
86
+ user = get_user(db, username)
87
+ if not user:
88
+ return None
89
+ if not verify_password(password, user.hashed_password):
90
+ return None
91
+ # Before returning, ensure any attributes needed by the caller are loaded
92
+ # if they might be lazy-loaded and the user object will be used after this session.
93
+ # For st.session_state.authenticated_user, this is important.
94
+ _ = user.id
95
+ _ = user.username
96
+ _ = user.email # etc.
97
+ # For relationships, you might need to configure eager loading (e.g. selectinload)
98
+ # or accept that they will be lazy-loaded (requiring a session) or not accessible.
99
+ return user