From 68edbca349ba46f1360ba53c2eba578ff06858e8 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Tue, 2 Jul 2024 01:45:07 -0500 Subject: [PATCH] Create and update Blogs Started work on author info --- internal/backend/README.md | 2 + internal/blog/README.md | 2 + internal/blog/author.go | 54 +++++++++++++--- internal/blog/blog.go | 123 ++++++++++++++++++++++++++++++------- internal/blog/main.go | 12 ++-- 5 files changed, 160 insertions(+), 33 deletions(-) diff --git a/internal/backend/README.md b/internal/backend/README.md index 7ccb267..cffe407 100644 --- a/internal/backend/README.md +++ b/internal/backend/README.md @@ -111,6 +111,8 @@ If an error status code is returned then the body will be as follows. * API Key is invalid or does not have the needed permission for the request. * invalidBody * Body of the request is malformed. +* unauthorized + * User is not authorized for the given task or no user token is given. * badRequest * Some part of your request is invalid * internal diff --git a/internal/blog/README.md b/internal/blog/README.md index 16a7161..cdbe1e1 100644 --- a/internal/blog/README.md +++ b/internal/blog/README.md @@ -13,6 +13,7 @@ A simple blog module for darkstorm-backend. ```json { id: "authorID", + name: "author name", about: "about", picurl: "picture URL" } @@ -26,6 +27,7 @@ Must have a auth token for a user with the `"blog": "admin"` permission. ```json { + name: "author name", about: "about", picurl: "picture url" } diff --git a/internal/blog/author.go b/internal/blog/author.go index 21ba3ec..8fd2052 100644 --- a/internal/blog/author.go +++ b/internal/blog/author.go @@ -2,6 +2,7 @@ package blog import ( "context" + "encoding/json" "log" "net/http" @@ -35,21 +36,58 @@ func (b *BlogApp) AboutMe() (*Author, error) { return &aboutMe, nil } -func (b *BlogApp) GetAuthorInfo(w http.ResponseWriter, r *http.Request) { +func (b *BlogApp) reqAuthorInfo(w http.ResponseWriter, r *http.Request) { + res := b.authCol.FindOne(context.Background(), r.PathValue("authorID")) + if res.Err() == mongo.ErrNoDocuments { + backend.ReturnError(w, http.StatusNotFound, "notFound", "Author with ID "+r.PathValue("authorID")+" not found") + return + } else if res.Err() != nil { + log.Println("error getting author info:", res.Err()) + backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error") + return + } + var auth Author + err := res.Decode(&auth) + if err != nil { + log.Println("error decoding author info:", err) + backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error") + return + } + json.NewEncoder(w).Encode(auth) +} + +func (b *BlogApp) addAuthorInfo(w http.ResponseWriter, r *http.Request) { + hdr, err := b.back.VerifyHeader(w, r, "blogManagement", false) + if hdr == nil { + if err != nil { + log.Println("request key parsing error:", err) + } + return + } else if hdr.Key.AppID != "blog" { + backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application is unauthorized") + return + } + if hdr.User == nil || hdr.User.Perm["blog"] != "admin" { + backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application is unauthorized") + return + } //TODO } -func (b *BlogApp) SetAuthorInfo(w http.ResponseWriter, r *http.Request) { - hdr, err := b.back.VerifyHeader(w, r, "managment", true) +func (b *BlogApp) updateAuthorInfo(w http.ResponseWriter, r *http.Request) { + hdr, err := b.back.VerifyHeader(w, r, "blogManagement", false) if hdr == nil { if err != nil { - log.Println("error verifying apiKey:", err) + log.Println("request key parsing error:", err) } return - } - if hdr.Key.AppID != "blog" { - backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized") + } else if hdr.Key.AppID != "blog" { + backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application is unauthorized") return } - + if hdr.User == nil || hdr.User.Perm["blog"] != "admin" { + backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application is unauthorized") + return + } + //TODO } diff --git a/internal/blog/blog.go b/internal/blog/blog.go index e278fd9..ce212a1 100644 --- a/internal/blog/blog.go +++ b/internal/blog/blog.go @@ -6,8 +6,10 @@ import ( "log" "net/http" "strconv" + "time" "github.com/CalebQ42/darkstorm-server/internal/backend" + "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -19,8 +21,8 @@ type Blog struct { Favicon string `json:"favicon" bson:"favicon"` Title string `json:"title" bson:"title"` Blog string `json:"blog" bson:"blog"` - CreateTime int `json:"createTime" bson:"createTime"` - UpdateTime int `json:"updateTime" bson:"updateTime"` + CreateTime int64 `json:"createTime" bson:"createTime"` + UpdateTime int64 `json:"updateTime" bson:"updateTime"` } func (b *BlogApp) ConvertBlog(blog *Blog) { @@ -41,7 +43,7 @@ func (b *BlogApp) GetAuthor(blog *Blog) (*Author, error) { return &author, err } -func (b *BlogApp) GetBlog(ID string) (*Blog, error) { +func (b *BlogApp) Blog(ID string) (*Blog, error) { res := b.blogCol.FindOne(context.Background(), bson.M{"_id": ID}) if res.Err() != nil { if res.Err() == mongo.ErrNoDocuments { @@ -58,13 +60,13 @@ func (b *BlogApp) GetBlog(ID string) (*Blog, error) { return &blog, nil } -func (b *BlogApp) Blog(w http.ResponseWriter, r *http.Request) { +func (b *BlogApp) reqBlog(w http.ResponseWriter, r *http.Request) { blogID := r.PathValue("blogID") if blogID == "" { backend.ReturnError(w, http.StatusBadRequest, "badRequest", "Must provide a blogID") return } - blog, err := b.GetBlog(blogID) + blog, err := b.Blog(blogID) if err != nil { if err == backend.ErrNotFound { backend.ReturnError(w, http.StatusNotFound, "notFound", "Not blog found with the given ID") @@ -77,23 +79,102 @@ func (b *BlogApp) Blog(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(blog) } -func (b *BlogApp) CreateBlog(w http.ResponseWriter, r *http.Request) { - hdr, err := b.back.ParseHeader(r) - if err != nil{ - if err == backend.ErrApiKeyUnauthorized{ - backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application unauthorized") - return - }else if err == backend.ErrTokenUnauthorized{ - backend.ReturnError(w, http.StatusUnauthorized, "") +func (b *BlogApp) createBlog(w http.ResponseWriter, r *http.Request) { + hdr, err := b.back.VerifyHeader(w, r, "blogManagement", false) + if hdr == nil { + if err != nil { + log.Println("request key parsing error:", err) } + return + } else if hdr.Key.AppID != "blog" { + backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application is unauthorized") + return } + if hdr.User == nil || hdr.User.Perm["blog"] != "admin" { + backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application is unauthorized") + return + } + var newBlog Blog + err = json.NewDecoder(r.Body).Decode(&newBlog) + r.Body.Close() + if err != nil { + backend.ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request") + return + } + id, err := uuid.NewV7() + if err != nil { + backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error") + return + } + tim := time.Now().Unix() + newBlog.ID = id.String() + newBlog.CreateTime = tim + newBlog.UpdateTime = tim + newBlog.Author = hdr.User.Username + _, err = b.blogCol.InsertOne(context.Background(), newBlog) + if err != nil { + log.Println("error when inserting new blog:", err) + backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error") + return + } + w.WriteHeader(http.StatusCreated) + } -func (b *BlogApp) UpdateBlog(w http.ResponseWriter, r *http.Request) { - //TODO +func (b *BlogApp) updateBlog(w http.ResponseWriter, r *http.Request) { + hdr, err := b.back.VerifyHeader(w, r, "blogManagement", false) + if hdr == nil { + if err != nil { + log.Println("request key parsing error:", err) + } + return + } else if hdr.Key.AppID != "blog" { + backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application is unauthorized") + return + } + if hdr.User == nil || hdr.User.Perm["blog"] != "admin" { + backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application is unauthorized") + return + } + if r.PathValue("blogID") == "" { + backend.ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request") + return + } + var reqUpdRaw map[string]string + err = json.NewDecoder(r.Body).Decode(&reqUpdRaw) + r.Body.Close() + if err != nil { + backend.ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request") + return + } + reqUpd := bson.M{} + if fav, ok := reqUpdRaw["favicon"]; ok && fav != "" { + reqUpd["favicon"] = fav + } + if titl, ok := reqUpdRaw["title"]; ok && titl != "" { + reqUpd["title"] = titl + } + if blog, ok := reqUpdRaw["blog"]; ok && blog != "" { + reqUpd["blog"] = blog + } + reqUpd["updateTime"] = time.Now().Unix() + res, err := b.blogCol.UpdateByID(context.Background(), r.PathValue("blogID"), reqUpd) + if err != nil { + if err == mongo.ErrNoDocuments { + backend.ReturnError(w, http.StatusNotFound, "notFound", "Blog with ID "+r.PathValue("blogID")+" not found") + } else { + backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error") + } + return + } + if res.MatchedCount == 0 { + backend.ReturnError(w, http.StatusNotFound, "notFound", "Blog with ID "+r.PathValue("blogID")+" not found") + return + } + w.WriteHeader(http.StatusCreated) } -func (b *BlogApp) GetLatestBlogs(page int64) ([]Blog, error) { +func (b *BlogApp) LatestBlogs(page int64) ([]Blog, error) { res, err := b.blogCol.Find(context.Background(), bson.M{}, options.Find(). SetSort(bson.M{"createTime": 1}). SetLimit(5). @@ -115,7 +196,7 @@ func (b *BlogApp) GetLatestBlogs(page int64) ([]Blog, error) { return out, nil } -func (b *BlogApp) LatestBlogs(w http.ResponseWriter, r *http.Request) { +func (b *BlogApp) reqLatestBlogs(w http.ResponseWriter, r *http.Request) { var page int var err error pagQuery := r.URL.Query().Get("page") @@ -125,7 +206,7 @@ func (b *BlogApp) LatestBlogs(w http.ResponseWriter, r *http.Request) { page = 0 } } - blogs, err := b.GetLatestBlogs(int64(page)) + blogs, err := b.LatestBlogs(int64(page)) if err != nil && err != backend.ErrNotFound { log.Println("error getting latest blogs:", err) backend.ReturnError(w, http.StatusInternalServerError, "internal", "internal error") @@ -145,7 +226,7 @@ type BlogListResult struct { CreateTime int `json:"createTime" bson:"createTime"` } -func (b *BlogApp) GetBlogList(page int64) ([]BlogListResult, error) { +func (b *BlogApp) BlogList(page int64) ([]BlogListResult, error) { res, err := b.blogCol.Find(context.Background(), bson.M{}, options.Find(). SetProjection(bson.M{"_id": 1, "createTime": 1}). SetSort(bson.M{"createTime": 1}). @@ -165,7 +246,7 @@ func (b *BlogApp) GetBlogList(page int64) ([]BlogListResult, error) { return out, nil } -func (b *BlogApp) BlogList(w http.ResponseWriter, r *http.Request) { +func (b *BlogApp) reqBlogList(w http.ResponseWriter, r *http.Request) { var page int var err error pagQuery := r.URL.Query().Get("page") @@ -175,7 +256,7 @@ func (b *BlogApp) BlogList(w http.ResponseWriter, r *http.Request) { page = 0 } } - blogList, err := b.GetBlogList(int64(page)) + blogList, err := b.BlogList(int64(page)) if err != nil && err != backend.ErrNotFound { log.Println("error getting blog list:", err) backend.ReturnError(w, http.StatusInternalServerError, "internal", "internal error") diff --git a/internal/blog/main.go b/internal/blog/main.go index 4da7fa6..6355b6c 100644 --- a/internal/blog/main.go +++ b/internal/blog/main.go @@ -24,11 +24,15 @@ func NewBlogApp(b *backend.Backend, db *mongo.Database, mux *http.ServeMux) *Blo } out.conv.ImplementDefaults() // setup mux - mux.HandleFunc("GET /blog", out.LatestBlogs) - mux.HandleFunc("GET /blog/list", out.BlogList) - mux.HandleFunc("GET /blog/{blogID}", out.Blog) + mux.HandleFunc("GET /blog", out.reqLatestBlogs) + mux.HandleFunc("GET /blog/list", out.reqBlogList) + mux.HandleFunc("GET /blog/{blogID}", out.reqBlog) + mux.HandleFunc("POST /blog", out.createBlog) + mux.HandleFunc("POST /blog/{blogID}", out.updateBlog) - mux.HandleFunc("POST /blog", out.CreateBlog) + mux.HandleFunc("GET /author/{authorID}", out.reqAuthorInfo) + mux.HandleFunc("POST /author", out.addAuthorInfo) + mux.HandleFunc("POST /author/{authorID}", out.updateAuthorInfo) //TODO return out }