summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2026-08-13 20:53:24 +0200
committerxengineering <me@xengineering.eu>2026-08-13 21:15:49 +0200
commit60bdc926dd7235fd55930ebd5b30c2e346f8ca59 (patch)
treea520a7d898af7bb0cda0a3d2644bd3cb7fc17070
parent5f66b2fc93d84cedd605bbacbd602cec98efc358 (diff)
downloadceres-60bdc926dd7235fd55930ebd5b30c2e346f8ca59.tar
ceres-60bdc926dd7235fd55930ebd5b30c2e346f8ca59.tar.zst
ceres-60bdc926dd7235fd55930ebd5b30c2e346f8ca59.zip
WIP: Add JSON-based API under `/api`step-re-ordering
TODO: Not all the information is provided there. This commit provides an unstable JSON API for communication with an upcoming Dart frontend. Later it might be part of the public interface for integration into other services. For this step it has to be ensured the semantics are not changing a lot in the future which is currently out of scope.
-rw-r--r--model/object.go1
-rw-r--r--model/recipe.go14
-rw-r--r--model/recipes.go4
-rw-r--r--server.go6
-rw-r--r--view/json.go65
5 files changed, 90 insertions, 0 deletions
diff --git a/model/object.go b/model/object.go
index 63ef419..0b1919b 100644
--- a/model/object.go
+++ b/model/object.go
@@ -9,4 +9,5 @@ type Object interface {
Read(tx *sql.Tx) error
Update(tx *sql.Tx) error
Delete(tx *sql.Tx) error
+ SetID(id int)
}
diff --git a/model/recipe.go b/model/recipe.go
index 684b158..adcafa1 100644
--- a/model/recipe.go
+++ b/model/recipe.go
@@ -5,6 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log"
+ "strconv"
)
type Recipe struct {
@@ -19,11 +21,23 @@ type Recipe struct {
IsFavorite bool `json:"is_favorite"`
}
+func NewRecipe() Object {
+ return &Recipe{}
+}
+
func (r Recipe) String() string {
b, _ := json.MarshalIndent(r, "", " ")
return string(b)
}
+func (r *Recipe) SetID(id int) {
+ if r == nil {
+ log.Println("Got nil as recipe!")
+ }
+
+ r.Id = strconv.Itoa(id)
+}
+
func (r *Recipe) Validate() error {
var err error
diff --git a/model/recipes.go b/model/recipes.go
index e4fd1b8..b711dab 100644
--- a/model/recipes.go
+++ b/model/recipes.go
@@ -14,6 +14,10 @@ type RecipesElement struct {
type Recipes []RecipesElement
+func (r *Recipes) SetID(id int) {
+ // singleton object, ignoring ID
+}
+
func (r *Recipes) Create(tx *sql.Tx) error {
return fmt.Errorf("Impossible to create a recipe list")
}
diff --git a/server.go b/server.go
index 3868315..07389a8 100644
--- a/server.go
+++ b/server.go
@@ -30,9 +30,15 @@ func NewServer(addr string, db *model.DB) *Server {
mux.Handle("GET /recipes", view.RecipesRead(db))
mux.Handle("GET /recipe/create", view.RecipeCreate())
+
+ mux.Handle("POST /api/recipe", view.CreateJSON[*model.Recipe](db, model.NewRecipe))
mux.Handle("POST /recipe", controller.RecipeCreate(db))
+
+ mux.Handle("GET /api/recipe/{id}", view.ReadJSON[*model.Recipe](db, model.NewRecipe))
mux.Handle("GET /recipe/{id}", view.RecipeRead(db))
+
mux.Handle("POST /recipe/{id}", controller.RecipeUpdate(db))
+
mux.Handle("DELETE /recipe/{id}", controller.RecipeDelete(db))
mux.Handle("GET /favicon.ico", view.FaviconRead())
diff --git a/view/json.go b/view/json.go
new file mode 100644
index 0000000..5f3a30c
--- /dev/null
+++ b/view/json.go
@@ -0,0 +1,65 @@
+package view
+
+import (
+ "encoding/json"
+ "io"
+ "log"
+ "net/http"
+ "strconv"
+
+ "xengineering.eu/ceres/model"
+)
+
+func CreateJSON[O model.Object](db *model.DB, constructor func() model.Object) http.Handler {
+ return http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ buf, err := io.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ object := constructor()
+ err = json.Unmarshal(buf, &object)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ err = db.Transaction(object.Create)
+ if err != nil {
+ log.Println("Could not create object.")
+ http.Error(w, "Could not create object.", http.StatusBadRequest)
+ return
+ }
+ },
+ )
+}
+
+func ReadJSON[O model.Object](db *model.DB, constructor func() model.Object) http.Handler {
+ return http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ object := constructor()
+
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ // TODO
+ return
+ }
+ object.SetID(id)
+
+ err = db.Transaction(object.Read)
+ if err != nil {
+ // TODO
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(object)
+ if err != nil {
+ // TODO
+ return
+ }
+ },
+ )
+}