Paramjit Singh commited on
Commit
6aef189
·
unverified ·
2 Parent(s): 8a8e4a623f1745

Merge pull request #214 from Exodus2004/feat/db-uuid-postgres

Browse files
Files changed (2) hide show
  1. backend/app/auth.py +4 -4
  2. backend/app/models.py +48 -8
backend/app/auth.py CHANGED
@@ -30,10 +30,10 @@ def verify_password(plain: str, hashed: str) -> bool:
30
 
31
  # ── JWT Token ────────────────────────────────────────
32
 
33
- def create_access_token(user_id: str) -> str:
34
  """Create a JWT access token with user_id as the subject."""
35
  payload = {
36
- "sub": user_id,
37
  "type": "access",
38
  "exp": datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_EXPIRY_MINUTES),
39
  "iat": datetime.now(timezone.utc),
@@ -41,10 +41,10 @@ def create_access_token(user_id: str) -> str:
41
  return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
42
 
43
 
44
- def create_refresh_token(user_id: str) -> str:
45
  """Create a JWT refresh token with user_id as the subject."""
46
  payload = {
47
- "sub": user_id,
48
  "type": "refresh",
49
  "exp": datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_EXPIRY_DAYS),
50
  "iat": datetime.now(timezone.utc),
 
30
 
31
  # ── JWT Token ────────────────────────────────────────
32
 
33
+ def create_access_token(user_id) -> str:
34
  """Create a JWT access token with user_id as the subject."""
35
  payload = {
36
+ "sub": str(user_id),
37
  "type": "access",
38
  "exp": datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_EXPIRY_MINUTES),
39
  "iat": datetime.now(timezone.utc),
 
41
  return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
42
 
43
 
44
+ def create_refresh_token(user_id) -> str:
45
  """Create a JWT refresh token with user_id as the subject."""
46
  payload = {
47
+ "sub": str(user_id),
48
  "type": "refresh",
49
  "exp": datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_EXPIRY_DAYS),
50
  "iat": datetime.now(timezone.utc),
backend/app/models.py CHANGED
@@ -8,6 +8,9 @@ import hashlib
8
  from datetime import datetime, timezone
9
 
10
  from cryptography.fernet import Fernet
 
 
 
11
  from sqlalchemy import Column, String, Integer, DateTime, ForeignKey, Text, Boolean, Enum as SQLAlchemyEnum
12
  from sqlalchemy.types import TypeDecorator
13
  from sqlalchemy.orm import relationship
@@ -15,6 +18,38 @@ from sqlalchemy.orm import relationship
15
  from app.database import Base
16
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  class EncryptedString(TypeDecorator):
19
  """
20
  A custom SQLAlchemy type that transparently encrypts strings in the database
@@ -72,7 +107,7 @@ class User(Base):
72
  """
73
  __tablename__ = "users"
74
 
75
- id = Column(String, primary_key=True, default=generate_uuid)
76
  username = Column(String(80), unique=True, nullable=False, index=True)
77
  email = Column(String(120), unique=True, nullable=False, index=True)
78
  hashed_password = Column(String(255), nullable=False)
@@ -102,8 +137,8 @@ class ApiKey(Base):
102
  """
103
  __tablename__ = "api_keys"
104
 
105
- id = Column(String, primary_key=True, default=generate_uuid)
106
- user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
107
  key_prefix = Column(String(10), nullable=False)
108
  hashed_key = Column(String(255), nullable=False, unique=True, index=True)
109
  created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
@@ -119,6 +154,11 @@ class Document(Base):
119
  """
120
  __tablename__ = "documents"
121
 
 
 
 
 
 
122
  id = Column(String, primary_key=True, default=generate_uuid)
123
  user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
124
  filename = Column(String(255), nullable=False) # Internal UUID-based filename
@@ -142,9 +182,9 @@ class ChatMessage(Base):
142
  """
143
  __tablename__ = "chat_messages"
144
 
145
- id = Column(String, primary_key=True, default=generate_uuid)
146
- user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
147
- document_id = Column(String, ForeignKey("documents.id"), nullable=True, index=True)
148
  role = Column(String(20), nullable=False) # "user" | "assistant"
149
  content = Column(Text, nullable=False)
150
  sources_json = Column(Text, nullable=True) # JSON representation of retrieved sources
@@ -162,8 +202,8 @@ class SharedMessage(Base):
162
  """
163
  __tablename__ = "shared_messages"
164
 
