@@ -7,27 +7,34 @@ import {
77 SingletonProto ,
88 Inject ,
99} from '@eggjs/tegg' ;
10+ import { ConflictError , ForbiddenError } from 'egg-errors' ;
11+ import semver from 'semver' ;
1012import { AbstractService } from '../../common/AbstractService' ;
1113import {
1214 calculateIntegrity ,
15+ getFullname ,
1316} from '../../common/PackageUtil' ;
1417import { createTempDir , mimeLookup } from '../../common/FileUtil' ;
1518import {
1619 PackageRepository ,
1720} from '../../repository/PackageRepository' ;
1821import { PackageVersionFileRepository } from '../../repository/PackageVersionFileRepository' ;
22+ import { PackageVersionRepository } from '../../repository/PackageVersionRepository' ;
1923import { DistRepository } from '../../repository/DistRepository' ;
2024import { PackageVersionFile } from '../entity/PackageVersionFile' ;
2125import { PackageVersion } from '../entity/PackageVersion' ;
2226import { Package } from '../entity/Package' ;
2327import { PackageManagerService } from './PackageManagerService' ;
2428import { CacheAdapter } from '../../common/adapter/CacheAdapter' ;
25- import { ConflictError } from 'egg-errors' ;
29+
30+ const unpkgWhiteListUrl = 'https://github.com/cnpm/unpkg-white-list' ;
2631
2732@SingletonProto ( {
2833 accessLevel : AccessLevel . PUBLIC ,
2934} )
3035export class PackageVersionFileService extends AbstractService {
36+ @Inject ( )
37+ private readonly packageVersionRepository : PackageVersionRepository ;
3138 @Inject ( )
3239 private readonly packageRepository : PackageRepository ;
3340 @Inject ( )
@@ -39,6 +46,12 @@ export class PackageVersionFileService extends AbstractService {
3946 @Inject ( )
4047 private readonly cacheAdapter : CacheAdapter ;
4148
49+ #unpkgWhiteListCurrentVersion: string = '' ;
50+ #unpkgWhiteListAllowPackages: Record < string , {
51+ version : string ;
52+ } > = { } ;
53+ #unpkgWhiteListAllowScopes: string [ ] = [ ] ;
54+
4255 async listPackageVersionFiles ( pkgVersion : PackageVersion , directory : string ) {
4356 await this . #ensurePackageVersionFilesSync( pkgVersion ) ;
4457 return await this . packageVersionFileRepository . listPackageVersionFiles ( pkgVersion . packageVersionId , directory ) ;
@@ -54,16 +67,57 @@ export class PackageVersionFileService extends AbstractService {
5467 async #ensurePackageVersionFilesSync( pkgVersion : PackageVersion ) {
5568 const hasFiles = await this . packageVersionFileRepository . hasPackageVersionFiles ( pkgVersion . packageVersionId ) ;
5669 if ( ! hasFiles ) {
57- const lockRes = await this . cacheAdapter . usingLock ( `${ pkgVersion . packageVersionId } :syncFiles` , 60 , async ( ) => {
70+ const lockName = `${ pkgVersion . packageVersionId } :syncFiles` ;
71+ const lockRes = await this . cacheAdapter . usingLock ( lockName , 60 , async ( ) => {
5872 await this . syncPackageVersionFiles ( pkgVersion ) ;
5973 } ) ;
6074 // lock fail
6175 if ( ! lockRes ) {
62- this . logger . warn ( '[package:version:syncPackageVersionFiles] check lock fail' ) ;
76+ this . logger . warn ( '[package:version:syncPackageVersionFiles] check lock:%s fail' , lockName ) ;
6377 throw new ConflictError ( 'Package version file sync is currently in progress. Please try again later.' ) ;
6478 }
6579 }
80+ }
81+
82+ async updateUnpkgWhiteList ( ) {
83+ if ( ! this . config . cnpmcore . enableSyncUnpkgFilesWhiteList ) return ;
84+ const whiteListScope = '' ;
85+ const whiteListPackageName = 'unpkg-white-list' ;
86+ const whiteListPackageVersion = await this . packageVersionRepository . findVersionByTag (
87+ whiteListScope , whiteListPackageName , 'latest' ) ;
88+ if ( ! whiteListPackageVersion ) return ;
89+ if ( this . #unpkgWhiteListCurrentVersion === whiteListPackageVersion ) return ;
90+
91+ // update the new version white list
92+ const { manifest } = await this . packageManagerService . showPackageVersionManifest (
93+ whiteListScope , whiteListPackageName , whiteListPackageVersion , false , true ) ;
94+ if ( ! manifest ) return ;
95+ this . #unpkgWhiteListCurrentVersion = manifest . version ;
96+ this . #unpkgWhiteListAllowPackages = manifest . allowPackages ?? { } as any ;
97+ this . #unpkgWhiteListAllowScopes = manifest . allowScopes ?? [ ] as any ;
98+ this . logger . info ( '[PackageVersionFileService.updateUnpkgWhiteList] version:%s, total %s packages, %s scopes' ,
99+ whiteListPackageVersion ,
100+ Object . keys ( this . #unpkgWhiteListAllowPackages) . length ,
101+ this . #unpkgWhiteListAllowScopes. length ,
102+ ) ;
103+ }
66104
105+ async #checkPackageVersionInUnpkgWhiteList( pkgScope : string , pkgName : string , pkgVersion : string ) {
106+ if ( ! this . config . cnpmcore . enableSyncUnpkgFilesWhiteList ) return ;
107+ await this . updateUnpkgWhiteList ( ) ;
108+
109+ // check allow scopes
110+ if ( this . #unpkgWhiteListAllowScopes. includes ( pkgScope ) ) return ;
111+
112+ // check allow packages
113+ const fullname = getFullname ( pkgScope , pkgName ) ;
114+ const pkgConfig = this . #unpkgWhiteListAllowPackages[ fullname ] ;
115+ if ( ! pkgConfig ) {
116+ throw new ForbiddenError ( `"${ fullname } " is not allow to unpkg files, see ${ unpkgWhiteListUrl } ` ) ;
117+ }
118+ if ( ! pkgConfig . version || ! semver . satisfies ( pkgVersion , pkgConfig . version ) ) {
119+ throw new ForbiddenError ( `"${ fullname } @${ pkgVersion } " not satisfies "${ pkgConfig . version } " to unpkg files, see ${ unpkgWhiteListUrl } ` ) ;
120+ }
67121 }
68122
69123 // 基于 latest version 同步 package readme
@@ -113,8 +167,16 @@ export class PackageVersionFileService extends AbstractService {
113167
114168 async syncPackageVersionFiles ( pkgVersion : PackageVersion ) {
115169 const files : PackageVersionFile [ ] = [ ] ;
170+ // must set enableUnpkg and enableSyncUnpkgFiles = true both
171+ if ( ! this . config . cnpmcore . enableUnpkg ) return files ;
172+ if ( ! this . config . cnpmcore . enableSyncUnpkgFiles ) return files ;
173+
116174 const pkg = await this . packageRepository . findPackageByPackageId ( pkgVersion . packageId ) ;
117175 if ( ! pkg ) return files ;
176+
177+ // check unpkg white list
178+ await this . #checkPackageVersionInUnpkgWhiteList( pkg . scope , pkg . name , pkgVersion . version ) ;
179+
118180 const dirname = `unpkg_${ pkg . fullname . replace ( '/' , '_' ) } @${ pkgVersion . version } _${ randomUUID ( ) } ` ;
119181 const tmpdir = await createTempDir ( this . config . dataDir , dirname ) ;
120182 const tarFile = `${ tmpdir } .tgz` ;
0 commit comments