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 @@ - - - - - - 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 },