feat: count characters consistently

This commit is contained in:
Sam 2023-04-02 22:50:22 +02:00
parent 80ca1cae00
commit 8433a1523a
No known key found for this signature in database
GPG Key ID: B4EF20DDE721CAA1
9 changed files with 54 additions and 20 deletions

11
backend/common/common.go Normal file
View File

@ -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)
}

View File

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"codeberg.org/u1f320/pronouns.cc/backend/common"
"codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/log"
"codeberg.org/u1f320/pronouns.cc/backend/server" "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 { if err := validateSlicePtr("name", &cmr.Names); err != nil {
return *err return *err
} }

View File

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"codeberg.org/u1f320/pronouns.cc/backend/common"
"codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/log"
"codeberg.org/u1f320/pronouns.cc/backend/server" "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 // validate display name/bio
if req.Name != nil && len(*req.Name) > db.MaxMemberNameLength { if common.StringLength(req.Name) > db.MaxMemberNameLength {
return server.APIError{ return server.APIError{
Code: server.ErrBadRequest, 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{ return server.APIError{
Code: server.ErrBadRequest, 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{ return server.APIError{
Code: server.ErrBadRequest, 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)),
} }
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"codeberg.org/u1f320/pronouns.cc/backend/common"
"codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/log"
"codeberg.org/u1f320/pronouns.cc/backend/server" "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 // validate display name/bio
if req.DisplayName != nil && len(*req.DisplayName) > db.MaxDisplayNameLength { if common.StringLength(req.Username) > db.MaxUsernameLength {
return server.APIError{ return server.APIError{
Code: server.ErrBadRequest, 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{ return server.APIError{
Code: server.ErrBadRequest, 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 common.StringLength(req.Bio) > db.MaxUserBioLength {
if req.MemberTitle != nil && len(*req.MemberTitle) > db.MaxDisplayNameLength {
return server.APIError{ return server.APIError{
Code: server.ErrBadRequest, 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)),
} }
} }

View File

@ -6,6 +6,8 @@ const md = new MarkdownIt({
breaks: true, breaks: true,
}).disable(["heading", "link", "table"]); }).disable(["heading", "link", "table"]);
export default function renderMarkdown(src: string | null) { export function renderMarkdown(src: string | null) {
return src ? sanitize(md.render(src)) : null; return src ? sanitize(md.render(src)) : null;
} }
export const charCount = (str: string) => [...str].length;

View File

@ -30,7 +30,7 @@
import { apiFetchClient } from "$lib/api/fetch"; import { apiFetchClient } from "$lib/api/fetch";
import ErrorAlert from "$lib/components/ErrorAlert.svelte"; import ErrorAlert from "$lib/components/ErrorAlert.svelte";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import renderMarkdown from "$lib/api/markdown"; import {renderMarkdown} from "$lib/utils"
import ReportButton from "./ReportButton.svelte"; import ReportButton from "./ReportButton.svelte";
import ProfileLink from "./ProfileLink.svelte"; import ProfileLink from "./ProfileLink.svelte";
import { memberNameRegex } from "$lib/api/regex"; import { memberNameRegex } from "$lib/api/regex";

View File

@ -9,7 +9,7 @@
import { memberAvatars, pronounDisplay, WordStatus } from "$lib/api/entities"; import { memberAvatars, pronounDisplay, WordStatus } from "$lib/api/entities";
import { PUBLIC_BASE_URL } from "$env/static/public"; import { PUBLIC_BASE_URL } from "$env/static/public";
import { userStore } from "$lib/store"; import { userStore } from "$lib/store";
import renderMarkdown from "$lib/api/markdown"; import { renderMarkdown } from "$lib/utils";
import ReportButton from "../ReportButton.svelte"; import ReportButton from "../ReportButton.svelte";
import ProfileLink from "../ProfileLink.svelte"; import ProfileLink from "../ProfileLink.svelte";
import StatusLine from "$lib/components/StatusLine.svelte"; import StatusLine from "$lib/components/StatusLine.svelte";

View File

@ -38,7 +38,7 @@
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import { addToast, delToast } from "$lib/toast"; import { addToast, delToast } from "$lib/toast";
import { memberNameRegex } from "$lib/api/regex"; 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; const MAX_AVATAR_BYTES = 1_000_000;
@ -445,7 +445,7 @@
<textarea class="form-control" style="height: 200px;" bind:value={bio} /> <textarea class="form-control" style="height: 200px;" bind:value={bio} />
</div> </div>
<p class="text-muted mt-1"> <p class="text-muted mt-1">
Using {bio.length}/{MAX_DESCRIPTION_LENGTH} characters Using {charCount(bio)}/{MAX_DESCRIPTION_LENGTH} characters
</p> </p>
<p class="text-muted my-2"> <p class="text-muted my-2">
<Icon name="info-circle-fill" aria-hidden /> Your bio supports limited <Icon name="info-circle-fill" aria-hidden /> Your bio supports limited

View File

@ -34,7 +34,7 @@
import ErrorAlert from "$lib/components/ErrorAlert.svelte"; import ErrorAlert from "$lib/components/ErrorAlert.svelte";
import { addToast, delToast } from "$lib/toast"; import { addToast, delToast } from "$lib/toast";
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import renderMarkdown from "$lib/api/markdown"; import { charCount, renderMarkdown } from "$lib/utils";
const MAX_AVATAR_BYTES = 1_000_000; const MAX_AVATAR_BYTES = 1_000_000;
@ -373,7 +373,7 @@
<textarea class="form-control" style="height: 200px;" bind:value={bio} /> <textarea class="form-control" style="height: 200px;" bind:value={bio} />
</div> </div>
<p class="text-muted mt-1"> <p class="text-muted mt-1">
Using {bio.length}/{MAX_DESCRIPTION_LENGTH} characters Using {charCount(bio)}/{MAX_DESCRIPTION_LENGTH} characters
</p> </p>
<p class="text-muted my-2"> <p class="text-muted my-2">
<Icon name="info-circle-fill" aria-hidden /> Your bio supports limited <Icon name="info-circle-fill" aria-hidden /> Your bio supports limited