Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: front-end redesign to VueJS/Bootstrap 5 & server improvements (#…
…188)

* feat: add ListLineageStats api endpoint and model

* feat: update Terraboard overview to list by lineage

* feat: add last modified path on lineage overview

* feat: add lineage column on search view with filter

* fix(lineage): code review issues
* rename GetStateActivity functions to GetLineageActivity
* change /api/state/activity to /api/lineage/activity
* restore dynamic lock status on overview
* fix missing version_id attribute in states quick access links
* remove lineage display from overview

* refactor: remove obsolete lineage.html file

* feat(bootstrap): upgrade project to Bootstrap 4.1 and JQuery 3.3.1

* feat(bootstrap): add FontAwesome instead of old Glyphicon
* fix layout/style issues

* feat(bootstrap): update state and compare views

* feat(bootstrap): upgrade to Bootstrap 5 + visuals improvements
Now fully responsive on all screen sizes

* feat(vuejs): init vue project + router config + icon and assets

* feat(vue): add dynamic title managing through mixin

* feat(vue): add navbar and footer components

* feat(vuejs): add overview with charts powered bu Chart.JS 3

* feat(vuejs): add sparkline chart on overview
* fix(vuejs): locks chart issue

* feat(vuejs): footer version fetching controller

* feat(vuejs): callback on state quick access selection
* add lock checking on every state in the overview list with display
* router enhancements
* others fixes / improvements

* feat(vuejs): state main view with routes

* feat(vuejs): add details/outputs panels to state view

* feat(vuejs): compare view & controller

* feat(api): replace version's id by version_id field on search result

* feat(vuejs): search view / controller

* fix(vuejs): issue with dynamic title mixin + code cleanup

* feat(vuejs): clickable charts for quick search

* feat(server): edit docker envs to build Vue project
* update Go file server to handle vue project
* delete old static files
* change vue-router mode to web history

* ci: add build pipeline for VueJS

* fix: gorilla/mux issues with Vue
  • Loading branch information
hbollon committed Jul 30, 2021
1 parent c576d95 commit 5001de9
Show file tree
Hide file tree
Showing 49 changed files with 32,171 additions and 1,443 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/build-vue.yml
@@ -0,0 +1,22 @@
name: Build-VueJS

on: [push, pull_request]

jobs:
build-vue:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./static/terraboard-vuejs
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Setup NodeJS
uses: actions/setup-node@master

- name: Install dependencies
run: npm install

- name: Build project
run: npm run build
9 changes: 8 additions & 1 deletion Dockerfile
Expand Up @@ -3,10 +3,17 @@ WORKDIR /opt/build
COPY . .
RUN make build

FROM node:16.5-alpine3.14 as node-builder
WORKDIR /opt/build
COPY static/terraboard-vuejs ./terraboard-vuejs
WORKDIR /opt/build/terraboard-vuejs
RUN npm install
RUN npm run build

FROM scratch
WORKDIR /
COPY static /static
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /opt/build/terraboard /
COPY --from=node-builder /opt/build/terraboard-vuejs/dist /static
ENTRYPOINT ["/terraboard"]
CMD [""]
10 changes: 6 additions & 4 deletions db/db.go
Expand Up @@ -338,7 +338,8 @@ func (db *Database) SearchAttribute(query url.Values) (results []types.SearchRes
sqlQuery += " JOIN modules ON states.id = modules.state_id" +
" JOIN resources ON modules.id = resources.module_id" +
" JOIN attributes ON resources.id = attributes.resource_id" +
" JOIN lineages ON lineages.id = states.lineage_id"
" JOIN lineages ON lineages.id = states.lineage_id" +
" JOIN versions ON states.version_id = versions.id"

var where []string
var params []interface{}
Expand Down Expand Up @@ -388,7 +389,7 @@ func (db *Database) SearchAttribute(query url.Values) (results []types.SearchRes

// Now get results
// gorm doesn't support subqueries...
sql := "SELECT states.path, states.version_id, states.tf_version, states.serial, lineages.value as lineage_value, modules.path as module_path, resources.type, resources.name, resources.index, attributes.key, attributes.value" +
sql := "SELECT states.path, versions.version_id, states.tf_version, states.serial, lineages.value as lineage_value, modules.path as module_path, resources.type, resources.name, resources.index, attributes.key, attributes.value" +
sqlQuery +
" ORDER BY states.path, states.serial, lineage_value, modules.path, resources.type, resources.name, resources.index, attributes.key" +
" LIMIT ?"
Expand Down Expand Up @@ -678,15 +679,16 @@ 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" +
" (SELECT states.path, max(states.serial) as mx FROM states GROUP BY states.path) t" +
" 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)
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Expand Up @@ -18,8 +18,6 @@ services:
depends_on:
db:
condition: service_healthy
volumes:
- ./static:/static:ro
ports:
- "8080:8080"

Expand Down
54 changes: 47 additions & 7 deletions main.go
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"time"

"github.com/camptocamp/terraboard/api"
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
11 changes: 0 additions & 11 deletions static/footer.html

This file was deleted.

41 changes: 0 additions & 41 deletions static/index.html

This file was deleted.

59 changes: 0 additions & 59 deletions static/main.html

This file was deleted.

50 changes: 0 additions & 50 deletions static/navbar.html

This file was deleted.

0 comments on commit 5001de9

Please sign in to comment.