feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -0,0 +1,122 @@
"""add tenant-bound one-time OnlyOffice sessions
Revision ID: 20260717_0027
Revises: 20260717_0026
Create Date: 2026-07-17 10:00:00
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "20260717_0027"
down_revision: str | None = "20260717_0026"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def _require_postgresql() -> None:
dialect_name = op.get_bind().dialect.name
if dialect_name != "postgresql":
raise RuntimeError(
"20260717_0027 only supports PostgreSQL; "
f"refusing to mutate {dialect_name} without transactional constraint DDL"
)
def _require_empty_for_downgrade() -> None:
count = int(
op.get_bind().scalar(
sa.text("SELECT COUNT(*) FROM knowledge_onlyoffice_sessions")
)
or 0
)
if count:
raise RuntimeError(
"cannot downgrade knowledge tenant security: OnlyOffice session evidence exists "
f"(knowledge_onlyoffice_sessions={count})"
)
def upgrade() -> None:
_require_postgresql()
op.create_table(
"knowledge_onlyoffice_sessions",
sa.Column("jti", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=64), nullable=False),
sa.Column("resource_scope", sa.String(length=16), nullable=False),
sa.Column("document_id", sa.String(length=64), nullable=False),
sa.Column("document_key", sa.String(length=160), nullable=False),
sa.Column("document_version", sa.Integer(), nullable=False),
sa.Column("audience", sa.String(length=80), nullable=False),
sa.Column("editable", sa.Boolean(), nullable=False),
sa.Column("status", sa.String(length=16), nullable=False),
sa.Column("created_by", sa.String(length=100), nullable=False),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("claimed_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("consumed_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("failure_reason", sa.Text(), nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.func.now(),
),
sa.CheckConstraint(
"resource_scope IN ('tenant', 'platform')",
name="ck_knowledge_onlyoffice_sessions_scope",
),
sa.CheckConstraint(
"status IN ('active', 'processing', 'consumed', 'failed', 'revoked')",
name="ck_knowledge_onlyoffice_sessions_status",
),
sa.CheckConstraint(
"tenant_id IS NOT NULL AND "
"(resource_scope = 'tenant' OR "
"(resource_scope = 'platform' AND editable = false))",
name="ck_knowledge_onlyoffice_sessions_scope_tenant",
),
sa.CheckConstraint(
"(status = 'active' AND claimed_at IS NULL AND consumed_at IS NULL) OR "
"(status IN ('processing', 'failed') AND claimed_at IS NOT NULL "
"AND consumed_at IS NULL) OR "
"(status = 'consumed' AND claimed_at IS NOT NULL AND consumed_at IS NOT NULL) OR "
"(status = 'revoked' AND consumed_at IS NULL)",
name="ck_knowledge_onlyoffice_sessions_lifecycle",
),
sa.ForeignKeyConstraint(
["tenant_id"],
["tenants.tenant_id"],
name="fk_knowledge_onlyoffice_sessions_tenant",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("jti"),
)
op.create_index(
"ix_knowledge_onlyoffice_sessions_tenant_document",
"knowledge_onlyoffice_sessions",
["tenant_id", "document_id", "created_at"],
)
op.create_index(
"ix_knowledge_onlyoffice_sessions_status_expiry",
"knowledge_onlyoffice_sessions",
["status", "expires_at"],
)
def downgrade() -> None:
_require_postgresql()
_require_empty_for_downgrade()
op.drop_index(
"ix_knowledge_onlyoffice_sessions_status_expiry",
table_name="knowledge_onlyoffice_sessions",
)
op.drop_index(
"ix_knowledge_onlyoffice_sessions_tenant_document",
table_name="knowledge_onlyoffice_sessions",
)
op.drop_table("knowledge_onlyoffice_sessions")