A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

updated favicons, fix domain rerouting, fix deploy provisioning

+180 -32
+52
deploy/upcloud/provision.go
··· 256 256 saveState(state) 257 257 } 258 258 259 + // Always reconcile forwarded headers rule (handles existing LBs) 260 + if err := ensureLBForwardedHeaders(ctx, svc, state.LB.UUID); err != nil { 261 + return fmt.Errorf("LB forwarded headers: %w", err) 262 + } 263 + 259 264 // Always reconcile TLS certs (handles partial failures and re-runs) 260 265 tlsDomains := []string{cfg.BaseDomain} 261 266 tlsDomains = append(tlsDomains, cfg.RegistryDomains...) ··· 567 572 }, 568 573 Rules: []request.LoadBalancerFrontendRule{ 569 574 { 575 + Name: "set-forwarded-headers", 576 + Priority: 1, 577 + Matchers: []upcloud.LoadBalancerMatcher{}, 578 + Actions: []upcloud.LoadBalancerAction{ 579 + request.NewLoadBalancerSetForwardedHeadersAction(), 580 + }, 581 + }, 582 + { 570 583 Name: "route-hold", 571 584 Priority: 10, 572 585 Matchers: []upcloud.LoadBalancerMatcher{ ··· 716 729 } 717 730 fmt.Printf(" TLS certificate: %s\n", domain) 718 731 } 732 + 733 + return nil 734 + } 735 + 736 + // ensureLBForwardedHeaders ensures the "https" frontend has a set_forwarded_headers rule. 737 + // This makes the LB set X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Port headers, 738 + // overwriting any pre-existing values (prevents spoofing). 739 + func ensureLBForwardedHeaders(ctx context.Context, svc *service.Service, lbUUID string) error { 740 + rules, err := svc.GetLoadBalancerFrontendRules(ctx, &request.GetLoadBalancerFrontendRulesRequest{ 741 + ServiceUUID: lbUUID, 742 + FrontendName: "https", 743 + }) 744 + if err != nil { 745 + return fmt.Errorf("get frontend rules: %w", err) 746 + } 747 + 748 + for _, r := range rules { 749 + if r.Name == "set-forwarded-headers" { 750 + fmt.Println(" Forwarded headers rule: exists") 751 + return nil 752 + } 753 + } 754 + 755 + _, err = svc.CreateLoadBalancerFrontendRule(ctx, &request.CreateLoadBalancerFrontendRuleRequest{ 756 + ServiceUUID: lbUUID, 757 + FrontendName: "https", 758 + Rule: request.LoadBalancerFrontendRule{ 759 + Name: "set-forwarded-headers", 760 + Priority: 1, 761 + Matchers: []upcloud.LoadBalancerMatcher{}, 762 + Actions: []upcloud.LoadBalancerAction{ 763 + request.NewLoadBalancerSetForwardedHeadersAction(), 764 + }, 765 + }, 766 + }) 767 + if err != nil { 768 + return fmt.Errorf("create forwarded headers rule: %w", err) 769 + } 770 + fmt.Println(" Forwarded headers rule: created") 719 771 720 772 return nil 721 773 }
pkg/appview/public/apple-touch-icon.png

This is a binary file and will not be displayed.

pkg/appview/public/atcr.png

This is a binary file and will not be displayed.

pkg/appview/public/favicon-96x96.png

This is a binary file and will not be displayed.

pkg/appview/public/favicon.ico

This is a binary file and will not be displayed.

