2024-04-27 20:00:30 +07:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stephenafamo/bob"
|
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite"
|
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite/dialect"
|
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite/sm"
|
|
|
|
"github.com/tigorlazuardi/redmage/models"
|
|
|
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ImageListParams struct {
|
|
|
|
Q string
|
|
|
|
SFW bool
|
|
|
|
OrderBy string
|
|
|
|
Sort string
|
|
|
|
Offset int64
|
|
|
|
Limit int64
|
2024-04-30 14:12:33 +07:00
|
|
|
Device string
|
|
|
|
Subreddit string
|
2024-04-27 20:00:30 +07:00
|
|
|
CreatedAt time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ilp *ImageListParams) FillFromQuery(query Queryable) {
|
|
|
|
ilp.Q = query.Get("q")
|
|
|
|
ilp.SFW, _ = strconv.ParseBool(query.Get("sfw"))
|
|
|
|
ilp.OrderBy = query.Get("order_by")
|
|
|
|
ilp.Sort = strings.ToLower(query.Get("sort"))
|
|
|
|
ilp.Offset, _ = strconv.ParseInt(query.Get("offset"), 10, 64)
|
|
|
|
ilp.Limit, _ = strconv.ParseInt(query.Get("limit"), 10, 64)
|
|
|
|
if ilp.Limit > 100 {
|
|
|
|
ilp.Limit = 100
|
|
|
|
}
|
|
|
|
if ilp.Limit < 1 {
|
|
|
|
ilp.Limit = 25
|
|
|
|
}
|
2024-04-30 14:12:33 +07:00
|
|
|
ilp.Device = query.Get("device")
|
|
|
|
ilp.Subreddit = query.Get("subreddit")
|
2024-04-27 20:00:30 +07:00
|
|
|
|
|
|
|
createdAtint, _ := strconv.ParseInt(query.Get("created_at"), 10, 64)
|
|
|
|
if createdAtint > 0 {
|
|
|
|
ilp.CreatedAt = time.Unix(createdAtint, 0)
|
2024-04-29 21:45:18 +07:00
|
|
|
} else if createdAtint < 0 {
|
|
|
|
// Negative value means relative to now.
|
|
|
|
ilp.CreatedAt = time.Now().Add(time.Duration(createdAtint) * time.Second)
|
2024-04-27 20:00:30 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ilp ImageListParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) {
|
|
|
|
if ilp.Q != "" {
|
|
|
|
arg := sqlite.Arg("%" + ilp.Q + "%")
|
|
|
|
expr = append(expr,
|
|
|
|
sm.Where(
|
2024-04-30 14:12:33 +07:00
|
|
|
models.ImageColumns.PostTitle.Like(arg).
|
|
|
|
Or(models.ImageColumns.PostAuthor.Like(arg)).
|
2024-04-27 20:00:30 +07:00
|
|
|
Or(models.ImageColumns.ImageRelativePath.Like(arg)),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if ilp.SFW {
|
|
|
|
expr = append(expr, models.SelectWhere.Images.NSFW.EQ(0))
|
|
|
|
}
|
|
|
|
|
2024-04-30 14:12:33 +07:00
|
|
|
if len(ilp.Device) > 0 {
|
|
|
|
expr = append(expr, models.SelectWhere.Images.Device.EQ(ilp.Device))
|
2024-04-27 20:00:30 +07:00
|
|
|
}
|
|
|
|
|
2024-04-30 14:12:33 +07:00
|
|
|
if len(ilp.Subreddit) > 0 {
|
|
|
|
expr = append(expr, models.SelectWhere.Images.Subreddit.EQ(ilp.Subreddit))
|
2024-04-27 20:00:30 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if !ilp.CreatedAt.IsZero() {
|
2024-04-29 21:45:18 +07:00
|
|
|
expr = append(expr, models.SelectWhere.Images.CreatedAt.GTE(ilp.CreatedAt.Unix()))
|
2024-04-27 20:00:30 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ilp ImageListParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) {
|
|
|
|
expr = append(expr, ilp.CountQuery()...)
|
|
|
|
if ilp.Limit > 0 {
|
|
|
|
expr = append(expr, sm.Limit(ilp.Limit))
|
|
|
|
}
|
|
|
|
|
|
|
|
if ilp.Offset > 0 {
|
|
|
|
expr = append(expr, sm.Offset(ilp.Offset))
|
|
|
|
}
|
|
|
|
|
|
|
|
if ilp.OrderBy != "" {
|
|
|
|
order := sm.OrderBy(sqlite.Quote(ilp.OrderBy))
|
|
|
|
if ilp.Sort == "desc" {
|
|
|
|
expr = append(expr, order.Desc())
|
|
|
|
} else {
|
|
|
|
expr = append(expr, order.Asc())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
expr = append(expr, sm.OrderBy(models.ImageColumns.CreatedAt).Desc())
|
|
|
|
}
|
|
|
|
return expr
|
|
|
|
}
|
|
|
|
|
|
|
|
type ImageListResult struct {
|
|
|
|
Total int64
|
|
|
|
Images models.ImageSlice
|
|
|
|
}
|
|
|
|
|
|
|
|
func (im ImageListResult) MarshalJSON() ([]byte, error) {
|
|
|
|
type I struct {
|
|
|
|
*models.Image
|
|
|
|
Device *models.Device `json:"device,omitempty"`
|
|
|
|
Subreddit *models.Subreddit `json:"subreddit,omitempty"`
|
|
|
|
}
|
|
|
|
type A struct {
|
|
|
|
Total int64 `json:"total"`
|
|
|
|
Images []I `json:"images"`
|
|
|
|
}
|
|
|
|
|
|
|
|
a := A{Total: im.Total}
|
|
|
|
a.Images = make([]I, len(im.Images))
|
|
|
|
for i := 0; i < len(a.Images); i++ {
|
|
|
|
a.Images[i].Image = im.Images[i]
|
|
|
|
a.Images[i].Device = im.Images[i].R.Device
|
|
|
|
a.Images[i].Subreddit = im.Images[i].R.Subreddit
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.Marshal(a)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *API) ImagesList(ctx context.Context, params ImageListParams) (result ImageListResult, err error) {
|
|
|
|
ctx, span := tracer.Start(ctx, "*API.ImagesList")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
result.Images, err = models.Images.Query(ctx, api.db, params.Query()...).All()
|
|
|
|
if err != nil {
|
|
|
|
return result, errs.Wrapw(err, "failed to query for images", "params", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
result.Total, err = models.Images.Query(ctx, api.db, params.CountQuery()...).Count()
|
|
|
|
if err != nil {
|
|
|
|
return result, errs.Wrapw(err, "failed to query for images", "params", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *API) ImagesListWithDevicesAndSubreddits(ctx context.Context, params ImageListParams) (result ImageListResult, err error) {
|
|
|
|
ctx, span := tracer.Start(ctx, "*API.ImagesListWithDevicesAndSubreddits")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
result, err = api.ImagesList(ctx, params)
|
|
|
|
if err != nil {
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := result.Images.LoadImageDevice(ctx, api.db); err != nil {
|
|
|
|
return result, errs.Wrapw(err, "failed to load image devices", "params", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := result.Images.LoadImageSubreddit(ctx, api.db); err != nil {
|
|
|
|
return result, errs.Wrapw(err, "failed to load image subreddits", "params", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|