"""Seed / upsert the Aliyun (阿里云) cloud integration. Idempotent: creates the integration the first time, refreshes credentials on subsequent runs. Works against both local MySQL and TiDB Cloud — whichever ``DATABASE_URL`` points at. **Never commits AK/SK to git.** Credentials come from env: ALIYUN_ACCESS_KEY_ID=LTAI... ALIYUN_ACCESS_KEY_SECRET=... [optional] ALIYUN_REGION=cn-shanghai [optional] ALIYUN_SLUG=aliyun-primary [optional] ALIYUN_NAME="阿里云" [optional] ALIYUN_DIRECTORY_ID=d-... [optional] ALIYUN_ACCESS_CONFIGURATION_ID=ac-... [optional] ALIYUN_ROLE_ARN=acs:ram:::role/... Run from the server/ dir: cd server source ../.venv/bin/activate ALIYUN_ACCESS_KEY_ID=... ALIYUN_ACCESS_KEY_SECRET=... \\ CREDENTIAL_ENCRYPTION_KEY=... \\ python ../scripts/seed_aliyun.py The script updates exactly one row. Per-capability ``scheduled_jobs`` rows are auto-provisioned by ``IntegrationService.create/update``. """ from __future__ import annotations import asyncio import os import sys import uuid from pathlib import Path def _ensure_path() -> None: here = Path(__file__).resolve() server = here.parent.parent / "server" if server.exists() and str(server) not in sys.path: sys.path.insert(0, str(server)) _ensure_path() async def _main() -> None: ak = os.environ.get("ALIYUN_ACCESS_KEY_ID", "").strip() sk = os.environ.get("ALIYUN_ACCESS_KEY_SECRET", "").strip() if not ak or not sk: print( "error: ALIYUN_ACCESS_KEY_ID and ALIYUN_ACCESS_KEY_SECRET must be set", file=sys.stderr, ) sys.exit(2) region = os.environ.get("ALIYUN_REGION", "cn-shanghai").strip() or "cn-shanghai" slug = os.environ.get("ALIYUN_SLUG", "aliyun-primary").strip() or "aliyun-primary" name = os.environ.get("ALIYUN_NAME", "阿里云").strip() or "阿里云" capabilities = [ "servers", "domains", "dns_records", "cdn", "object_storage", "load_balancers", "user_revoke", ] from sqlalchemy import select import app.integrations.drivers # noqa: F401 — register @register_driver from app.auth.models import AuthUser # noqa: F401 — imported for ORM setup from app.core.database import async_session_factory from app.identity.person_models import IdentityPerson from app.integrations.models import Integration from app.integrations.service import IntegrationService async with async_session_factory() as session: existing = ( await session.execute( select(Integration).where(Integration.slug == slug) ) ).scalar_one_or_none() actor_row = ( await session.execute(select(IdentityPerson).limit(1)) ).scalar_one_or_none() actor_id = actor_row.id if actor_row else uuid.uuid4() config: dict[str, str] = { "access_key_id": ak, "access_key_secret": sk, "account_mode": "sso", "region": region, } for env_name, cfg_key in ( ("ALIYUN_DIRECTORY_ID", "directory_id"), ("ALIYUN_ACCESS_CONFIGURATION_ID", "access_configuration_id"), ("ALIYUN_ROLE_ARN", "role_arn"), ): val = (os.environ.get(env_name) or "").strip() if val: config[cfg_key] = val trace_id = f"seed-aliyun-{uuid.uuid4().hex[:12]}" service = IntegrationService(session) if existing is None: print(f"[seed] creating cloud integration slug={slug!r}") resp = await service.create( kind="cloud", provider="aliyun", name=name, slug=slug, description=( "阿里云主租户 — ECS / 域名 / 云解析 DNS / CDN / OSS / " "SLB 同步 + 资源目录子账号生命周期 + RAM 用户 + " "离职全量撤销。" ), is_primary=False, enabled=True, capabilities=capabilities, config=config, trace_id=trace_id, actor_user_id=actor_id, ) else: print(f"[seed] updating cloud integration slug={slug!r}") resp = await service.update( existing.slug, name=name, description=None, enabled=True, is_primary=None, capabilities=capabilities, config=config, trace_id=trace_id, actor_user_id=actor_id, ) await session.commit() print(f"[seed] done. integration_id={resp.id}") print( "[seed] scheduled_jobs auto-provisioned for each capability. " "Verify at /admin/scheduled-jobs." ) if __name__ == "__main__": asyncio.run(_main())