+76 -15
pkg/appview/public/favicon.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><svg version="1.1" id="svg1" width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> 2 - <defs id="defs1"> 3 - <rect x="264" y="264" width="203.43417" height="218.82669" id="rect63"></rect> 4 - </defs> 5 - <g id="g1"> 6 - <rect style="fill:#5ecac1;fill-opacity:1;stroke:#000000;stroke-width:18.0441;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" id="rect16" width="246.95587" height="246.95584" x="9.0220766" y="9.0220766"></rect> 7 - <rect style="fill:#254365;fill-opacity:1;stroke:#000000;stroke-width:18.0441;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" id="rect17" width="246.95587" height="246.95584" x="9.0220766" y="256.02206"></rect> 8 - <rect style="fill:#f29b54;fill-opacity:1;stroke:#000000;stroke-width:18.0012;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" id="rect18" width="246.99879" height="246.99883" x="256.00061" y="9.0005999"></rect> 9 - <rect style="display:inline;fill:#47a5d8;fill-opacity:1;stroke:#000000;stroke-width:18.0441;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke" id="rect19" width="246.95586" height="246.95587" x="256.02206" y="256.02206"></rect> 10 - <path id="use60" style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" d="m 309,42 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z"></path> 11 - <path id="use60-1" style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" d="m 57,42 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 C 66,46.46 61.986,42 57,42 Z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 52 c 0,-5.54 -4.014,-10 -9,-10 z"></path> 12 - <path id="use60-3" style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" d="m 57,288 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 298 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 298 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 298 c 0,-5.54 -4.014,-10 -9,-10 z m 48,0 c -4.986,0 -9,4.46 -9,10 v 160 c 0,5.54 4.014,10 9,10 4.986,0 9,-4.46 9,-10 V 298 c 0,-5.54 -4.014,-10 -9,-10 z"></path> 13 - <text xml:space="preserve" id="text63" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:151.33px;font-family:'Noto Sans Telugu';-inkscape-font-specification:'Noto Sans Telugu';text-align:center;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect63);shape-padding:0;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" x="126" y="0" transform="matrix(1.4849596,0,0,1.4775815,-169.00723,-145.87951)"><tspan x="300.94749" y="401.69141" id="tspan2"><tspan style="font-style:italic;font-weight:bold;font-family:'Droid Sans Thai';-inkscape-font-specification:'Droid Sans Thai Bold Italic'" id="tspan1">@</tspan></tspan></text> 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <svg 3 + version="1.1" 4 + width="511.99997" 5 + height="512" 6 + id="svg5" 7 + xmlns="http://www.w3.org/2000/svg" 8 + xmlns:svg="http://www.w3.org/2000/svg"> 9 + <defs 10 + id="defs5" /> 11 + <g 12 + id="g1"> 13 + <rect 14 + style="fill:#5ecac1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" 15 + id="teal" 16 + width="256" 17 + height="256" 18 + x="0" 19 + y="0" /> 20 + <rect 21 + style="fill:#254365;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" 22 + id="blue-dark" 23 + width="256" 24 + height="256" 25 + x="0" 26 + y="256" /> 27 + <rect 28 + style="fill:#f29b54;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" 29 + id="orange" 30 + width="256" 31 + height="256" 32 + x="256" 33 + y="0" 34 + ry="0" /> 35 + <rect 36 + style="display:inline;fill:#47a5d8;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" 37 + id="blue-light" 38 + width="256" 39 + height="256" 40 + x="256" 41 + y="256" /> 42 + <path 43 + id="use60" 44 + style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" 45 + d="M 313.33334,40 C 308.16267,40 304,44.162668 304,49.333334 V 198.66665 c 0,5.17067 4.16267,9.33335 9.33334,9.33335 5.17065,0 9.33332,-4.16268 9.33332,-9.33335 V 49.333334 C 322.66666,44.162668 318.50399,40 313.33334,40 Z m 49.77777,0 c -5.17066,0 -9.33333,4.162668 -9.33333,9.333334 V 198.66665 c 0,5.17067 4.16267,9.33335 9.33333,9.33335 5.17066,0 9.33333,-4.16268 9.33333,-9.33335 V 49.333334 C 372.44444,44.162668 368.28177,40 363.11111,40 Z m 49.77778,0 c -5.17066,0 -9.33333,4.162668 -9.33333,9.333334 V 198.66665 c 0,5.17067 4.16267,9.33335 9.33333,9.33335 5.17066,0 9.33333,-4.16268 9.33333,-9.33335 V 49.333334 C 422.22222,44.162668 418.05955,40 412.88889,40 Z m 49.77777,0 c -5.17065,0 -9.33332,4.162668 -9.33332,9.333334 V 198.66665 c 0,5.17067 4.16267,9.33335 9.33332,9.33335 C 467.83733,208 472,203.83732 472,198.66665 V 49.333334 C 472,44.162668 467.83733,40 462.66666,40 Z" /> 46 + <path 47 + id="use60-1" 48 + style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" 49 + d="M 49.333334,40 C 44.162667,40 40,44.162668 40,49.333333 V 198.66664 C 40,203.83732 44.162667,208 49.333334,208 c 5.170665,0 9.333333,-4.16268 9.333333,-9.33336 V 49.333333 C 58.666667,44.162668 54.503999,40 49.333334,40 Z m 49.777781,0 c -5.170672,0 -9.333336,4.162668 -9.333336,9.333333 V 198.66664 c 0,5.17068 4.162664,9.33336 9.333336,9.33336 5.170645,0 9.333325,-4.16268 9.333325,-9.33336 V 49.333333 C 108.44444,44.162668 104.28176,40 99.111115,40 Z m 49.777765,0 c -5.17064,0 -9.33332,4.162668 -9.33332,9.333333 V 198.66664 c 0,5.17068 4.16268,9.33336 9.33332,9.33336 5.17066,0 9.33334,-4.16268 9.33334,-9.33336 V 49.333333 C 158.22222,44.162668 154.05954,40 148.88888,40 Z m 49.77777,0 c -5.17064,0 -9.33332,4.162668 -9.33332,9.333333 V 198.66664 c 0,5.17068 4.16268,9.33336 9.33332,9.33336 C 203.83733,208 208,203.83732 208,198.66664 V 49.333333 C 208,44.162668 203.83733,40 198.66665,40 Z" /> 50 + <path 51 + id="use60-3" 52 + style="fill:#193045;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:0;paint-order:normal" 53 + d="M 49.333334,304 C 44.162667,304 40,308.16267 40,313.33334 V 462.66666 C 40,467.83733 44.162667,472 49.333334,472 c 5.170665,0 9.333333,-4.16267 9.333333,-9.33334 V 313.33334 C 58.666667,308.16267 54.503999,304 49.333334,304 Z m 49.777777,0 c -5.170668,0 -9.333332,4.16267 -9.333332,9.33334 v 149.33332 c 0,5.17067 4.162664,9.33334 9.333332,9.33334 5.170659,0 9.333329,-4.16267 9.333329,-9.33334 V 313.33334 c 0,-5.17067 -4.16267,-9.33334 -9.333329,-9.33334 z m 49.777779,0 c -5.17065,0 -9.33333,4.16267 -9.33333,9.33334 v 149.33332 c 0,5.17067 4.16268,9.33334 9.33333,9.33334 5.17065,0 9.33333,-4.16267 9.33333,-9.33334 V 313.33334 c 0,-5.17067 -4.16268,-9.33334 -9.33333,-9.33334 z m 49.77777,0 c -5.17065,0 -9.33333,4.16267 -9.33333,9.33334 v 149.33332 c 0,5.17067 4.16268,9.33334 9.33333,9.33334 C 203.83733,472 208,467.83733 208,462.66666 V 313.33334 C 208,308.16267 203.83733,304 198.66666,304 Z" /> 54 + <path 55 + style="font-style:italic;font-weight:bold;font-size:151.33px;font-family:'Droid Sans Thai';-inkscape-font-specification:'Droid Sans Thai Bold Italic';text-align:center;white-space:pre;fill:#ffffff;stroke-width:24.8853;stroke-linejoin:round;stroke-opacity:0" 56 + d="m 376.56929,472 q -17.82771,0 -31.46068,-5.42608 -13.63295,-5.21741 -22.86141,-14.8174 -9.01873,-9.59999 -13.63297,-22.33043 Q 304,416.69565 304,402.08695 q 0,-18.9913 5.03371,-34.43478 5.24343,-15.44346 14.4719,-27.33912 9.4382,-11.89566 21.81274,-20.03479 12.37453,-8.13914 26.84645,-12.10436 Q 386.84643,304 402.36705,304 q 21.81272,0 37.33333,7.51305 15.52059,7.51302 23.9101,21.70435 Q 472,347.40869 472,367.44346 q 0,9.8087 -2.30712,19.40873 -2.09736,9.39129 -6.29212,17.73913 -4.19476,8.13911 -10.48691,14.39998 -6.0824,6.05217 -14.05242,9.59999 -7.97004,3.54785 -17.61797,3.54785 -8.1798,0 -14.26219,-3.54785 -6.08238,-3.75651 -7.97003,-11.47826 h -1.04869 q -4.40451,6.88696 -10.48689,11.06088 -5.87266,3.96523 -15.73035,3.96523 -11.53557,0 -19.50563,-7.30436 -7.97002,-7.51304 -7.97002,-24.20868 0,-8.34785 2.30711,-16.48698 2.51685,-8.34781 6.92136,-15.65217 4.61422,-7.30433 11.11611,-12.73044 6.50186,-5.63477 14.89137,-8.76521 8.38952,-3.13044 18.24719,-3.13044 11.32584,0 19.2959,1.66957 7.97003,1.66957 14.26218,3.96521 l -10.90637,42.78261 q -1.0487,4.59131 -1.88765,7.93044 -0.83895,3.13044 -0.83895,6.46958 0,3.75651 1.6779,5.42607 1.6779,1.46088 4.19476,1.46088 4.4045,0 8.38951,-2.50436 3.98501,-2.71303 7.13108,-7.30434 3.14607,-4.59131 5.45317,-10.43479 2.30712,-6.05217 3.56556,-12.52173 1.25841,-6.46958 1.25841,-12.93913 0,-13.77391 -5.45317,-23.7913 -5.24345,-10.01741 -16.1498,-15.44348 -10.90638,-5.4261 -27.89514,-5.4261 -12.37453,0 -23.49064,3.54783 -11.11609,3.33913 -20.13482,10.01738 -9.01876,6.46959 -15.73036,15.86088 -6.50185,9.39131 -10.06741,21.28697 -3.56554,11.89566 -3.56554,25.87826 0,14.1913 5.24344,26.29565 5.24346,12.10436 16.77902,19.40868 11.53561,7.09566 30.20227,7.09566 13.00373,0 23.49064,-2.29565 Q 413.2734,448 424.17977,443.82608 v 18.99132 q -9.85767,3.96521 -21.81272,6.46955 Q 390.62173,472 376.56929,472 Z m 0.62921,-58.43478 q 8.17977,0 13.21348,-7.51305 5.24346,-7.72174 8.59926,-20.03478 l 5.87265,-22.53914 q -1.88764,-0.41737 -3.77528,-0.62607 -1.88762,-0.41741 -4.61422,-0.41741 -7.34082,0 -12.79402,3.75653 -5.45317,3.54783 -9.01872,9.3913 -3.56555,5.63477 -5.45319,12.10436 -1.6779,6.26086 -1.6779,11.68695 0,7.93045 2.93634,11.06087 2.93631,3.13044 6.7116,3.13044 z" 57 + id="text63" 58 + aria-label="@" /> 59 + <rect 60 + style="fill:#000000;fill-opacity:1;stroke-width:0;stroke-linejoin:round;stroke-opacity:0;paint-order:markers fill stroke" 61 + id="rect1" 62 + width="512" 63 + height="16" 64 + x="0" 65 + y="248" /> 66 + <rect 67 + style="fill:#000000;fill-opacity:1;stroke-width:0;stroke-linejoin:round;stroke-opacity:0;paint-order:markers fill stroke" 68 + id="rect2" 69 + width="16" 70 + height="512" 71 + x="248" 72 + y="0" /> 14 73 </g> 15 - </svg></svg><style>@media (prefers-color-scheme: light) { :root { filter: none; } } 74 + <style 75 + id="style4">@media (prefers-color-scheme: light) { :root { filter: none; } } 16 76 @media (prefers-color-scheme: dark) { :root { filter: none; } } 17 - </style></svg> 77 + </style> 78 + </svg>
pkg/appview/public/web-app-manifest-192x192.png

