feat: custom domain setting

This commit is contained in:
DIYgod 2022-08-17 02:52:23 +01:00
parent c96edcdc79
commit a23536bdda
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
6 changed files with 83 additions and 64 deletions

View File

@ -10,6 +10,7 @@ type InputProps<TMultiline extends boolean> = {
error?: string
help?: React.ReactNode
multiline?: TMultiline
onChangeText?: (value: string) => void
} & React.ComponentPropsWithRef<TMultiline extends true ? "textarea" : "input">
export const Input = forwardRef(function Input<
@ -24,6 +25,7 @@ export const Input = forwardRef(function Input<
error,
help,
multiline,
onChangeText,
...inputProps
}: InputProps<TMutliline>,
ref: TMutliline extends true
@ -53,6 +55,7 @@ export const Input = forwardRef(function Input<
isBlock && `is-block`,
className,
)}
onChange={onChangeText}
/>
{addon && (
<span className="flex items-center px-3 text-gray-600 bg-gray-50 h-10 border border-l-0 rounded-r-lg relative -z-10">

View File

@ -196,6 +196,7 @@ textarea.input {
@apply text-sm;
@apply font-bold;
@apply text-gray-700;
@apply capitalize;
}
.post-status-circle {

View File

@ -85,6 +85,7 @@ export type Profile = UniProfile & {
navigation?: SiteNavigationItem[]
css?: string
ga?: string
custom_domain?: string
}
export type Profiles = {

View File

@ -1,57 +0,0 @@
import { string } from "zod"
import { prismaRead, MembershipRole, prismaPrimary } from "~/lib/db.server"
export const getMembership = async (data: {
siteId: string
userId: string
role: MembershipRole
}) => {
const first = await prismaRead.membership.findFirst({
where: {
role: data.role,
userId: data.userId,
siteId: data.siteId,
},
})
return first
}
export const getMemberships = async ({
userId,
// Only return memberships that the user is an owner/admin of
canManage,
}: {
userId: string
canManage?: boolean
}) => {
const memberships = await prismaRead.membership.findMany({
where: {
userId,
role: canManage
? {
in: [MembershipRole.ADMIN, MembershipRole.OWNER],
}
: undefined,
},
include: {
site: true,
},
})
return memberships
}
export const updateMembership = async (
membershipId: string,
input: { lastSwitchedTo?: Date },
) => {
await prismaPrimary.membership.update({
where: {
id: membershipId,
},
data: {
lastSwitchedTo: input.lastSwitchedTo,
},
})
}

View File

@ -89,6 +89,10 @@ export const getSite = async (input: string) => {
site.ga =
site.metadata?.raw?.attributes?.find((a: any) => a.trait_type === "xlog_ga")
?.value || ""
site.custom_domain =
site.metadata?.raw?.attributes?.find(
(a: any) => a.trait_type === "xlog_custom_domain",
)?.value || ""
site.name = site.name || site.username
return site
@ -118,6 +122,7 @@ export async function updateSite(payload: {
navigation?: SiteNavigationItem[]
css?: string
ga?: string
custom_domain?: string
}) {
return await unidata.profiles.set(
{
@ -131,7 +136,10 @@ export async function updateSite(payload: {
...(payload.description && { bio: payload.description }),
...(payload.icon && { avatars: [payload.icon] }),
...(payload.subdomain && { username: payload.subdomain }),
...((payload.navigation || payload.css || payload.ga) && {
...((payload.navigation ||
payload.css ||
payload.ga ||
payload.custom_domain) && {
attributes: [
...(payload.navigation
? [
@ -157,6 +165,14 @@ export async function updateSite(payload: {
},
]
: []),
...(payload.custom_domain
? [
{
trait_type: "xlog_custom_domain",
value: payload.custom_domain,
},
]
: []),
],
}),
},

View File

@ -6,7 +6,7 @@ import { DashboardLayout } from "~/components/dashboard/DashboardLayout"
import { SettingsLayout } from "~/components/dashboard/SettingsLayout"
import { Button } from "~/components/ui/Button"
import { Input } from "~/components/ui/Input"
import { OUR_DOMAIN } from "~/lib/env"
import { OUR_DOMAIN, APP_NAME } from "~/lib/env"
import { useGetSite, useUpdateSite } from "~/queries/site"
export default function SettingsDomainsPage() {
@ -19,6 +19,7 @@ export default function SettingsDomainsPage() {
const form = useForm({
defaultValues: {
subdomain: "",
custom_domain: "",
},
})
@ -26,9 +27,19 @@ export default function SettingsDomainsPage() {
updateSite.mutate({
site: subdomain,
subdomain: values.subdomain,
custom_domain: values.custom_domain,
})
})
const getSubdomain = (domain?: string) => {
return domain?.split(".").slice(0, -2).join(".") || ""
}
const [customDomain, setCustomDomain] = useState("")
const customDomainChange = (e: any) => {
setCustomDomain(e.target.value)
}
useEffect(() => {
if (updateSite.isSuccess) {
if (updateSite.data?.code === 0) {
@ -42,28 +53,72 @@ export default function SettingsDomainsPage() {
} else if (updateSite.isError) {
toast.error("Failed to update site")
}
}, [updateSite])
}, [updateSite.data?.code])
const [hasSet, setHasSet] = useState(false)
useEffect(() => {
if (site.isSuccess && site.data && !form.getValues("subdomain")) {
if (site.isSuccess && site.data && !hasSet) {
setHasSet(true)
form.setValue("subdomain", site.data.username || "")
form.setValue("custom_domain", site.data.custom_domain || "")
setCustomDomain(site.data.custom_domain || "")
}
}, [form, site.data, site.isSuccess])
}, [form, site.data, site.isSuccess, customDomain])
const title = "Site Settings"
return (
<DashboardLayout title={"Domains"}>
<SettingsLayout title={"Site Settings"} type="site">
<form onSubmit={handleSubmit}>
<div className="">
<div>
<Input
id="subdomain"
label="Subdomain"
label={`${APP_NAME} subdomain`}
addon={`.${OUR_DOMAIN}`}
className="w-28"
{...form.register("subdomain")}
/>
</div>
<div className="mt-5">
<Input
id="custom_domain"
label="Custom Domain"
className="w-64"
onChangeText={customDomainChange}
{...form.register("custom_domain")}
/>
{customDomain && (
<div className="mt-2 text-xs">
<p className="mb-2">
Set the following record on your DNS provider to active your
custom domain:
</p>
<table className="">
<tbody>
<tr className="border-b">
<th className="text-center p-3">Type</th>
<th className="text-center p-3">Name</th>
<th className="text-center p-3">Value</th>
</tr>
<tr className="border-b">
<td className="text-center p-3">CNAME</td>
<td className="text-center p-3">
{getSubdomain(customDomain) || "@"}
</td>
<td className="text-center p-3">cname.xlog.app</td>
</tr>
<tr className="border-b">
<td className="text-center p-3">TXT</td>
<td className="text-center p-3">{`_xlog-challenge.${getSubdomain(
customDomain,
)}`}</td>
<td className="text-center p-3">{subdomain}</td>
</tr>
</tbody>
</table>
</div>
)}
</div>
<div className="mt-5">
<Button type="submit" isLoading={updateSite.isLoading}>
Save