chore: strict Subdomain Validation with Detailed Error Messages (#42)

Co-authored-by: userAdityaa <aditya.chaudhary1558@gmail.com>
Co-committed-by: userAdityaa <aditya.chaudhary1558@gmail.com>
This commit was merged in pull request #42.
This commit is contained in:
2026-04-21 13:09:37 +00:00
committed by hodlbod
parent bc79da34cf
commit a26bc1127d
4 changed files with 137 additions and 53 deletions
+7
View File
@@ -1,6 +1,7 @@
import { createEffect, createMemo, createSignal, For } from "solid-js"
import type { Relay } from "@/lib/hooks"
import { slugify } from "@/lib/slugify"
import { validateSubdomainLabel } from "@/lib/subdomain"
import { setToastMessage } from "@/components/Toast"
import { plans } from "@/lib/state"
@@ -31,6 +32,12 @@ export default function RelayForm(props: RelayFormProps) {
return
}
const subdomainError = validateSubdomainLabel(subdomain())
if (subdomainError) {
setToastMessage(subdomainError)
return
}
setToastMessage("")
setSubmitting(true)
+22
View File
@@ -0,0 +1,22 @@
const SUBDOMAIN_LABEL_MAX_LEN = 63
const RESERVED_SUBDOMAIN_LABELS = new Set(["api", "admin"])
export function validateSubdomainLabel(subdomain: string): string | null {
if (subdomain.length === 0) {
return "subdomain is required"
}
if (subdomain.length > SUBDOMAIN_LABEL_MAX_LEN) {
return "subdomain must be 63 characters or fewer"
}
if (subdomain.startsWith("-") || subdomain.endsWith("-")) {
return "subdomain cannot start or end with a hyphen"
}
if (RESERVED_SUBDOMAIN_LABELS.has(subdomain)) {
return "subdomain is reserved"
}
if (!/^[a-z0-9-]+$/.test(subdomain)) {
return "subdomain may only contain lowercase letters, numbers, and hyphens"
}
return null
}
+1 -2
View File
@@ -1,7 +1,6 @@
import { useNavigate, useParams } from "@solidjs/router"
import { Show } from "solid-js"
import RelayForm, { type RelayFormValues } from "@/components/RelayForm"
import { slugify } from "@/lib/slugify"
import BackLink from "@/components/BackLink"
import PageContainer from "@/components/PageContainer"
import ResourceState from "@/components/ResourceState"
@@ -18,7 +17,7 @@ export default function RelayEdit(props: { basePath?: string; title?: string })
async function handleSubmit(values: RelayFormValues) {
await updateRelayById(relayId(), {
subdomain: slugify(values.subdomain),
subdomain: values.subdomain,
info_name: values.info_name.trim(),
info_icon: values.info_icon.trim(),
info_description: values.info_description.trim(),