129 lines
3.2 KiB
Go
Raw Normal View History

2023-02-06 02:51:05 +09:00
package app
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"time"
2023-02-26 13:05:51 +09:00
"github.com/google/uuid"
2023-02-06 02:51:05 +09:00
"github.com/mailru/easyjson"
2023-02-26 12:26:48 +09:00
"github.com/syumai/workers/cloudflare/d1"
2023-02-06 02:51:05 +09:00
_ "github.com/syumai/workers/cloudflare/d1" // register driver
"github.com/syumai/workers/examples/d1-blog-server/app/model"
)
type articleHandler struct{}
var _ http.Handler = (*articleHandler)(nil)
func NewArticleHandler() http.Handler {
return &articleHandler{}
}
func (h *articleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
2023-02-26 12:26:48 +09:00
// initialize DB.
// D1 connector requires request's context to initialize DB.
c, err := d1.OpenConnector(req.Context(), "BlogDB")
2023-02-06 02:51:05 +09:00
if err != nil {
h.handleErr(w, http.StatusInternalServerError, fmt.Sprintf("failed to initialize DB: %v", err))
}
2023-02-26 12:26:48 +09:00
// use sql.OpenDB instead of sql.Open.
db := sql.OpenDB(c)
2023-02-06 02:51:05 +09:00
switch req.Method {
case http.MethodGet:
2023-02-12 01:57:04 +09:00
h.listArticles(w, req, db)
2023-02-06 02:51:05 +09:00
return
case http.MethodPost:
h.createArticle(w, req, db)
return
}
w.WriteHeader(http.StatusNotFound)
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("not found"))
}
func (h *articleHandler) handleErr(w http.ResponseWriter, status int, msg string) {
w.WriteHeader(status)
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(msg))
}
func (h *articleHandler) createArticle(w http.ResponseWriter, req *http.Request, db *sql.DB) {
var createArticleReq model.CreateArticleRequest
if err := easyjson.UnmarshalFromReader(req.Body, &createArticleReq); err != nil {
h.handleErr(w, http.StatusBadRequest,
"request format is invalid")
return
}
now := time.Now().Unix()
article := model.Article{
2023-02-26 13:05:51 +09:00
ID: uuid.New().String(),
2023-02-06 02:51:05 +09:00
Title: createArticleReq.Title,
Body: createArticleReq.Body,
CreatedAt: uint64(now),
}
2023-02-26 12:26:48 +09:00
_, err := db.Exec(`
2023-02-06 02:51:05 +09:00
INSERT INTO articles (id, title, body, created_at)
VALUES (?, ?, ?, ?)
`, article.ID, article.Title, article.Body, article.CreatedAt)
if err != nil {
log.Println(err)
h.handleErr(w, http.StatusInternalServerError,
"failed to save article")
return
}
res := model.CreateArticleResponse{
Article: article,
}
w.Header().Set("Content-Type", "application/json")
if _, err := easyjson.MarshalToWriter(&res, w); err != nil {
fmt.Fprintf(os.Stderr, "failed to encode response: %w\n", err)
}
}
2023-02-12 01:57:04 +09:00
func (h *articleHandler) listArticles(w http.ResponseWriter, req *http.Request, db *sql.DB) {
2023-02-26 12:26:48 +09:00
rows, err := db.Query(`
2023-02-06 02:51:05 +09:00
SELECT id, title, body, created_at FROM articles
ORDER BY created_at DESC;
`)
if err != nil {
h.handleErr(w, http.StatusInternalServerError,
"failed to load article")
return
}
articles := []model.Article{}
for rows.Next() {
var (
id, title, body string
createdAt float64 // number value is always retrieved as float64.
)
err = rows.Scan(&id, &title, &body, &createdAt)
if err != nil {
break
}
articles = append(articles, model.Article{
ID: id,
Title: title,
Body: body,
CreatedAt: uint64(createdAt),
})
}
res := model.ListArticlesResponse{
Articles: articles,
}
w.Header().Set("Content-Type", "application/json")
if _, err := easyjson.MarshalToWriter(&res, w); err != nil {
fmt.Fprintf(os.Stderr, "failed to encode response: %w\n", err)
}
}