from __future__ import annotations from fastapi.encoders import jsonable_encoder from flask import request from flask_restx import Resource, fields from pydantic import BaseModel, Field from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, setup_required from libs.login import current_account_with_tenant, login_required from services.sandbox.sandbox_file_service import SandboxFileService DEFAULT_REF_TEMPLATE_SWAGGER_2_0 = "#/definitions/{model}" class SandboxFileListQuery(BaseModel): path: str | None = Field(default=None, description="Workspace relative path") recursive: bool = Field(default=False, description="List recursively") class SandboxFileDownloadRequest(BaseModel): path: str = Field(..., description="Workspace relative file path") console_ns.schema_model( SandboxFileListQuery.__name__, SandboxFileListQuery.model_json_schema(ref_template=DEFAULT_REF_TEMPLATE_SWAGGER_2_0), ) console_ns.schema_model( SandboxFileDownloadRequest.__name__, SandboxFileDownloadRequest.model_json_schema(ref_template=DEFAULT_REF_TEMPLATE_SWAGGER_2_0), ) SANDBOX_FILE_NODE_FIELDS = { "path": fields.String, "is_dir": fields.Boolean, "size": fields.Raw, "mtime": fields.Raw, "extension": fields.String, } SANDBOX_FILE_DOWNLOAD_TICKET_FIELDS = { "download_url": fields.String, "expires_in": fields.Integer, "export_id": fields.String, } sandbox_file_node_model = console_ns.model("SandboxFileNode", SANDBOX_FILE_NODE_FIELDS) sandbox_file_download_ticket_model = console_ns.model("SandboxFileDownloadTicket", SANDBOX_FILE_DOWNLOAD_TICKET_FIELDS) @console_ns.route("/apps//sandbox/files") class SandboxFilesApi(Resource): """List sandbox files for the current user. The sandbox_id is derived from the current user's ID, as each user has their own sandbox workspace per app. """ @setup_required @login_required @account_initialization_required @console_ns.expect(console_ns.models[SandboxFileListQuery.__name__]) @console_ns.marshal_list_with(sandbox_file_node_model) def get(self, app_id: str): args = SandboxFileListQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore[arg-type] account, tenant_id = current_account_with_tenant() sandbox_id = account.id return jsonable_encoder( SandboxFileService.list_files( tenant_id=tenant_id, app_id=app_id, sandbox_id=sandbox_id, path=args.path, recursive=args.recursive, ) ) @console_ns.route("/apps//sandbox/files/download") class SandboxFileDownloadApi(Resource): """Download a sandbox file for the current user. The sandbox_id is derived from the current user's ID, as each user has their own sandbox workspace per app. """ @setup_required @login_required @account_initialization_required @console_ns.expect(console_ns.models[SandboxFileDownloadRequest.__name__]) @console_ns.marshal_with(sandbox_file_download_ticket_model) def post(self, app_id: str): payload = SandboxFileDownloadRequest.model_validate(console_ns.payload or {}) account, tenant_id = current_account_with_tenant() sandbox_id = account.id res = SandboxFileService.download_file( tenant_id=tenant_id, app_id=app_id, sandbox_id=sandbox_id, path=payload.path ) return jsonable_encoder(res)