diff --git a/backend/db/avatars.go b/backend/db/avatars.go index f35ef26..e59c682 100644 --- a/backend/db/avatars.go +++ b/backend/db/avatars.go @@ -19,6 +19,7 @@ import ( const ErrInvalidDataURI = errors.Sentinel("invalid data URI") const ErrInvalidContentType = errors.Sentinel("invalid avatar content type") +const ErrFileTooLarge = errors.Sentinel("file to be converted exceeds maximum size") // ConvertAvatar parses an avatar from a data URI, converts it to WebP and JPEG, and returns the results. func (db *DB) ConvertAvatar(data string) ( diff --git a/backend/db/flags.go b/backend/db/flags.go index 6f3458b..94e70ea 100644 --- a/backend/db/flags.go +++ b/backend/db/flags.go @@ -59,7 +59,7 @@ const ( ) func (db *DB) AccountFlags(ctx context.Context, userID xid.ID) (fs []PrideFlag, err error) { - sql, args, err := sq.Select("*").From("pride_flags").Where("user_id = ?", userID).OrderBy("id").ToSql() + sql, args, err := sq.Select("*").From("pride_flags").Where("user_id = ?", userID).OrderBy("lower(name)").ToSql() if err != nil { return nil, errors.Wrap(err, "building query") } @@ -285,6 +285,8 @@ func (db *DB) FlagObject(ctx context.Context, flagID xid.ID, hash string) (io.Re return obj, nil } +const MaxFlagInputSize = 512_000 + // ConvertFlag parses a flag from a data URI, converts it to WebP, and returns the result. func (db *DB) ConvertFlag(data string) (webpOut *bytes.Buffer, err error) { defer vips.ShutdownThread() @@ -300,6 +302,10 @@ func (db *DB) ConvertFlag(data string) (webpOut *bytes.Buffer, err error) { return nil, errors.Wrap(err, "invalid base64 data") } + if len(rawData) > MaxFlagInputSize { + return nil, ErrFileTooLarge + } + image, err := vips.LoadImageFromBuffer(rawData, nil) if err != nil { return nil, errors.Wrap(err, "decoding image") diff --git a/backend/routes/user/flags.go b/backend/routes/user/flags.go index 77654f3..60219b9 100644 --- a/backend/routes/user/flags.go +++ b/backend/routes/user/flags.go @@ -91,6 +91,8 @@ func (s *Server) postUserFlag(w http.ResponseWriter, r *http.Request) error { if err != nil { if err == db.ErrInvalidDataURI { return server.APIError{Code: server.ErrBadRequest, Message: "invalid data URI"} + } else if err == db.ErrFileTooLarge { + return server.APIError{Code: server.ErrBadRequest, Message: "data URI exceeds 512 KB"} } return errors.Wrap(err, "converting flag") } diff --git a/frontend/src/lib/api/entities.ts b/frontend/src/lib/api/entities.ts index acb2767..df2ad47 100644 --- a/frontend/src/lib/api/entities.ts +++ b/frontend/src/lib/api/entities.ts @@ -96,6 +96,13 @@ export interface MemberPartialUser { custom_preferences: CustomPreferences; } +export interface PrideFlag { + id: string; + hash: string; + name: string; + description: string | null; +} + export interface Invite { code: string; created: string; @@ -192,6 +199,8 @@ export const memberAvatars = (member: Member | PartialMember) => { ]; }; +export const flagURL = ({ hash }: PrideFlag) => `${PUBLIC_MEDIA_URL}/flags/${hash}.webp`; + export const defaultAvatars = [ `${PUBLIC_BASE_URL}/default/512.webp`, `${PUBLIC_BASE_URL}/default/512.jpg`, diff --git a/frontend/src/routes/settings/+layout.svelte b/frontend/src/routes/settings/+layout.svelte index 785c43b..4e66453 100644 --- a/frontend/src/routes/settings/+layout.svelte +++ b/frontend/src/routes/settings/+layout.svelte @@ -42,20 +42,13 @@
-
+

Settings

Your profile - - Authentication - {#if hasHiddenMembers} +
+ {#if data.invitesEnabled} Log out
-
+
diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 0b9e229..c28872e 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -155,8 +155,9 @@ {/if}
- -

+

+ +
To change your avatar, go to edit profile.

diff --git a/frontend/src/routes/settings/flags/+page.svelte b/frontend/src/routes/settings/flags/+page.svelte new file mode 100644 index 0000000..01e1dfa --- /dev/null +++ b/frontend/src/routes/settings/flags/+page.svelte @@ -0,0 +1,164 @@ + + +

Pride flags ({data.flags.length})

+ +

+ You can upload pride flags to use on your profiles here. Flags you upload here will not automatically + show up on your profile. +

+ +
+ + +
+ +
+ {#each filtered as flag} + + {:else} + {#if data.flags.length === 0} + You haven't uploaded any flags yet, press the button above to do so. + {:else} + There are no flags matching your search {search} + {/if} + {/each} +
+ + + Upload flag + + {#if error} + + {/if} +
+ New flag + +
+

+ Only PNG, JPEG, GIF, and WebP images can be uploaded + as flags. The file cannot be larger than 512 kilobytes. +

+

+ + +

+

+ This name will be shown beside the flag. +

+

+ +