feat(webui): add renaming a state path
All checks were successful
main / main (push) Successful in 1m39s
main / deploy (push) Has been skipped
main / publish (push) Has been skipped

Closes #9
This commit is contained in:
Julien Dessaux 2025-04-22 00:02:00 +02:00
parent d084a07bb5
commit 3192078b05
Signed by: adyxax
GPG key ID: F92E51B86E07177E
3 changed files with 101 additions and 0 deletions

View file

@ -166,6 +166,28 @@ func (db *DB) LoadStates() ([]model.State, error) {
return states, nil
}
// Returns (true, nil) on successful save
func (db *DB) SaveState(state *model.State) (bool, error) {
_, err := db.Exec(
`UPDATE states
SET lock = ?,
path = ?
WHERE id = ?`,
state.Lock,
state.Path,
state.Id)
if err != nil {
var sqliteErr sqlite3.Error
if errors.As(err, &sqliteErr) {
if sqliteErr.Code == sqlite3.ErrNo(sqlite3.ErrConstraint) {
return false, nil
}
}
return false, fmt.Errorf("failed to update state id %s: %w", state.Id, err)
}
return true, nil
}
// returns true in case of lock mismatch
func (db *DB) SetState(path string, accountId uuid.UUID, data []byte, lock string) (bool, error) {
encryptedData, err := db.dataEncryptionKey.EncryptAES256(data)

View file

@ -27,6 +27,7 @@ func addRoutes(
mux.Handle("GET /states", requireLogin(handleStatesGET(db)))
mux.Handle("POST /states", requireLogin(handleStatesPOST(db)))
mux.Handle("GET /states/{id}", requireLogin(handleStatesIdGET(db)))
mux.Handle("POST /states/{id}", requireLogin(handleStatesIdPOST(db)))
mux.Handle("GET /static/", cache(http.FileServer(http.FS(staticFS))))
mux.Handle("GET /versions/{id}", requireLogin(handleVersionsGET(db)))
mux.Handle("GET /", requireLogin(handleIndexGET()))

View file

@ -3,6 +3,8 @@ package webui
import (
"html/template"
"net/http"
"net/url"
"path"
"git.adyxax.org/adyxax/tfstated/pkg/database"
"git.adyxax.org/adyxax/tfstated/pkg/model"
@ -54,3 +56,79 @@ func handleStatesIdGET(db *database.DB) http.Handler {
})
})
}
func handleStatesIdPOST(db *database.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
errorResponse(w, r, http.StatusBadRequest, err)
return
}
action := r.FormValue("action")
var stateId uuid.UUID
if err := stateId.Parse(r.PathValue("id")); err != nil {
errorResponse(w, r, http.StatusBadRequest, err)
return
}
state, err := db.LoadStateById(stateId)
if err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
versions, err := db.LoadVersionsByState(state)
if err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
usernames, err := db.LoadAccountUsernames()
if err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
switch action {
case "delete":
errorResponse(w, r, http.StatusNotImplemented, err)
case "edit":
statePath := r.FormValue("path")
parsedStatePath, err := url.Parse(statePath)
if err != nil || path.Clean(parsedStatePath.Path) != statePath || statePath[0] != '/' {
render(w, statesIdTemplate, http.StatusBadRequest, StatesIdPage{
Page: makePage(r, &Page{Title: state.Path, Section: "states"}),
Path: statePath,
PathError: true,
State: state,
Usernames: usernames,
Versions: versions,
})
return
}
state.Path = statePath
success, err := db.SaveState(state)
if err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
if !success {
render(w, statesIdTemplate, http.StatusBadRequest, StatesIdPage{
Page: makePage(r, &Page{Title: state.Path, Section: "states"}),
Path: statePath,
PathDuplicate: true,
State: state,
Usernames: usernames,
Versions: versions,
})
return
}
render(w, statesIdTemplate, http.StatusOK, StatesIdPage{
Page: makePage(r, &Page{
Section: "states",
Title: state.Path,
}),
State: state,
Usernames: usernames,
Versions: versions,
})
default:
errorResponse(w, r, http.StatusBadRequest, err)
}
})
}