feat: apikey alias/renewal + markdown/patch authorship
- APIKey.alias (unique, required). Creating with an existing alias renews that key: same key string kept, validity reset to 15d, reactivated, name/roles updated (response has renewed=true). - get_actor(): X-API-Key -> key alias, Bearer -> 'admin'. - markdown & patch create/update record author / created_at / updated_at / last_modified_by from the actor. - Idempotent run_migrations() (information_schema-guarded ALTERs + backfill) so existing tables/data gain the new columns on startup; create_all still covers fresh DBs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,9 @@ class APIKey(Base):
|
||||
__tablename__ = 'apikey'
|
||||
|
||||
key = Column(String(64), primary_key=True)
|
||||
# Stable human identity of the key. Unique; creating with an existing
|
||||
# alias is treated as a renewal of that key (see api/apikey).
|
||||
alias = Column(String(255), nullable=False, unique=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(UTC))
|
||||
last_used_at = Column(DateTime)
|
||||
@@ -16,6 +19,7 @@ class APIKey(Base):
|
||||
def to_dict(self):
|
||||
return {
|
||||
"key": self.key,
|
||||
"alias": self.alias,
|
||||
"name": self.name,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"last_used_at": self.last_used_at.isoformat() if self.last_used_at else None,
|
||||
|
||||
@@ -9,7 +9,15 @@ class Markdown(Base):
|
||||
id = Column(Integer, primary_key=True)
|
||||
title = Column(String(255), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now(datetime.UTC))
|
||||
created_at = Column(DateTime, default=lambda: datetime.datetime.now(datetime.UTC))
|
||||
updated_at = Column(
|
||||
DateTime,
|
||||
default=lambda: datetime.datetime.now(datetime.UTC),
|
||||
onupdate=lambda: datetime.datetime.now(datetime.UTC),
|
||||
)
|
||||
# Actor strings: alias of the API key, or 'admin' for KC-logged-in UI.
|
||||
author = Column(String(255), nullable=True)
|
||||
last_modified_by = Column(String(255), nullable=True)
|
||||
path_id = Column(Integer, ForeignKey('path.id'), nullable=False)
|
||||
order = Column(String(36), default=lambda: str(uuid.uuid4()))
|
||||
shortcut = Column(String(36), default="")
|
||||
@@ -21,6 +29,9 @@ class Markdown(Base):
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'created_at': self.created_at,
|
||||
'updated_at': self.updated_at,
|
||||
'author': self.author,
|
||||
'last_modified_by': self.last_modified_by,
|
||||
'path_id': self.path_id,
|
||||
'order': self.order,
|
||||
'shortcut': self.shortcut,
|
||||
|
||||
@@ -17,6 +17,9 @@ class MarkdownPatch(Base):
|
||||
)
|
||||
title = Column(String(255), nullable=True)
|
||||
content = Column(Text, nullable=False)
|
||||
# Actor strings: alias of the API key, or 'admin' for KC-logged-in UI.
|
||||
author = Column(String(255), nullable=True)
|
||||
last_modified_by = Column(String(255), nullable=True)
|
||||
order = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, default=lambda: datetime.datetime.now(datetime.UTC))
|
||||
updated_at = Column(
|
||||
@@ -31,6 +34,8 @@ class MarkdownPatch(Base):
|
||||
'markdown_id': self.markdown_id,
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'author': self.author,
|
||||
'last_modified_by': self.last_modified_by,
|
||||
'order': self.order,
|
||||
'created_at': self.created_at,
|
||||
'updated_at': self.updated_at,
|
||||
|
||||
Reference in New Issue
Block a user