165
- id = Column(String, primary_key=True, default=generate_uuid)
166
- message_id = Column(String, ForeignKey("chat_messages.id"), nullable=False, unique=True, index=True)
167
  created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
168
 
169
  # Relationships
 
8
  from datetime import datetime, timezone
9
 
10
  from cryptography.fernet import Fernet
11
+ from sqlalchemy import Column, String, Integer, DateTime, ForeignKey, Text, Boolean
12
+ from sqlalchemy.types import TypeDecorator, CHAR
13
+ from sqlalchemy.dialects.postgresql import UUID as PG_UUID
14
  from sqlalchemy import Column, String, Integer, DateTime, ForeignKey, Text, Boolean, Enum as SQLAlchemyEnum
15
  from sqlalchemy.types import TypeDecorator
16
  from sqlalchemy.orm import relationship
 
18
  from app.database import Base
19
 
20
 
21
+ class GUID(TypeDecorator):
22
+ """Platform-independent GUID type.
23
+ Uses PostgreSQL's UUID type, otherwise uses CHAR(36).
24
+ """
25
+ impl = CHAR
26
+ cache_ok = True
27
+
28
+ def load_dialect_impl(self, dialect):
29
+ if dialect.name == 'postgresql':
30
+ return dialect.type_descriptor(PG_UUID(as_uuid=True))
31
+ else:
32
+ return dialect.type_descriptor(CHAR(36))
33
+
34
+ def process_bind_param(self, value, dialect):
35
+ if value is None:
36
+ return value
37
+ if isinstance(value, uuid.UUID):
38
+ return value if dialect.name == 'postgresql' else str(value)
39
+ try:
40
+ val_uuid = uuid.UUID(value)
41
+ return val_uuid if dialect.name == 'postgresql' else str(val_uuid)
42
+ except ValueError:
43
+ if dialect.name == 'postgresql':
44
+ return uuid.UUID(int=0)
45
+ return value
46
+
47
+ def process_result_value(self, value, dialect):
48
+ if value is None:
49
+ return value
50
+ return str(value)
51
+
52
+
53
  class EncryptedString(TypeDecorator):
54
  """
55
  A custom SQLAlchemy type that transparently encrypts strings in the database
 
107
  """
108
  __tablename__ = "users"
109
 
110
+ id = Column(GUID, primary_key=True, default=uuid.uuid4)
111
  username = Column(String(80), unique=True, nullable=False, index=True)
112
  email = Column(String(120), unique=True, nullable=False, index=True)
113
  hashed_password = Column(String(255), nullable=False)
 
137
  """
138
  __tablename__ = "api_keys"
139
 
140
+ id = Column(GUID, primary_key=True, default=uuid.uuid4)
141
+ user_id = Column(GUID, ForeignKey("users.id"), nullable=False, index=True)
142
  key_prefix = Column(String(10), nullable=False)
143
  hashed_key = Column(String(255), nullable=False, unique=True, index=True)
144
  created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
 
154
  """
155
  __tablename__ = "documents"
156
 
157
+ id = Column(GUID, primary_key=True, default=uuid.uuid4)
158
+ user_id = Column(GUID, ForeignKey("users.id"), nullable=False, index=True)
159
+ filename = Column(String(255), nullable=False) # Stored filename (UUID-based)
160
+ original_name = Column(String(255), nullable=False) # User's original filename
161
+ file_size = Column(Integer, default=0) # Size in bytes
162
  id = Column(String, primary_key=True, default=generate_uuid)
163
  user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
164
  filename = Column(String(255), nullable=False) # Internal UUID-based filename
 
182
  """
183
  __tablename__ = "chat_messages"
184
 
185
+ id = Column(GUID, primary_key=True, default=uuid.uuid4)
186
+ user_id = Column(GUID, ForeignKey("users.id"), nullable=False, index=True)
187
+ document_id = Column(GUID, ForeignKey("documents.id"), nullable=True, index=True)
188
  role = Column(String(20), nullable=False) # "user" | "assistant"
189
  content = Column(Text, nullable=False)
190
  sources_json = Column(Text, nullable=True) # JSON representation of retrieved sources
 
202
  """
203
  __tablename__ = "shared_messages"
204
 
205
+ id = Column(GUID, primary_key=True, default=uuid.uuid4)
206
+ message_id = Column(GUID, ForeignKey("chat_messages.id"), nullable=False, unique=True, index=True)
207
  created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
208
 
209
  # Relationships