diff --git a/app/scanningcabinet/handler.go b/app/scanningcabinet/handler.go index 2607ef661..9b3b470b1 100644 --- a/app/scanningcabinet/handler.go +++ b/app/scanningcabinet/handler.go @@ -30,11 +30,13 @@ import ( "strings" "time" + uistatic "camlistore.org/app/scanningcabinet/ui" "camlistore.org/pkg/app" "camlistore.org/pkg/auth" "camlistore.org/pkg/blob" "camlistore.org/pkg/client" "camlistore.org/pkg/constants" + "camlistore.org/pkg/fileembed" "camlistore.org/pkg/httputil" "camlistore.org/pkg/magic" "camlistore.org/pkg/search" @@ -61,9 +63,10 @@ var ( // config is used to unmarshal the application configuration JSON // that we get from Camlistore when we request it at $CAMLI_APP_CONFIG_URL. type extraConfig struct { - Auth string `json:"auth,omitempty"` // userpass:username:password - HTTPSCert string `json:"httpsCert,omitempty"` // path to the HTTPS certificate file. - HTTPSKey string `json:"httpsKey,omitempty"` // path to the HTTPS key file. + Auth string `json:"auth,omitempty"` // userpass:username:password + HTTPSCert string `json:"httpsCert,omitempty"` // path to the HTTPS certificate file. + HTTPSKey string `json:"httpsKey,omitempty"` // path to the HTTPS key file. + SourceRoot string `json:"sourceRoot,omitempty"` // Path to the app's resources dir, such as html and css files. } func appConfig() (*extraConfig, error) { @@ -107,12 +110,22 @@ func newHandler() (*handler, error) { cl: cl, } + config, err := appConfig() + if err != nil { + return nil, err + } + + // Serve files from source root when running devcam + if config.SourceRoot != "" { + log.Printf("Using UI resources (HTML, JS, CSS) from disk, under %v", config.SourceRoot) + uistatic.Files = &fileembed.Files{ + DirFallback: config.SourceRoot, + } + } + mux := http.NewServeMux() mux.HandleFunc("/", h.handleRoot) - mux.HandleFunc("/scanner.css", func(w http.ResponseWriter, r *http.Request) { - // TODO(mpl): set proper MIME type - w.Write([]byte(scannerCSS)) - }) + mux.HandleFunc("/ui/", handleUiFile) mux.HandleFunc("/uploadurl", h.handleUploadURL) mux.HandleFunc("/upload", h.handleUpload) mux.HandleFunc("/resource/", h.handleResource) @@ -126,10 +139,6 @@ func newHandler() (*handler, error) { return nil, err } - config, err := appConfig() - if err != nil { - return nil, err - } if config != nil { h.httpsCert = config.HTTPSCert h.httpsKey = config.HTTPSKey @@ -630,3 +639,28 @@ func (h *handler) handleChangedoc(w http.ResponseWriter, r *http.Request) { func handleRobots(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "User-agent: *\nDisallow: /\n") } + +func handleUiFile(w http.ResponseWriter, r *http.Request) { + file := strings.TrimPrefix(r.URL.Path, "/ui") + + root := uistatic.Files + + f, err := root.Open("/" + file) + if err != nil { + http.NotFound(w, r) + // TODO(stevearm): Replace all log.Printf with logf for proper prefixing + log.Printf("Failed to open file %v from embedded resources: %v", file, err) + return + } + defer f.Close() + var modTime time.Time + if fi, err := f.Stat(); err == nil { + modTime = fi.ModTime() + } + if strings.HasSuffix(file, ".css") { + w.Header().Set("Content-Type", "text/css") + } else if strings.HasSuffix(file, ".js") { + w.Header().Set("Content-Type", "application/javascript") + } + http.ServeContent(w, r, file, modTime, f) +} diff --git a/app/scanningcabinet/templates.go b/app/scanningcabinet/templates.go index 95b2bc820..5b9b823aa 100644 --- a/app/scanningcabinet/templates.go +++ b/app/scanningcabinet/templates.go @@ -25,7 +25,7 @@ var rootHTML = ` Scanning Cabinet - + {{ if .AllTags }} @@ -152,7 +152,7 @@ var docHTML = ` Scanning Cabinet - +
[Scanning Cabinet]
@@ -203,18 +203,3 @@ var docHTML = ` {{ end }} {{ end }} ` - -var scannerCSS = ` -.word-cloud { - width: 60em; -} - -.doc-page-single { - border: 1px solid grey; - display: block; -} - -.doc-page-row { - border: 1px solid grey; -} -` diff --git a/app/scanningcabinet/ui/fileembed.go b/app/scanningcabinet/ui/fileembed.go new file mode 100644 index 000000000..1452b867d --- /dev/null +++ b/app/scanningcabinet/ui/fileembed.go @@ -0,0 +1,30 @@ +/* +Copyright 2017 The Camlistore Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +TODO(steve.armstrong): Until app/scanningcabinet/handler.go handleUiFile() +properly manages Content-Type, be sure to update it whenever adding a new +file type to the pattern below. + +#fileembed pattern .+\.(js|css)$ +*/ +package ui // import "camlistore.org/app/scanningcabinet/ui" + +import ( + "camlistore.org/pkg/fileembed" +) + +var Files = &fileembed.Files{} diff --git a/app/scanningcabinet/ui/scanner.css b/app/scanningcabinet/ui/scanner.css new file mode 100644 index 000000000..85b05203f --- /dev/null +++ b/app/scanningcabinet/ui/scanner.css @@ -0,0 +1,28 @@ +/* +Copyright 2017 The Camlistore Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.word-cloud { + width: 60em; +} + +.doc-page-single { + border: 1px solid grey; + display: block; +} + +.doc-page-row { + border: 1px solid grey; +} diff --git a/config/dev-server-config.json b/config/dev-server-config.json index 4e0b99986..17e6b2120 100644 --- a/config/dev-server-config.json +++ b/config/dev-server-config.json @@ -39,7 +39,10 @@ "handlerArgs": { "prefix": "/scancab/", "serverListen": "localhost:3179", - "program": "scanningcabinet" + "program": "scanningcabinet", + "appConfig": { + "sourceRoot": ["_env", "${CAMLI_DEV_CAMLI_ROOT}/app/scanningcabinet/ui", ""] + } } }, diff --git a/make.go b/make.go index a838bc0c9..a83610484 100644 --- a/make.go +++ b/make.go @@ -821,7 +821,7 @@ func genEmbeds() error { if runtime.GOOS == "windows" { cmdName += ".exe" } - for _, embeds := range []string{"server/camlistored/ui", "pkg/server", "vendor/embed/react", "vendor/embed/less", "vendor/embed/glitch", "vendor/embed/fontawesome", "app/publisher"} { + for _, embeds := range []string{"server/camlistored/ui", "pkg/server", "vendor/embed/react", "vendor/embed/less", "vendor/embed/glitch", "vendor/embed/fontawesome", "app/publisher", "app/scanningcabinet/ui"} { embeds := buildSrcPath(embeds) args := []string{"--output-files-stderr", embeds} cmd := exec.Command(cmdName, args...)