refactor: simpler data columns
This commit is contained in:
parent
547e73846d
commit
4876930563
|
@ -9,11 +9,11 @@ import (
|
||||||
"github.com/tigorlazuardi/redmage/pkg/errs"
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) DevicesUpdate(ctx context.Context, id int, update *models.DeviceSetter) (device *models.Device, err error) {
|
func (api *API) DevicesUpdate(ctx context.Context, slug string, update *models.DeviceSetter) (device *models.Device, err error) {
|
||||||
ctx, span := tracer.Start(ctx, "*API.DevicesUpdate")
|
ctx, span := tracer.Start(ctx, "*API.DevicesUpdate")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
device = &models.Device{ID: int32(id)}
|
device = &models.Device{Slug: slug}
|
||||||
|
|
||||||
err = models.Devices.Update(ctx, api.db, update, device)
|
err = models.Devices.Update(ctx, api.db, update, device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,11 +23,11 @@ func (api *API) DevicesUpdate(ctx context.Context, id int, update *models.Device
|
||||||
return device, errs.Wrapw(err, "a device with the same slug id already exists").Code(409)
|
return device, errs.Wrapw(err, "a device with the same slug id already exists").Code(409)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return device, errs.Wrapw(err, "failed to update device", "id", id, "values", update)
|
return device, errs.Wrapw(err, "failed to update device", "slug", slug, "values", update)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := device.Reload(ctx, api.db); err != nil {
|
if err := device.Reload(ctx, api.db); err != nil {
|
||||||
return device, errs.Wrapw(err, "failed to reload device", "id", id)
|
return device, errs.Wrapw(err, "failed to reload device", "slug", slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
return device, nil
|
return device, nil
|
||||||
|
|
|
@ -100,11 +100,11 @@ func (api *API) downloadSubredditListImage(ctx context.Context, list reddit.List
|
||||||
if !post.IsImagePost() {
|
if !post.IsImagePost() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
devices := api.getDevicesThatAcceptPost(ctx, post, devices)
|
acceptedDevices := api.getDevicesThatAcceptPost(ctx, post, devices)
|
||||||
if len(devices) == 0 {
|
if len(acceptedDevices) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.New(ctx).Debug("downloading image", "post_id", post.GetID(), "post_url", post.GetImageURL(), "devices", devices)
|
log.New(ctx).Debug("downloading image", "post_id", post.GetID(), "post_url", post.GetImageURL(), "devices", acceptedDevices)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
api.imageSemaphore <- struct{}{}
|
api.imageSemaphore <- struct{}{}
|
||||||
go func(ctx context.Context, post reddit.Post) {
|
go func(ctx context.Context, post reddit.Post) {
|
||||||
|
@ -113,7 +113,15 @@ func (api *API) downloadSubredditListImage(ctx context.Context, list reddit.List
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := api.downloadSubredditImage(ctx, post, subreddit, devices); err != nil {
|
if imageFile := api.findImageFileForDevices(ctx, post, devices); imageFile != nil {
|
||||||
|
err := api.saveImageToFSAndDatabase(ctx, imageFile, subreddit, post, acceptedDevices)
|
||||||
|
if err != nil {
|
||||||
|
log.New(ctx).Err(err).Error("failed to download subreddit image")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := api.downloadSubredditImage(ctx, post, subreddit, acceptedDevices); err != nil {
|
||||||
log.New(ctx).Err(err).Error("failed to download subreddit image")
|
log.New(ctx).Err(err).Error("failed to download subreddit image")
|
||||||
}
|
}
|
||||||
}(ctx, post)
|
}(ctx, post)
|
||||||
|
@ -174,13 +182,21 @@ func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, su
|
||||||
return errs.Wrapw(err, "failed to encode thumbnail file to jpeg", "filename", thumbnailPath)
|
return errs.Wrapw(err, "failed to encode thumbnail file to jpeg", "filename", thumbnailPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return api.saveImageToFSAndDatabase(ctx, tmpImageFile, subreddit, post, devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) saveImageToFSAndDatabase(ctx context.Context, image io.ReadCloser, subreddit *models.Subreddit, post reddit.Post, devices models.DeviceSlice) (err error) {
|
||||||
|
ctx, span := tracer.Start(ctx, "*API.saveImageToFSAndDatabase")
|
||||||
|
defer span.End()
|
||||||
|
defer image.Close()
|
||||||
|
|
||||||
w, close, err := api.createDeviceImageWriters(post, devices)
|
w, close, err := api.createDeviceImageWriters(post, devices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrapw(err, "failed to create image files")
|
return errs.Wrapw(err, "failed to create image files")
|
||||||
}
|
}
|
||||||
log.New(ctx).Debug("saving image files", "post_id", post.GetID(), "post_url", post.GetImageURL(), "devices", devices)
|
log.New(ctx).Debug("saving image files", "post_id", post.GetID(), "post_url", post.GetImageURL(), "devices", devices)
|
||||||
defer close()
|
defer close()
|
||||||
_, err = io.Copy(w, tmpImageFile)
|
_, err = io.Copy(w, image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrapw(err, "failed to save image files")
|
return errs.Wrapw(err, "failed to save image files")
|
||||||
}
|
}
|
||||||
|
@ -192,20 +208,27 @@ func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, su
|
||||||
if post.IsNSFW() {
|
if post.IsNSFW() {
|
||||||
nsfw = 1
|
nsfw = 1
|
||||||
}
|
}
|
||||||
|
width, height := post.GetImageSize()
|
||||||
|
var size int64
|
||||||
|
if fi, err := os.Stat(post.GetImageTargetPath(api.config, device)); err == nil {
|
||||||
|
size = fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
many = append(many, &models.ImageSetter{
|
many = append(many, &models.ImageSetter{
|
||||||
SubredditID: omit.From(subreddit.ID),
|
Subreddit: omit.From(subreddit.Name),
|
||||||
DeviceID: omit.From(device.ID),
|
Device: omit.From(device.Slug),
|
||||||
Title: omit.From(post.GetTitle()),
|
PostTitle: omit.From(post.GetTitle()),
|
||||||
PostID: omit.From(post.GetID()),
|
|
||||||
PostURL: omit.From(post.GetPostURL()),
|
PostURL: omit.From(post.GetPostURL()),
|
||||||
PostCreated: omit.From(post.GetCreated().Unix()),
|
PostCreated: omit.From(post.GetCreated().Unix()),
|
||||||
PostName: omit.From(post.GetName()),
|
PostName: omit.From(post.GetName()),
|
||||||
Poster: omit.From(post.GetAuthor()),
|
PostAuthor: omit.From(post.GetAuthor()),
|
||||||
PosterURL: omit.From(post.GetAuthorURL()),
|
PostAuthorURL: omit.From(post.GetAuthorURL()),
|
||||||
|
ImageWidth: omit.From(int32(width)),
|
||||||
|
ImageHeight: omit.From(int32(height)),
|
||||||
|
ImageSize: omit.From(size),
|
||||||
ImageRelativePath: omit.From(post.GetImageRelativePath(device)),
|
ImageRelativePath: omit.From(post.GetImageRelativePath(device)),
|
||||||
ThumbnailRelativePath: omit.From(post.GetThumbnailRelativePath()),
|
ThumbnailRelativePath: omit.From(post.GetThumbnailRelativePath()),
|
||||||
ImageOriginalURL: omit.From(post.GetImageURL()),
|
ImageOriginalURL: omit.From(post.GetImageURL()),
|
||||||
ThumbnailOriginalURL: omit.From(post.GetThumbnailURL()),
|
|
||||||
NSFW: omit.From(nsfw),
|
NSFW: omit.From(nsfw),
|
||||||
CreatedAt: omit.From(now.Unix()),
|
CreatedAt: omit.From(now.Unix()),
|
||||||
UpdatedAt: omit.From(now.Unix()),
|
UpdatedAt: omit.From(now.Unix()),
|
||||||
|
@ -217,7 +240,6 @@ func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, su
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrapw(err, "failed to insert images to database", "params", many)
|
return errs.Wrapw(err, "failed to insert images to database", "params", many)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,20 +287,22 @@ func (api *API) createDeviceImageWriters(post reddit.Post, devices models.Device
|
||||||
|
|
||||||
func (api *API) getDevicesThatAcceptPost(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (devs models.DeviceSlice) {
|
func (api *API) getDevicesThatAcceptPost(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (devs models.DeviceSlice) {
|
||||||
for _, device := range devices {
|
for _, device := range devices {
|
||||||
if shouldDownloadPostForDevice(post, device) && !api.isImageExists(ctx, post, device) {
|
if shouldDownloadPostForDevice(post, device) && !api.isImageEntryExists(ctx, post, device) {
|
||||||
devs = append(devs, device)
|
devs = append(devs, device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return devs
|
return devs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) isImageExists(ctx context.Context, post reddit.Post, device *models.Device) (found bool) {
|
// isImageEntryExists checks if the image entry already exists in the database and
|
||||||
|
// the image file actually exists in the filesystem.
|
||||||
|
func (api *API) isImageEntryExists(ctx context.Context, post reddit.Post, device *models.Device) (found bool) {
|
||||||
ctx, span := tracer.Start(ctx, "*API.IsImageExists")
|
ctx, span := tracer.Start(ctx, "*API.IsImageExists")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
_, errQuery := models.Images.Query(ctx, api.db,
|
_, errQuery := models.Images.Query(ctx, api.db,
|
||||||
models.SelectWhere.Images.DeviceID.EQ(device.ID),
|
models.SelectWhere.Images.Device.EQ(device.Slug),
|
||||||
models.SelectWhere.Images.PostID.EQ(post.GetID()),
|
models.SelectWhere.Images.PostName.EQ(post.GetName()),
|
||||||
).One()
|
).One()
|
||||||
|
|
||||||
_, errStat := os.Stat(post.GetImageTargetPath(api.config, device))
|
_, errStat := os.Stat(post.GetImageTargetPath(api.config, device))
|
||||||
|
@ -286,6 +310,29 @@ func (api *API) isImageExists(ctx context.Context, post reddit.Post, device *mod
|
||||||
return errQuery == nil && errStat == nil
|
return errQuery == nil && errStat == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findImageFileForDevice finds if any of the image file exists for given devices.
|
||||||
|
//
|
||||||
|
// This helps to avoid downloading the same image for different devices.
|
||||||
|
//
|
||||||
|
// Return nil if no image file exists for the devices.
|
||||||
|
//
|
||||||
|
// Ensure to close the file after use.
|
||||||
|
func (api *API) findImageFileForDevices(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (file *os.File) {
|
||||||
|
for _, device := range devices {
|
||||||
|
_, err := os.Stat(post.GetImageTargetPath(api.config, device))
|
||||||
|
if err == nil {
|
||||||
|
file, err = os.Open(post.GetImageTargetPath(api.config, device))
|
||||||
|
if err != nil {
|
||||||
|
log.New(ctx).Err(err).Error("failed to open image file", "filename", post.GetImageTargetPath(api.config, device))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func shouldDownloadPostForDevice(post reddit.Post, device *models.Device) bool {
|
func shouldDownloadPostForDevice(post reddit.Post, device *models.Device) bool {
|
||||||
if post.IsNSFW() && device.NSFW == 0 {
|
if post.IsNSFW() && device.NSFW == 0 {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -22,8 +22,8 @@ type ImageListParams struct {
|
||||||
Sort string
|
Sort string
|
||||||
Offset int64
|
Offset int64
|
||||||
Limit int64
|
Limit int64
|
||||||
Device int32
|
Device string
|
||||||
Subreddit int32
|
Subreddit string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,10 +40,8 @@ func (ilp *ImageListParams) FillFromQuery(query Queryable) {
|
||||||
if ilp.Limit < 1 {
|
if ilp.Limit < 1 {
|
||||||
ilp.Limit = 25
|
ilp.Limit = 25
|
||||||
}
|
}
|
||||||
device, _ := strconv.ParseInt(query.Get("device"), 10, 32)
|
ilp.Device = query.Get("device")
|
||||||
ilp.Device = int32(device)
|
ilp.Subreddit = query.Get("subreddit")
|
||||||
subreddit, _ := strconv.ParseInt(query.Get("subreddit"), 10, 32)
|
|
||||||
ilp.Subreddit = int32(subreddit)
|
|
||||||
|
|
||||||
createdAtint, _ := strconv.ParseInt(query.Get("created_at"), 10, 64)
|
createdAtint, _ := strconv.ParseInt(query.Get("created_at"), 10, 64)
|
||||||
if createdAtint > 0 {
|
if createdAtint > 0 {
|
||||||
|
@ -59,8 +57,8 @@ func (ilp ImageListParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) {
|
||||||
arg := sqlite.Arg("%" + ilp.Q + "%")
|
arg := sqlite.Arg("%" + ilp.Q + "%")
|
||||||
expr = append(expr,
|
expr = append(expr,
|
||||||
sm.Where(
|
sm.Where(
|
||||||
models.ImageColumns.Title.Like(arg).
|
models.ImageColumns.PostTitle.Like(arg).
|
||||||
Or(models.ImageColumns.Poster.Like(arg)).
|
Or(models.ImageColumns.PostAuthor.Like(arg)).
|
||||||
Or(models.ImageColumns.ImageRelativePath.Like(arg)),
|
Or(models.ImageColumns.ImageRelativePath.Like(arg)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -70,12 +68,12 @@ func (ilp ImageListParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) {
|
||||||
expr = append(expr, models.SelectWhere.Images.NSFW.EQ(0))
|
expr = append(expr, models.SelectWhere.Images.NSFW.EQ(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ilp.Device > 0 {
|
if len(ilp.Device) > 0 {
|
||||||
expr = append(expr, models.SelectWhere.Images.DeviceID.EQ(ilp.Device))
|
expr = append(expr, models.SelectWhere.Images.Device.EQ(ilp.Device))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ilp.Subreddit > 0 {
|
if len(ilp.Subreddit) > 0 {
|
||||||
expr = append(expr, models.SelectWhere.Images.SubredditID.EQ(ilp.Subreddit))
|
expr = append(expr, models.SelectWhere.Images.Subreddit.EQ(ilp.Subreddit))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ilp.CreatedAt.IsZero() {
|
if !ilp.CreatedAt.IsZero() {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
-- +goose Up
|
-- +goose Up
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
CREATE TABLE subreddits (
|
CREATE TABLE subreddits (
|
||||||
id INTEGER PRIMARY KEY,
|
name VARCHAR(30) NOT NULL PRIMARY KEY,
|
||||||
name VARCHAR(30) NOT NULL,
|
|
||||||
enable_schedule INT NOT NULL DEFAULT 1,
|
enable_schedule INT NOT NULL DEFAULT 1,
|
||||||
subtype INT NOT NULL DEFAULT 0,
|
subtype INT NOT NULL DEFAULT 0,
|
||||||
schedule VARCHAR(20) NOT NULL DEFAULT '@daily',
|
schedule VARCHAR(20) NOT NULL DEFAULT '@daily',
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
-- +goose Up
|
-- +goose Up
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
CREATE TABLE devices(
|
CREATE TABLE devices(
|
||||||
id INTEGER PRIMARY KEY,
|
slug VARCHAR(255) NOT NULL PRIMARY KEY,
|
||||||
enable INTEGER NOT NULL DEFAULT 1,
|
enable INTEGER NOT NULL DEFAULT 1,
|
||||||
slug VARCHAR(255) NOT NULL,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
resolution_x DOUBLE NOT NULL,
|
resolution_x DOUBLE NOT NULL,
|
||||||
resolution_y DOUBLE NOT NULL,
|
resolution_y DOUBLE NOT NULL,
|
||||||
|
|
|
@ -2,36 +2,37 @@
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
CREATE TABLE images(
|
CREATE TABLE images(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
subreddit_id INTEGER NOT NULL,
|
subreddit VARCHAR(255) NOT NULL,
|
||||||
device_id INTEGER NOT NULL,
|
device VARCHAR(250) NOT NULL,
|
||||||
title VARCHAR(255) NOT NULL,
|
post_title VARCHAR(255) NOT NULL,
|
||||||
post_id VARCHAR(50) NOT NULL,
|
post_name VARCHAR(255) NOT NULL,
|
||||||
post_url VARCHAR(255) NOT NULL,
|
post_url VARCHAR(255) NOT NULL,
|
||||||
post_created BIGINT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
post_created BIGINT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
post_name VARCHAR(255) NOT NULL,
|
post_author VARCHAR(50) NOT NULL,
|
||||||
poster VARCHAR(50) NOT NULL,
|
post_author_url VARCHAR(255) NOT NULL,
|
||||||
poster_url VARCHAR(255) NOT NULL,
|
|
||||||
image_relative_path VARCHAR(255) NOT NULL,
|
image_relative_path VARCHAR(255) NOT NULL,
|
||||||
thumbnail_relative_path VARCHAR(255) NOT NULL DEFAULT '',
|
|
||||||
image_original_url VARCHAR(255) NOT NULL,
|
image_original_url VARCHAR(255) NOT NULL,
|
||||||
thumbnail_original_url VARCHAR(255) NOT NULL DEFAULT '',
|
image_height INTEGER NOT NULL DEFAULT 0,
|
||||||
|
image_width INTEGER NOT NULL DEFAULT 0,
|
||||||
|
image_size BIGINT NOT NULL DEFAULT 0,
|
||||||
|
thumbnail_relative_path VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
nsfw INTEGER NOT NULL DEFAULT 0,
|
nsfw INTEGER NOT NULL DEFAULT 0,
|
||||||
created_at BIGINT DEFAULT 0 NOT NULL,
|
created_at BIGINT DEFAULT 0 NOT NULL,
|
||||||
updated_at BIGINT DEFAULT 0 NOT NULL,
|
updated_at BIGINT DEFAULT 0 NOT NULL,
|
||||||
CONSTRAINT fk_subreddit_id
|
CONSTRAINT fk_image_subreddit
|
||||||
FOREIGN KEY (subreddit_id)
|
FOREIGN KEY (subreddit)
|
||||||
REFERENCES subreddits(id)
|
REFERENCES subreddits(name)
|
||||||
ON DELETE CASCADE,
|
ON DELETE CASCADE,
|
||||||
CONSTRAINT fk_devices_id
|
CONSTRAINT fk_image_devices_slug
|
||||||
FOREIGN KEY (device_id)
|
FOREIGN KEY (device)
|
||||||
REFERENCES devices(id)
|
REFERENCES devices(slug)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_subreddit_id_images ON images(subreddit_id);
|
CREATE INDEX idx_subreddit_images ON images(subreddit);
|
||||||
CREATE INDEX idx_nsfw_images ON images(nsfw);
|
CREATE INDEX idx_nsfw_images ON images(nsfw);
|
||||||
CREATE INDEX idx_images_created_at ON images(created_at DESC);
|
CREATE INDEX idx_images_created_at_nsfw ON images(created_at DESC, nsfw);
|
||||||
CREATE INDEX idx_unique_device_images_id ON images(device_id, post_id DESC);
|
CREATE UNIQUE INDEX idx_unique_images_per_device ON images(device, post_name);
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|
BIN
pubsub.db-journal
Normal file
BIN
pubsub.db-journal
Normal file
Binary file not shown.
|
@ -1,7 +1,6 @@
|
||||||
POST http://localhost:8080/api/v1/subreddits/check HTTP/1.1
|
POST http://localhost:8080/api/v1/subreddits/check HTTP/1.1
|
||||||
Host: localhost:8080
|
Host: localhost:8080
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Content-Length: 35
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"subreddit": "Wallpapers"
|
"subreddit": "Wallpapers"
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aarondl/opt/omit"
|
"github.com/aarondl/opt/omit"
|
||||||
|
@ -36,11 +35,10 @@ func (routes *Routes) APIDeviceUpdate(rw http.ResponseWriter, r *http.Request) {
|
||||||
dec = json.NewDecoder(r.Body)
|
dec = json.NewDecoder(r.Body)
|
||||||
)
|
)
|
||||||
|
|
||||||
id, err := strconv.Atoi(chi.URLParam(r, "id"))
|
slug := chi.URLParam(r, "slug")
|
||||||
if err != nil {
|
if slug == "" {
|
||||||
log.New(ctx).Err(err).Error("failed to parse id")
|
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
_ = json.NewEncoder(rw).Encode(map[string]string{"error": fmt.Sprintf("bad id: %s", err)})
|
_ = json.NewEncoder(rw).Encode(map[string]string{"error": "missing name"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +51,7 @@ func (routes *Routes) APIDeviceUpdate(rw http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
device, err := routes.API.DevicesUpdate(ctx, id, &models.DeviceSetter{
|
device, err := routes.API.DevicesUpdate(ctx, slug, &models.DeviceSetter{
|
||||||
Slug: omit.FromCond(body.Slug, body.Slug != ""),
|
|
||||||
Name: omit.FromCond(body.Name, body.Name != ""),
|
Name: omit.FromCond(body.Name, body.Name != ""),
|
||||||
ResolutionX: omit.FromCond(body.ResolutionX, body.ResolutionX != 0),
|
ResolutionX: omit.FromCond(body.ResolutionX, body.ResolutionX != 0),
|
||||||
ResolutionY: omit.FromCond(body.ResolutionY, body.ResolutionY != 0),
|
ResolutionY: omit.FromCond(body.ResolutionY, body.ResolutionY != 0),
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (routes *Routes) registerV1APIRoutes(router chi.Router) {
|
||||||
|
|
||||||
router.Get("/devices", routes.APIDeviceList)
|
router.Get("/devices", routes.APIDeviceList)
|
||||||
router.Post("/devices", routes.APIDeviceCreate)
|
router.Post("/devices", routes.APIDeviceCreate)
|
||||||
router.Patch("/devices/{id}", routes.APIDeviceUpdate)
|
router.Patch("/devices/{slug}", routes.APIDeviceUpdate)
|
||||||
|
|
||||||
router.Get("/images", routes.ImagesListAPI)
|
router.Get("/images", routes.ImagesListAPI)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ templ HomeContent(c *views.Context, data Data) {
|
||||||
} else {
|
} else {
|
||||||
<section class="mb-4 mx-auto">
|
<section class="mb-4 mx-auto">
|
||||||
<div
|
<div
|
||||||
class="flex content-center gap-8"
|
class="flex flex-wrap content-center gap-x-8"
|
||||||
hx-get="/"
|
hx-get="/"
|
||||||
hx-target="#recently-added-images"
|
hx-target="#recently-added-images"
|
||||||
hx-select="#recently-added-images"
|
hx-select="#recently-added-images"
|
||||||
|
@ -35,7 +35,7 @@ templ HomeContent(c *views.Context, data Data) {
|
||||||
hx-include="this"
|
hx-include="this"
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
>
|
>
|
||||||
<h1>
|
<h1 class="mb-4">
|
||||||
Recently Added -
|
Recently Added -
|
||||||
{ strconv.FormatInt(data.TotalImages, 10) } Images
|
{ strconv.FormatInt(data.TotalImages, 10) } Images
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -51,7 +51,7 @@ templ HomeContent(c *views.Context, data Data) {
|
||||||
<h2 class="mt-4">{ recently.Device.Name }</h2>
|
<h2 class="mt-4">{ recently.Device.Name }</h2>
|
||||||
for _, subreddit := range recently.Subreddits {
|
for _, subreddit := range recently.Subreddits {
|
||||||
<h4>
|
<h4>
|
||||||
<a class="text-primary" href={ templ.SafeURL(fmt.Sprintf("/subreddits/details/%s?device=%d", subreddit.Subreddit.Name, recently.Device.ID)) }>
|
<a class="text-primary" href={ templ.SafeURL(fmt.Sprintf("/subreddits/details/%s?device=%d", subreddit.Subreddit.Name, recently.Device.Slug)) }>
|
||||||
{ subreddit.Subreddit.Name }
|
{ subreddit.Subreddit.Name }
|
||||||
</a>
|
</a>
|
||||||
- { strconv.Itoa(len(subreddit.Images)) } images
|
- { strconv.Itoa(len(subreddit.Images)) } images
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package homeview
|
package homeview
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -42,11 +41,11 @@ func NewRecentlyAddedImages(images models.ImageSlice) RecentlyAddedImages {
|
||||||
}
|
}
|
||||||
var deviceFound bool
|
var deviceFound bool
|
||||||
for i, ra := range r {
|
for i, ra := range r {
|
||||||
if ra.Device.ID == image.R.Device.ID {
|
if ra.Device.Slug == image.R.Device.Slug {
|
||||||
deviceFound = true
|
deviceFound = true
|
||||||
var subredditFound bool
|
var subredditFound bool
|
||||||
for j, subreddit := range r[i].Subreddits {
|
for j, subreddit := range r[i].Subreddits {
|
||||||
if subreddit.Subreddit.ID == image.R.Subreddit.ID {
|
if subreddit.Subreddit.Name == image.R.Subreddit.Name {
|
||||||
subredditFound = true
|
subredditFound = true
|
||||||
r[i].Subreddits[j].Images = append(r[i].Subreddits[j].Images, image)
|
r[i].Subreddits[j].Images = append(r[i].Subreddits[j].Images, image)
|
||||||
count++
|
count++
|
||||||
|
@ -89,7 +88,5 @@ func NewRecentlyAddedImages(images models.ImageSlice) RecentlyAddedImages {
|
||||||
return strings.Compare(leftName, rightName)
|
return strings.Compare(leftName, rightName)
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Println("image count", count)
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
|
||||||
<img
|
<img
|
||||||
class="object-contain w-[256px] h-[256px]"
|
class="object-contain w-[256px] h-[256px]"
|
||||||
src={ fmt.Sprintf("/img/%s", data.ThumbnailRelativePath) }
|
src={ fmt.Sprintf("/img/%s", data.ThumbnailRelativePath) }
|
||||||
alt={ data.Title }
|
alt={ data.PostTitle }
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</figure>
|
</figure>
|
||||||
|
@ -37,9 +37,9 @@ templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
|
||||||
<a
|
<a
|
||||||
href={ templ.URL(data.PostURL) }
|
href={ templ.URL(data.PostURL) }
|
||||||
class="card-title font-bold underline text-base text-primary"
|
class="card-title font-bold underline text-base text-primary"
|
||||||
>{ truncateTitle(data.Title) }</a>
|
>{ truncateTitle(data.PostTitle) }</a>
|
||||||
}
|
}
|
||||||
<a class="text-primary underline" href={ templ.URL(data.PosterURL) }>{ data.Poster }</a>
|
<a class="text-primary underline" href={ templ.URL(data.PostAuthorURL) }>{ data.PostAuthor }</a>
|
||||||
<div>
|
<div>
|
||||||
@utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt)
|
@utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt)
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue