summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJulien Dessaux2025-01-28 00:30:30 +0100
committerJulien Dessaux2025-01-28 00:30:30 +0100
commit21c8d6601a274262074e39845be24d8020d2e5aa (patch)
treecad793b4984574c0763e148fb2ac6008a11951a6 /pkg
parentfeat(webui): implement state versions list (diff)
downloadtfstated-21c8d6601a274262074e39845be24d8020d2e5aa.tar.gz
tfstated-21c8d6601a274262074e39845be24d8020d2e5aa.tar.bz2
tfstated-21c8d6601a274262074e39845be24d8020d2e5aa.zip
feat(webui): bootstrap state version page
Diffstat (limited to 'pkg')
-rw-r--r--pkg/database/versions.go32
-rw-r--r--pkg/webui/html/version.html10
-rw-r--r--pkg/webui/routes.go1
-rw-r--r--pkg/webui/version.go58
4 files changed, 101 insertions, 0 deletions
diff --git a/pkg/database/versions.go b/pkg/database/versions.go
index 5a6f11f..f05d230 100644
--- a/pkg/database/versions.go
+++ b/pkg/database/versions.go
@@ -1,12 +1,44 @@
package database
import (
+ "database/sql"
+ "errors"
"fmt"
"time"
"git.adyxax.org/adyxax/tfstated/pkg/model"
)
+func (db *DB) LoadVersionById(id int) (*model.Version, error) {
+ version := model.Version{
+ Id: id,
+ }
+ var (
+ created int64
+ encryptedData []byte
+ )
+ err := db.QueryRow(
+ `SELECT account_id, state_id, data, lock, created FROM versions WHERE id = ?;`,
+ id).Scan(
+ &version.AccountId,
+ &version.StateId,
+ &encryptedData,
+ &version.Lock,
+ &created)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("failed to load version id %d from database: %w", id, err)
+ }
+ version.Created = time.Unix(created, 0)
+ version.Data, err = db.dataEncryptionKey.DecryptAES256(encryptedData)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt version %d data: %w", id, err)
+ }
+ return &version, nil
+}
+
func (db *DB) LoadVersionsByState(state *model.State) ([]model.Version, error) {
rows, err := db.Query(
`SELECT account_id, created, data, id, lock
diff --git a/pkg/webui/html/version.html b/pkg/webui/html/version.html
new file mode 100644
index 0000000..b849783
--- /dev/null
+++ b/pkg/webui/html/version.html
@@ -0,0 +1,10 @@
+{{ define "main" }}
+<main class="responsive" id="main">
+ <p>
+ Created by
+ <a href="/users/{{ .Account.Id }}">{{ .Account.Username }}</a>
+ at {{ .Version.Created }}
+ </p>
+ <pre>{{ .VersionData }}</pre>
+</main>
+{{ end }}
diff --git a/pkg/webui/routes.go b/pkg/webui/routes.go
index 4330630..84017dd 100644
--- a/pkg/webui/routes.go
+++ b/pkg/webui/routes.go
@@ -19,5 +19,6 @@ func addRoutes(
mux.Handle("GET /states", requireLogin(handleStatesGET(db)))
mux.Handle("GET /state/{id}", requireLogin(handleStateGET(db)))
mux.Handle("GET /static/", cache(http.FileServer(http.FS(staticFS))))
+ mux.Handle("GET /version/{id}", requireLogin(handleVersionGET(db)))
mux.Handle("GET /", requireLogin(handleIndexGET()))
}
diff --git a/pkg/webui/version.go b/pkg/webui/version.go
new file mode 100644
index 0000000..04c3e6d
--- /dev/null
+++ b/pkg/webui/version.go
@@ -0,0 +1,58 @@
+package webui
+
+import (
+ "fmt"
+ "html/template"
+ "net/http"
+ "strconv"
+
+ "git.adyxax.org/adyxax/tfstated/pkg/database"
+ "git.adyxax.org/adyxax/tfstated/pkg/model"
+)
+
+var versionTemplate = template.Must(template.ParseFS(htmlFS, "html/base.html", "html/version.html"))
+
+func handleVersionGET(db *database.DB) http.Handler {
+ type VersionsData struct {
+ Page
+ Account *model.Account
+ State *model.State
+ Version *model.Version
+ VersionData string
+ }
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ versionIdStr := r.PathValue("id")
+ versionId, err := strconv.Atoi(versionIdStr)
+ if err != nil {
+ errorResponse(w, http.StatusBadRequest, err)
+ return
+ }
+ version, err := db.LoadVersionById(versionId)
+ if err != nil {
+ errorResponse(w, http.StatusInternalServerError, err)
+ return
+ }
+ state, err := db.LoadStateById(version.StateId)
+ if err != nil {
+ errorResponse(w, http.StatusInternalServerError, err)
+ return
+ }
+ account, err := db.LoadAccountById(version.AccountId)
+ if err != nil {
+ errorResponse(w, http.StatusInternalServerError, err)
+ return
+ }
+ versionData := string(version.Data[:])
+ render(w, versionTemplate, http.StatusOK, VersionsData{
+ Page: Page{
+ Precedent: fmt.Sprintf("/state/%d", state.Id),
+ Section: "versions",
+ Title: state.Path,
+ },
+ Account: account,
+ State: state,
+ Version: version,
+ VersionData: versionData,
+ })
+ })
+}