This commit is contained in:
Rainer Leit
2026-09-25 17:02:24 +03:00
parent cc43ed8dc8
commit 9597629951
2149 changed files with 460234 additions and 1770 deletions
+52
View File
@@ -0,0 +1,52 @@
package studio
import (
"net/http"
"os"
)
// Sharing the studio's files with another tool on the same machine.
//
// World Orogen (Tools/Orogen, D-66) reads the same painting and the same legends this studio edits, and until
// now the only way to get them there was a file picker: choose the PNG, choose the legend, choose the overlay,
// choose its legend, retype the manifest's numbers. The studio already holds every one of those - the painting
// in memory, exactly as the next plan will read it - so it serves them, and Orogen loads a planet with one
// button.
//
// Two rules keep this from turning the studio into something a web page can drive:
//
// - **Only GET is shared.** The CORS header goes on GET responses and nothing else, and no preflight is ever
// answered. A cross-origin POST with a JSON body needs a preflight, so every endpoint that paints, saves,
// plans or bakes stays reachable from this page and from nothing else. The studio listens on loopback, but
// a browser on the same machine visits other origins all day, and "any tab can start a two-hour bake" is
// not a property to give away for a convenience.
// - **The files are served as they are on disk.** The legend and the manifest are text somebody wrote, with
// commentary; Orogen reads the same keys the plan does and ignores the rest. Nothing is re-marshalled, so
// what Orogen sees is byte for byte what `terrain plan` will see.
// readOnlyCORS lets any origin *read* the API and touches nothing else.
func readOnlyCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet || r.Method == http.MethodHead {
w.Header().Set("Access-Control-Allow-Origin", "*")
}
h.ServeHTTP(w, r)
})
}
// serveJSONFile answers a GET for one of the planet's JSON files, or says there is none. The POST handlers
// call it first and return, so an endpoint that edits a file also hands the file out.
func serveJSONFile(w http.ResponseWriter, path string) {
if path == "" {
http.Error(w, "the manifest names no such file", http.StatusNotFound)
return
}
data, err := os.ReadFile(path)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
_, _ = w.Write(data)
}