- 迁移 14:plugin(kind 四形态、撤架三态)、plugin_version(Manifest JSONB + 规范化内容哈希签名、(plugin,version) 唯一)、plugin_installation(enabled + granted_permissions、(domain,plugin) 唯一) - Registry:发布版本(Manifest 必须声明 permissions,签名=canonical 哈希);撤架阻断新调用与新安装,历史审计保留(验证:撤架后 invoked 审计仍在) - 三态分离(购买≠安装≠启用≠授权):安装默认 disabled+零授权;启用必须显式授予权限子集(越界 422);调用门逐级校验(未装/未启/撤架各 403) - 升级:Manifest 新增权限必须重新确认(未确认 422 且明示新增项);确认后升级成功 - 调用:按 Manifest response_template 真实执行(声明式确定性回填);remote/isolated 形态明示 501 随沙箱交付(WP-14),不静默降级;审计含版本哈希(快照语义) - 权限标识统一 group:item 格式 - 测试 86 项全绿(+4):三态流、越界、升级确认、撤架阻断与历史保留
83 lines
4.6 KiB
Python
83 lines
4.6 KiB
Python
"""插件生态:Registry、版本与安装
|
|
|
|
Revision ID: f7bf2b464f1f
|
|
Revises: bd36c0a63816
|
|
Create Date: 2026-09-12 10:15:58.892690
|
|
|
|
"""
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = 'f7bf2b464f1f'
|
|
down_revision: str | Sequence[str] | None = 'bd36c0a63816'
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Upgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.create_table('plugin',
|
|
sa.Column('name', sa.String(), nullable=False),
|
|
sa.Column('publisher_domain_id', sa.String(), nullable=False),
|
|
sa.Column('kind', sa.String(), nullable=False),
|
|
sa.Column('status', sa.String(), nullable=False),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
sa.CheckConstraint("kind IN ('declarative', 'remote', 'isolated_process', 'container')", name=op.f('ck_plugin_kind_values')),
|
|
sa.CheckConstraint("status IN ('published', 'suspended', 'removed')", name=op.f('ck_plugin_status_values')),
|
|
sa.ForeignKeyConstraint(['publisher_domain_id'], ['personal_domain.id'], name=op.f('fk_plugin_personal_domain'), ondelete='cascade'),
|
|
sa.PrimaryKeyConstraint('name', name=op.f('pk_plugin'))
|
|
)
|
|
op.create_index(op.f('ix_plugin_publisher_domain_id'), 'plugin', ['publisher_domain_id'], unique=False)
|
|
op.create_table('plugin_version',
|
|
sa.Column('id', sa.String(), nullable=False),
|
|
sa.Column('plugin_name', sa.String(), nullable=False),
|
|
sa.Column('version', sa.String(), nullable=False),
|
|
sa.Column('manifest', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
|
sa.Column('content_hash', sa.String(), nullable=False),
|
|
sa.Column('status', sa.String(), nullable=False),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
sa.CheckConstraint("status IN ('published', 'suspended', 'removed')", name=op.f('ck_plugin_version_status_values')),
|
|
sa.ForeignKeyConstraint(['plugin_name'], ['plugin.name'], name=op.f('fk_plugin_version_plugin'), ondelete='cascade'),
|
|
sa.PrimaryKeyConstraint('id', name=op.f('pk_plugin_version'))
|
|
)
|
|
op.create_index(op.f('ix_plugin_version_plugin_name'), 'plugin_version', ['plugin_name'], unique=False)
|
|
op.create_index('uq_plugin_version', 'plugin_version', ['plugin_name', 'version'], unique=True)
|
|
op.create_table('plugin_installation',
|
|
sa.Column('id', sa.String(), nullable=False),
|
|
sa.Column('domain_id', sa.String(), nullable=False),
|
|
sa.Column('plugin_name', sa.String(), nullable=False),
|
|
sa.Column('version_id', sa.String(), nullable=False),
|
|
sa.Column('enabled', sa.Boolean(), nullable=False),
|
|
sa.Column('granted_permissions', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
sa.ForeignKeyConstraint(['domain_id'], ['personal_domain.id'], name=op.f('fk_plugin_installation_personal_domain'), ondelete='cascade'),
|
|
sa.ForeignKeyConstraint(['plugin_name'], ['plugin.name'], name=op.f('fk_plugin_installation_plugin'), ondelete='cascade'),
|
|
sa.ForeignKeyConstraint(['version_id'], ['plugin_version.id'], name=op.f('fk_plugin_installation_plugin_version'), ondelete='restrict'),
|
|
sa.PrimaryKeyConstraint('id', name=op.f('pk_plugin_installation'))
|
|
)
|
|
op.create_index(op.f('ix_plugin_installation_domain_id'), 'plugin_installation', ['domain_id'], unique=False)
|
|
op.create_index('uq_plugin_installation', 'plugin_installation', ['domain_id', 'plugin_name'], unique=True)
|
|
op.create_foreign_key(op.f('fk_project_project_version'), 'project', 'project_version', ['current_version_id'], ['id'], ondelete='set null', use_alter=True)
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Downgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.drop_constraint(op.f('fk_project_project_version'), 'project', type_='foreignkey')
|
|
op.drop_index('uq_plugin_installation', table_name='plugin_installation')
|
|
op.drop_index(op.f('ix_plugin_installation_domain_id'), table_name='plugin_installation')
|
|
op.drop_table('plugin_installation')
|
|
op.drop_index('uq_plugin_version', table_name='plugin_version')
|
|
op.drop_index(op.f('ix_plugin_version_plugin_name'), table_name='plugin_version')
|
|
op.drop_table('plugin_version')
|
|
op.drop_index(op.f('ix_plugin_publisher_domain_id'), table_name='plugin')
|
|
op.drop_table('plugin')
|
|
# ### end Alembic commands ###
|