add endpoint to update blog article
This commit is contained in:
+44
-1
@@ -41,6 +41,8 @@ type ArticleFile struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxArticleFormSize = 10 * 1024 * 1024
|
||||||
|
|
||||||
func ParseArticle(reader io.Reader, filePrefix string) (*Article, error) {
|
func ParseArticle(reader io.Reader, filePrefix string) (*Article, error) {
|
||||||
tarFiles := make(map[string][]byte)
|
tarFiles := make(map[string][]byte)
|
||||||
tarReader := tar.NewReader(reader)
|
tarReader := tar.NewReader(reader)
|
||||||
@@ -197,7 +199,7 @@ func (h *ApiHandler) ServeBlogGet(writer http.ResponseWriter, request *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) ServeBlogPut(writer http.ResponseWriter, request *http.Request) {
|
func (h *ApiHandler) ServeBlogPut(writer http.ResponseWriter, request *http.Request) {
|
||||||
err := request.ParseMultipartForm(10 * 1024 * 1024)
|
err := request.ParseMultipartForm(maxArticleFormSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteError(writer, http.StatusBadRequest, "failed to parse multipart form", nil)
|
WriteError(writer, http.StatusBadRequest, "failed to parse multipart form", nil)
|
||||||
return
|
return
|
||||||
@@ -259,6 +261,47 @@ func (h *ApiHandler) ServeBlogGetSingle(writer http.ResponseWriter, request *htt
|
|||||||
WriteResponse(writer, http.StatusOK, article)
|
WriteResponse(writer, http.StatusOK, article)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *ApiHandler) ServeBlogPatch(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
idStr := request.PathValue("id")
|
||||||
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "invalid id", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = request.ParseMultipartForm(maxArticleFormSize)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "failed to parse multipart form", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, _, err := request.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "failed to parse file", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
article, err := ParseArticle(file, "/api/blog/"+strconv.FormatInt(id, 10)+"/file/")
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusInternalServerError, "failed to parse article: "+err.Error(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
article.Id = id
|
||||||
|
err = h.db.UpdateBlogArticle(article)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusInternalServerError, "failed to write database", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("updated blog article '%s' with id %d", article.Title, article.Id)
|
||||||
|
|
||||||
|
WriteResponse(writer, http.StatusOK, map[string]interface{}{})
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) ServeBlogDelete(writer http.ResponseWriter, request *http.Request) {
|
func (h *ApiHandler) ServeBlogDelete(writer http.ResponseWriter, request *http.Request) {
|
||||||
idStr := request.PathValue("id")
|
idStr := request.PathValue("id")
|
||||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
|||||||
+101
-31
@@ -55,6 +55,38 @@ func (db *Database) ValidateRootPassword(password string) (bool, error) {
|
|||||||
return slices.Compare(passwordHash[:], rootPasswordHash) == 0, nil
|
return slices.Compare(passwordHash[:], rootPasswordHash) == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) createBlogArticleSupplements(tx *sql.Tx, id int64, tags []string, files []ArticleFile) error {
|
||||||
|
for _, tag := range tags {
|
||||||
|
tagId, err := createOrGetTag(tx, tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(
|
||||||
|
"INSERT INTO blog_article_to_tag(tag_id, article_id) VALUES (?, ?)",
|
||||||
|
tagId,
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
_, err := tx.Exec(
|
||||||
|
"INSERT INTO blog_file(id, article_id, data) VALUES (?, ?, ?)",
|
||||||
|
file.Id,
|
||||||
|
id,
|
||||||
|
file.Data,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Database) CreateBlogArticle() (int64, func(*Article) error, error) {
|
func (db *Database) CreateBlogArticle() (int64, func(*Article) error, error) {
|
||||||
tx, err := db.db.Begin()
|
tx, err := db.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,11 +101,13 @@ func (db *Database) CreateBlogArticle() (int64, func(*Article) error, error) {
|
|||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
return -1, nil, err
|
return -1, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := res.LastInsertId()
|
id, err := res.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
return -1, nil, err
|
return -1, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,35 +135,12 @@ func (db *Database) CreateBlogArticle() (int64, func(*Article) error, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tag := range article.Tags {
|
err = db.createBlogArticleSupplements(tx, id, article.Tags, article.Files)
|
||||||
tagId, err := createOrGetTag(tx, tag)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec(
|
|
||||||
"INSERT INTO blog_article_to_tag(tag_id, article_id) VALUES (?, ?)",
|
|
||||||
tagId,
|
|
||||||
id,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range article.Files {
|
|
||||||
_, err = tx.Exec(
|
|
||||||
"INSERT INTO blog_file(id, article_id, data) VALUES (?, ?, ?)",
|
|
||||||
file.Id,
|
|
||||||
id,
|
|
||||||
file.Data,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
@@ -354,19 +365,78 @@ func (db *Database) SetBlogArticleStatus(id int64, status ArticleStatus) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateBlogArticle(article *Article) error {
|
||||||
|
tx, err := db.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.deleteBlogArticleSupplements(tx, article.Id)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var modificationDate *string
|
||||||
|
if article.ModificationDate != nil {
|
||||||
|
tmp := article.ModificationDate.Format(time.DateOnly)
|
||||||
|
modificationDate = &tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := tx.Exec(
|
||||||
|
"UPDATE blog_article SET title = ?, date = ?, modification_date = ?, content = ? WHERE id = ?",
|
||||||
|
article.Title,
|
||||||
|
article.ReleaseDate.Format(time.DateOnly),
|
||||||
|
modificationDate,
|
||||||
|
article.Content,
|
||||||
|
article.Id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
affectedCount, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if affectedCount == 0 {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.createBlogArticleSupplements(tx, article.Id, article.Tags, article.Files)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) deleteBlogArticleSupplements(tx *sql.Tx, id int64) error {
|
||||||
|
_, err := tx.Exec("DELETE FROM blog_file WHERE article_id = ?", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("DELETE FROM blog_article_to_tag WHERE article_id = ?", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Database) DeleteBlogArticle(id int64) error {
|
func (db *Database) DeleteBlogArticle(id int64) error {
|
||||||
tx, err := db.db.Begin()
|
tx, err := db.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("DELETE FROM blog_file WHERE article_id = ?", id)
|
err = db.deleteBlogArticleSupplements(tx, id)
|
||||||
if err != nil {
|
|
||||||
_ = tx.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec("DELETE FROM blog_article_to_tag WHERE article_id = ?", id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ func main() {
|
|||||||
mux.Handle("GET /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
mux.Handle("GET /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
apiHandler.ServeBlogGetSingle(writer, request)
|
apiHandler.ServeBlogGetSingle(writer, request)
|
||||||
}), false))
|
}), false))
|
||||||
|
mux.Handle("PATCH /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
apiHandler.ServeBlogPatch(writer, request)
|
||||||
|
}), true))
|
||||||
mux.Handle("DELETE /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
mux.Handle("DELETE /api/blog/{id}", apiHandler.ProcessAuth(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
apiHandler.ServeBlogDelete(writer, request)
|
apiHandler.ServeBlogDelete(writer, request)
|
||||||
}), true))
|
}), true))
|
||||||
|
|||||||
Reference in New Issue
Block a user