diff options
Diffstat (limited to 'pkg/handler')
-rw-r--r-- | pkg/handler/add/add.go (renamed from pkg/handler/add.go) | 4 | ||||
-rw-r--r-- | pkg/handler/add/handler.go | 21 | ||||
-rw-r--r-- | pkg/handler/admin/admin.go (renamed from pkg/handler/admin.go) | 36 | ||||
-rw-r--r-- | pkg/handler/captcha/handler.go | 17 | ||||
-rw-r--r-- | pkg/handler/feed/feed.go | 73 | ||||
-rw-r--r-- | pkg/handler/feed/handler.go | 21 | ||||
-rw-r--r-- | pkg/handler/handler.go | 14 | ||||
-rw-r--r-- | pkg/handler/quote/handler.go | 26 | ||||
-rw-r--r-- | pkg/handler/quote/index.go (renamed from pkg/handler/index.go) | 10 | ||||
-rw-r--r-- | pkg/handler/quote/quote.go (renamed from pkg/handler/quote.go) | 4 | ||||
-rw-r--r-- | pkg/handler/quote/random.go | 18 | ||||
-rw-r--r-- | pkg/handler/quote/top.go | 28 | ||||
-rw-r--r-- | pkg/handler/random.go | 31 | ||||
-rw-r--r-- | pkg/handler/rate/handler.go | 19 | ||||
-rw-r--r-- | pkg/handler/rate/rate.go | 69 |
15 files changed, 341 insertions, 50 deletions
diff --git a/pkg/handler/add.go b/pkg/handler/add/add.go index a6a5ced..dbcce47 100644 --- a/pkg/handler/add.go +++ b/pkg/handler/add/add.go @@ -1,4 +1,4 @@ -package handler +package add import ( "net/http" @@ -40,7 +40,7 @@ func (h *Handler) AddQuotePost(c echo.Context) error { Approved: false, Archive: false, } - if _, err := h.DB.NewInsert().Model(q).Exec(c.Request().Context()); err != nil { + if _, err := h.db.NewInsert().Model(q).Exec(c.Request().Context()); err != nil { return err } diff --git a/pkg/handler/add/handler.go b/pkg/handler/add/handler.go new file mode 100644 index 0000000..8f744ab --- /dev/null +++ b/pkg/handler/add/handler.go @@ -0,0 +1,21 @@ +package add + +import ( + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +type Handler struct { + db *bun.DB +} + +// NewHandler returns new Handler. +func NewHandler(db *bun.DB) *Handler { + return &Handler{db: db} +} + +func (h *Handler) Register(g *echo.Group) { + g.GET("", h.AddQuote) + g.POST("", h.AddQuotePost) + g.GET("/success", h.AddQuoteSuccess) +} diff --git a/pkg/handler/admin.go b/pkg/handler/admin/admin.go index 75fb650..494da05 100644 --- a/pkg/handler/admin.go +++ b/pkg/handler/admin/admin.go @@ -1,16 +1,42 @@ -package handler +package admin import ( "net/http" "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/uptrace/bun" + "sh.org.ru/pkg/config" "sh.org.ru/pkg/model" "sh.org.ru/pkg/tpl" ) +type Handler struct { + db *bun.DB + cfg *config.Config +} + +// NewHandler returns new Handler. +func NewHandler(db *bun.DB, cfg *config.Config) *Handler { + return &Handler{ + db: db, + cfg: cfg, + } +} + +func (h *Handler) Register(g *echo.Group) { + g.Use(middleware.BasicAuth(func(u, p string, ctx echo.Context) (bool, error) { + return h.cfg.Admins[u] == p, nil + })) + + g.GET("/", h.Admin) + g.POST("/action", h.AdminAction) + g.GET("/export", h.AdminExport) +} + func (h *Handler) Admin(c echo.Context) error { quotes := make([]model.Quote, 0, 20) - count, err := h.DB.NewSelect(). + count, err := h.db.NewSelect(). Model((*model.Quote)(nil)). Order("id ASC"). Where("approved = ?", false). @@ -30,7 +56,7 @@ func (h *Handler) AdminAction(c echo.Context) error { switch form.Action { case "approve": - _, err := h.DB.NewUpdate(). + _, err := h.db.NewUpdate(). Model(&model.Quote{ ID: int64(form.ID), Approved: true, @@ -42,7 +68,7 @@ func (h *Handler) AdminAction(c echo.Context) error { return err } case "decline": - _, err := h.DB.NewDelete(). + _, err := h.db.NewDelete(). Model(&model.Quote{ ID: int64(form.ID), }). @@ -58,7 +84,7 @@ func (h *Handler) AdminAction(c echo.Context) error { func (h *Handler) AdminExport(c echo.Context) error { quotes := []model.Quote{} - err := h.DB.NewSelect(). + err := h.db.NewSelect(). Model((*model.Quote)(nil)). Order("id ASC"). Scan(c.Request().Context(), "es) diff --git a/pkg/handler/captcha/handler.go b/pkg/handler/captcha/handler.go new file mode 100644 index 0000000..2e3b483 --- /dev/null +++ b/pkg/handler/captcha/handler.go @@ -0,0 +1,17 @@ +package captcha + +import ( + "github.com/labstack/echo/v4" + "github.com/ssoda/captcha" +) + +type Handler struct{} + +// NewHandler returns new Handler. +func NewHandler() *Handler { + return &Handler{} +} + +func (h *Handler) Register(g *echo.Group) { + g.GET("/*", echo.WrapHandler(captcha.Server(400, 65))) +} diff --git a/pkg/handler/feed/feed.go b/pkg/handler/feed/feed.go new file mode 100644 index 0000000..05921e9 --- /dev/null +++ b/pkg/handler/feed/feed.go @@ -0,0 +1,73 @@ +package feed + +import ( + "fmt" + "net/http" + "time" + + "github.com/gorilla/feeds" + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" +) + +func (h *Handler) Feed(c echo.Context) error { + feedType := c.Param("type") + + quotes := make([]model.Quote, 0, 20) + err := h.db.NewSelect(). + Model((*model.Quote)(nil)). + Order("id DESC"). + Where("approved = ?", true). + Limit(20). + Scan(c.Request().Context(), "es) + if err != nil { + return err + } + + feed := &feeds.Feed{ + Title: "sh.org.ru - Новый цитатник Рунета", + Link: &feeds.Link{Href: h.cfg.Host}, + Description: "", + Author: &feeds.Author{Name: "NeonXP", Email: "i@neonxp.ru"}, + Created: time.Now(), + } + + for _, q := range quotes { + uid := fmt.Sprintf("%s/quote/%d", h.cfg.Host, q.ID) + feed.Items = append(feed.Items, &feeds.Item{ + Id: uid, + Title: fmt.Sprintf("Цитата #%d", q.ID), + Link: &feeds.Link{Href: uid}, + Created: q.CreatedAt, + Description: q.Text(), + }) + } + switch feedType { + case "rss": + result, err := feed.ToRss() + if err != nil { + return err + } + + c.Response().Header().Set("Content-Type", "application/rss+xml") + return c.String(http.StatusOK, result) + case "atom": + result, err := feed.ToAtom() + if err != nil { + return err + } + + c.Response().Header().Set("Content-Type", "application/atom+xml") + return c.String(http.StatusOK, result) + case "json": + result, err := feed.ToJSON() + if err != nil { + return err + } + + c.Response().Header().Set("Content-Type", "application/json") + return c.String(http.StatusOK, result) + default: + return echo.ErrNotFound + } +} diff --git a/pkg/handler/feed/handler.go b/pkg/handler/feed/handler.go new file mode 100644 index 0000000..868e477 --- /dev/null +++ b/pkg/handler/feed/handler.go @@ -0,0 +1,21 @@ +package feed + +import ( + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" + "sh.org.ru/pkg/config" +) + +type Handler struct { + db *bun.DB + cfg *config.Config +} + +// NewHandler returns new Handler. +func NewHandler(db *bun.DB, cfg *config.Config) *Handler { + return &Handler{db: db, cfg: cfg} +} + +func (h *Handler) Register(g *echo.Group) { + g.GET("/:type", h.Feed) +} diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 5ba3966..15eb42d 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -1,7 +1,15 @@ package handler -import "github.com/uptrace/bun" +import "github.com/labstack/echo/v4" -type Handler struct { - DB *bun.DB +type Handler interface { + Register(g *echo.Group) +} + +type Router map[string]Handler + +func (r Router) Register(e *echo.Echo) { + for groupName, handlers := range r { + handlers.Register(e.Group(groupName)) + } } diff --git a/pkg/handler/quote/handler.go b/pkg/handler/quote/handler.go new file mode 100644 index 0000000..04807c0 --- /dev/null +++ b/pkg/handler/quote/handler.go @@ -0,0 +1,26 @@ +package quote + +import ( + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +type Handler struct { + db *bun.DB +} + +// NewHandler returns new Handler. +func NewHandler(db *bun.DB) *Handler { + return &Handler{db: db} +} + +func (h *Handler) Register(g *echo.Group) { + g.GET("", h.Index) + g.GET("quote/:id", h.Quote) + g.GET("random", h.Random) + g.GET("top", h.Top) +} + +type Pagination struct { + Page int `query:"page" default:"0"` +} diff --git a/pkg/handler/index.go b/pkg/handler/quote/index.go index 611a544..9dd21f8 100644 --- a/pkg/handler/index.go +++ b/pkg/handler/quote/index.go @@ -1,4 +1,4 @@ -package handler +package quote import ( "github.com/labstack/echo/v4" @@ -13,7 +13,7 @@ func (h *Handler) Index(c echo.Context) error { } quotes := make([]model.Quote, 0, 20) - count, err := h.DB.NewSelect(). + count, err := h.db.NewSelect(). Model((*model.Quote)(nil)). Order("id DESC"). Where("approved = ?", true). @@ -24,9 +24,5 @@ func (h *Handler) Index(c echo.Context) error { return err } - return tpl.Index(quotes, p.Page, count).Render(c.Request().Context(), c.Response()) -} - -type Pagination struct { - Page int `query:"page" default:"0"` + return tpl.List(quotes, p.Page, count).Render(c.Request().Context(), c.Response()) } diff --git a/pkg/handler/quote.go b/pkg/handler/quote/quote.go index 2a5f7e6..b25eb90 100644 --- a/pkg/handler/quote.go +++ b/pkg/handler/quote/quote.go @@ -1,4 +1,4 @@ -package handler +package quote import ( "strconv" @@ -16,7 +16,7 @@ func (h *Handler) Quote(c echo.Context) error { } quote := new(model.Quote) - err = h.DB.NewSelect(). + err = h.db.NewSelect(). Model(quote). Where("id = ?", id).Scan(c.Request().Context(), quote) if err != nil { diff --git a/pkg/handler/quote/random.go b/pkg/handler/quote/random.go new file mode 100644 index 0000000..1e04ff0 --- /dev/null +++ b/pkg/handler/quote/random.go @@ -0,0 +1,18 @@ +package quote + +import ( + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Random(c echo.Context) error { + quotes := make([]model.Quote, 0, 20) + err := h.db.NewRaw(`select q.* from quotes q where q.approved = true order by random() limit 20`). + Scan(c.Request().Context(), "es) + if err != nil { + return err + } + + return tpl.Random(quotes).Render(c.Request().Context(), c.Response()) +} diff --git a/pkg/handler/quote/top.go b/pkg/handler/quote/top.go new file mode 100644 index 0000000..c2803ea --- /dev/null +++ b/pkg/handler/quote/top.go @@ -0,0 +1,28 @@ +package quote + +import ( + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Top(c echo.Context) error { + p := &Pagination{} + if err := c.Bind(p); err != nil { + return err + } + + quotes := make([]model.Quote, 0, 20) + count, err := h.db.NewSelect(). + Model((*model.Quote)(nil)). + Order("rating DESC"). + Where("approved = ?", true). + Limit(20). + Offset(p.Page*20). + ScanAndCount(c.Request().Context(), "es) + if err != nil { + return err + } + + return tpl.List(quotes, p.Page, count).Render(c.Request().Context(), c.Response()) +} diff --git a/pkg/handler/random.go b/pkg/handler/random.go deleted file mode 100644 index 29c5f6f..0000000 --- a/pkg/handler/random.go +++ /dev/null @@ -1,31 +0,0 @@ -package handler - -import ( - "github.com/a-h/templ" - "github.com/labstack/echo/v4" - "sh.org.ru/pkg/model" - "sh.org.ru/pkg/tpl" -) - -func (h *Handler) Random(c echo.Context) error { - quotes := make([]model.Quote, 0, 20) - err := h.DB.NewRaw(`select q.* from quotes q where q.approved = true order by random() limit 20`). - Scan(c.Request().Context(), "es) - if err != nil { - return err - } - - comp := tpl.Random(quotes) - - if c.Request().Header.Get("Hx-Request") == "true" { - return comp.Render(c.Request().Context(), c.Response()) - } - - ctx := templ.WithChildren(c.Request().Context(), comp) - - return tpl.Layout(tpl.HeaderParams{ - Title: "Цитатник Рунета", - Description: "Новый цитатник Рунета", - URL: "https://sh.org.ru/", - }).Render(ctx, c.Response()) -} diff --git a/pkg/handler/rate/handler.go b/pkg/handler/rate/handler.go new file mode 100644 index 0000000..1a2ecf3 --- /dev/null +++ b/pkg/handler/rate/handler.go @@ -0,0 +1,19 @@ +package rate + +import ( + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +type Handler struct { + db *bun.DB +} + +// NewHandler returns new Handler. +func NewHandler(db *bun.DB) *Handler { + return &Handler{db: db} +} + +func (h *Handler) Register(g *echo.Group) { + g.POST("/:id", h.Rate) +} diff --git a/pkg/handler/rate/rate.go b/pkg/handler/rate/rate.go new file mode 100644 index 0000000..df6713f --- /dev/null +++ b/pkg/handler/rate/rate.go @@ -0,0 +1,69 @@ +package rate + +import ( + "strconv" + + "github.com/labstack/echo-contrib/session" + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Rate(c echo.Context) error { + voted, err := session.Get("votes", c) + if err != nil { + return err + } + sid := c.Param("id") + id, err := strconv.Atoi(sid) + if err != nil { + return err + } + f := new(rateForm) + if err := c.Bind(f); err != nil { + return err + } + + quote := new(model.Quote) + + if _, ok := voted.Values[id]; !ok { + voted.Values[id] = true + if err := voted.Save(c.Request(), c.Response()); err != nil { + return err + } + set := "" + switch f.Vote { + case "up": + set = "rating = rating + 1" + case "down": + set = "rating = rating - 1" + } + _, err = h.db.NewUpdate(). + Model(quote). + Where("id = ?", id). + Set(set). + Returning("*"). + Exec(c.Request().Context(), quote) + } else { + err = h.db.NewSelect(). + Model(quote). + Where("id = ?", id). + Scan(c.Request().Context(), quote) + } + if err != nil { + return err + } + + return tpl.Rate(quote, 0).Render(c.Request().Context(), c.Response()) +} + +type rateForm struct { + Vote string `form:"vote"` +} + +type voteType string + +const ( + voteUp voteType = "up" + voteDown voteType = "down" +) |