From 36bb2ce2d5098f9c75c1b00fa04a4c8629cdde10 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Tue, 7 Sep 2021 19:55:52 -0500 Subject: [PATCH] Put timestamp in avatar filenames to avoid cache problems --- src/config/config.go.example | 12 +++++ src/discord/history.go | 2 +- .../2021-09-08T004357Z_ImageFileNotNull.go | 52 +++++++++++++++++++ src/models/imagefile.go | 11 ++++ src/website/imagefile_helper.go | 13 ++--- src/website/podcast.go | 4 +- src/website/user.go | 18 ++++++- 7 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 src/migration/migrations/2021-09-08T004357Z_ImageFileNotNull.go create mode 100644 src/models/imagefile.go diff --git a/src/config/config.go.example b/src/config/config.go.example index 0578b141..d0970128 100644 --- a/src/config/config.go.example +++ b/src/config/config.go.example @@ -42,6 +42,18 @@ var Config = HMNConfig{ AssetsPathPrefix: "", // Empty is fine for production, but may be necessary for dev AssetsPublicUrlRoot: "", // e.g. "https://bucket-name.region.cdn.digitaloceanspaces.com/". Note the trailing slash... }, + Discord: DiscordConfig{ + BotToken: "", + BotUserID: "", + + OAuthClientID: "", + OAuthClientSecret: "", + + GuildID: "", + MemberRoleID: "", + ShowcaseChannelID: "", + LibraryChannelID: "", + }, EpisodeGuide: EpisodeGuide{ CineraOutputPath: "./annotations/", Projects: map[string]string{"hero": "code", "riscy": "riscy", "bitwise": "bitwise"}, diff --git a/src/discord/history.go b/src/discord/history.go index 828332d4..635096cf 100644 --- a/src/discord/history.go +++ b/src/discord/history.go @@ -167,7 +167,7 @@ func Scrape(ctx context.Context, dbConn *pgxpool.Pool, channelID string, earlies return } - err := handleHistoryMessage(ctx, dbConn, &msg, true) + err := handleHistoryMessage(ctx, dbConn, &msg, createSnippets) if err != nil { errLog := logging.ExtractLogger(ctx).Error() if errors.Is(err, errNotEnoughInfo) { diff --git a/src/migration/migrations/2021-09-08T004357Z_ImageFileNotNull.go b/src/migration/migrations/2021-09-08T004357Z_ImageFileNotNull.go new file mode 100644 index 00000000..cb76fb13 --- /dev/null +++ b/src/migration/migrations/2021-09-08T004357Z_ImageFileNotNull.go @@ -0,0 +1,52 @@ +package migrations + +import ( + "context" + "time" + + "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" + "github.com/jackc/pgx/v4" +) + +func init() { + registerMigration(ImageFileNotNull{}) +} + +type ImageFileNotNull struct{} + +func (m ImageFileNotNull) Version() types.MigrationVersion { + return types.MigrationVersion(time.Date(2021, 9, 8, 0, 43, 57, 0, time.UTC)) +} + +func (m ImageFileNotNull) Name() string { + return "ImageFileNotNull" +} + +func (m ImageFileNotNull) Description() string { + return "Don't allow the filename of an image file to be null" +} + +func (m ImageFileNotNull) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` + ALTER TABLE handmade_imagefile + ALTER file SET NOT NULL; + `) + if err != nil { + return oops.New(err, "failed to make imagefile filename not nullable") + } + + return nil +} + +func (m ImageFileNotNull) Down(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` + ALTER TABLE handmade_imagefile + ALTER file SET NULL; + `) + if err != nil { + return oops.New(err, "failed to make imagefile filename nullable again") + } + + return nil +} diff --git a/src/models/imagefile.go b/src/models/imagefile.go new file mode 100644 index 00000000..ea47800e --- /dev/null +++ b/src/models/imagefile.go @@ -0,0 +1,11 @@ +package models + +type ImageFile struct { + ID int `db:"id"` + File string `db:"file"` // relative to public/media + Size int `db:"size"` + Sha1Sum string `db:"sha1sum"` + Protected bool `db:"protected"` + Height int `db:"height"` + Width int `db:"width"` +} diff --git a/src/website/imagefile_helper.go b/src/website/imagefile_helper.go index 65d07d6b..7d44bf34 100644 --- a/src/website/imagefile_helper.go +++ b/src/website/imagefile_helper.go @@ -11,11 +11,12 @@ import ( "os" "git.handmade.network/hmn/hmn/src/db" + "git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/oops" ) type SaveImageFileResult struct { - ImageFileID int + ImageFile *models.ImageFile ValidationError string FatalError error } @@ -88,15 +89,15 @@ func SaveImageFile(c *RequestContext, dbConn db.ConnOrTx, fileFieldName string, img.Seek(0, io.SeekStart) io.Copy(hasher, img) // NOTE(asaf): Writing to hash.Hash never returns an error according to the docs sha1sum := hasher.Sum(nil) - var imageId int - err = dbConn.QueryRow(c.Context(), + // TODO(db): Should use insert helper + imageFile, err := db.QueryOne(c.Context(), dbConn, models.ImageFile{}, ` INSERT INTO handmade_imagefile (file, size, sha1sum, protected, width, height) VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id + RETURNING $columns `, filename, header.Size, hex.EncodeToString(sha1sum), false, width, height, - ).Scan(&imageId) + ) if err != nil { return SaveImageFileResult{ FatalError: oops.New(err, "Failed to insert image file row"), @@ -104,7 +105,7 @@ func SaveImageFile(c *RequestContext, dbConn db.ConnOrTx, fileFieldName string, } return SaveImageFileResult{ - ImageFileID: imageId, + ImageFile: imageFile.(*models.ImageFile), } } diff --git a/src/website/podcast.go b/src/website/podcast.go index dc2c7fb2..a8c21fff 100644 --- a/src/website/podcast.go +++ b/src/website/podcast.go @@ -156,7 +156,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(imageSaveResult.FatalError, "Failed to save podcast image")) } - if imageSaveResult.ImageFileID != 0 { + if imageSaveResult.ImageFile != nil { _, err = tx.Exec(c.Context(), ` UPDATE handmade_podcast @@ -168,7 +168,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData { `, title, description, - imageSaveResult.ImageFileID, + imageSaveResult.ImageFile.ID, podcastResult.Podcast.ID, ) if err != nil { diff --git a/src/website/user.go b/src/website/user.go index 099fbb03..af3afd5f 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -6,6 +6,7 @@ import ( "net/http" "sort" "strings" + "time" "git.handmade.network/hmn/hmn/src/auth" "git.handmade.network/hmn/hmn/src/config" @@ -452,11 +453,26 @@ func UserSettingsSave(c *RequestContext) ResponseData { } // Update avatar - imageSaveResult := SaveImageFile(c, tx, "avatar", 1*1024*1024, fmt.Sprintf("members/avatars/%s", c.CurrentUser.Username)) + imageSaveResult := SaveImageFile(c, tx, "avatar", 1*1024*1024, fmt.Sprintf("members/avatars/%s-%d", c.CurrentUser.Username, time.Now().UTC().Unix())) if imageSaveResult.ValidationError != "" { return RejectRequest(c, imageSaveResult.ValidationError) } else if imageSaveResult.FatalError != nil { return c.ErrorResponse(http.StatusInternalServerError, oops.New(imageSaveResult.FatalError, "failed to save new avatar")) + } else if imageSaveResult.ImageFile != nil { + _, err = tx.Exec(c.Context(), + ` + UPDATE auth_user + SET + avatar = $2 + WHERE + id = $1 + `, + c.CurrentUser.ID, + imageSaveResult.ImageFile.File, + ) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user")) + } } err = tx.Commit(c.Context())