diff --git a/backend/common/common.go b/backend/common/common.go new file mode 100644 index 0000000..92c6169 --- /dev/null +++ b/backend/common/common.go @@ -0,0 +1,11 @@ +// Package common contains functions and types common to all (or most) packages. +package common + +import "unicode/utf8" + +func StringLength(s *string) int { + if s == nil { + return -1 + } + return utf8.RuneCountInString(*s) +} diff --git a/backend/routes/member/create_member.go b/backend/routes/member/create_member.go index d3520ba..567099d 100644 --- a/backend/routes/member/create_member.go +++ b/backend/routes/member/create_member.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" + "codeberg.org/u1f320/pronouns.cc/backend/common" "codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" @@ -83,6 +84,25 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error } } + if common.StringLength(&cmr.Name) > db.MaxMemberNameLength { + return server.APIError{ + Code: server.ErrBadRequest, + Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, common.StringLength(&cmr.Name)), + } + } + if common.StringLength(cmr.DisplayName) > db.MaxDisplayNameLength { + return server.APIError{ + Code: server.ErrBadRequest, + Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(cmr.DisplayName)), + } + } + if common.StringLength(&cmr.Bio) > db.MaxUserBioLength { + return server.APIError{ + Code: server.ErrBadRequest, + Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(&cmr.Bio)), + } + } + if err := validateSlicePtr("name", &cmr.Names); err != nil { return *err } diff --git a/backend/routes/member/patch_member.go b/backend/routes/member/patch_member.go index f512c72..a61d620 100644 --- a/backend/routes/member/patch_member.go +++ b/backend/routes/member/patch_member.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" + "codeberg.org/u1f320/pronouns.cc/backend/common" "codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" @@ -109,22 +110,22 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { } // validate display name/bio - if req.Name != nil && len(*req.Name) > db.MaxMemberNameLength { + if common.StringLength(req.Name) > db.MaxMemberNameLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, len(*req.Name)), + Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, common.StringLength(req.Name)), } } - if req.DisplayName != nil && len(*req.DisplayName) > db.MaxDisplayNameLength { + if common.StringLength(req.DisplayName) > db.MaxDisplayNameLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.DisplayName)), + Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(req.DisplayName)), } } - if req.Bio != nil && len(*req.Bio) > db.MaxUserBioLength { + if common.StringLength(req.Bio) > db.MaxUserBioLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, len(*req.Bio)), + Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(req.Name)), } } diff --git a/backend/routes/user/patch_user.go b/backend/routes/user/patch_user.go index 35901ca..56a89cc 100644 --- a/backend/routes/user/patch_user.go +++ b/backend/routes/user/patch_user.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" + "codeberg.org/u1f320/pronouns.cc/backend/common" "codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" @@ -64,23 +65,22 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error { } // validate display name/bio - if req.DisplayName != nil && len(*req.DisplayName) > db.MaxDisplayNameLength { + if common.StringLength(req.Username) > db.MaxUsernameLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.DisplayName)), + Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxUsernameLength, common.StringLength(req.Username)), } } - if req.Bio != nil && len(*req.Bio) > db.MaxUserBioLength { + if common.StringLength(req.DisplayName) > db.MaxDisplayNameLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, len(*req.Bio)), + Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(req.DisplayName)), } } - // this is considered a name - if req.MemberTitle != nil && len(*req.MemberTitle) > db.MaxDisplayNameLength { + if common.StringLength(req.Bio) > db.MaxUserBioLength { return server.APIError{ Code: server.ErrBadRequest, - Details: fmt.Sprintf("Member title too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.MemberTitle)), + Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(req.Bio)), } } diff --git a/frontend/src/lib/api/markdown.ts b/frontend/src/lib/utils.ts similarity index 66% rename from frontend/src/lib/api/markdown.ts rename to frontend/src/lib/utils.ts index 4557feb..2eb0258 100644 --- a/frontend/src/lib/api/markdown.ts +++ b/frontend/src/lib/utils.ts @@ -6,6 +6,8 @@ const md = new MarkdownIt({ breaks: true, }).disable(["heading", "link", "table"]); -export default function renderMarkdown(src: string | null) { +export function renderMarkdown(src: string | null) { return src ? sanitize(md.render(src)) : null; } + +export const charCount = (str: string) => [...str].length; diff --git a/frontend/src/routes/@[username]/+page.svelte b/frontend/src/routes/@[username]/+page.svelte index e2b5ba8..f57cb27 100644 --- a/frontend/src/routes/@[username]/+page.svelte +++ b/frontend/src/routes/@[username]/+page.svelte @@ -30,7 +30,7 @@ import { apiFetchClient } from "$lib/api/fetch"; import ErrorAlert from "$lib/components/ErrorAlert.svelte"; import { goto } from "$app/navigation"; - import renderMarkdown from "$lib/api/markdown"; + import {renderMarkdown} from "$lib/utils" import ReportButton from "./ReportButton.svelte"; import ProfileLink from "./ProfileLink.svelte"; import { memberNameRegex } from "$lib/api/regex"; diff --git a/frontend/src/routes/@[username]/[memberName]/+page.svelte b/frontend/src/routes/@[username]/[memberName]/+page.svelte index f71fc5f..b4c29e9 100644 --- a/frontend/src/routes/@[username]/[memberName]/+page.svelte +++ b/frontend/src/routes/@[username]/[memberName]/+page.svelte @@ -9,7 +9,7 @@ import { memberAvatars, pronounDisplay, WordStatus } from "$lib/api/entities"; import { PUBLIC_BASE_URL } from "$env/static/public"; import { userStore } from "$lib/store"; - import renderMarkdown from "$lib/api/markdown"; + import { renderMarkdown } from "$lib/utils"; import ReportButton from "../ReportButton.svelte"; import ProfileLink from "../ProfileLink.svelte"; import StatusLine from "$lib/components/StatusLine.svelte"; diff --git a/frontend/src/routes/edit/member/[id]/+page.svelte b/frontend/src/routes/edit/member/[id]/+page.svelte index 0cb0e03..8c2a508 100644 --- a/frontend/src/routes/edit/member/[id]/+page.svelte +++ b/frontend/src/routes/edit/member/[id]/+page.svelte @@ -38,7 +38,7 @@ import type { PageData } from "./$types"; import { addToast, delToast } from "$lib/toast"; import { memberNameRegex } from "$lib/api/regex"; - import renderMarkdown from "$lib/api/markdown"; + import { charCount, renderMarkdown } from "$lib/utils"; const MAX_AVATAR_BYTES = 1_000_000; @@ -445,7 +445,7 @@