feat(sandbox): add command to setup system-level sandbox provider configuration

This commit is contained in:
Harry 2026-01-09 17:39:44 +08:00
parent eb821efda7
commit 78acfb0040
3 changed files with 67 additions and 19 deletions

View File

@ -1395,6 +1395,60 @@ def file_usage(
click.echo(click.style(f"Use --offset {offset + limit} to see next page", fg="white"))
@click.command("setup-sandbox-system-config", help="Setup system-level sandbox provider configuration.")
@click.option(
"--provider-type", prompt=True, type=click.Choice(["e2b", "docker", "local"]), help="Sandbox provider type"
)
@click.option("--config", prompt=True, help='Configuration JSON (e.g., {"api_key": "xxx"} for e2b)')
def setup_sandbox_system_config(provider_type: str, config: str):
"""
Setup system-level sandbox provider configuration.
Examples:
flask setup-sandbox-system-config --provider-type e2b --config '{"api_key": "e2b_xxx"}'
flask setup-sandbox-system-config --provider-type docker --config '{"docker_sock": "unix:///var/run/docker.sock"}'
flask setup-sandbox-system-config --provider-type local --config '{}'
"""
from models.sandbox import SandboxProviderSystemConfig
from services.sandbox.sandbox_provider_service import PROVIDER_CONFIG_MODELS
try:
click.echo(click.style(f"Validating config: {config}", fg="yellow"))
config_dict = TypeAdapter(dict[str, Any]).validate_json(config)
click.echo(click.style("Config validated successfully.", fg="green"))
click.echo(click.style(f"Validating config schema for provider type: {provider_type}", fg="yellow"))
model_class = PROVIDER_CONFIG_MODELS.get(provider_type)
if model_class:
model_class.model_validate(config_dict)
click.echo(click.style("Config schema validated successfully.", fg="green"))
click.echo(click.style("Encrypting config...", fg="yellow"))
click.echo(click.style(f"Using SECRET_KEY: `{dify_config.SECRET_KEY}`", fg="yellow"))
encrypted_config = encrypt_system_params(config_dict)
click.echo(click.style("Config encrypted successfully.", fg="green"))
except Exception as e:
click.echo(click.style(f"Error validating/encrypting config: {str(e)}", fg="red"))
return
deleted_count = db.session.query(SandboxProviderSystemConfig).filter_by(provider_type=provider_type).delete()
if deleted_count > 0:
click.echo(
click.style(
f"Deleted {deleted_count} existing system config for provider type: {provider_type}", fg="yellow"
)
)
system_config = SandboxProviderSystemConfig(
provider_type=provider_type,
encrypted_config=encrypted_config,
)
db.session.add(system_config)
db.session.commit()
click.echo(click.style(f"Sandbox system config setup successfully. id: {system_config.id}", fg="green"))
click.echo(click.style(f"Provider type: {provider_type}", fg="green"))
@click.command("setup-system-tool-oauth-client", help="Setup system tool oauth client.")
@click.option("--provider", prompt=True, help="Provider name")
@click.option("--client-params", prompt=True, help="Client Params")

View File

@ -23,6 +23,7 @@ def init_app(app: DifyApp):
reset_encrypt_key_pair,
reset_password,
setup_datasource_oauth_client,
setup_sandbox_system_config,
setup_system_tool_oauth_client,
setup_system_trigger_oauth_client,
transform_datasource_credentials,
@ -49,6 +50,7 @@ def init_app(app: DifyApp):
clear_orphaned_file_records,
remove_orphaned_files_on_storage,
file_usage,
setup_sandbox_system_config,
setup_system_tool_oauth_client,
setup_system_trigger_oauth_client,
cleanup_orphaned_draft_variables,

View File

@ -120,29 +120,24 @@ class SandboxProviderService:
@classmethod
def list_providers(cls, tenant_id: str) -> list[SandboxProviderInfo]:
available_types = cls.get_available_provider_types()
result: list[SandboxProviderInfo] = []
with Session(db.engine, expire_on_commit=False) as session:
tenant_configs = session.query(SandboxProvider).filter(SandboxProvider.tenant_id == tenant_id).all()
tenant_config_map = {cfg.provider_type: cfg for cfg in tenant_configs}
tenant_configs = {
cfg.provider_type: cfg
for cfg in session.query(SandboxProvider).filter(SandboxProvider.tenant_id == tenant_id).all()
}
system_defaults = {cfg.provider_type for cfg in session.query(SandboxProviderSystemConfig).all()}
system_defaults = session.query(SandboxProviderSystemConfig).all()
system_default_map = {cfg.provider_type: cfg for cfg in system_defaults}
for provider_type in available_types:
for provider_type in cls.get_available_provider_types():
tenant_config = tenant_configs.get(provider_type)
schema = PROVIDER_CONFIG_SCHEMAS.get(provider_type, [])
metadata = PROVIDER_METADATA.get(provider_type, {})
config_schema = PROVIDER_CONFIG_SCHEMAS.get(provider_type, [])
tenant_config = tenant_config_map.get(provider_type)
system_default = system_default_map.get(provider_type)
config: Mapping[str, Any] = {}
if tenant_config and tenant_config.config:
schema = PROVIDER_CONFIG_SCHEMAS.get(provider_type, [])
encrypter, _ = create_sandbox_config_encrypter(tenant_id, schema, provider_type)
decrypted = encrypter.decrypt(tenant_config.config)
config = masked_config(schema, decrypted)
config = masked_config(schema, encrypter.decrypt(tenant_config.config))
result.append(
SandboxProviderInfo(
@ -150,11 +145,11 @@ class SandboxProviderService:
label=metadata.get("label", provider_type),
description=metadata.get("description", ""),
icon=metadata.get("icon", provider_type),
is_system_configured=system_default is not None,
is_system_configured=provider_type in system_defaults and tenant_config is None,
is_tenant_configured=tenant_config is not None,
is_active=tenant_config.is_active if tenant_config else False,
config=config,
config_schema=[{"name": c.name, "type": c.type.value} for c in config_schema],
config_schema=[{"name": c.name, "type": c.type.value} for c in schema],
)
)
@ -243,9 +238,6 @@ class SandboxProviderService:
if not config:
return {"result": "success"}
if config.is_active:
raise ValueError("Cannot delete config for the active provider. Switch to another provider first.")
session.delete(config)
session.commit()