feat(webui): add state force unlock
All checks were successful
main / main (push) Successful in 2m49s
main / deploy (push) Has been skipped
main / publish (push) Has been skipped

Closes #13
This commit is contained in:
Julien Dessaux 2025-05-02 00:42:50 +02:00
parent 8f17a3661e
commit 20bc9fe17a
Signed by: adyxax
GPG key ID: F92E51B86E07177E
3 changed files with 31 additions and 11 deletions

View file

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"git.adyxax.org/adyxax/tfstated/pkg/model"
"go.n16f.net/uuid"
)
@ -83,4 +84,15 @@ func (db *DB) Unlock(path string, lock any) (bool, error) {
}
return n == 1, nil
}
func (db *DB) ForceUnlock(state *model.State) error {
_, err := db.Exec(
`UPDATE states
SET lock = NULL
WHERE id = ?;`,
state.Id)
if err != nil {
return fmt.Errorf("failed to update state: %w", err)
}
return nil
}

View file

@ -70,7 +70,8 @@
<input name="csrf_token" type="hidden" value="{{ .Page.Session.Data.CsrfToken }}">
<fieldset>
<legend>Danger Zone</legend>
<button action="delete" type="submit" value="delete">Delete State</button>
<button name="action" type="submit" value="delete">Delete State</button>
<button {{ if eq .State.Lock nil }}disabled{{ end }} name="action" type="submit" value="unlock">Unlock State</button>
</fieldset>
</form>
</div>

View file

@ -66,7 +66,6 @@ func handleStatesIdPOST(db *database.DB) http.Handler {
if !verifyCSRFToken(w, r) {
return
}
action := r.FormValue("action")
var stateId uuid.UUID
if err := stateId.Parse(r.PathValue("id")); err != nil {
errorResponse(w, r, http.StatusBadRequest, err)
@ -87,6 +86,7 @@ func handleStatesIdPOST(db *database.DB) http.Handler {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
action := r.FormValue("action")
switch action {
case "delete":
errorResponse(w, r, http.StatusNotImplemented, err)
@ -121,17 +121,24 @@ func handleStatesIdPOST(db *database.DB) http.Handler {
})
return
}
render(w, statesIdTemplate, http.StatusOK, StatesIdPage{
Page: makePage(r, &Page{
Section: "states",
Title: state.Path,
}),
State: state,
Usernames: usernames,
Versions: versions,
})
case "unlock":
if err := db.ForceUnlock(state); err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
state.Lock = nil
default:
errorResponse(w, r, http.StatusBadRequest, err)
return
}
render(w, statesIdTemplate, http.StatusOK, StatesIdPage{
Page: makePage(r, &Page{
Section: "states",
Title: state.Path,
}),
State: state,
Usernames: usernames,
Versions: versions,
})
})
}