@@ -512,19 +512,7 @@ func CommonRoutes() *web.Route {
512
512
r .Get ("/files/{id}/{version}/{filename}" , pypi .DownloadPackageFile )
513
513
r .Get ("/simple/{id}" , pypi .PackageMetadata )
514
514
}, reqPackageAccess (perm .AccessModeRead ))
515
- r .Group ("/rpm" , func () {
516
- r .Get (".repo" , rpm .GetRepositoryConfig )
517
- r .Get ("/repository.key" , rpm .GetRepositoryKey )
518
- r .Put ("/upload" , reqPackageAccess (perm .AccessModeWrite ), rpm .UploadPackageFile )
519
- r .Group ("/package/{name}/{version}/{architecture}" , func () {
520
- r .Get ("" , rpm .DownloadPackageFile )
521
- r .Delete ("" , reqPackageAccess (perm .AccessModeWrite ), rpm .DeletePackageFile )
522
- })
523
- r .Group ("/repodata/{filename}" , func () {
524
- r .Head ("" , rpm .CheckRepositoryFileExistence )
525
- r .Get ("" , rpm .GetRepositoryFile )
526
- })
527
- }, reqPackageAccess (perm .AccessModeRead ))
515
+ r .Group ("/rpm" , RpmRoutes (r ), reqPackageAccess (perm .AccessModeRead ))
528
516
r .Group ("/rubygems" , func () {
529
517
r .Get ("/specs.4.8.gz" , rubygems .EnumeratePackages )
530
518
r .Get ("/latest_specs.4.8.gz" , rubygems .EnumeratePackagesLatest )
@@ -589,6 +577,82 @@ func CommonRoutes() *web.Route {
589
577
return r
590
578
}
591
579
580
+ // Support for uploading rpm packages with arbitrary depth paths
581
+ func RpmRoutes (r * web.Route ) func () {
582
+ var (
583
+ groupRepoInfo = regexp .MustCompile (`\A((?:/(?:[^/]+))*|)\.repo\z` )
584
+ groupUpload = regexp .MustCompile (`\A((?:/(?:[^/]+))*|)/upload\z` )
585
+ groupRpm = regexp .MustCompile (`\A((?:/(?:[^/]+))*|)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z` )
586
+ groupMetadata = regexp .MustCompile (`\A((?:/(?:[^/]+))*|)/repodata/([^/]+)\z` )
587
+ )
588
+
589
+ return func () {
590
+ r .Methods ("HEAD,GET,POST,PUT,PATCH,DELETE" , "*" , func (ctx * context.Context ) {
591
+ path := ctx .Params ("*" )
592
+ isHead := ctx .Req .Method == "HEAD"
593
+ isGetHead := ctx .Req .Method == "HEAD" || ctx .Req .Method == "GET"
594
+ isPut := ctx .Req .Method == "PUT"
595
+ isDelete := ctx .Req .Method == "DELETE"
596
+
597
+ if path == "/repository.key" && isGetHead {
598
+ rpm .GetRepositoryKey (ctx )
599
+ return
600
+ }
601
+
602
+ // get repo
603
+ m := groupRepoInfo .FindStringSubmatch (path )
604
+ if len (m ) == 2 && isGetHead {
605
+ ctx .SetParams ("group" , strings .Trim (m [1 ], "/" ))
606
+ rpm .GetRepositoryConfig (ctx )
607
+ return
608
+ }
609
+ // get meta
610
+ m = groupMetadata .FindStringSubmatch (path )
611
+ if len (m ) == 3 && isGetHead {
612
+ ctx .SetParams ("group" , strings .Trim (m [1 ], "/" ))
613
+ ctx .SetParams ("filename" , m [2 ])
614
+ if isHead {
615
+ rpm .CheckRepositoryFileExistence (ctx )
616
+ } else {
617
+ rpm .GetRepositoryFile (ctx )
618
+ }
619
+ return
620
+ }
621
+ // upload
622
+ m = groupUpload .FindStringSubmatch (path )
623
+ if len (m ) == 2 && isPut {
624
+ reqPackageAccess (perm .AccessModeWrite )(ctx )
625
+ if ctx .Written () {
626
+ return
627
+ }
628
+ ctx .SetParams ("group" , strings .Trim (m [1 ], "/" ))
629
+ rpm .UploadPackageFile (ctx )
630
+ return
631
+ }
632
+ // rpm down/delete
633
+ m = groupRpm .FindStringSubmatch (path )
634
+ if len (m ) == 6 {
635
+ ctx .SetParams ("group" , strings .Trim (m [1 ], "/" ))
636
+ ctx .SetParams ("name" , m [2 ])
637
+ ctx .SetParams ("version" , m [3 ])
638
+ ctx .SetParams ("architecture" , m [4 ])
639
+ if isGetHead {
640
+ rpm .DownloadPackageFile (ctx )
641
+ return
642
+ } else if isDelete {
643
+ reqPackageAccess (perm .AccessModeWrite )(ctx )
644
+ if ctx .Written () {
645
+ return
646
+ }
647
+ rpm .DeletePackageFile (ctx )
648
+ }
649
+ }
650
+ // default
651
+ ctx .Status (http .StatusNotFound )
652
+ })
653
+ }
654
+ }
655
+
592
656
// ContainerRoutes provides endpoints that implement the OCI API to serve containers
593
657
// These have to be mounted on `/v2/...` to comply with the OCI spec:
594
658
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
0 commit comments