Skip to content

Commit

Permalink
Updates Domains API template (#925)
Browse files Browse the repository at this point in the history
  • Loading branch information
manovotny committed Jun 14, 2024
1 parent c1f0e04 commit 7634f4c
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 60 deletions.
47 changes: 22 additions & 25 deletions solutions/domains-api/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: Domains API
slug: domains-api
description: Learn to leverage Vercel's Domains API to add and remove domains programmatically from your Platforms on Vercel project.
description: Learn how to use Vercel's Domains API to add or remove domains programmatically from your Vercel app.
framework: Next.js
useCase: Documentation
css: Tailwind
Expand All @@ -13,30 +13,23 @@ relatedTemplates:

# Domains API

This code snippet shows you how you can leverage Vercel's Domains API to add and remove domains programmatically from your Platforms on Vercel project.
This template shows how you can use Vercel's Domains API to add and remove domains programmatically from your Vercel app.

## Dependencies
## Deploy your own

- Tailwind CSS (`npm install tailwindcss`)
- SWR (`npm install swr`)
- React Hot Toast (`npm install react-hot-toast`) (optional)
- JS Cookie (`npm install js-cookie`) (optional)

## Demo

https://domains-api.vercel.app/
Deploy the template using [Vercel](https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/solutions/domains-api&project-name=domains-api&repository-name=domains-api&env=AUTH_BEARER_TOKEN,PROJECT_ID_VERCEL,TEAM_ID_VERCEL) [view the demo](https://domains-api.vercel.app/), or read the [documentation](https://vercel.com/docs/rest-api/endpoints/domains).

## Features

### 0. Configure Env Vars
### 1. Configure Environment Variables

You'll need to configure 3 different environment variables for this project to work:
You'll need to configure the follow environment variables for this project to work:

- `PROJECT_ID_VERCEL`: The ID of the Vercel project you want to add/remove domains from. You can find it under the "Settings" tab in your project's dashboard.
- `TEAM_ID_VERCEL`: The ID of the Vercel team you want to add/remove domains from. You can find it under the "Settings" tab in your team's dashboard.
- `AUTH_BEARER_TOKEN`: Your personal auth bearer token that gives you programmatic access to your Vercel account. You can create one under the "Tokens" tab in your personal account's settings tab.
- `PROJECT_ID_VERCEL`: The ID of the Vercel Project you want to add or remove domains from. Lean how to [find your Vercel Project ID](https://vercel.com/docs/projects/overview#project-id).
- `TEAM_ID_VERCEL`: The ID of the Vercel Team you want to add or remove domains from. Lean how to [find your Vercel Team ID](https://vercel.com/docs/accounts/create-a-team#find-your-team-id).
- `AUTH_BEARER_TOKEN`: A token that allows programmatic access to your Vercel account. Lean how to [create a Vercel Acces Token](https://vercel.com/docs/rest-api#creating-an-access-token).

### 1. Adding Domains
### 2. Adding Domains

To add a domain, you can use the `/v8/projects/{idOrName}/domains` endpoint as shown [here](./pages/api/add-domain.js) ([full documentation](https://vercel.com/docs/rest-api#endpoints/projects/add-a-domain-to-a-project)).

Expand All @@ -50,7 +43,7 @@ When a domain is added, there are 3 possible outcomes:

Verifying a project domain can be done with the `/v9/projects/{projectId}/domains/{domain}/verify` endpoint as shown [here](./pages/api/verify-domain.js) ([full documentation](https://vercel.com/docs/rest-api#endpoints/projects/verify-project-domain)).

### 2. Auto-checking Domain Configuration
### 3. Auto-checking Domain Configuration

When a domain is first added to a project, we use [SWR](https://swr.vercel.app) to periodically check if the domain's DNS records are configured correctly. This is done using the `/v6/domains/{domain}/config` endpoint as shown [here](./pages/api/check-domain.js).

Expand All @@ -65,13 +58,13 @@ There are two ways that your users can configure their domains after they are ad
Example:
![CleanShot 2021-12-08 at 19 00 52](https://user-images.githubusercontent.com/28986134/145327099-137dc60e-d260-4ba3-b8bb-413e7d70b9b1.png)

### 3. Removing Domains
### 4. Removing Domains

To remove a domain, you can use the `/v8/projects/{idOrName}/domains` endpoint as shown [here](./pages/api/remove-domain.js) ([full documentation](https://vercel.com/docs/rest-api#endpoints/projects/remove-a-domain-from-a-project)).

## How to Use

You can choose from one of the following two methods to use this repository:
You can choose from one of the following methods to use this repository:

### One-Click Deploy

Expand All @@ -81,16 +74,20 @@ Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_mediu

### Clone and Deploy

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [pnpm](https://pnpm.io/installation) to bootstrap the example:
Use [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) to bootstrap the example:

```bash
pnpm create next-app --example https://github.com/vercel/examples/tree/main/solutions/domains-api domains-api
npx create-next-app --example https://github.com/vercel/examples/tree/main/solutions/domains-api domains-api
```

Next, run Next.js in development mode:
Then, run Next.js in development mode:

```bash
pnpm dev
npm run dev
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=platforms-eap) ([Documentation](https://nextjs.org/docs/deployment)).
When you're ready to deploy to [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=platforms-eap), install and use the [Vercel CLI](https://vercel.com/docs/cli) to create a new project and deployment.

```bash
vercel deploy
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const ConfiguredSectionPlaceholder = () => {
return (
<div className="flex items-center space-x-3 mt-3 px-2 sm:px-10">
<div className="flex items-center space-x-3 mt-3 px-10">
<div className="w-6 h-6 rounded-full bg-gray-300 animate-pulse" />
<p className="text-black text-gray-500 font-normal text-sm">
Loading Configuration
Expand Down
12 changes: 6 additions & 6 deletions solutions/domains-api/components/configured-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const ConfiguredSection = ({ domainInfo }) => {
)
return (
<>
<div className="flex items-center space-x-3 my-3 px-2 sm:px-10">
<div className="flex items-center space-x-3 my-3 px-10">
<svg
viewBox="0 0 24 24"
width="24"
Expand All @@ -46,7 +46,7 @@ const ConfiguredSection = ({ domainInfo }) => {

<div className="w-full border-t border-gray-100 mt-5 mb-8" />

<div className="px-2 sm:px-10">
<div className="px-10">
<div className="flex justify-start space-x-4">
<div
onClick={() => setRecordType('CNAME')}
Expand Down Expand Up @@ -80,10 +80,10 @@ const ConfiguredSection = ({ domainInfo }) => {
)}
</p>
</div>
<div>
<div className="break-all">
<p className="text-sm font-bold">Value</p>
<p className="text-sm font-mono mt-2">
<span className="text-ellipsis">{txtVerification.value}</span>
{txtVerification.value}
</p>
</div>
</div>
Expand All @@ -100,7 +100,7 @@ const ConfiguredSection = ({ domainInfo }) => {

return (
<>
<div className="flex items-center space-x-3 my-3 px-2 sm:px-10">
<div className="flex items-center space-x-3 my-3 px-10">
<svg
viewBox="0 0 24 24"
width="24"
Expand Down Expand Up @@ -146,7 +146,7 @@ const ConfiguredSection = ({ domainInfo }) => {
<>
<div className="w-full border-t border-gray-100 mt-5 mb-8" />

<div className="px-2 sm:px-10">
<div className="px-10">
<div className="flex justify-start space-x-4">
<button
onClick={() => setRecordType('CNAME')}
Expand Down
4 changes: 2 additions & 2 deletions solutions/domains-api/components/domain-card-placeholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import ConfiguredSectionPlaceholder from './configured-section-placeholder'

const DomainCardPlaceholder = () => {
return (
<div className="w-full mt-10 sm:shadow-md border-y sm:border border-black sm:border-gray-50 sm:rounded-lg py-10">
<div className="flex justify-between space-x-4 px-2 sm:px-10">
<div className="w-full mt-10 shadow-md border-y border border-gray-50 rounded-lg py-10">
<div className="flex justify-between space-x-4 px-10">
<div className="h-7 w-36 bg-gray-300 rounded-md animate-pulse" />
<div className="flex space-x-3">
<button
Expand Down
47 changes: 25 additions & 22 deletions solutions/domains-api/components/domain-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useSWR, { mutate } from 'swr'
import fetcher from '../lib/fetcher'
import { useState } from 'react'
import LoadingDots from '../components/loading-dots'
import { restrictedDomains } from '../lib/consts'

const DomainCard = ({ domain, revalidateDomains }) => {
const { data: domainInfo, isValidating } = useSWR(
Expand All @@ -12,8 +13,8 @@ const DomainCard = ({ domain, revalidateDomains }) => {
)
const [removing, setRemoving] = useState(false)
return (
<div className="w-full mt-10 sm:shadow-md border-y sm:border border-black sm:border-gray-50 sm:rounded-lg py-10">
<div className="flex justify-between space-x-4 px-2 sm:px-10">
<div className="w-full mt-10 shadow-md border-y border border-gray-50 rounded-lg py-10">
<div className="flex justify-between space-x-4 px-10">
<a
href={`http://${domain}`}
target="_blank"
Expand All @@ -39,7 +40,7 @@ const DomainCard = ({ domain, revalidateDomains }) => {
</svg>
</span>
</a>
<div className="flex space-x-3">
<div className="flex gap-3 flex-col sm:flex-row">
<button
onClick={() => {
mutate(`/api/check-domain?domain=${domain}`)
Expand All @@ -53,25 +54,27 @@ const DomainCard = ({ domain, revalidateDomains }) => {
>
{isValidating ? <LoadingDots /> : 'Refresh'}
</button>
<button
onClick={async () => {
setRemoving(true)
try {
await fetch(`/api/remove-domain?domain=${domain}`)
await revalidateDomains()
} catch (error) {
alert(`Error removing domain`)
} finally {
setRemoving(false)
}
}}
disabled={removing}
className={`${
removing ? 'cursor-not-allowed bg-gray-100' : ''
}bg-red-500 text-white border-red-500 hover:text-red-500 hover:bg-white py-1.5 w-24 text-sm border-solid border rounded-md focus:outline-none transition-all ease-in-out duration-150`}
>
{removing ? <LoadingDots /> : 'Remove'}
</button>
{!restrictedDomains.includes(domain) ? (
<button
onClick={async () => {
setRemoving(true)
try {
await fetch(`/api/remove-domain?domain=${domain}`)
await revalidateDomains()
} catch (error) {
alert(`Error removing domain`)
} finally {
setRemoving(false)
}
}}
disabled={removing}
className={`${
removing ? 'cursor-not-allowed ' : ''
}bg-red-500 text-white border-red-500 hover:text-red-500 hover:bg-white py-1.5 w-24 text-sm border-solid border rounded-md focus:outline-none transition-all ease-in-out duration-150`}
>
{removing ? <LoadingDots /> : 'Remove'}
</button>
) : null}
</div>
</div>

Expand Down
1 change: 1 addition & 0 deletions solutions/domains-api/lib/consts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const restrictedDomains = ['cat.vercel.pub']
4 changes: 2 additions & 2 deletions solutions/domains-api/pages/api/remove-domain.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { restrictedDomains } from '../../lib/consts'

export default async function handler(req, res) {
const { domain } = req.query

Expand All @@ -19,5 +21,3 @@ export default async function handler(req, res) {
const json = await response.json()
res.status(200).send(json)
}

const restrictedDomains = ['portfolio.steventey.com', 'cat.vercel.pub']
4 changes: 2 additions & 2 deletions solutions/domains-api/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default function Home() {
<Image src="/github.svg" alt="Github" width={25} height={25} />
</a>

<main className="flex flex-col items-center justify-center w-full flex-1 sm:px-20 text-center my-20">
<main className="flex flex-col items-center justify-center w-full flex-1 px-8 text-center my-20">
<h1 className="text-4xl sm:text-6xl font-bold">Domains API</h1>

<form
Expand Down Expand Up @@ -80,7 +80,7 @@ export default function Home() {
placeholder="mydomain.com"
pattern="^(?:[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.)?[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$"
required
className="rounded-md border border-gray-300 focus:ring-0 focus:border-black px-4 flex-auto min-w-0 sm:text-sm"
className="rounded-md border border-gray-300 focus:ring-0 focus:border-black px-4 flex-auto min-w-0 text-sm"
/>
<button
type="submit"
Expand Down

0 comments on commit 7634f4c

Please sign in to comment.