add endpoints to get single blog article
Signed-off-by: Tobias Erbshäußer <tobias@tesoft.dev>
This commit is contained in:
+48
-4
@@ -32,8 +32,8 @@ type ArticleProperties struct {
|
|||||||
|
|
||||||
type Article struct {
|
type Article struct {
|
||||||
ArticleProperties
|
ArticleProperties
|
||||||
Content string
|
Content string `json:"content"`
|
||||||
Files []ArticleFile
|
Files []ArticleFile `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArticleFile struct {
|
type ArticleFile struct {
|
||||||
@@ -230,9 +230,53 @@ func (h *ApiHandler) ServeBlogPut(writer http.ResponseWriter, request *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) ServeBlogGetSingle(writer http.ResponseWriter, request *http.Request) {
|
func (h *ApiHandler) ServeBlogGetSingle(writer http.ResponseWriter, request *http.Request) {
|
||||||
// TODO
|
idStr := request.PathValue("id")
|
||||||
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "invalid id", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
article, err := h.db.GetBlogArticle(IsAuthorized(request), id)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, ErrNotFound) {
|
||||||
|
WriteError(writer, http.StatusNotFound, "article not found", nil)
|
||||||
|
} else {
|
||||||
|
WriteError(writer, http.StatusInternalServerError, "failed to query database", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteResponse(writer, http.StatusOK, article)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) ServeBlogFileGetSingle(writer http.ResponseWriter, request *http.Request) {
|
func (h *ApiHandler) ServeBlogFileGetSingle(writer http.ResponseWriter, request *http.Request) {
|
||||||
// TODO
|
idStr := request.PathValue("articleId")
|
||||||
|
articleId, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "invalid article id", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
idStr = request.PathValue("fileId")
|
||||||
|
fileId, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(writer, http.StatusBadRequest, "invalid file id", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := h.db.GetBlogArticleFile(IsAuthorized(request), articleId, fileId)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, ErrNotFound) {
|
||||||
|
WriteError(writer, http.StatusNotFound, "article file not found", nil)
|
||||||
|
} else {
|
||||||
|
WriteError(writer, http.StatusInternalServerError, "failed to query database", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = writer.Write(file.Data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
@@ -13,6 +13,8 @@ import (
|
|||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNotFound = errors.New("not found")
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
}
|
}
|
||||||
@@ -203,6 +205,105 @@ func (db *Database) GetBlogArticles(showAll bool, offset int, limit int) ([]Arti
|
|||||||
return articles, nil
|
return articles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetBlogArticle(showAll bool, id int64) (*Article, error) {
|
||||||
|
filter := "WHERE blog_article.id = ?"
|
||||||
|
if !showAll {
|
||||||
|
filter = filter + " AND status = " + strconv.Itoa(ArticleStatusPublished)
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := "SELECT blog_article.status, blog_article.title, blog_article.date, blog_article.modification_date, blog_article.content, blog_tag.name" +
|
||||||
|
" FROM blog_article" +
|
||||||
|
" LEFT JOIN blog_article_to_tag ON blog_article.id = blog_article_to_tag.article_id" +
|
||||||
|
" LEFT JOIN blog_tag ON blog_article_to_tag.tag_id = blog_tag.id" +
|
||||||
|
" " + filter +
|
||||||
|
" ORDER BY blog_tag.name"
|
||||||
|
|
||||||
|
rows, err := db.db.Query(statement, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = rows.Close() }()
|
||||||
|
|
||||||
|
if !rows.Next() {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var status ArticleStatus
|
||||||
|
var title string
|
||||||
|
var dateStr string
|
||||||
|
var modificationDateStr *string
|
||||||
|
var content string
|
||||||
|
var tag *string
|
||||||
|
|
||||||
|
err = rows.Scan(&status, &title, &dateStr, &modificationDateStr, &content, &tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
date, _ := time.Parse(time.DateOnly, dateStr)
|
||||||
|
var modificationDate *time.Time
|
||||||
|
if modificationDateStr != nil {
|
||||||
|
tmp, _ := time.Parse(time.DateOnly, *modificationDateStr)
|
||||||
|
modificationDate = &tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
article := &Article{
|
||||||
|
ArticleProperties{
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
status,
|
||||||
|
make([]string, 0),
|
||||||
|
date,
|
||||||
|
modificationDate,
|
||||||
|
},
|
||||||
|
content,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag != nil {
|
||||||
|
article.Tags = append(article.Tags, *tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&status, &title, &dateStr, &modificationDateStr, &content, &tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag != nil {
|
||||||
|
article.Tags = append(article.Tags, *tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return article, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetBlogArticleFile(showAll bool, articleId int64, fileId int64) (ArticleFile, error) {
|
||||||
|
filter := "WHERE blog_file.article_id = ? AND blog_file.id = ?"
|
||||||
|
if !showAll {
|
||||||
|
filter = filter + " AND blog_article.status = " + strconv.Itoa(ArticleStatusPublished)
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := "SELECT blog_file.data FROM blog_file" +
|
||||||
|
" INNER JOIN blog_article ON blog_article.id = blog_file.article_id" +
|
||||||
|
" " + filter
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
err := db.db.QueryRow(statement, articleId, fileId).Scan(&data)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return ArticleFile{}, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArticleFile{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArticleFile{
|
||||||
|
fileId,
|
||||||
|
data,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func migrate(db *sql.DB, rootPassword string) error {
|
func migrate(db *sql.DB, rootPassword string) error {
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user