Skip to content

Commit

Permalink
enable rewrites for root domains too
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Sep 3, 2023
1 parent 7f1e432 commit 136c438
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 24 deletions.
2 changes: 1 addition & 1 deletion components/app/modals/add-edit-domain-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ function AddEditDomainModal({
className="mt-1 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-gray-500 shadow-sm focus:border-gray-500 focus:outline-none focus:ring-gray-500 sm:text-sm"
>
<option value="redirect">Redirect</option>
<option value="rewrite">Rewrite</option>
<option value="rewrite">Rewrite (Link Cloaking)</option>
</select>
</motion.div>
)}
Expand Down
34 changes: 26 additions & 8 deletions lib/api/domains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import prisma from "#/lib/prisma";
import { redis } from "#/lib/upstash";
import cloudinary from "cloudinary";
import { getApexDomain, validDomainRegex } from "#/lib/utils";
import { isIframeable } from "../middleware/utils";

export const validateDomain = async (
domain: string,
Expand Down Expand Up @@ -164,19 +165,36 @@ export const verifyDomain = async (domain: string) => {
).then((res) => res.json());
};

export async function setRootDomain(
domain: string,
target: string,
rewrite: boolean,
newDomain?: string, // if the domain is changed, this will be the new domain
) {
export async function setRootDomain({
domain,
target,
rewrite,
newDomain,
}: {
domain: string;
target: string;
rewrite: boolean;
newDomain?: string; // if the domain is changed, this will be the new domain
}) {
if (newDomain) {
const pipeline = redis.pipeline();
pipeline.del(`root:${domain}`);
pipeline.set(`root:${newDomain}`, { target, rewrite });
pipeline.set(`root:${newDomain}`, {
target,
...(rewrite && {
rewrite: true,
iframeable: await isIframeable({ url: target, requestDomain: domain }),
}),
});
return await pipeline.exec();
} else {
await redis.set(`root:${domain}`, { target, rewrite });
await redis.set(`root:${domain}`, {
target,
...(rewrite && {
rewrite: true,
iframeable: await isIframeable({ url: target, requestDomain: domain }),
}),
});
}
}

Expand Down
25 changes: 21 additions & 4 deletions lib/middleware/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { redis } from "#/lib/upstash";
import { recordClick } from "#/lib/tinybird";
import { parse } from "./utils";
import { RootDomainProps } from "../types";
import { DUB_HEADERS } from "#/lib/constants";

export default async function RootMiddleware(
Expand All @@ -18,11 +17,29 @@ export default async function RootMiddleware(
// record clicks on root page
ev.waitUntil(recordClick(domain, req));

const { target, rewrite } =
(await redis.get<RootDomainProps>(`root:${domain}`)) || {};
const response = await redis.get<{
target: string;
rewrite?: boolean;
iframeable?: boolean;
}>(`root:${domain}`);

const { target, rewrite, iframeable } = response || {};

if (target) {
if (rewrite) {
return NextResponse.rewrite(target);
if (iframeable) {
// note: there's a discrepancy between the implementation here and for
// link rewriting in `lib/api/links` – here we do `encodeURIComponent` but for links we
// don't need to. This is because link targets are already encoded, while root targets
// are not. TODO: standardize this in the future.
return NextResponse.rewrite(
new URL(`/rewrite/${encodeURIComponent(target)}`, req.url),
DUB_HEADERS,
);
} else {
// if link is not iframeable, use Next.js rewrite instead
return NextResponse.rewrite(target, DUB_HEADERS);
}
} else {
return NextResponse.redirect(target, DUB_HEADERS);
}
Expand Down
5 changes: 0 additions & 5 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ export type DomainVerificationStatusProps =
| "Domain Not Found"
| "Unknown Error";

export interface RootDomainProps {
target: string;
rewrite?: boolean;
}

export interface DomainProps {
slug: string;
verified: boolean;
Expand Down
10 changes: 6 additions & 4 deletions pages/api/projects/[slug]/domains/[domain]/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,14 @@ export default withProjectAuth(
*/
project.plan !== "free" &&
(target
? setRootDomain(
? setRootDomain({
domain,
target,
type === "rewrite",
newDomain !== domain && newDomain,
)
rewrite: type === "rewrite",
...(newDomain !== domain && {
newDomain,
}),
})
: redis.del(`root:${domain}`)),
// if the domain is being set as the primary domain, set the current primary domain to false
primary &&
Expand Down
10 changes: 8 additions & 2 deletions pages/api/projects/[slug]/domains/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { withProjectAuth } from "#/lib/auth";
import prisma from "#/lib/prisma";
import { addDomainToVercel, validateDomain } from "#/lib/api/domains";
import {
addDomainToVercel,
setRootDomain,
validateDomain,
} from "#/lib/api/domains";
import { redis } from "#/lib/upstash";
import { isIframeable } from "#/lib/middleware/utils";

export default withProjectAuth(async (req, res, project) => {
// GET /api/projects/[slug]/domains – get all domains for a project
Expand Down Expand Up @@ -44,7 +49,8 @@ export default withProjectAuth(async (req, res, project) => {
*/
const response = await Promise.allSettled([
target &&
redis.set(`root:${domain}`, {
setRootDomain({
domain,
target,
rewrite: type === "rewrite",
}),
Expand Down

0 comments on commit 136c438

Please sign in to comment.