Skip to content

Commit

Permalink
Merge pull request dubinc#574 from dubinc/shortlink
Browse files Browse the repository at this point in the history
Polyfill `shortLink` in API responses
  • Loading branch information
steven-tey authored Jan 20, 2024
2 parents ddfd060 + 568a470 commit c410a4f
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 73 deletions.
130 changes: 64 additions & 66 deletions apps/web/app/api/links/[linkId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,80 +13,78 @@ export const GET = withAuth(async ({ headers, link }) => {
});

// PUT /api/links/[linkId] – update a link
export const PUT = withAuth(
async ({ req, headers, project, link, session }) => {
let body;
try {
body = await req.json();
} catch (error) {
return new Response("Missing or invalid body.", { status: 400, headers });
}
if (Object.keys(body).length === 0) {
return new Response("No fields to update.", { status: 304, headers });
}
export const PUT = withAuth(async ({ req, headers, project, link }) => {
let body;
try {
body = await req.json();
} catch (error) {
return new Response("Missing or invalid body.", { status: 400, headers });
}
if (Object.keys(body).length === 0) {
return new Response("No fields to update.", { status: 304, headers });
}

const updatedLink = {
...link,
...body,
};
const updatedLink = {
...link,
...body,
};

if (updatedLink.projectId !== link?.projectId) {
return new Response(
"Transferring links to another project is not yet supported.",
{
status: 403,
headers,
},
);
}

const {
link: processedLink,
error,
status,
} = await processLink({
payload: updatedLink,
project,
});
if (updatedLink.projectId !== link?.projectId) {
return new Response(
"Transferring links to another project is not yet supported.",
{
status: 403,
headers,
},
);
}

if (error) {
return new Response(error, { status, headers });
}
const {
link: processedLink,
error,
status,
} = await processLink({
payload: updatedLink,
project,
});

const [response, _] = await Promise.allSettled([
editLink({
// link is guaranteed to exist because if not we will return 404
domain: link!.domain,
key: link!.key,
updatedLink: processedLink,
}),
...(link &&
processedLink.domain === "dub.sh" &&
processedLink.url !== link.url
? [
qstash.publishJSON({
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/verify`,
body: {
linkId: link.id,
},
}),
]
: []),
// @ts-ignore
]).then((results) => results.map((result) => result.value));
if (error) {
return new Response(error, { status, headers });
}

if (response === null) {
return new Response("Duplicate key: This short link already exists.", {
status: 409,
headers,
});
}
const [response, _] = await Promise.allSettled([
editLink({
// link is guaranteed to exist because if not we will return 404
domain: link!.domain,
key: link!.key,
updatedLink: processedLink,
}),
...(link &&
processedLink.domain === "dub.sh" &&
processedLink.url !== link.url
? [
qstash.publishJSON({
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/verify`,
body: {
linkId: link.id,
},
}),
]
: []),
// @ts-ignore
]).then((results) => results.map((result) => result.value));

return NextResponse.json(response, {
if (response === null) {
return new Response("Duplicate key: This short link already exists.", {
status: 409,
headers,
});
},
);
}

return NextResponse.json(response, {
headers,
});
});

// DELETE /api/links/[linkId] – delete a link
export const DELETE = withAuth(async ({ headers, link, project }) => {
Expand Down
16 changes: 13 additions & 3 deletions apps/web/app/api/links/info/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { withAuth } from "@/lib/auth";
import prisma from "@/lib/prisma";
import { linkConstructor } from "@dub/utils";
import { NextResponse } from "next/server";

// GET /api/links/info – get the info for a link
Expand All @@ -25,7 +26,16 @@ export const GET = withAuth(async ({ headers, searchParams }) => {
headers,
});
}
return NextResponse.json(response, {
headers,
});
return NextResponse.json(
{
...response,
shortLink: linkConstructor({
domain: response.domain,
key: response.key,
}),
},
{
headers,
},
);
});
6 changes: 6 additions & 0 deletions apps/web/app/api/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,12 @@ export function GET(): NextResponse<OpenAPIV3.Document> {
LinkResponse: {
type: "object",
properties: {
shortLink: {
type: "string",
description:
"The full URL of the short link, including the https protocol (e.g. https://dub.sh/try).",
readOnly: true,
},
utm_source: {
type: "string",
description: "The UTM source of the short link.",
Expand Down
38 changes: 34 additions & 4 deletions apps/web/lib/api/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getParamsFromURL,
getUrlFromString,
isDubDomain,
linkConstructor,
nanoid,
truncate,
validKeyRegex,
Expand Down Expand Up @@ -42,7 +43,7 @@ export async function getLinksForProject({
userId?: string | null;
showArchived?: boolean;
}): Promise<LinkProps[]> {
return await prisma.link.findMany({
const links = await prisma.link.findMany({
where: {
projectId,
archived: showArchived ? undefined : false,
Expand Down Expand Up @@ -71,6 +72,14 @@ export async function getLinksForProject({
skip: (parseInt(page) - 1) * 100,
}),
});

return links.map((link) => ({
...link,
shortLink: linkConstructor({
domain: link.domain,
key: link.key,
}),
}));
}

export async function getLinksCount({
Expand Down Expand Up @@ -379,6 +388,9 @@ export async function processLink({
}
}

// remove shortLink attribute from payload since it's a polyfill
delete payload["shortLink"];

return {
link: {
...payload,
Expand Down Expand Up @@ -487,7 +499,13 @@ export async function addLink(link: LinkProps) {
},
});
}
return response;
return {
...response,
shortLink: linkConstructor({
domain: response.domain,
key: response.key,
}),
};
}

export async function bulkCreateLinks(links: LinkProps[]) {
Expand Down Expand Up @@ -545,7 +563,13 @@ export async function bulkCreateLinks(links: LinkProps[]) {
}),
);

return createdLinks;
return createdLinks.map((link) => ({
...link,
shortLink: linkConstructor({
domain: link?.domain,
key: link?.key,
}),
}));
}

export async function editLink({
Expand Down Expand Up @@ -657,7 +681,13 @@ export async function editLink({
});
}

return response;
return {
...response,
shortLink: linkConstructor({
domain: response.domain,
key: response.key,
}),
};
}

export async function deleteLink({
Expand Down

0 comments on commit c410a4f

Please sign in to comment.