This is a binary file and will not be displayed.

pkg/appview/public/web-app-manifest-512x512.png

This is a binary file and will not be displayed.

+52 -17
pkg/appview/server.go
··· 9 9 "log/slog" 10 10 "net" 11 11 "net/http" 12 + "net/url" 12 13 "os" 13 14 "os/signal" 14 15 "strings" 15 16 "syscall" 16 17 "time" 17 18 19 + "github.com/distribution/distribution/v3/registry/api/errcode" 18 20 "github.com/distribution/distribution/v3/registry/handlers" 19 21 "github.com/go-chi/chi/v5" 20 22 chimiddleware "github.com/go-chi/chi/v5/middleware" ··· 239 241 mainRouter.Use(chimiddleware.GetHead) 240 242 mainRouter.Use(routes.CORSMiddleware()) 241 243 242 - // Registry domain redirect middleware 244 + // Domain routing middleware 243 245 if len(cfg.Server.RegistryDomains) > 0 { 244 - mainRouter.Use(RegistryDomainRedirect(cfg.Server.RegistryDomains, cfg.Server.BaseURL)) 245 - slog.Info("Registry domain redirect enabled", 246 + mainRouter.Use(DomainRoutingMiddleware(cfg.Server.RegistryDomains, cfg.Server.BaseURL)) 247 + slog.Info("Domain routing middleware enabled", 246 248 "registry_domains", cfg.Server.RegistryDomains, 247 249 "ui_base_url", cfg.Server.BaseURL) 248 250 } ··· 580 582 ) 581 583 } 582 584 583 - // RegistryDomainRedirect redirects all non-registry requests from registry 584 - // domains to the UI domain. Only /v2 and /v2/* pass through for Docker clients. 585 - // Uses 307 (Temporary Redirect) to preserve POST method/body. 586 - func RegistryDomainRedirect(registryDomains []string, uiBaseURL string) func(http.Handler) http.Handler { 587 - domains := make(map[string]bool, len(registryDomains)) 585 + // DomainRoutingMiddleware enforces three-tier domain routing: 586 + // 587 + // 1. UI domain (BaseURL hostname): serves web UI, auth, and static assets. 588 + // Blocks /v2/* with an OCI UNSUPPORTED error — registry API lives on 589 + // the dedicated registry domain(s). 590 + // 2. Registry domains: allows /v2/* for Docker clients. Redirects everything 591 + // else to the UI domain with 307 Temporary Redirect. 592 + // 3. Unknown domains (CDN origins, IPs, etc.): redirects all requests to the 593 + // UI domain with 307, except /health for load balancer probes. 594 + func DomainRoutingMiddleware(registryDomains []string, uiBaseURL string) func(http.Handler) http.Handler { 595 + regDomains := make(map[string]bool, len(registryDomains)) 588 596 for _, d := range registryDomains { 589 - domains[d] = true 597 + regDomains[d] = true 598 + } 599 + 600 + // Extract UI hostname from BaseURL (e.g., "https://seamark.dev" -> "seamark.dev") 601 + var uiHost string 602 + if parsed, err := url.Parse(uiBaseURL); err == nil { 603 + uiHost = parsed.Hostname() 590 604 } 591 605 606 + primaryReg := primaryRegistryDomain(registryDomains) 607 + 592 608 return func(next http.Handler) http.Handler { 593 609 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 594 610 host := r.Host ··· 596 612 host = host[:idx] 597 613 } 598 614 599 - if domains[host] { 600 - path := r.URL.Path 601 - if path == "/v2" || path == "/v2/" || strings.HasPrefix(path, "/v2/") { 615 + path := r.URL.Path 616 + isV2 := path == "/v2" || path == "/v2/" || strings.HasPrefix(path, "/v2/") 617 + 618 + switch { 619 + case host == uiHost: 620 + // UI domain: block /v2/*, serve everything else 621 + if isV2 { 622 + if err := errcode.ServeJSON(w, errcode.ErrorCodeUnsupported.WithMessage( 623 + fmt.Sprintf("registry API is not available on this domain, use %s", primaryReg), 624 + )); err != nil { 625 + slog.Error("failed to write OCI error response", "error", err) 626 + } 627 + return 628 + } 629 + next.ServeHTTP(w, r) 630 + 631 + case regDomains[host]: 632 + // Registry domain: allow /v2/*, redirect everything else 633 + if isV2 { 602 634 next.ServeHTTP(w, r) 603 635 return 604 636 } 637 + http.Redirect(w, r, uiBaseURL+r.URL.RequestURI(), http.StatusTemporaryRedirect) 605 638 606 - target := uiBaseURL + r.URL.RequestURI() 607 - http.Redirect(w, r, target, http.StatusTemporaryRedirect) 608 - return 639 + default: 640 + // Unknown domain: allow /health, redirect everything else 641 + if path == "/health" { 642 + next.ServeHTTP(w, r) 643 + return 644 + } 645 + http.Redirect(w, r, uiBaseURL+r.URL.RequestURI(), http.StatusTemporaryRedirect) 609 646 } 610 - 611 - next.ServeHTTP(w, r) 612 647 }) 613 648 } 614 649 }