From db432f4dc95470b3d6aed315cd28e138651f9373 Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Thu, 25 Apr 2024 12:31:20 +0700 Subject: [PATCH] removed sqlc, moved to bob --- Makefile | 3 + api/api.go | 53 ++++++++---- api/download_subreddit_images.go | 83 +++++++++++++------ api/download_subreddit_posts.go | 2 +- api/pubsub_download.go | 52 ++++++++++++ api/reddit/get_posts.go | 4 +- api/reddit/image_download_reader.go | 2 - api/reddit/post.go | 25 ++++-- cli/serve.go | 10 +-- config/default.go | 2 + .../20240409222145_create_images_table.sql | 1 + db/queries/devices.sql | 28 ------- db/queries/images.sql | 5 -- db/queries/subreddits.sql | 28 ------- flake.nix | 1 - go.mod | 6 ++ go.sum | 13 +++ rest/devices/update.http | 2 +- 18 files changed, 202 insertions(+), 118 deletions(-) create mode 100644 api/pubsub_download.go delete mode 100644 db/queries/devices.sql delete mode 100644 db/queries/images.sql delete mode 100644 db/queries/subreddits.sql diff --git a/Makefile b/Makefile index de2973a..faf6077 100644 --- a/Makefile +++ b/Makefile @@ -55,3 +55,6 @@ migrate-new: migrate-redo: @goose redo + +migrate-up: + @goose up \ No newline at end of file diff --git a/api/api.go b/api/api.go index f504f8e..6c73aed 100644 --- a/api/api.go +++ b/api/api.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "os" "github.com/robfig/cron/v3" "github.com/stephenafamo/bob" @@ -11,18 +12,21 @@ import ( "github.com/tigorlazuardi/redmage/api/bmessage" "github.com/tigorlazuardi/redmage/api/reddit" "github.com/tigorlazuardi/redmage/config" - "github.com/tigorlazuardi/redmage/db/queries" + "github.com/tigorlazuardi/redmage/models" "github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/log" + + "github.com/ThreeDotsLabs/watermill" + watermillSql "github.com/ThreeDotsLabs/watermill-sql/v3/pkg/sql" + "github.com/ThreeDotsLabs/watermill/message" ) type API struct { - queries *queries.Queries - db *sql.DB - exec bob.Executor + db *sql.DB + exec bob.Executor scheduler *cron.Cron - scheduleMap map[cron.EntryID]queries.Subreddit + scheduleMap map[cron.EntryID]*models.Subreddit downloadBroadcast *broadcast.Relay[bmessage.ImageDownloadMessage] @@ -32,32 +36,54 @@ type API struct { subredditSemaphore chan struct{} reddit *reddit.Reddit + + subscriber message.Subscriber + publisher message.Publisher } type Dependencies struct { - Queries *queries.Queries - DB *sql.DB - Config *config.Config - Reddit *reddit.Reddit + DB *sql.DB + Config *config.Config + Reddit *reddit.Reddit } +const downloadTopic = "subreddit.download" + func New(deps Dependencies) *API { + ackDeadline := deps.Config.Duration("download.pubsub.ack.deadline") + subscriber, err := watermillSql.NewSubscriber(deps.DB, watermillSql.SubscriberConfig{ + AckDeadline: &ackDeadline, + SchemaAdapter: watermillSql.DefaultPostgreSQLSchema{}, + OffsetsAdapter: watermillSql.DefaultPostgreSQLOffsetsAdapter{}, + InitializeSchema: true, + }, watermill.NewStdLoggerWithOut(os.Stderr, true, true)) + if err != nil { + panic(err) + } + publisher, err := watermillSql.NewPublisher(deps.DB, watermillSql.PublisherConfig{ + SchemaAdapter: watermillSql.DefaultPostgreSQLSchema{}, + AutoInitializeSchema: true, + }, watermill.NewStdLoggerWithOut(os.Stderr, true, true)) + if err != nil { + panic(err) + } return &API{ - queries: deps.Queries, db: deps.DB, exec: bob.New(deps.DB), scheduler: cron.New(), - scheduleMap: make(map[cron.EntryID]queries.Subreddit, 8), + scheduleMap: make(map[cron.EntryID]*models.Subreddit, 8), downloadBroadcast: broadcast.NewRelay[bmessage.ImageDownloadMessage](), config: deps.Config, imageSemaphore: make(chan struct{}, deps.Config.Int("download.concurrency.images")), subredditSemaphore: make(chan struct{}, deps.Config.Int("download.concurrency.subreddits")), reddit: deps.Reddit, + subscriber: subscriber, + publisher: publisher, } } func (api *API) StartScheduler(ctx context.Context) error { - subreddits, err := api.queries.SubredditsGetAll(ctx) + subreddits, err := models.Subreddits.Query(ctx, api.exec, nil).All() if err != nil { return errs.Wrapw(err, "failed to get all subreddits") } @@ -76,13 +102,12 @@ func (api *API) StartScheduler(ctx context.Context) error { return nil } -func (api *API) scheduleSubreddit(subreddit queries.Subreddit) error { +func (api *API) scheduleSubreddit(subreddit *models.Subreddit) error { id, err := api.scheduler.AddFunc(subreddit.Schedule, func() { }) if err != nil { return errs.Wrap(err) } - api.scheduleMap[id] = subreddit return nil diff --git a/api/download_subreddit_images.go b/api/download_subreddit_images.go index bceb388..e754663 100644 --- a/api/download_subreddit_images.go +++ b/api/download_subreddit_images.go @@ -15,17 +15,18 @@ import ( "github.com/disintegration/imaging" "github.com/tigorlazuardi/redmage/api/reddit" - "github.com/tigorlazuardi/redmage/db/queries" + "github.com/tigorlazuardi/redmage/models" "github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/log" "github.com/tigorlazuardi/redmage/pkg/telemetry" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "golang.org/x/sync/errgroup" ) type DownloadSubredditParams struct { Countback int - Devices []queries.Device + Devices models.DeviceSlice SubredditType reddit.SubredditType } @@ -51,15 +52,19 @@ func (api *API) DownloadSubredditImages(ctx context.Context, subredditName strin countback := params.Countback - for page := 1; countback > 0; page += 1 { - limit := countback - if limit > 100 { - limit = 100 + var ( + list reddit.Listing + err error + ) + for countback > 0 { + limit := 100 + if limit > countback { + limit = countback } - list, err := api.reddit.GetPosts(ctx, reddit.GetPostsParam{ + list, err = api.reddit.GetPosts(ctx, reddit.GetPostsParam{ Subreddit: subredditName, Limit: limit, - Page: page, + After: list.GetLastAfter(), SubredditType: params.SubredditType, }) if err != nil { @@ -73,6 +78,9 @@ func (api *API) DownloadSubredditImages(ctx context.Context, subredditName strin log.New(ctx).Err(err).Error("failed to download image") } }(ctx, list) + if len(list.GetPosts()) == 0 { + break + } countback -= len(list.GetPosts()) } @@ -91,7 +99,7 @@ func (api *API) downloadSubredditListImage(ctx context.Context, list reddit.List if !post.IsImagePost() { continue } - devices := getDevicesThatAcceptPost(post, params.Devices) + devices := api.getDevicesThatAcceptPost(ctx, post, params.Devices) if len(devices) == 0 { continue } @@ -114,7 +122,7 @@ func (api *API) downloadSubredditListImage(ctx context.Context, list reddit.List return nil } -func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, devices []queries.Device) error { +func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, devices models.DeviceSlice) error { ctx, span := tracer.Start(ctx, "*API.downloadSubredditImage") defer span.End() @@ -170,7 +178,7 @@ func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, de return nil } -func (api *API) createDeviceImageWriters(post reddit.Post, devices []queries.Device) (writer io.Writer, close func(), err error) { +func (api *API) createDeviceImageWriters(post reddit.Post, devices models.DeviceSlice) (writer io.Writer, close func(), err error) { // open file for each device var files []*os.File var writers []io.Writer @@ -181,7 +189,7 @@ func (api *API) createDeviceImageWriters(post reddit.Post, devices []queries.Dev } else { filename = post.GetImageTargetPath(api.config, device) } - file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { for _, f := range files { _ = f.Close() @@ -203,40 +211,67 @@ func (api *API) createDeviceImageWriters(post reddit.Post, devices []queries.Dev }, nil } -func getDevicesThatAcceptPost(post reddit.Post, devices []queries.Device) []queries.Device { - var devs []queries.Device +func (api *API) getDevicesThatAcceptPost(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (devs models.DeviceSlice) { + var mu sync.Mutex + errgrp, ctx := errgroup.WithContext(ctx) for _, device := range devices { if shouldDownloadPostForDevice(post, device) { - devs = append(devices, device) + device := device + errgrp.Go(func() error { + if !api.isImageExists(ctx, post, device) { + mu.Lock() + defer mu.Unlock() + devs = append(devices, device) + } + return nil + }) } } + _ = errgrp.Wait() return devs } -func shouldDownloadPostForDevice(post reddit.Post, device queries.Device) bool { - if post.IsNSFW() && device.Nsfw == 0 { +func (api *API) isImageExists(ctx context.Context, post reddit.Post, device *models.Device) (found bool) { + ctx, span := tracer.Start(ctx, "*API.IsImageExists") + defer span.End() + + // Image does not exist in target image. + if _, err := os.Stat(post.GetImageTargetPath(api.config, device)); err != nil { + return false + } + + _, err := models.Images.Query(ctx, api.exec, + models.SelectWhere.Images.DeviceID.EQ(device.ID), + models.SelectWhere.Images.PostID.EQ(post.GetID()), + ).One() + + return err == nil +} + +func shouldDownloadPostForDevice(post reddit.Post, device *models.Device) bool { + if post.IsNSFW() && device.NSFW == 0 { return false } if math.Abs(deviceAspectRatio(device)-post.GetImageAspectRatio()) > device.AspectRatioTolerance { // outside of aspect ratio tolerance return false } width, height := post.GetImageSize() - if device.MaxX > 0 && width > device.MaxX { + if device.MaxX > 0 && width > int64(device.MaxX) { return false } - if device.MaxY > 0 && height > device.MaxY { + if device.MaxY > 0 && height > int64(device.MaxY) { return false } - if device.MinX > 0 && width < device.MinX { + if device.MinX > 0 && width < int64(device.MinX) { return false } - if device.MinY > 0 && height < device.MinY { + if device.MinY > 0 && height < int64(device.MinY) { return false } return true } -func deviceAspectRatio(device queries.Device) float64 { +func deviceAspectRatio(device *models.Device) float64 { return float64(device.ResolutionX) / float64(device.ResolutionY) } @@ -269,10 +304,10 @@ func (api *API) copyImageToTempDir(ctx context.Context, img reddit.PostImage) (t split := strings.Split(url.Path, "/") imageFilename := split[len(split)-1] tmpDirname := path.Join(os.TempDir(), "redmage") - _ = os.MkdirAll(tmpDirname, 0644) + _ = os.MkdirAll(tmpDirname, 0o644) tmpFilename := path.Join(tmpDirname, imageFilename) - file, err := os.OpenFile(tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + file, err := os.OpenFile(tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { return nil, errs.Wrapw(err, "failed to open temp image file", "temp_file_path", tmpFilename, diff --git a/api/download_subreddit_posts.go b/api/download_subreddit_posts.go index 7ea04be..cbf6157 100644 --- a/api/download_subreddit_posts.go +++ b/api/download_subreddit_posts.go @@ -11,6 +11,6 @@ type DownloadSubredditPostsParams struct { Limit int } -func (api *API) DownloadSubredditPosts(ctx context.Context, subredditName string, params DownloadSubredditParams) (posts []reddit.Listing, err error) { +func (api *API) DownloadSubredditPosts(ctx context.Context, subredditName string, params DownloadSubredditPostsParams) (posts reddit.Listing, err error) { return posts, err } diff --git a/api/pubsub_download.go b/api/pubsub_download.go new file mode 100644 index 0000000..64452ba --- /dev/null +++ b/api/pubsub_download.go @@ -0,0 +1,52 @@ +package api + +import ( + "context" + + "github.com/ThreeDotsLabs/watermill/message" + "github.com/tigorlazuardi/redmage/api/reddit" + "github.com/tigorlazuardi/redmage/models" + "github.com/tigorlazuardi/redmage/pkg/log" + "github.com/tigorlazuardi/redmage/pkg/telemetry" + "go.opentelemetry.io/otel/attribute" +) + +func (api *API) startSubredditDownloadPubsub(messages <-chan *message.Message) { + for msg := range messages { + api.subredditSemaphore <- struct{}{} + go func(msg *message.Message) { + defer func() { + msg.Ack() + <-api.subredditSemaphore + }() + var err error + ctx, span := tracer.Start(context.Background(), "Download Subreddit Pubsub") + defer func() { telemetry.EndWithStatus(span, err) }() + span.AddEvent("pubsub." + downloadTopic) + subredditName := string(msg.Payload) + span.SetAttributes(attribute.String("subreddit", subredditName)) + + subreddit, err := models.Subreddits.Query(ctx, api.exec, models.SelectWhere.Subreddits.Name.EQ(subredditName)).One() + if err != nil { + log.New(ctx).Err(err).Error("failed to find subreddit", "subreddit", subredditName) + return + } + + devices, err := models.Devices.Query(ctx, api.exec).All() + if err != nil { + log.New(ctx).Err(err).Error("failed to query devices") + return + } + + err = api.DownloadSubredditImages(ctx, subredditName, DownloadSubredditParams{ + Countback: int(subreddit.Countback), + Devices: devices, + SubredditType: reddit.SubredditType(subreddit.Subtype), + }) + if err != nil { + log.New(ctx).Err(err).Error("failed to download subreddit images", "subreddit", subredditName) + return + } + }(msg) + } +} diff --git a/api/reddit/get_posts.go b/api/reddit/get_posts.go index da4ad9f..e2382e4 100644 --- a/api/reddit/get_posts.go +++ b/api/reddit/get_posts.go @@ -31,12 +31,12 @@ func (s SubredditType) Code() string { type GetPostsParam struct { Subreddit string Limit int - Page int + After string SubredditType SubredditType } func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts Listing, err error) { - url := fmt.Sprintf("https://reddit.com/%s/%s.json?limit=%d&page=%d", params.SubredditType.Code(), params.Subreddit, params.Limit, params.Page) + url := fmt.Sprintf("https://reddit.com/%s/%s.json?limit=%d&after=%s", params.SubredditType.Code(), params.Subreddit, params.Limit, params.After) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody) if err != nil { return posts, errs.Wrapw(err, "reddit: failed to create http request instance", "url", url, "params", params) diff --git a/api/reddit/image_download_reader.go b/api/reddit/image_download_reader.go index 044c475..a6cd966 100644 --- a/api/reddit/image_download_reader.go +++ b/api/reddit/image_download_reader.go @@ -29,8 +29,6 @@ type ImageDownloadReader struct { deltastart time.Time deltavalue atomic.Int64 - end time.Time - exit chan struct{} mu sync.Mutex diff --git a/api/reddit/post.go b/api/reddit/post.go index bcc64fd..9ea0149 100644 --- a/api/reddit/post.go +++ b/api/reddit/post.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/tigorlazuardi/redmage/config" - "github.com/tigorlazuardi/redmage/db/queries" + "github.com/tigorlazuardi/redmage/models" ) type Listing struct { @@ -19,6 +19,17 @@ func (l *Listing) GetPosts() []Post { return l.Data.Children } +// GetLastAfter returns the last post namee for pagination. +// +// Returns empty string if there is no more posts to look up. +func (l *Listing) GetLastAfter() string { + posts := l.GetPosts() + if len(posts) == 0 { + return "" + } + return posts[len(posts)-1].GetName() +} + type ( MediaEmbed struct{} SecureMediaEmbed struct{} @@ -219,12 +230,16 @@ func (post *Post) GetImageAspectRatio() float64 { return float64(width) / float64(height) } -func (post *Post) GetImageTargetPath(cfg *config.Config, device queries.Device) string { +func (post *Post) GetName() string { + return post.Data.Name +} + +func (post *Post) GetImageTargetPath(cfg *config.Config, device *models.Device) string { baseDownloadDir := cfg.String("download.directory") return path.Join(baseDownloadDir, device.Name, post.GetSubreddit(), post.GetImageFilename()) } -func (post *Post) GetWindowsWallpaperImageTargetPath(cfg *config.Config, device queries.Device) string { +func (post *Post) GetWindowsWallpaperImageTargetPath(cfg *config.Config, device *models.Device) string { baseDownloadDir := cfg.String("download.directory") filename := fmt.Sprintf("%s_%s", post.GetSubreddit(), post.GetImageFilename()) return path.Join(baseDownloadDir, device.Name, filename) @@ -239,11 +254,11 @@ func (post *Post) GetThumbnailRelativePath() string { return path.Join("_thumbnails", post.GetSubreddit(), post.GetImageFilename()) } -func (post *Post) GetImageRelativePath(device queries.Device) string { +func (post *Post) GetImageRelativePath(device *models.Device) string { return path.Join(device.Slug, post.GetSubreddit(), post.GetImageFilename()) } -func (post *Post) GetWindowsWallpaperImageRelativePath(device queries.Device) string { +func (post *Post) GetWindowsWallpaperImageRelativePath(device *models.Device) string { filename := fmt.Sprintf("%s_%s", post.GetSubreddit(), post.GetImageFilename()) return path.Join(device.Slug, filename) } diff --git a/cli/serve.go b/cli/serve.go index 2f71d45..5ba18a8 100644 --- a/cli/serve.go +++ b/cli/serve.go @@ -9,7 +9,6 @@ import ( "github.com/tigorlazuardi/redmage/api" "github.com/tigorlazuardi/redmage/api/reddit" "github.com/tigorlazuardi/redmage/db" - "github.com/tigorlazuardi/redmage/db/queries" "github.com/tigorlazuardi/redmage/pkg/log" "github.com/tigorlazuardi/redmage/pkg/telemetry" "github.com/tigorlazuardi/redmage/server" @@ -35,18 +34,15 @@ var serveCmd = &cobra.Command{ os.Exit(1) } - queries := queries.New(db) - red := &reddit.Reddit{ Client: http.DefaultClient, Config: cfg, } api := api.New(api.Dependencies{ - Queries: queries, - DB: db, - Config: cfg, - Reddit: red, + DB: db, + Config: cfg, + Reddit: red, }) server := server.New(cfg, api, PublicDir) diff --git a/config/default.go b/config/default.go index d59a9f9..37f094a 100644 --- a/config/default.go +++ b/config/default.go @@ -22,6 +22,8 @@ var DefaultConfig = map[string]any{ "download.timeout.idle": "5s", "download.timeout.idlespeed": "10KB", + "download.pubsub.ack.deadline": "3h", + "http.port": "8080", "http.host": "0.0.0.0", "http.shutdown_timeout": "5s", diff --git a/db/migrations/20240409222145_create_images_table.sql b/db/migrations/20240409222145_create_images_table.sql index 899c687..098f9a1 100644 --- a/db/migrations/20240409222145_create_images_table.sql +++ b/db/migrations/20240409222145_create_images_table.sql @@ -8,6 +8,7 @@ CREATE TABLE images( post_id VARCHAR(50) NOT NULL, post_url VARCHAR(255) NOT NULL, post_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + post_name VARCHAR(255) NOT NULL, poster VARCHAR(50) NOT NULL, poster_url VARCHAR(255) NOT NULL, image_relative_path VARCHAR(255) NOT NULL, diff --git a/db/queries/devices.sql b/db/queries/devices.sql deleted file mode 100644 index 9d36b0d..0000000 --- a/db/queries/devices.sql +++ /dev/null @@ -1,28 +0,0 @@ --- name: DeviceGetAll :many -SELECT * FROM devices -ORDER BY name; - --- name: DeviceCount :one -SELECT COUNT(*) FROM devices; - --- name: DeviceList :many -SELECT * FROM devices -ORDER BY name -LIMIT ? OFFSET ?; - --- name: DeviceSearch :many -SELECT * FROM devices -WHERE (name LIKE ? OR slug LIKE ?) -ORDER BY name -LIMIT ? OFFSET ?; - --- name: DeviceSearchCount :one -SELECT COUNT(*) FROM devices -WHERE (name LIKE ? OR slug LIKE ?) -ORDER BY name -LIMIT ? OFFSET ?; - --- name: DeviceCreate :one -INSERT INTO devices (name, slug, resolution_x, resolution_y, aspect_ratio_tolerance, min_x, min_y, max_x, max_y, nsfw, windows_wallpaper_mode) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -RETURNING *; diff --git a/db/queries/images.sql b/db/queries/images.sql deleted file mode 100644 index 67cfa0e..0000000 --- a/db/queries/images.sql +++ /dev/null @@ -1,5 +0,0 @@ --- name: RecentlyAddedImages :many -SELECT * FROM images -WHERE created_at > ? -ORDER BY created_at -LIMIT ? OFFSET ?; \ No newline at end of file diff --git a/db/queries/subreddits.sql b/db/queries/subreddits.sql deleted file mode 100644 index 701d680..0000000 --- a/db/queries/subreddits.sql +++ /dev/null @@ -1,28 +0,0 @@ --- name: SubredditsGetAll :many -SELECT * FROM subreddits; - --- name: SubredditsList :many -SELECT * FROM subreddits -ORDER BY name -LIMIT ? OFFSET ?; - --- name: SubredditsListCount :one -SELECT COUNT(*) From subreddits; - --- name: SubredditsSearch :many -SELECT * FROM subreddits -WHERE name LIKE ? -ORDER BY name -LIMIT ? OFFSET ?; - --- name: SubredditsSearchCount :one -SELECT COUNT(*) FROM subreddits -WHERE name LIKE ? -ORDER BY name -LIMIT ? OFFSET ?; - --- name: SubredditCreate :one -INSERT INTO subreddits (name, subtype, schedule) -VALUES (?, ?, ?) -RETURNING *; - diff --git a/flake.nix b/flake.nix index db58542..c4a23ca 100644 --- a/flake.nix +++ b/flake.nix @@ -19,7 +19,6 @@ modd nodePackages_latest.nodejs goose - sqlc air ]; }; diff --git a/go.mod b/go.mod index b33a3e8..9fc4c1e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/tigorlazuardi/redmage go 1.22.1 require ( + github.com/ThreeDotsLabs/watermill v1.3.5 github.com/a-h/templ v0.2.648 github.com/aarondl/opt v0.0.0-20240108180805-338d04d857dc github.com/adrg/xdg v0.4.0 @@ -33,9 +34,14 @@ require ( ) require ( + github.com/ThreeDotsLabs/watermill-sql/v3 v3.0.1 // indirect github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/lithammer/shortuuid/v3 v3.0.7 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect github.com/samber/lo v1.38.1 // indirect github.com/stephenafamo/scan v0.4.2 // indirect diff --git a/go.sum b/go.sum index 7d487fd..6772fdb 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,10 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/ThreeDotsLabs/watermill v1.3.5 h1:50JEPEhMGZQMh08ct0tfO1PsgMOAOhV3zxK2WofkbXg= +github.com/ThreeDotsLabs/watermill v1.3.5/go.mod h1:O/u/Ptyrk5MPTxSeWM5vzTtZcZfxXfO9PK9eXTYiFZY= +github.com/ThreeDotsLabs/watermill-sql/v3 v3.0.1 h1:+uW9Db+7Ep4uon7enOq1cozCRua3REH7zdmtXIuGQ7c= +github.com/ThreeDotsLabs/watermill-sql/v3 v3.0.1/go.mod h1:iYZqlHt0tJPQIFwQSXoI6GnxDhTZhAzxVR1/EIS3DOw= github.com/XSAM/otelsql v0.29.0 h1:pEw9YXXs8ZrGRYfDc0cmArIz9lci5b42gmP5+tA1Huc= github.com/XSAM/otelsql v0.29.0/go.mod h1:d3/0xGIGC5RVEE+Ld7KotwaLy6zDeaF3fLJHOPpdN2w= github.com/a-h/templ v0.2.648 h1:A1ggHGIE7AONOHrFaDTM8SrqgqHL6fWgWCijQ21Zy9I= @@ -87,10 +91,15 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= @@ -143,6 +152,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 h1:6PfEMwfInASh9hkN83aR0j4W/eKaAZt/AURtXAXlas0= github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475/go.mod h1:20nXSmcf0nAscrzqsXeC2/tA3KkV2eCiJqYuyAgl+ss= +github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= +github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -164,6 +175,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= diff --git a/rest/devices/update.http b/rest/devices/update.http index d16eddb..564025c 100644 --- a/rest/devices/update.http +++ b/rest/devices/update.http @@ -4,7 +4,7 @@ Content-Type: application/json Content-Length: 78 { - "slug": "sync-l", + "slug": "laptop", "aspect_ratio_tolerance": 0.2, "nsfw": 1 }