@@ -15,7 +15,11 @@ use crate::mcp::model::mcp::McpServerDto;
1515use crate :: raft:: store:: { ClientRequest , ClientResponse } ;
1616use crate :: sequence:: { SequenceRequest , SequenceResult } ;
1717use actix_web:: { web, HttpMessage , HttpRequest , HttpResponse , Responder } ;
18+ use std:: fs:: File ;
19+ use std:: io:: { Read , Seek , SeekFrom , Write } ;
1820use std:: sync:: Arc ;
21+ use zip:: write:: FileOptions ;
22+ use zip:: ZipWriter ;
1923
2024/// 查询McpServer列表
2125pub async fn query_mcp_server_list (
@@ -460,3 +464,102 @@ async fn do_publish_history_mcp_server(
460464 ) ) ,
461465 }
462466}
467+
468+ /// 批量导出McpServer
469+ pub async fn download_mcp_servers (
470+ _req : HttpRequest ,
471+ request : web:: Query < McpServerQueryRequest > ,
472+ appdata : web:: Data < Arc < AppShareData > > ,
473+ ) -> impl Responder {
474+ // 验证查询参数
475+ if let Err ( err) = request. validate ( ) {
476+ return handle_param_error ( err, "McpServer download parameter validation failed" ) ;
477+ }
478+
479+ // 转换查询参数,设置最大限制10万
480+ let mut query_param = request. to_mcp_query_param ( ) ;
481+ query_param. limit = 100_000 ;
482+ query_param. offset = 0 ;
483+
484+ // 发送查询请求到MCP Manager
485+ let cmd = McpManagerReq :: QueryServer ( query_param) ;
486+ match appdata. mcp_manager . send ( cmd) . await {
487+ Ok ( res) => match res {
488+ Ok ( McpManagerResult :: ServerPageInfo ( _, list) ) => {
489+ let mut tmpfile: File = tempfile:: tempfile ( ) . unwrap ( ) ;
490+ {
491+ let write = std:: io:: Write :: by_ref ( & mut tmpfile) ;
492+ let zip = ZipWriter :: new ( write) ;
493+ generate_mcp_server_zip ( zip, list) . ok ( ) ;
494+ }
495+ // Seek to start
496+ tmpfile. seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
497+ let mut buf = vec ! [ ] ;
498+ tmpfile. read_to_end ( & mut buf) . unwrap ( ) ;
499+
500+ let filename = format ! ( "rnacos_mcserver_export_{}.zip" , crate :: now_millis( ) ) ;
501+ HttpResponse :: Ok ( )
502+ . insert_header ( actix_web:: http:: header:: ContentType :: octet_stream ( ) )
503+ . insert_header ( actix_web:: http:: header:: ContentDisposition :: attachment (
504+ filename,
505+ ) )
506+ . body ( buf)
507+ }
508+ Ok ( _) => handle_unexpected_response_error ( "MCP Manager download McpServer" ) ,
509+ Err ( err) => handle_mcp_manager_error ( err, "download McpServer" ) ,
510+ } ,
511+ Err ( err) => handle_system_error (
512+ format ! ( "Unable to connect to MCP Manager: {}" , err) ,
513+ "Failed to send download request to MCP Manager" ,
514+ ) ,
515+ }
516+ }
517+
518+ /// 生成McpServer的zip文件
519+ fn generate_mcp_server_zip (
520+ mut zip : ZipWriter < & mut File > ,
521+ list : Vec < McpServerDto > ,
522+ ) -> anyhow:: Result < ( ) > {
523+ if list. is_empty ( ) {
524+ let options = FileOptions :: default ( )
525+ . compression_method ( zip:: CompressionMethod :: Stored )
526+ . unix_permissions ( 0o755 ) ;
527+ zip. start_file ( ".ignore" , options) ?;
528+ zip. write_all ( "empty mcpservers" . as_bytes ( ) ) ?;
529+ }
530+
531+ for item in & list {
532+ // 生成YAML内容
533+ let yaml_content = generate_mcp_server_yaml ( item) ;
534+
535+ // 文件名格式: {unique_key}.yaml
536+ let filename = format ! ( "{}.yaml" , item. unique_key. as_str( ) ) ;
537+
538+ let options = FileOptions :: default ( )
539+ . compression_method ( zip:: CompressionMethod :: Stored )
540+ . unix_permissions ( 0o755 ) ;
541+
542+ zip. start_file ( filename, options) ?;
543+ zip. write_all ( yaml_content. as_bytes ( ) ) ?;
544+ }
545+
546+ zip. finish ( ) ?;
547+ Ok ( ( ) )
548+ }
549+
550+ /// 生成McpServer的YAML内容
551+ fn generate_mcp_server_yaml ( mcp_server : & McpServerDto ) -> String {
552+ use crate :: console:: model:: mcp_server_model:: McpServerImportDto ;
553+
554+ // 转换为McpServerImportDto
555+ let import_dto = McpServerImportDto :: from ( mcp_server) ;
556+
557+ // 使用serde_yml序列化
558+ serde_yml:: to_string ( & import_dto) . unwrap_or_else ( |_| {
559+ // 如果序列化失败,返回基本错误信息
560+ format ! (
561+ "Failed to serialize McpServer: {}" ,
562+ mcp_server. unique_key. as_str( )
563+ )
564+ } )
565+ }
0 commit comments