Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
alaister committed Oct 26, 2024
1 parent cccee44 commit 370ec98
Show file tree
Hide file tree
Showing 34 changed files with 1,634 additions and 674 deletions.
83 changes: 28 additions & 55 deletions app/api/domains/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextRequest } from 'next/server'
import { ParseResultType, parseDomain } from 'parse-domain'
import * as z from 'zod'
import supabaseAdmin, { getUserFromRequest } from '~/lib/supabase-admin'
import { ParseResultType, parseDomain } from 'parse-domain'

const schema = z.object({
domainName: z.string().min(1, 'Domain must not be empty'),
Expand Down Expand Up @@ -48,7 +48,7 @@ export async function POST(request: NextRequest) {
const parseResult = parseDomain(rawDomainName)
if (parseResult.type === ParseResultType.Listed) {
const { domain, topLevelDomains } = parseResult
domainName = `${domain}.${topLevelDomains.join('.')}`
domainName = `${domain}.${topLevelDomains.join('.')}`.toLowerCase()
}
if (domainName === undefined) {
return new Response(
Expand All @@ -64,60 +64,33 @@ export async function POST(request: NextRequest) {
)
}

await supabaseAdmin.from('domain_names').insert({
domain_name: domainName,
user_id: user.id,
})

// We have to add the domains in order because of the redirect on the second domain
const apexResponse = await fetch(
`https://api.vercel.com/v10/projects/${process.env.VERCEL_PROJECT_ID}/domains?teamId=${process.env.VERCEL_TEAM_ID}`,
{
body: JSON.stringify({
name: domainName,
}),
headers: {
Authorization: `Bearer ${process.env.VERCEL_AUTH_BEARER_TOKEN}`,
'Content-Type': 'application/json',
},
method: 'POST',
}
)
const { data, error } = await supabaseAdmin
.from('domain_names')
.insert({
domain_name: domainName,
user_id: user.id,
})
.select('id')
.single()

const wwwResponse = await fetch(
`https://api.vercel.com/v10/projects/${process.env.VERCEL_PROJECT_ID}/domains?teamId=${process.env.VERCEL_TEAM_ID}`,
{
body: JSON.stringify({
name: `www.${domainName}`,
redirect: domainName,
redirectStatusCode: 308,
if (error) {
return new Response(
JSON.stringify({
code: 'INSERT_ERROR',
message: error.message,
}),
headers: {
Authorization: `Bearer ${process.env.VERCEL_AUTH_BEARER_TOKEN}`,
'Content-Type': 'application/json',
},
method: 'POST',
}
)

const apexData = await apexResponse.json()
const wwwData = await wwwResponse.json()
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
}
)
}

return new Response(
JSON.stringify({
apex: {
domainName: apexData.name,
verificationRecords: apexData.verification,
},
www: {
domainName: wwwData.name,
verificationRecords: wwwData.verification,
},
}),
{
headers: {
'Content-Type': 'application/json;',
},
}
)
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json;',
},
})
}
159 changes: 159 additions & 0 deletions components/WhoisInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Building, Globe, Phone, Shield, User } from 'lucide-react'
import { useCallback, useMemo } from 'react'
import { Badge } from './ui/badge'

type ContactInfo = {
id?: string
fax?: string
city?: string
name?: string
email?: string
phone?: string
street?: string
country?: string
province?: string
postal_code?: string
organization?: string
}

type DomainInfo = {
id?: string
name?: string
domain?: string
status?: string[]
punycode?: string
extension?: string
created_date?: string
name_servers?: string[]
updated_date?: string
whois_server?: string
expiration_date?: string
created_date_in_time?: string
updated_date_in_time?: string
expiration_date_in_time?: string
}

type RegistrarInfo = {
id?: string
name?: string
email?: string
phone?: string
referral_url?: string
}

type WhoisData = {
domain?: DomainInfo
registrar?: RegistrarInfo
technical?: ContactInfo
registrant?: ContactInfo
administrative?: ContactInfo
}

interface WhoisInfoProps {
data: WhoisData
}

const WhoisInfo = ({ data }: WhoisInfoProps) => {
const renderSection = useCallback(
(data: Record<string, any> | undefined, excludeFields: string[] = []) => {
if (!data) return null
return Object.entries(data)
.filter(([key]) => !excludeFields.includes(key))
.map(([key, value]) => (
<div key={key} className="flex flex-col gap-1 py-1.5">
<dt className="text-xs font-semibold text-gray-600 capitalize">
{key.replace(/_/g, ' ')}
</dt>
<dd className="text-sm text-gray-900">
{Array.isArray(value) ? (
<div className="flex flex-wrap gap-1">
{value.map((item, index) => (
<Badge key={index} variant="secondary" className="text-xs">
{item}
</Badge>
))}
</div>
) : (
value || 'N/A'
)}
</dd>
</div>
))
},
[]
)

const primarySection = useMemo(() => {
if (!data?.domain) return null

return (
<div className="bg-white rounded-lg border border-gray-200 p-4">
<div className="flex items-center gap-2 mb-3">
<Globe className="h-4 w-4" />
<h3 className="text-sm font-semibold">Domain Information</h3>
</div>
<dl className="divide-y divide-gray-100">
{renderSection(data.domain, [
'created_date_in_time',
'updated_date_in_time',
'expiration_date_in_time',
])}
</dl>
</div>
)
}, [data.domain, renderSection])

const contactSections = useMemo(
() => [
{
title: 'Registrar',
icon: Building,
data: data?.registrar,
},
{
title: 'Technical Contact',
icon: Shield,
data: data?.technical,
},
{
title: 'Registrant',
icon: User,
data: data?.registrant,
},
{
title: 'Administrative Contact',
icon: Phone,
data: data?.administrative,
},
],
[data]
)

return (
<div className="space-y-4">
{primarySection}

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{contactSections.map(
({ title, icon: Icon, data: sectionData }) =>
sectionData && (
<div
key={title}
className="bg-white rounded-lg border border-gray-200 p-4"
>
<div className="flex items-center gap-2 mb-3">
<Icon className="h-4 w-4" />
<h3 className="text-sm font-semibold">{title}</h3>
</div>
<dl className="divide-y divide-gray-100">
{renderSection(sectionData)}
</dl>
</div>
)
)}
</div>
</div>
)
}

export default WhoisInfo
5 changes: 4 additions & 1 deletion components/add-domain.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Plus } from 'lucide-react'
import { useEffect } from 'react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
Expand Down Expand Up @@ -68,7 +69,7 @@ const AddDomain = () => {
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex items-end gap-4 shadow-lg p-4 rounded-lg"
className="flex flex-col gap-4 shadow-lg p-4 rounded-lg"
>
<FormField
control={form.control}
Expand All @@ -89,7 +90,9 @@ const AddDomain = () => {
type="submit"
isLoading={form.formState.isSubmitting}
disabled={form.formState.isSubmitting}
className="self-end"
>
<Plus className="h-4 w-4" />
Add Domain
</Button>
</form>
Expand Down
12 changes: 0 additions & 12 deletions components/configured-section-placeholder.js

This file was deleted.

Loading

0 comments on commit 370ec98

Please sign in to comment.