diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b96397..f802958 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: TURBO_TEAM: ${{ vars.TURBO_TEAM }} strategy: matrix: - node-version: [18.x, 20.x] + node-version: [20.x] steps: - name: Checkout uses: actions/checkout@v3 @@ -34,8 +34,5 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Install Playwright Browsers - run: pnpm exec playwright install --with-deps - - - name: Run tests - run: pnpm test + - name: Build + run: pnpm build \ No newline at end of file diff --git a/README.md b/README.md index aca197d..692b70e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For this tutorial you'll need a [Vercel](https://vercel.com/) account. 1. Create a new storage edge config. 2. Create a new vercel project and import configuration-api. 3. Create a new project and import custom-domain-proxy. -4. Connect the storage to the project custom-domain-proxy. +4. Connect the storage to the project custom-domain-proxy. 5. Add the required env vars for the configuration-api ```bash @@ -20,4 +20,4 @@ AUTH_BEARER_TOKEN= # create your own bearer token that can access the projects ``` 6. Add a new domain using the configuration-api -7. Finish the domain configuration \ No newline at end of file +7. Finish the domain configuration diff --git a/apps/configuration-api/README.md b/apps/configuration-api/README.md index e0e0ed3..7773270 100644 --- a/apps/configuration-api/README.md +++ b/apps/configuration-api/README.md @@ -1 +1 @@ -# UI and API to configure custom domains \ No newline at end of file +# UI and API to configure custom domains diff --git a/apps/configuration-api/app/api/assign/route.ts b/apps/configuration-api/app/api/assign/route.ts index 4b49e44..7358b3f 100644 --- a/apps/configuration-api/app/api/assign/route.ts +++ b/apps/configuration-api/app/api/assign/route.ts @@ -1,97 +1,128 @@ - -import { addDomainToEdgeConfig, getEdgeconfigItem, updateEdgeConfigItem, removeDomainFromEdgeConfig, getAllItemsFromEdgeConfig } from "@customdomainready/sdk"; -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { + addDomainToEdgeConfig, + getEdgeconfigItem, + updateEdgeConfigItem, + removeDomainFromEdgeConfig, + getAllItemsFromEdgeConfig, +} from '@customdomainready/sdk'; +import { NextResponse } from 'next/server'; +import { z } from 'zod'; const customDomainSchema = z.object({ - domain: z.string(), - slug: z.string(), - destination: z.string() -}) - -export async function GET( - req: Request, -) { - try { - const response = await getAllItemsFromEdgeConfig(process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN) - console.log(response) - return NextResponse.json({ - response: response.map((item: any) => ({ - id: item.key, - sourceDomain: `https://${item.key.split('-')[0].replace(/_/g, '.')}`, - slug: item.key.split('-').slice(1).join('/').replace(/_/g, '.'), - destinationPath: item.value, - })) - }); - } catch (error) { - console.error(error) - - return new Response("Internal Server Error", { status: 500 }) - } + domain: z.string(), + slug: z.string(), + destination: z.string(), +}); + +export async function GET(req: Request) { + try { + const response = await getAllItemsFromEdgeConfig( + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + console.log(response); + return NextResponse.json({ + response: response.map((item: any) => ({ + id: item.key, + sourceDomain: `https://${item.key.split('-')[0].replace(/_/g, '.')}`, + slug: item.key.split('-').slice(1).join('/').replace(/_/g, '.'), + destinationPath: item.value, + })), + }); + } catch (error) { + console.error(error); + + return new Response('Internal Server Error', { status: 500 }); + } } - - -export async function PATCH( - req: Request -) { - try { - const body = await req.json() - const payload = customDomainSchema.parse(body) - - const domainWithoutProtocol = payload.domain.replace(/^https?:\/\//, ''); - const key = `${domainWithoutProtocol.replace(/\./g, '_')}${payload.slug.replace(/\//g, '-')}`; - - // validate if key already exist and if yes update it instead of creating - const existingDomain = await getEdgeconfigItem(key, process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - - if (existingDomain){ - const updateResponse = await updateEdgeConfigItem(key, payload.destination, process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - console.log(updateResponse); - } else { - const createResponse = await addDomainToEdgeConfig(key, payload.destination, process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - console.log(createResponse); - } - - return new Response('created', { status: 201}) - } catch (error) { - console.log(error) - if (error instanceof z.ZodError) { - return new Response(JSON.stringify(error.issues), { status: 422 }) - } - - return new Response(null, { status: 500 }) +export async function PATCH(req: Request) { + try { + const body = await req.json(); + const payload = customDomainSchema.parse(body); + + const domainWithoutProtocol = payload.domain.replace(/^https?:\/\//, ''); + const key = `${domainWithoutProtocol.replace( + /\./g, + '_', + )}${payload.slug.replace(/\//g, '-')}`; + + // validate if key already exist and if yes update it instead of creating + const existingDomain = await getEdgeconfigItem( + key, + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + + if (existingDomain) { + const updateResponse = await updateEdgeConfigItem( + key, + payload.destination, + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + console.log(updateResponse); + } else { + const createResponse = await addDomainToEdgeConfig( + key, + payload.destination, + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + console.log(createResponse); } -} - -export async function DELETE( - req: Request -) { - try { - const body = await req.json(); - const payload = customDomainSchema.parse(body); - - const domainWithoutProtocol = payload.domain.replace(/^https?:\/\//, ''); - const key = `${domainWithoutProtocol.replace(/\./g, '_')}${payload.slug.replace(/\//g, '-')}`; - - const existingDomain = await getEdgeconfigItem(key, process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - - if (existingDomain) { - const deleteResponse = await removeDomainFromEdgeConfig(key, process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - console.log(deleteResponse); - return NextResponse.json('deleted', { status: 204 }); - } else { - return NextResponse.json('Domain not found', { status: 404 }); - } - } catch (error) { - console.log(error); - if (error instanceof z.ZodError) { - return NextResponse.json(JSON.stringify(error.issues), { status: 422 }); - } - return NextResponse.json(null, { status: 500 }); + return new Response('created', { status: 201 }); + } catch (error) { + console.log(error); + if (error instanceof z.ZodError) { + return new Response(JSON.stringify(error.issues), { status: 422 }); } + + return new Response(null, { status: 500 }); + } } +export async function DELETE(req: Request) { + try { + const body = await req.json(); + const payload = customDomainSchema.parse(body); + + const domainWithoutProtocol = payload.domain.replace(/^https?:\/\//, ''); + const key = `${domainWithoutProtocol.replace( + /\./g, + '_', + )}${payload.slug.replace(/\//g, '-')}`; + + const existingDomain = await getEdgeconfigItem( + key, + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + + if (existingDomain) { + const deleteResponse = await removeDomainFromEdgeConfig( + key, + process.env.VERCEL_CUSTOM_DOMAIN_PROXY_EDGE_CONFIG_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + console.log(deleteResponse); + return NextResponse.json('deleted', { status: 204 }); + } else { + return NextResponse.json('Domain not found', { status: 404 }); + } + } catch (error) { + console.log(error); + if (error instanceof z.ZodError) { + return NextResponse.json(JSON.stringify(error.issues), { status: 422 }); + } - \ No newline at end of file + return NextResponse.json(null, { status: 500 }); + } +} diff --git a/apps/configuration-api/app/api/domain/[slug]/route.ts b/apps/configuration-api/app/api/domain/[slug]/route.ts index 3efbc36..30f8dca 100644 --- a/apps/configuration-api/app/api/domain/[slug]/route.ts +++ b/apps/configuration-api/app/api/domain/[slug]/route.ts @@ -1,17 +1,20 @@ -import { - removeDomainFromVercelProject, -} from "@customdomainready/sdk"; +import { removeDomainFromVercelProject } from '@customdomainready/sdk'; export async function DELETE( - req: Request, - { params }: { params: { slug: string } }, + req: Request, + { params }: { params: { slug: string } }, ) { - const domain = decodeURIComponent(params.slug); - const response = await removeDomainFromVercelProject(domain, process.env.VERCEL_PROJECT_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); + const domain = decodeURIComponent(params.slug); + const response = await removeDomainFromVercelProject( + domain, + process.env.VERCEL_PROJECT_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); - if (response.error) { - return new Response(response.error.message, { status: 400 }) - } + if (response.error) { + return new Response(response.error.message, { status: 400 }); + } - return new Response(null, { status: 204 }) -} \ No newline at end of file + return new Response(null, { status: 204 }); +} diff --git a/apps/configuration-api/app/api/domain/[slug]/verify/route.ts b/apps/configuration-api/app/api/domain/[slug]/verify/route.ts index 815c61a..20f172a 100644 --- a/apps/configuration-api/app/api/domain/[slug]/verify/route.ts +++ b/apps/configuration-api/app/api/domain/[slug]/verify/route.ts @@ -1,55 +1,69 @@ import { - getConfigResponse, - getDomainResponse, - verifyDomain, -} from "@customdomainready/sdk"; -import { NextResponse } from "next/server"; + getConfigResponse, + getDomainResponse, + verifyDomain, +} from '@customdomainready/sdk'; +import { NextResponse } from 'next/server'; export type DomainVerificationStatusProps = -| "Valid Configuration" -| "Invalid Configuration" -| "Pending Verification" -| "Domain Not Found" -| "Unknown Error"; + | 'Valid Configuration' + | 'Invalid Configuration' + | 'Pending Verification' + | 'Domain Not Found' + | 'Unknown Error'; export async function GET( - _req: Request, - { params }: { params: { slug: string } }, + _req: Request, + { params }: { params: { slug: string } }, ) { - const domain = decodeURIComponent(params.slug); - - let status: DomainVerificationStatusProps = "Valid Configuration"; - - const [domainJson, configJson] = await Promise.all([ - getDomainResponse(domain, process.env.VERCEL_PROJECT_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN), - getConfigResponse(domain, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN), - ]); - - if (domainJson?.error?.code === "not_found") { - // domain not found on Vercel project - status = "Domain Not Found"; - - // unknown error - } else if (domainJson.error) { - status = "Unknown Error"; - - // if domain is not verified, we try to verify now - } else if (!domainJson.verified) { - status = "Pending Verification"; - const verificationJson = await verifyDomain(domain, process.env.VERCEL_PROJECT_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - - // domain was just verified - if (verificationJson && verificationJson.verified) { - status = "Valid Configuration"; - } - } else if (configJson.misconfigured) { - status = "Invalid Configuration"; - } else { - status = "Valid Configuration"; + const domain = decodeURIComponent(params.slug); + + let status: DomainVerificationStatusProps = 'Valid Configuration'; + + const [domainJson, configJson] = await Promise.all([ + getDomainResponse( + domain, + process.env.VERCEL_PROJECT_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ), + getConfigResponse( + domain, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ), + ]); + + if (domainJson?.error?.code === 'not_found') { + // domain not found on Vercel project + status = 'Domain Not Found'; + + // unknown error + } else if (domainJson.error) { + status = 'Unknown Error'; + + // if domain is not verified, we try to verify now + } else if (!domainJson.verified) { + status = 'Pending Verification'; + const verificationJson = await verifyDomain( + domain, + process.env.VERCEL_PROJECT_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + + // domain was just verified + if (verificationJson && verificationJson.verified) { + status = 'Valid Configuration'; } + } else if (configJson.misconfigured) { + status = 'Invalid Configuration'; + } else { + status = 'Valid Configuration'; + } - return NextResponse.json({ - status, - domainJson, - }); -} \ No newline at end of file + return NextResponse.json({ + status, + domainJson, + }); +} diff --git a/apps/configuration-api/app/api/domain/route.ts b/apps/configuration-api/app/api/domain/route.ts index 7280cf1..a17d12b 100644 --- a/apps/configuration-api/app/api/domain/route.ts +++ b/apps/configuration-api/app/api/domain/route.ts @@ -1,42 +1,51 @@ -import { addDomainToVercel, getDomains, getAllItemsFromEdgeConfig } from "@customdomainready/sdk"; -import { NextResponse } from "next/server"; +import { + addDomainToVercel, + getDomains, + getAllItemsFromEdgeConfig, +} from '@customdomainready/sdk'; +import { NextResponse } from 'next/server'; -import * as z from "zod" +import * as z from 'zod'; const customDomainSchema = z.object({ - domain: z.string(), -}) - -export async function POST( - req: Request, -) { - try { - // Get the request body and validate it. - const body = await req.json() - const payload = customDomainSchema.parse(body) - - const domain = payload.domain; - - // Check if the domain already exists - const existingDomains = await getDomains(process.env.VERCEL_PROJECT_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - if (existingDomains.domains.some((d: any) => d.name === domain)) { - return new Response("Domain already exists", { status: 200 }); - } - - const response = await addDomainToVercel(domain, process.env.VERCEL_PROJECT_ID!, process.env.VERCEL_TEAM_ID, process.env.AUTH_BEARER_TOKEN); - - if (response.error) { - return new Response(response.error.message, { status: 400 }) - } - - return NextResponse.json({ - response - }); - } catch (error) { - console.error(error) - - return new Response("Internal Server Error", { status: 500 }) + domain: z.string(), +}); + +export async function POST(req: Request) { + try { + // Get the request body and validate it. + const body = await req.json(); + const payload = customDomainSchema.parse(body); + + const domain = payload.domain; + + // Check if the domain already exists + const existingDomains = await getDomains( + process.env.VERCEL_PROJECT_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + if (existingDomains.domains.some((d: any) => d.name === domain)) { + return new Response('Domain already exists', { status: 200 }); } -} + const response = await addDomainToVercel( + domain, + process.env.VERCEL_PROJECT_ID!, + process.env.VERCEL_TEAM_ID, + process.env.AUTH_BEARER_TOKEN, + ); + + if (response.error) { + return new Response(response.error.message, { status: 400 }); + } + return NextResponse.json({ + response, + }); + } catch (error) { + console.error(error); + + return new Response('Internal Server Error', { status: 500 }); + } +} diff --git a/apps/configuration-api/app/layout.tsx b/apps/configuration-api/app/layout.tsx index 94657d6..28b7e42 100644 --- a/apps/configuration-api/app/layout.tsx +++ b/apps/configuration-api/app/layout.tsx @@ -1,6 +1,6 @@ import { Inter } from 'next/font/google'; -import './global.css' +import './global.css'; const inter = Inter({ subsets: ['latin'] }); diff --git a/apps/configuration-api/app/page.tsx b/apps/configuration-api/app/page.tsx index a7661f5..7a621a0 100644 --- a/apps/configuration-api/app/page.tsx +++ b/apps/configuration-api/app/page.tsx @@ -1,4 +1,4 @@ -import CustomDomainConfig from "@/components/custom-domain-config" +import CustomDomainConfig from '@/components/custom-domain-config'; export default function Home() { return ( @@ -6,6 +6,5 @@ export default function Home() {