Put timestamp in avatar filenames to avoid cache problems
This commit is contained in:
parent
f1e2e99663
commit
36bb2ce2d5
|
@ -42,6 +42,18 @@ var Config = HMNConfig{
|
||||||
AssetsPathPrefix: "", // Empty is fine for production, but may be necessary for dev
|
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...
|
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{
|
EpisodeGuide: EpisodeGuide{
|
||||||
CineraOutputPath: "./annotations/",
|
CineraOutputPath: "./annotations/",
|
||||||
Projects: map[string]string{"hero": "code", "riscy": "riscy", "bitwise": "bitwise"},
|
Projects: map[string]string{"hero": "code", "riscy": "riscy", "bitwise": "bitwise"},
|
||||||
|
|
|
@ -167,7 +167,7 @@ func Scrape(ctx context.Context, dbConn *pgxpool.Pool, channelID string, earlies
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := handleHistoryMessage(ctx, dbConn, &msg, true)
|
err := handleHistoryMessage(ctx, dbConn, &msg, createSnippets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errLog := logging.ExtractLogger(ctx).Error()
|
errLog := logging.ExtractLogger(ctx).Error()
|
||||||
if errors.Is(err, errNotEnoughInfo) {
|
if errors.Is(err, errNotEnoughInfo) {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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"`
|
||||||
|
}
|
|
@ -11,11 +11,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/db"
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
"git.handmade.network/hmn/hmn/src/oops"
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SaveImageFileResult struct {
|
type SaveImageFileResult struct {
|
||||||
ImageFileID int
|
ImageFile *models.ImageFile
|
||||||
ValidationError string
|
ValidationError string
|
||||||
FatalError error
|
FatalError error
|
||||||
}
|
}
|
||||||
|
@ -88,15 +89,15 @@ func SaveImageFile(c *RequestContext, dbConn db.ConnOrTx, fileFieldName string,
|
||||||
img.Seek(0, io.SeekStart)
|
img.Seek(0, io.SeekStart)
|
||||||
io.Copy(hasher, img) // NOTE(asaf): Writing to hash.Hash never returns an error according to the docs
|
io.Copy(hasher, img) // NOTE(asaf): Writing to hash.Hash never returns an error according to the docs
|
||||||
sha1sum := hasher.Sum(nil)
|
sha1sum := hasher.Sum(nil)
|
||||||
var imageId int
|
// TODO(db): Should use insert helper
|
||||||
err = dbConn.QueryRow(c.Context(),
|
imageFile, err := db.QueryOne(c.Context(), dbConn, models.ImageFile{},
|
||||||
`
|
`
|
||||||
INSERT INTO handmade_imagefile (file, size, sha1sum, protected, width, height)
|
INSERT INTO handmade_imagefile (file, size, sha1sum, protected, width, height)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
RETURNING id
|
RETURNING $columns
|
||||||
`,
|
`,
|
||||||
filename, header.Size, hex.EncodeToString(sha1sum), false, width, height,
|
filename, header.Size, hex.EncodeToString(sha1sum), false, width, height,
|
||||||
).Scan(&imageId)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SaveImageFileResult{
|
return SaveImageFileResult{
|
||||||
FatalError: oops.New(err, "Failed to insert image file row"),
|
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{
|
return SaveImageFileResult{
|
||||||
ImageFileID: imageId,
|
ImageFile: imageFile.(*models.ImageFile),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
|
||||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(imageSaveResult.FatalError, "Failed to save podcast image"))
|
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(),
|
_, err = tx.Exec(c.Context(),
|
||||||
`
|
`
|
||||||
UPDATE handmade_podcast
|
UPDATE handmade_podcast
|
||||||
|
@ -168,7 +168,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
|
||||||
`,
|
`,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
imageSaveResult.ImageFileID,
|
imageSaveResult.ImageFile.ID,
|
||||||
podcastResult.Podcast.ID,
|
podcastResult.Podcast.ID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/auth"
|
"git.handmade.network/hmn/hmn/src/auth"
|
||||||
"git.handmade.network/hmn/hmn/src/config"
|
"git.handmade.network/hmn/hmn/src/config"
|
||||||
|
@ -452,11 +453,26 @@ func UserSettingsSave(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update avatar
|
// 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 != "" {
|
if imageSaveResult.ValidationError != "" {
|
||||||
return RejectRequest(c, imageSaveResult.ValidationError)
|
return RejectRequest(c, imageSaveResult.ValidationError)
|
||||||
} else if imageSaveResult.FatalError != nil {
|
} else if imageSaveResult.FatalError != nil {
|
||||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(imageSaveResult.FatalError, "failed to save new avatar"))
|
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())
|
err = tx.Commit(c.Context())
|
||||||
|
|
Loading…
Reference in New Issue