Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: front-end redesign to VueJS/Bootstrap 5 & server improvements #188

Merged
merged 27 commits into from Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cecdc60
feat: add ListLineageStats api endpoint and model
hbollon Jun 28, 2021
b49a536
feat: update Terraboard overview to list by lineage
hbollon Jun 28, 2021
f33c21c
feat: add last modified path on lineage overview
hbollon Jun 29, 2021
fa5a39b
feat: add lineage column on search view with filter
hbollon Jun 30, 2021
d524026
fix(lineage): code review issues
hbollon Jul 15, 2021
81278be
refactor: remove obsolete lineage.html file
hbollon Jul 15, 2021
d13ac18
feat(bootstrap): upgrade project to Bootstrap 4.1 and JQuery 3.3.1
hbollon Jul 1, 2021
dd81412
feat(bootstrap): add FontAwesome instead of old Glyphicon
hbollon Jul 1, 2021
3fcf20a
feat(bootstrap): update state and compare views
hbollon Jul 1, 2021
3fd211d
feat(bootstrap): upgrade to Bootstrap 5 + visuals improvements
hbollon Jul 6, 2021
9b4169b
feat(vuejs): init vue project + router config + icon and assets
hbollon Jul 6, 2021
5fbfb6b
feat(vue): add dynamic title managing through mixin
hbollon Jul 8, 2021
661b2f4
feat(vue): add navbar and footer components
hbollon Jul 9, 2021
ffe2e70
feat(vuejs): add overview with charts powered bu Chart.JS 3
hbollon Jul 12, 2021
d67b732
feat(vuejs): add sparkline chart on overview
hbollon Jul 13, 2021
354062c
feat(vuejs): footer version fetching controller
hbollon Jul 13, 2021
257cb90
feat(vuejs): callback on state quick access selection
hbollon Jul 15, 2021
6be6d56
feat(vuejs): state main view with routes
hbollon Jul 19, 2021
8134596
feat(vuejs): add details/outputs panels to state view
hbollon Jul 19, 2021
4655e03
feat(vuejs): compare view & controller
hbollon Jul 20, 2021
61ad065
feat(api): replace version's id by version_id field on search result
hbollon Jul 21, 2021
8bf072d
feat(vuejs): search view / controller
hbollon Jul 21, 2021
aeb669b
fix(vuejs): issue with dynamic title mixin + code cleanup
hbollon Jul 21, 2021
b1c650c
feat(vuejs): clickable charts for quick search
hbollon Jul 21, 2021
56f5959
feat(server): edit docker envs to build Vue project
hbollon Jul 21, 2021
3c172ca
ci: add build pipeline for VueJS
hbollon Jul 22, 2021
45fc110
fix: gorilla/mux issues with Vue
hbollon Jul 28, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.