File size: 5,140 Bytes
8e8c6a4 ced5eff 8e8c6a4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from .db import Base
class User(Base):
"""
Stores user information from Firebase or OTP authentication.
"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
name = Column(String, nullable=True)
picture = Column(String, nullable=True)
# Auth method: 'firebase' or 'otp'
auth_method = Column(String, default='firebase')
# Firebase-specific
firebase_uid = Column(String, unique=True, index=True, nullable=True)
# OTP-specific
email_verified = Column(Boolean, default=False)
created_at = Column(
DateTime(timezone=True),
server_default=func.now(),
)
# Relationship to extraction records (explicitly specify user_id as the foreign key)
# Note: primaryjoin must be specified because ExtractionRecord has multiple foreign keys to User
extractions = relationship(
"ExtractionRecord",
back_populates="user",
primaryjoin="User.id == ExtractionRecord.user_id"
)
# Relationship to API keys (newly added for API key authentication)
api_keys = relationship(
"APIKey",
back_populates="user",
cascade="all, delete-orphan"
)
class ExtractionRecord(Base):
"""
Stores one extraction run so the History page can show past jobs.
We'll fill it from the /api/extract endpoint later.
"""
__tablename__ = "extractions"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
file_name = Column(String, index=True)
file_type = Column(String)
file_size = Column(String)
status = Column(String) # "completed" | "failed"
confidence = Column(Float) # overall confidence (0–100)
fields_extracted = Column(Integer) # number of fields extracted
total_time_ms = Column(Integer) # total processing time in ms
raw_output = Column(Text) # JSON string from the model
file_base64 = Column(Text, nullable=True) # Base64 encoded original file for preview
error_message = Column(Text, nullable=True)
created_at = Column(
DateTime(timezone=True),
server_default=func.now(),
)
# Relationship to user (explicitly specify user_id as the foreign key)
# Note: primaryjoin must be specified because ExtractionRecord has multiple foreign keys to User
user = relationship(
"User",
back_populates="extractions",
primaryjoin="ExtractionRecord.user_id == User.id"
)
# Track if this extraction was shared (original extraction ID)
shared_from_extraction_id = Column(Integer, ForeignKey("extractions.id"), nullable=True, index=True)
shared_by_user_id = Column(Integer, ForeignKey("users.id"), nullable=True, index=True)
class ShareToken(Base):
"""
Stores share tokens for sharing extractions with other users.
"""
__tablename__ = "share_tokens"
id = Column(Integer, primary_key=True, index=True)
token = Column(String, unique=True, index=True, nullable=False) # Unique share token
extraction_id = Column(Integer, ForeignKey("extractions.id"), nullable=False, index=True)
sender_user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
recipient_email = Column(String, nullable=True, index=True) # Nullable for public share links
expires_at = Column(DateTime(timezone=True), nullable=True) # Optional expiration
accessed = Column(Boolean, default=False) # Track if link was accessed
accessed_at = Column(DateTime(timezone=True), nullable=True)
accessed_by_user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
created_at = Column(
DateTime(timezone=True),
server_default=func.now(),
)
class APIKey(Base):
"""
Stores API keys for external application authentication.
API keys are hashed before storage for security.
"""
__tablename__ = "api_keys"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
name = Column(String, nullable=False) # User-friendly name for the API key
key_hash = Column(String, unique=True, index=True, nullable=False) # Hashed API key
key_prefix = Column(String, nullable=False) # First 8 chars of key for display (e.g., "sk_live_")
is_active = Column(Boolean, default=True, nullable=False)
last_used_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(
DateTime(timezone=True),
server_default=func.now(),
)
# Relationship to user
user = relationship(
"User",
back_populates="api_keys"
)
|