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:
h z
2026-05-16 22:51:40 +01:00
parent 9e2477df8c
commit bf4c0dbbbd
8 changed files with 164 additions and 8 deletions

View File

@@ -223,4 +223,25 @@ def update_last_used(api_key):
session.query(APIKey).filter_by(key=api_key.key).update(
{APIKey.last_used_at: datetime.now(UTC)}
)
session.commit()
session.commit()
def get_actor():
"""Identity string to record as author/last_modified_by.
- X-API-Key request -> the key's alias
- Keycloak Bearer request -> the literal 'admin' (the backend does not
track individual KC identities)
- otherwise -> None
Call only from endpoints already behind @require_auth.
"""
api_key_header = request.headers.get('X-API-Key')
if api_key_header:
api_key = get_api_key(api_key_header)
if api_key:
return api_key.alias
return None
auth_header = request.headers.get('Authorization')
if auth_header and auth_header.startswith('Bearer'):
return 'admin'
return None