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/Navbar.vue b/static/terraboard-vuejs/src/components/Navbar.vue
index ea02462e..dd5f4ac9 100644
--- a/static/terraboard-vuejs/src/components/Navbar.vue
+++ b/static/terraboard-vuejs/src/components/Navbar.vue
@@ -94,18 +94,18 @@ import router from "../router";
},
goToState(value: any) {
if (value != null) {
- router.push({name: "State", params: {lineage: value.lineage}, query: { versionid: value.version_id } });
+ router.push({name: "State", params: {lineage: value.lineage_value}, query: { versionid: value.version_id } });
}
},
clearSelect() {
this.$refs.quickAccess.clear()
},
fetchStates() {
- const url = `http://localhost:8080/api/states_lineages`
+ const url = `http://localhost:8080/api/lineages/stats`
axios.get(url)
.then((response) => {
// handle success
- response.data.forEach((obj: any) => {
+ response.data.states.forEach((obj: any) => {
let entry = {value: obj, label: obj.path}
this.states_select.options.push(entry)
});
diff --git a/static/terraboard-vuejs/src/components/StatesList.vue b/static/terraboard-vuejs/src/components/StatesList.vue
index ba125bc1..da416e95 100644
--- a/static/terraboard-vuejs/src/components/StatesList.vue
+++ b/static/terraboard-vuejs/src/components/StatesList.vue
@@ -95,7 +95,7 @@ Chart.register( CategoryScale, LineElement, LineController, LinearScale, PointEl
return false;
},
getActivity(idx: number, lineage: string, elementId: string): void {
- const url = `http://localhost:8080/api/lineage/activity/`+lineage;
+ const url = `http://localhost:8080/api/lineages/` + lineage + `/activity`;
axios.get(url)
.then((response) => {
let states = response.data;
@@ -193,7 +193,7 @@ Chart.register( CategoryScale, LineElement, LineController, LinearScale, PointEl
this.itemsInPage = Math.min(this.itemsPerPage * this.page, this.results.total);
},
fetchStats(page: number): void {
- const url = `http://localhost:8080/api/states/stats?page=`+page;
+ const url = `http://localhost:8080/api/lineages/stats?page=`+page;
axios.get(url)
.then((response) => {
this.updatePager(response);
diff --git a/static/terraboard-vuejs/src/views/State.vue b/static/terraboard-vuejs/src/views/State.vue
index 15131f4a..6a481e69 100644
--- a/static/terraboard-vuejs/src/views/State.vue
+++ b/static/terraboard-vuejs/src/views/State.vue
@@ -221,7 +221,7 @@ import StatesCompare from "../components/StatesCompare.vue";
},
getVersions(): void {
const url =
- `http://localhost:8080/api/lineage/activity/` + this.url.lineage;
+ `http://localhost:8080/api/lineages/` + this.url.lineage + `/activity`;
axios
.get(url)
.then((response) => {
@@ -281,7 +281,7 @@ import StatesCompare from "../components/StatesCompare.vue";
versionId = "";
}
const url =
- "http://localhost:8080/api/state/" +
+ "http://localhost:8080/api/lineages/" +
this.url.lineage +
"?versionid=" +
versionId +
@@ -323,9 +323,9 @@ import StatesCompare from "../components/StatesCompare.vue";
this.display.compare = true;
const url =
- `http://localhost:8080/api/state/compare/` +
+ `http://localhost:8080/api/lineages/` +
this.url.lineage +
- "?from=" +
+ "/compare?from=" +
this.selectedVersion +
"&to=" +
this.compareVersion;
@@ -411,7 +411,7 @@ import StatesCompare from "../components/StatesCompare.vue";
},
"$data.selectedVersion": {
handler: function(nv, ov) {
- router.replace({
+ router.push({
name: "State",
params: { lineage: this.url.lineage },
query: { versionid: nv },