feat: count characters consistently
This commit is contained in:
parent
80ca1cae00
commit
8433a1523a
9 changed files with 54 additions and 20 deletions
11
backend/common/common.go
Normal file
11
backend/common/common.go
Normal 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)
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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";
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue