Openstatus www.openstatus.dev
at 4c0f4c00a38753a5d0dfd7e7b7b7706dec6f1503 178 lines 5.7 kB view raw
1import { z } from "zod"; 2 3import { TRPCError } from "@trpc/server"; 4import { env } from "../env"; 5import { createTRPCRouter, protectedProcedure } from "../trpc"; 6 7export const domainConfigResponseSchema = z.object({ 8 configuredBy: z 9 .union([z.literal("CNAME"), z.literal("A"), z.literal("http")]) 10 .optional() 11 .nullable(), 12 acceptedChallenges: z 13 .array(z.union([z.literal("dns-01"), z.literal("http-01")])) 14 .optional() 15 .nullable(), 16 misconfigured: z.boolean().prefault(true).optional(), 17}); 18 19export const domainResponseSchema = z.object({ 20 name: z.string().optional(), 21 apexName: z.string().optional(), 22 projectId: z.string().optional(), 23 redirect: z.string().optional().nullable(), 24 redirectStatusCode: z 25 .union([z.literal(307), z.literal(301), z.literal(302), z.literal(308)]) 26 .optional() 27 .nullable(), 28 gitBranch: z.string().optional().nullable(), 29 updatedAt: z.number().optional(), 30 createdAt: z.number().optional(), 31 verified: z.boolean().optional(), 32 verification: z 33 .array( 34 z.object({ 35 type: z.string(), 36 domain: z.string(), 37 value: z.string(), 38 reason: z.string(), 39 }), 40 ) 41 .optional(), 42}); 43 44export type DomainVerificationResponse = z.infer<typeof domainResponseSchema>; 45export type DomainConfigResponse = z.infer<typeof domainConfigResponseSchema>; 46export type DomainResponse = z.infer<typeof domainResponseSchema>; 47export type DomainVerificationStatusProps = 48 | "Valid Configuration" 49 | "Invalid Configuration" 50 | "Pending Verification" 51 | "Domain Not Found" 52 | "Unknown Error"; 53 54export const domainRouter = createTRPCRouter({ 55 addDomainToVercel: protectedProcedure 56 .input(z.object({ domain: z.string() })) 57 .mutation(async (opts) => { 58 if (opts.input.domain.toLowerCase().includes("openstatus")) { 59 throw new TRPCError({ 60 code: "BAD_REQUEST", 61 message: "Domain cannot contain 'openstatus'", 62 }); 63 } 64 65 const data = await fetch( 66 `https://api.vercel.com/v9/projects/${env.PROJECT_ID_VERCEL}/domains?teamId=${env.TEAM_ID_VERCEL}`, 67 { 68 body: `{\n "name": "${opts.input.domain}"\n}`, 69 headers: { 70 Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 71 "Content-Type": "application/json", 72 }, 73 method: "POST", 74 }, 75 ); 76 const json = await data.json(); 77 console.log({ json }); 78 return domainResponseSchema.parse(json); 79 }), 80 removeDomainFromVercelProject: protectedProcedure 81 .input(z.object({ domain: z.string() })) 82 .mutation(async (opts) => { 83 const data = await fetch( 84 `https://api.vercel.com/v9/projects/${env.PROJECT_ID_VERCEL}/domains/${opts.input.domain}?teamId=${env.TEAM_ID_VERCEL}`, 85 { 86 headers: { 87 Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 88 }, 89 method: "DELETE", 90 }, 91 ); 92 return await data.json(); 93 }), 94 // removeDomainFromVercelTeam: protectedProcedure 95 // .input(z.object({ domain: z.string() })) 96 // .mutation(async (opts) => { 97 // const data = await fetch( 98 // `https://api.vercel.com/v6/domains/${opts.input.domain}?teamId=${env.TEAM_ID_VERCEL}`, 99 // { 100 // headers: { 101 // Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 102 // }, 103 // method: "DELETE", 104 // }, 105 // ); 106 // return await data.json(); 107 // }), 108 getDomainResponse: protectedProcedure 109 .input(z.object({ domain: z.string().optional() })) 110 .query(async (opts) => { 111 if (!opts.input.domain) { 112 return null; 113 } 114 const data = await fetch( 115 `https://api.vercel.com/v9/projects/${env.PROJECT_ID_VERCEL}/domains/${opts.input.domain}?teamId=${env.TEAM_ID_VERCEL}`, 116 { 117 method: "GET", 118 headers: { 119 Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 120 "Content-Type": "application/json", 121 }, 122 }, 123 ); 124 const json = await data.json(); 125 const result = domainResponseSchema 126 .extend({ 127 error: z 128 .object({ 129 code: z.string(), 130 message: z.string(), 131 }) 132 .optional(), 133 }) 134 .parse(json); 135 console.log({ result }); 136 return result; 137 }), 138 getConfigResponse: protectedProcedure 139 .input(z.object({ domain: z.string().optional() })) 140 .query(async (opts) => { 141 if (!opts.input.domain) { 142 return null; 143 } 144 const data = await fetch( 145 `https://api.vercel.com/v6/domains/${opts.input.domain}/config?teamId=${env.TEAM_ID_VERCEL}`, 146 { 147 method: "GET", 148 headers: { 149 Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 150 "Content-Type": "application/json", 151 }, 152 }, 153 ); 154 const json = await data.json(); 155 const result = domainConfigResponseSchema.parse(json); 156 return result; 157 }), 158 verifyDomain: protectedProcedure 159 .input(z.object({ domain: z.string().optional() })) 160 .query(async (opts) => { 161 if (!opts.input.domain) { 162 return null; 163 } 164 const data = await fetch( 165 `https://api.vercel.com/v9/projects/${env.PROJECT_ID_VERCEL}/domains/${opts.input.domain}/verify?teamId=${env.TEAM_ID_VERCEL}`, 166 { 167 method: "POST", 168 headers: { 169 Authorization: `Bearer ${env.VERCEL_AUTH_BEARER_TOKEN}`, 170 "Content-Type": "application/json", 171 }, 172 }, 173 ); 174 const json = await data.json(); 175 const result = domainResponseSchema.parse(json); 176 return result; 177 }), 178});