diff --git a/db/db.go b/db/db.go
index 6e574d7e..8e1963cc 100644
--- a/db/db.go
+++ b/db/db.go
@@ -679,7 +679,7 @@ func (db *Database) GetLineages(limitStr string) (lineages []types.Lineage) {
return
}
-// DefaultVersion returns the detault VersionID for a given State path
+// DefaultVersion returns the default VersionID for a given Lineage
// Copied and adapted from github.com/hashicorp/terraform/command/jsonstate/state.go
func (db *Database) DefaultVersion(lineage string) (version string, err error) {
sqlQuery := "SELECT versions.version_id FROM" +
@@ -687,7 +687,8 @@ func (db *Database) DefaultVersion(lineage string) (version string, err error) {
" JOIN states ON t.path = states.path AND t.mx = states.serial" +
" JOIN versions on states.version_id=versions.id" +
" JOIN lineages on lineages.id=states.lineage_id" +
- " WHERE lineages.value = ?"
+ " WHERE lineages.value = ?" +
+ " ORDER BY versions.last_modified DESC"
row := db.Raw(sqlQuery, lineage).Row()
err = row.Scan(&version)
diff --git a/main.go b/main.go
index 7eded273..41465768 100644
--- a/main.go
+++ b/main.go
@@ -5,6 +5,8 @@ import (
"fmt"
"io"
"net/http"
+ "os"
+ "path/filepath"
"time"
"github.com/camptocamp/terraboard/api"
@@ -189,13 +191,8 @@ func main() {
apiRouter.HandleFunc(util.GetFullPath("plans"), handleWithDB(api.ManagePlans, database))
// Serve static files (CSS, JS, images) from dir
- staticFs := http.FileServer(http.Dir("static"))
- r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticFs))
-
- // Serve index page on all unhandled routes
- r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- http.ServeFile(w, r, "./static/index.html")
- })
+ spa := spaHandler{staticPath: "static", indexPath: "index.html"}
+ r.PathPrefix("/").Handler(spa)
// Add CORS Middleware to mux router
r.Use(corsMiddleware)
@@ -204,3 +201,46 @@ func main() {
log.Debugf("Listening on port %d\n", c.Web.Port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", c.Web.Port), r))
}
+
+// spaHandler implements the http.Handler interface, so we can use it
+// to respond to HTTP requests. The path to the static directory and
+// path to the index file within that static directory are used to
+// serve the SPA in the given static directory.
+type spaHandler struct {
+ staticPath string
+ indexPath string
+}
+
+// ServeHTTP inspects the URL path to locate a file within the static dir
+// on the SPA handler. If a file is found, it will be served. If not, the
+// file located at the index path on the SPA handler will be served. This
+// is suitable behavior for serving an SPA (single page application).
+func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // get the absolute path to prevent directory traversal
+ path, err := filepath.Abs(r.URL.Path)
+ if err != nil {
+ // if we failed to get the absolute path respond with a 400 bad request
+ // and stop
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ // prepend the path with the path to the static directory
+ path = filepath.Join(h.staticPath, path)
+
+ // check whether a file exists at the given path
+ _, err = os.Stat(path)
+ if os.IsNotExist(err) {
+ // file does not exist, serve index.html
+ http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
+ return
+ } else if err != nil {
+ // if we got an error (that wasn't that the file doesn't exist) stating the
+ // file, return a 500 internal server error and stop
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // otherwise, use http.FileServer to serve the static dir
+ http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
+}
diff --git a/static/terraboard-vuejs/public/index.html b/static/terraboard-vuejs/public/index.html
index 397d1dd0..82641af9 100644
--- a/static/terraboard-vuejs/public/index.html
+++ b/static/terraboard-vuejs/public/index.html
@@ -10,7 +10,6 @@
-
diff --git a/static/terraboard-vuejs/src/components/Charts.vue b/static/terraboard-vuejs/src/components/Charts.vue
index 27ddf881..4d62eba7 100644
--- a/static/terraboard-vuejs/src/components/Charts.vue
+++ b/static/terraboard-vuejs/src/components/Charts.vue
@@ -152,7 +152,7 @@ const chartOptionsLocked =
});
},
fetchVersions(): void {
- const url = `http://localhost:8080/api/states/tfversion/count?orderBy=version`;
+ const url = `http://localhost:8080/api/lineages/tfversion/count?orderBy=version`;
axios.get(url)
.then((response) => {
response.data.forEach((value: any, i: number) => {
@@ -259,7 +259,7 @@ const chartOptionsLocked =
this.fetchResourceTypes();
this.fetchVersions();
- const url = `http://localhost:8080/api/states/stats?page=1`;
+ const url = `http://localhost:8080/api/lineages/stats?page=1`;
axios.get(url)
.then((response) => {
this.statesTotal = response.data.total;
diff --git a/static/terraboard-vuejs/src/components/HelloWorld.vue b/static/terraboard-vuejs/src/components/HelloWorld.vue
deleted file mode 100644
index da2f6fb9..00000000
--- a/static/terraboard-vuejs/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
{{ msg }}
-
- For a guide and recipes on how to configure / customize this project,
- check out the
- vue-cli documentation.
-