@@ -5,14 +5,18 @@ import {
55 RULES_KEY ,
66 SUBS_KEY ,
77 FILES_KEY ,
8+ SETTINGS_KEY ,
89} from '@/constants' ;
910import { failed , success } from '@/restful/response' ;
1011import { InternalServerError , ResourceNotFoundError } from '@/restful/errors' ;
1112import { findByName } from '@/utils/database' ;
1213import download from '@/utils/download' ;
1314import { ProxyUtils } from '@/core/proxy-utils' ;
1415import { RuleUtils } from '@/core/rule-utils' ;
15- import { syncToGist } from '@/restful/artifacts' ;
16+ import {
17+ normalizeArtifactSyncBatchSize ,
18+ syncToGist ,
19+ } from '@/restful/artifacts' ;
1620import {
1721 buildEmptySubscriptionOutput ,
1822 handleIgnoreFailedRemoteSubError ,
@@ -701,6 +705,122 @@ async function produceArtifact({
701705 }
702706}
703707
708+ function createArtifactUploadBatches ( names , batchSize ) {
709+ const batches = [ ] ;
710+ for ( let index = 0 ; index < names . length ; index += batchSize ) {
711+ batches . push ( names . slice ( index , index + batchSize ) ) ;
712+ }
713+ return batches ;
714+ }
715+
716+ function normalizeUploadResponseFiles ( files ) {
717+ if ( Array . isArray ( files ) ) {
718+ return {
719+ isGitLab : true ,
720+ files : Object . fromEntries ( files . map ( ( item ) => [ item . path , item ] ) ) ,
721+ } ;
722+ }
723+
724+ return {
725+ isGitLab : false ,
726+ files : files || { } ,
727+ } ;
728+ }
729+
730+ function logUploadResponse ( body ) {
731+ delete body . history ;
732+ delete body . forks ;
733+ delete body . owner ;
734+ if ( body . files ) {
735+ Object . values ( body . files ) . forEach ( ( file ) => {
736+ delete file . content ;
737+ } ) ;
738+ }
739+ $ . info ( '上传配置响应:' ) ;
740+ $ . info ( JSON . stringify ( body , null , 2 ) ) ;
741+ }
742+
743+ function resolveArtifactUploadUrl ( body , artifactName ) {
744+ const { files, isGitLab } = normalizeUploadResponseFiles ( body . files ) ;
745+ const encodedName = encodeURIComponent ( artifactName ) ;
746+ const raw_url = files [ encodedName ] ?. raw_url ;
747+ const new_url = isGitLab
748+ ? raw_url
749+ : raw_url ?. replace ( / \/ r a w \/ [ ^ / ] * \/ ( .* ) / , '/raw/$1' ) ;
750+ $ . info (
751+ `上传配置完成\n文件列表: ${ Object . keys ( files ) . join (
752+ ', ' ,
753+ ) } \n当前文件: ${ encodedName } \n响应返回的原始链接: ${ raw_url } \n处理完的新链接: ${ new_url } `,
754+ ) ;
755+ return new_url ;
756+ }
757+
758+ async function uploadArtifactBatches ( { allArtifacts, files, valid, invalid } ) {
759+ const settings = $ . read ( SETTINGS_KEY ) || { } ;
760+ const batchSize = normalizeArtifactSyncBatchSize (
761+ settings . artifactSyncBatchSize ,
762+ ) ;
763+ const batches = createArtifactUploadBatches ( valid , batchSize ) ;
764+ const uploaded = [ ] ;
765+
766+ $ . info (
767+ `准备分批上传同步配置: 共 ${ valid . length } 个, 每批 ${ batchSize } 个, 批次数 ${ batches . length } ` ,
768+ ) ;
769+
770+ for ( let index = 0 ; index < batches . length ; index ++ ) {
771+ const batchNames = batches [ index ] ;
772+ const batchFiles = Object . fromEntries (
773+ batchNames . map ( ( name ) => [
774+ encodeURIComponent ( name ) ,
775+ files [ encodeURIComponent ( name ) ] ,
776+ ] ) ,
777+ ) ;
778+
779+ try {
780+ $ . info (
781+ `正在上传第 ${ index + 1 } /${ batches . length } 批同步配置: ${ batchNames . join (
782+ ', ' ,
783+ ) } `,
784+ ) ;
785+ const resp = await syncToGist ( batchFiles ) ;
786+ const body = JSON . parse ( resp . body ) ;
787+ logUploadResponse ( body ) ;
788+
789+ for ( const artifact of allArtifacts ) {
790+ if (
791+ artifact . sync &&
792+ artifact . source &&
793+ batchNames . includes ( artifact . name )
794+ ) {
795+ const newUrl = resolveArtifactUploadUrl (
796+ body ,
797+ artifact . name ,
798+ ) ;
799+ if ( newUrl ) {
800+ artifact . updated = new Date ( ) . getTime ( ) ;
801+ artifact . url = newUrl ;
802+ uploaded . push ( artifact . name ) ;
803+ } else {
804+ $ . error (
805+ `同步配置 ${ artifact . name } 上传成功但响应中未找到文件链接` ,
806+ ) ;
807+ invalid . push ( artifact . name ) ;
808+ }
809+ }
810+ }
811+ } catch ( e ) {
812+ $ . error (
813+ `第 ${ index + 1 } /${ batches . length } 批同步配置上传失败: ${ batchNames . join (
814+ ', ' ,
815+ ) } , 原因: ${ e . message ?? e } `,
816+ ) ;
817+ invalid . push ( ...batchNames ) ;
818+ }
819+ }
820+
821+ return uploaded ;
822+ }
823+
704824async function syncArtifacts ( ) {
705825 $ . info ( '开始同步所有远程配置...' ) ;
706826 const allArtifacts = $ . read ( ARTIFACTS_KEY ) ;
@@ -815,59 +935,22 @@ async function syncArtifacts() {
815935 ) ;
816936 }
817937
818- const resp = await syncToGist ( files ) ;
819- const body = JSON . parse ( resp . body ) ;
820-
821- delete body . history ;
822- delete body . forks ;
823- delete body . owner ;
824- Object . values ( body . files ) . forEach ( ( file ) => {
825- delete file . content ;
938+ const uploaded = await uploadArtifactBatches ( {
939+ allArtifacts,
940+ files,
941+ valid,
942+ invalid,
826943 } ) ;
827- $ . info ( '上传配置响应:' ) ;
828- $ . info ( JSON . stringify ( body , null , 2 ) ) ;
829-
830- for ( const artifact of allArtifacts ) {
831- if (
832- artifact . sync &&
833- artifact . source &&
834- valid . includes ( artifact . name )
835- ) {
836- artifact . updated = new Date ( ) . getTime ( ) ;
837- // extract real url from gist
838- let files = body . files ;
839- let isGitLab ;
840- if ( Array . isArray ( files ) ) {
841- isGitLab = true ;
842- files = Object . fromEntries (
843- files . map ( ( item ) => [ item . path , item ] ) ,
844- ) ;
845- }
846- const raw_url =
847- files [ encodeURIComponent ( artifact . name ) ] ?. raw_url ;
848- const new_url = isGitLab
849- ? raw_url
850- : raw_url ?. replace ( / \/ r a w \/ [ ^ / ] * \/ ( .* ) / , '/raw/$1' ) ;
851- $ . info (
852- `上传配置完成\n文件列表: ${ Object . keys ( files ) . join (
853- ', ' ,
854- ) } \n当前文件: ${ encodeURIComponent (
855- artifact . name ,
856- ) } \n响应返回的原始链接: ${ raw_url } \n处理完的新链接: ${ new_url } `,
857- ) ;
858- artifact . url = new_url ;
859- }
860- }
861944
862945 $ . write ( allArtifacts , ARTIFACTS_KEY ) ;
863946 $ . info ( '上传配置成功' ) ;
864947
865948 if ( invalid . length > 0 ) {
866949 throw new Error (
867- `同步配置成功 ${ valid . length } 个, 失败 ${ invalid . length } 个, 详情请查看日志` ,
950+ `同步配置成功 ${ uploaded . length } 个, 失败 ${ invalid . length } 个, 详情请查看日志` ,
868951 ) ;
869952 } else {
870- $ . info ( `同步配置成功 ${ valid . length } 个` ) ;
953+ $ . info ( `同步配置成功 ${ uploaded . length } 个` ) ;
871954 }
872955 } catch ( e ) {
873956 $ . error ( `同步配置失败,原因:${ e . message ?? e } ` ) ;
@@ -958,32 +1041,8 @@ async function syncArtifact(req, res) {
9581041 artifact . updated = new Date ( ) . getTime ( ) ;
9591042 const body = JSON . parse ( resp . body ) ;
9601043
961- delete body . history ;
962- delete body . forks ;
963- delete body . owner ;
964- Object . values ( body . files ) . forEach ( ( file ) => {
965- delete file . content ;
966- } ) ;
967- $ . info ( '上传配置响应:' ) ;
968- $ . info ( JSON . stringify ( body , null , 2 ) ) ;
969-
970- let files = body . files ;
971- let isGitLab ;
972- if ( Array . isArray ( files ) ) {
973- isGitLab = true ;
974- files = Object . fromEntries ( files . map ( ( item ) => [ item . path , item ] ) ) ;
975- }
976- const raw_url = files [ encodeURIComponent ( artifact . name ) ] ?. raw_url ;
977- const new_url = isGitLab
978- ? raw_url
979- : raw_url ?. replace ( / \/ r a w \/ [ ^ / ] * \/ ( .* ) / , '/raw/$1' ) ;
980- $ . info (
981- `上传配置完成\n文件列表: ${ Object . keys ( files ) . join (
982- ', ' ,
983- ) } \n当前文件: ${ encodeURIComponent (
984- artifact . name ,
985- ) } \n响应返回的原始链接: ${ raw_url } \n处理完的新链接: ${ new_url } `,
986- ) ;
1044+ logUploadResponse ( body ) ;
1045+ const new_url = resolveArtifactUploadUrl ( body , artifact . name ) ;
9871046 artifact . url = new_url ;
9881047 $ . write ( allArtifacts , ARTIFACTS_KEY ) ;
9891048 success ( res , artifact ) ;
@@ -1000,4 +1059,4 @@ async function syncArtifact(req, res) {
10001059 }
10011060}
10021061
1003- export { produceArtifact , syncArtifacts } ;
1062+ export { produceArtifact , syncArtifacts , uploadArtifactBatches } ;
0 commit comments