feat: add PAT storage and API access fields

This commit is contained in:
Anish Sarkar 2026-06-19 20:26:28 +05:30
parent 7b981b6d16
commit 4463990ca4
4 changed files with 136 additions and 0 deletions

View file

@ -919,6 +919,10 @@ class Config:
REFRESH_TOKEN_LIFETIME_SECONDS = int(
os.getenv("REFRESH_TOKEN_LIFETIME_SECONDS", str(14 * 24 * 60 * 60)) # 2 weeks
)
_PAT_MAX_EXPIRY_DAYS = os.getenv("PAT_MAX_EXPIRY_DAYS", "").strip()
PAT_MAX_EXPIRY_DAYS = (
int(_PAT_MAX_EXPIRY_DAYS) if _PAT_MAX_EXPIRY_DAYS else None
)
# ETL Service
ETL_SERVICE = os.getenv("ETL_SERVICE")

View file

@ -368,6 +368,9 @@ class Permission(StrEnum):
SETTINGS_UPDATE = "settings:update"
SETTINGS_DELETE = "settings:delete" # Delete the entire search space
# API Access
API_ACCESS_MANAGE = "api_access:manage"
# Public Sharing
PUBLIC_SHARING_VIEW = "public_sharing:view"
PUBLIC_SHARING_CREATE = "public_sharing:create"
@ -1693,6 +1696,9 @@ class SearchSpace(BaseModel, TimestampMixin):
citations_enabled = Column(
Boolean, nullable=False, default=True
) # Enable/disable citations
api_access_enabled = Column(
Boolean, nullable=False, default=False, server_default="false"
)
qna_custom_instructions = Column(
Text, nullable=True, default=""
) # User's custom instructions
@ -2330,6 +2336,11 @@ if config.AUTH_TYPE == "GOOGLE":
back_populates="user",
cascade="all, delete-orphan",
)
personal_access_tokens = relationship(
"PersonalAccessToken",
back_populates="user",
cascade="all, delete-orphan",
)
else:
@ -2462,6 +2473,11 @@ else:
back_populates="user",
cascade="all, delete-orphan",
)
personal_access_tokens = relationship(
"PersonalAccessToken",
back_populates="user",
cascade="all, delete-orphan",
)
class AgentActionLog(BaseModel):
@ -2712,6 +2728,36 @@ class RefreshToken(Base, TimestampMixin):
return not self.is_expired and not self.is_revoked
class PersonalAccessToken(BaseModel, TimestampMixin):
"""
Stores hashed Personal Access Tokens for programmatic API access.
Plaintext tokens are shown once on creation and are never persisted.
"""
__tablename__ = "personal_access_tokens"
user_id = Column(
UUID(as_uuid=True),
ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
user = relationship("User", back_populates="personal_access_tokens")
token_hash = Column(String(64), unique=True, nullable=False, index=True)
token_prefix = Column(String(16), nullable=False)
label = Column(String, nullable=False)
expires_at = Column(TIMESTAMP(timezone=True), nullable=True, index=True)
last_used_at = Column(TIMESTAMP(timezone=True), nullable=True)
@property
def is_expired(self) -> bool:
return self.expires_at is not None and datetime.now(UTC) >= self.expires_at
@property
def is_valid(self) -> bool:
return not self.is_expired
# Register model packages that live outside this file so their classes
# are present in Base.metadata before configure_mappers() resolves any
# string-based relationship() references.