feat: add withCORSMiddleware for CORS support

sinwoojin committed Dec 15, 2025 at 13:38 UTC 598b54aded9e29290d48d434ab2371c07182741a
1 file changed +19 -28
cmd/relay-server/serve.go
+19 -28
@@ -44,23 +44,13 @@ func serveHTTP(addr string, serv *portal.RelayServer, admin *Admin, frontend *Fr
44 }
45
46 // Portal app assets (JS, CSS, etc.) - served from /app/
47 - appMux.HandleFunc("/app/", func(w http.ResponseWriter, r *http.Request) {
48 - utils.SetCORSHeaders(w)
49 - if r.Method == http.MethodOptions {
50 - w.WriteHeader(http.StatusOK)
51 - return
52 - }
47 + appMux.HandleFunc("/app/", withCORSMiddleware(func(w http.ResponseWriter, r *http.Request) {
48 p := strings.TrimPrefix(r.URL.Path, "/app/")
49 frontend.ServeAppStatic(w, r, p, serv)
55 - })
50 + }))
51
52 // Portal frontend files (for unified caching)
58 - appMux.HandleFunc("/frontend/", func(w http.ResponseWriter, r *http.Request) {
59 - utils.SetCORSHeaders(w)
60 - if r.Method == http.MethodOptions {
61 - w.WriteHeader(http.StatusOK)
62 - return
63 - }
53 + appMux.HandleFunc("/frontend/", withCORSMiddleware(func(w http.ResponseWriter, r *http.Request) {
54 p := strings.TrimPrefix(r.URL.Path, "/frontend/")
55 if p == "manifest.json" {
56 frontend.ServeDynamicManifest(w, r)
@@ -68,7 +58,7 @@ func serveHTTP(addr string, serv *portal.RelayServer, admin *Admin, frontend *Fr
58 }
59
60 frontend.ServePortalStaticFile(w, r, p)
71 - })
61 + }))
62
63 // Tunnel installer script and binaries
64 appMux.HandleFunc("/tunnel", func(w http.ResponseWriter, r *http.Request) {
@@ -133,19 +123,14 @@ func serveHTTP(addr string, serv *portal.RelayServer, admin *Admin, frontend *Fr
123 portalMux := http.NewServeMux()
124
125 // Static file handler for /frontend/ (for unified caching)
136 - portalMux.HandleFunc("/frontend/", func(w http.ResponseWriter, r *http.Request) {
137 - utils.SetCORSHeaders(w)
138 - if r.Method == http.MethodOptions {
139 - w.WriteHeader(http.StatusOK)
140 - return
141 - }
126 + portalMux.HandleFunc("/frontend/", withCORSMiddleware(func(w http.ResponseWriter, r *http.Request) {
127 p := strings.TrimPrefix(r.URL.Path, "/frontend/")
128 if p == "manifest.json" {
129 frontend.ServeDynamicManifest(w, r)
130 return
131 }
132 frontend.ServePortalStaticFile(w, r, p)
148 - })
133 + }))
134
135 // Service worker for portal subdomains (serve from dist/wasm)
136 portalMux.HandleFunc("/service-worker.js", func(w http.ResponseWriter, r *http.Request) {
@@ -153,19 +138,14 @@ func serveHTTP(addr string, serv *portal.RelayServer, admin *Admin, frontend *Fr
138 })
139
140 // Root and SPA fallback for portal subdomains
156 - portalMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
157 - utils.SetCORSHeaders(w)
158 - if r.Method == http.MethodOptions {
159 - w.WriteHeader(http.StatusOK)
160 - return
161 - }
141 + portalMux.HandleFunc("/", withCORSMiddleware(func(w http.ResponseWriter, r *http.Request) {
142 if r.URL.Path == "/" {
143 // Serve portal HTML from dist/wasm
144 frontend.ServeStaticFile(w, r, "portal.html", "text/html; charset=utf-8")
145 return
146 }
147 frontend.ServePortalStatic(w, r)
168 - })
148 + }))
149
150 // routes based on host and path
151 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -194,6 +174,17 @@ func serveHTTP(addr string, serv *portal.RelayServer, admin *Admin, frontend *Fr
174 return srv
175 }
176
177 +func withCORSMiddleware(h http.HandlerFunc) http.HandlerFunc {
178 + return func(w http.ResponseWriter, r *http.Request) {
179 + utils.SetCORSHeaders(w)
180 + if r.Method == http.MethodOptions {
181 + w.WriteHeader(http.StatusOK)
182 + return
183 + }
184 + h(w, r)
185 + }
186 +}
187 +
188 type leaseRow struct {
189 Peer string
190 Name string