diff --git a/bin/dsadmin.js b/bin/dsadmin.js index ba4952c..c526f86 100755 --- a/bin/dsadmin.js +++ b/bin/dsadmin.js @@ -29,6 +29,11 @@ const args = yargs(process.argv) "Datastore emulator hostname & port. Defaults to DATASTORE_EMULATOR_HOST environment variable.", demandOption: true, }) + .option("base-path", { + type: "string", + default: "", + description: "Base HTTP path to serve the interface on", + }) .option("port", { type: "number", default: 8080, @@ -42,8 +47,11 @@ if (emulatorHost.length != 2) { } emulatorHost[1] = parseInt(emulatorHost[1], 10); +const basePath = args.basePath; + const dsadminEnv = { DATASTORE_PROJECT_ID: args.project, + BASE_PATH: basePath, }; const index = fs @@ -54,7 +62,8 @@ const index = fs `
`, - ); + ) + .replaceAll("{{BASE}}", basePath); function serveIndex(req, res) { res.writeHead(200); @@ -64,10 +73,16 @@ function serveIndex(req, res) { var file = new static.Server(publicDir); console.log( - `dsadmin (project ${args.project}) listening on http://localhost:${args.port}`, + `dsadmin (project ${args.project}) listening on http://localhost:${args.port}${basePath}`, ); http .createServer(function (req, res) { + if (!req.url.startsWith(basePath)) { + res.writeHead(400); + res.end("not found"); + return; + } + req.url = req.url.substring(basePath.length); if (req.url.startsWith("/v1/")) { proxy.web(req, res, { hostname: emulatorHost[0], diff --git a/cmd/dsadmin/dsadmin.go b/cmd/dsadmin/dsadmin.go index f054418..def9246 100644 --- a/cmd/dsadmin/dsadmin.go +++ b/cmd/dsadmin/dsadmin.go @@ -6,8 +6,8 @@ import ( "flag" "fmt" "html/template" + "io" "io/fs" - "io/ioutil" "log" "net/http" "net/http/httputil" @@ -30,6 +30,7 @@ func NewAssetFS(root fs.FS, fallback []byte) http.Handler { func main() { project := flag.String("project", os.Getenv("DATASTORE_PROJECT_ID"), "Datastore project ID. Defaults to DATASTORE_PROJECT_ID environment variable.") port := flag.Uint("port", 8080, "Port to listen on") + basePath := flag.String("base-path", "", "Base HTTP path to serve the interface on") defaultEmulatorHost := os.Getenv("DATASTORE_EMULATOR_HOST") if defaultEmulatorHost == "" { defaultEmulatorHost = "localhost:8081" @@ -47,27 +48,29 @@ func main() { if err != nil { panic(err) } - index, err := ioutil.ReadAll(f) + index, err := io.ReadAll(f) if err != nil { panic(err) } dsadminEnv, err := json.Marshal(map[string]interface{}{ "DATASTORE_PROJECT_ID": project, + "BASE_PATH": basePath, }) if err != nil { panic(err) } index = bytes.Replace(index, []byte(""), []byte(fmt.Sprintf("", template.JSEscapeString(string(dsadminEnv)))), 1) + index = bytes.Replace(index, []byte("{{BASE}}"), []byte(*basePath), -1) - http.Handle("/v1/", &httputil.ReverseProxy{ + http.Handle(*basePath+"/v1/", http.StripPrefix(*basePath, &httputil.ReverseProxy{ Director: func(req *http.Request) { req.URL.Scheme = "http" req.URL.Host = *emulatorHost req.Host = *emulatorHost }}, - ) - http.Handle("/", NewAssetFS(dsadmin.WebAppAssets, index)) + )) + http.Handle(*basePath+"/", http.StripPrefix(*basePath, NewAssetFS(dsadmin.WebAppAssets, index))) - log.Printf("dsadmin (project '%s') listening on http://localhost:%d", *project, *port) + log.Printf("dsadmin (project '%s') listening on http://localhost:%d%s", *project, *port, *basePath) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)) } diff --git a/public/index.html b/public/index.html index 025a64e..bb79da9 100644 --- a/public/index.html +++ b/public/index.html @@ -2,14 +2,14 @@ - +