From 4f770d01d8bc428c8b8f57b450b0835d3ef6ccf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Erbsh=C3=A4u=C3=9Fer?= Date: Sun, 24 May 2026 09:22:22 +0200 Subject: [PATCH] implement endpoints to create and query all blog articles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Erbshäußer --- backend/blog.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ backend/main.go | 40 ++++++++++++++++---- 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/backend/blog.go b/backend/blog.go index 909dcbc..9f14d02 100644 --- a/backend/blog.go +++ b/backend/blog.go @@ -149,3 +149,100 @@ func ParseArticle(reader io.Reader, filePrefix string) (*Article, error) { files, }, nil } + +func (h *ApiHandler) ServeBlogGet(writer http.ResponseWriter, request *http.Request) { + query := request.URL.Query() + + var err error + var offset int + var limit int + + offsetStr := query.Get("offset") + if offsetStr == "" { + offset = 0 + } else { + offset, err = strconv.Atoi(offsetStr) + if err != nil || offset < 0 { + http.Error(writer, "invalid offset", http.StatusBadRequest) + return + } + } + + limitStr := query.Get("limit") + if limitStr == "" { + limit = 50 + } else { + limit, err = strconv.Atoi(limitStr) + if err != nil || limit <= 0 || limit > 100 { + http.Error(writer, "invalid limit", http.StatusBadRequest) + return + } + } + + articles, err := h.db.GetBlogArticles(IsAuthorized(request), offset, limit) + if err != nil { + log.Println("Error getting articles:", err) + http.Error(writer, "failed to query database", http.StatusInternalServerError) + return + } + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + err = json.NewEncoder(writer).Encode(articles) + if err != nil { + http.Error(writer, "failed to serialize results", http.StatusInternalServerError) + } +} + +func (h *ApiHandler) ServeBlogPut(writer http.ResponseWriter, request *http.Request) { + err := request.ParseMultipartForm(10 * 1024 * 1024) + if err != nil { + http.Error(writer, "failed to parse multipart form", http.StatusBadRequest) + return + } + + file, _, err := request.FormFile("file") + if err != nil { + http.Error(writer, "failed to parse file", http.StatusBadRequest) + return + } + defer func() { + _ = file.Close() + }() + + log.Println("Creating new blog article") + + id, commit, err := h.db.CreateBlogArticle() + if err != nil { + log.Println("Error creating new blog article:", err) + http.Error(writer, "failed to add article to database", http.StatusInternalServerError) + return + } + + article, err := ParseArticle(file, "/api/blog/"+strconv.FormatInt(id, 10)+"/file/") + if err != nil { + _ = commit(nil) + log.Println("Error creating new blog article:", err) + http.Error(writer, "failed to add article to database", http.StatusInternalServerError) + return + } + + err = commit(article) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + err = json.NewEncoder(writer).Encode(map[string]interface{}{ + "id": id, + }) + if err != nil { + http.Error(writer, "failed to serialize results", http.StatusInternalServerError) + } +} + +func (h *ApiHandler) ServeBlogGetSingle(writer http.ResponseWriter, request *http.Request) { + // TODO +} + +func (h *ApiHandler) ServeBlogFileGetSingle(writer http.ResponseWriter, request *http.Request) { + // TODO +} diff --git a/backend/main.go b/backend/main.go index 74a9a23..d6d620d 100644 --- a/backend/main.go +++ b/backend/main.go @@ -12,20 +12,44 @@ func main() { port = "8080" } - staticFolder := os.Getenv("STATIC_FOLDER") - if staticFolder == "" { - staticFolder = "static" + frontendPath := os.Getenv("FRONTEND_PATH") + if frontendPath == "" { + frontendPath = "static" } - fsHandler := http.FileServer(http.Dir(staticFolder)) - apiHandler := NewApiHandler() + dbPath := os.Getenv("DB_PATH") + if dbPath == "" { + dbPath = "./database.sqlite" + } + + db, err := NewDatabase(dbPath) + if err != nil { + log.Fatal(err.Error()) + } + defer db.Close() + + apiHandler := &ApiHandler{db: db} mux := http.NewServeMux() - mux.Handle("/", fsHandler) - mux.Handle("/api/", apiHandler) + mux.Handle("/", http.FileServer(http.Dir(frontendPath))) + mux.Handle("GET /api/blog", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + apiHandler.ServeBlogGet(writer, request) + }), false)) + mux.Handle("PUT /api/blog", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + apiHandler.ServeBlogPut(writer, request) + }), true)) + mux.Handle("GET /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + apiHandler.ServeBlogGetSingle(writer, request) + }), false)) + mux.Handle("GET /api/blog/{articleId}/file/{fileId}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + apiHandler.ServeBlogFileGetSingle(writer, request) + }), false)) + mux.HandleFunc("/api/", func(writer http.ResponseWriter, request *http.Request) { + http.NotFound(writer, request) + }) log.Println("Listening on port", port) - err := http.ListenAndServe(":"+port, mux) + err = http.ListenAndServe(":"+port, mux) if err != nil { log.Fatalln(err.Error()) }