Skip to content

Commit 76b571d

Browse files
committed
feat!: move to stable elysia version + ignore bots + safer link code updates
1 parent 2996454 commit 76b571d

File tree

5 files changed

+42
-26
lines changed

5 files changed

+42
-26
lines changed

bun.lockb

1.07 KB
Binary file not shown.

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"name": "app",
3-
"version": "1.0.50",
2+
"name": "link-manager-api",
3+
"version": "2.0.0",
44
"scripts": {
55
"dev": "bun --watch src/index.ts",
6-
"build": "bun build src/index.ts",
7-
"start": "NODE_ENV=production bun src/index.ts",
6+
"build": "bun build src/index.ts --target bun --outdir ./dist",
7+
"start": "NODE_ENV=production bun dist/index.js",
88
"test": "bun test"
99
},
1010
"dependencies": {
@@ -16,9 +16,10 @@
1616
"dotenv": "^16.4.5",
1717
"drizzle-orm": "^0.30.6",
1818
"drizzle-typebox": "^0.1.1",
19-
"elysia": "latest",
19+
"elysia": "^1.1.25",
2020
"elysia-helmet": "^1.0.2",
21-
"elysia-ip": "^1.0.3",
21+
"elysia-ip": "^1.0.7",
22+
"isbot": "^5.1.17",
2223
"nanoid": "^5.0.6",
2324
"ua-parser-js": "^1.0.37"
2425
},

src/functions/links.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import { count, desc, eq, sql, sum } from "drizzle-orm";
22
import { db } from "../db/db";
33
import { InsertLink, SelectLink, linksTable } from "../db/schema";
4+
import { generateCode } from "./utils";
45

56
export async function codeAlreadyUsed(code: SelectLink["code"]): Promise<boolean> {
67
const link = await db.select().from(linksTable).where(eq(linksTable.code, code));
78
return link.length > 0;
89
}
910

11+
export async function validateCode(code: string | undefined): Promise<string> {
12+
if (!code || code.includes("/")) {
13+
let generatedCode = "";
14+
do {
15+
generatedCode = generateCode();
16+
} while (await codeAlreadyUsed(generatedCode));
17+
return generatedCode;
18+
} else {
19+
return code;
20+
}
21+
}
22+
1023
export async function insertLink(data: InsertLink): Promise<SelectLink[]> {
1124
return await db.insert(linksTable).values(data).returning();
1225
}

src/index.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { redirectsRoutes } from "./routes/redirects";
1212
import bearer from "@elysiajs/bearer";
1313
import { demoRoutes } from "./routes/demo";
1414
import { NOT_FOUND_PAGE, PERSONAL_WEBSITE } from "./utils/constants";
15+
import { isbot } from "isbot";
1516

1617
const app = new Elysia()
1718
.use(cors())
@@ -38,51 +39,54 @@ const app = new Elysia()
3839
}
3940
})
4041
// Logging
41-
.onResponse(({ path, request }) => {
42+
.onAfterResponse(({ path, request }) => {
4243
console.log(`🦊 ${request.method} - ${path}`);
4344
})
4445
// Mesure request performance
45-
.trace(async ({ handle }) => {
46-
const { time, end } = await handle;
47-
console.log("⏳ it took", (await end) - time, "ms");
46+
.trace(async ({ onHandle }) => {
47+
onHandle(({ begin, onStop }) => {
48+
onStop(({ end }) => {
49+
console.log("⏳ it took", end - begin, "ms");
50+
});
51+
});
4852
})
4953
// Health check
5054
.get("/ping", () => "pong")
5155
// Homepage redirects to personal website
52-
.get("/", ({ set }) => (set.redirect = PERSONAL_WEBSITE))
56+
.get("/", ({ redirect }) => redirect(PERSONAL_WEBSITE))
5357
// Link redirection, where all the magic happens
5458
.get(
5559
"/:code",
56-
async ({ params, set }) => {
60+
async ({ params, set, redirect }) => {
5761
const link = await getLink(params.code);
5862

5963
if (!link) {
60-
set.redirect = `${NOT_FOUND_PAGE}?code=${params.code}`;
64+
return redirect(`${NOT_FOUND_PAGE}?code=${params.code}`);
6165
} else {
6266
set.status = 301;
63-
set.redirect = link.url;
67+
return redirect(link.url);
6468
}
6569
},
6670
{
6771
params: t.Object({
6872
code: t.String(),
6973
}),
7074
// Save analytics data after redirection, for performance reasons
71-
async onResponse({ params, ip, headers }) {
75+
async afterResponse({ params, ip, headers }) {
7276
const link = await getLink(params.code);
73-
if (!link) {
77+
if (!link || isbot(headers["user-agent"])) {
7478
return;
7579
}
7680

77-
const parser = new UAParser(headers["user-agent"]);
81+
const parserResult = new UAParser(headers["user-agent"]).getResult();
7882
const lang = headers["accept-language"]?.split(",")[0];
7983
const loc = await getIPLocation(ip);
8084
await insertRedirect({
8185
linkId: link.id,
8286
location: loc ? JSON.stringify(loc) : null,
8387
language: lang,
8488
referrer: headers["referer"],
85-
userAgent: JSON.stringify(parser.getResult()),
89+
userAgent: JSON.stringify(parserResult),
8690
});
8791
await incrementRedirects(params.code);
8892
},

src/routes/links.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import {
77
deleteLink,
88
deleteAllLinks,
99
editLink,
10+
validateCode,
1011
} from "../functions/links";
11-
import { generateCode, isValidUrl } from "../functions/utils";
12+
import { isValidUrl } from "../functions/utils";
1213

1314
export const linksRoutes = new Elysia({ prefix: "/links" })
1415
// Get all short links
@@ -23,12 +24,8 @@ export const linksRoutes = new Elysia({ prefix: "/links" })
2324
set.status = 400;
2425
return { status: 400, message: "Invalid URL" };
2526
}
26-
if (!body.code || body.code.includes("/")) {
27-
do {
28-
body.code = generateCode();
29-
} while (await codeAlreadyUsed(body.code));
30-
}
31-
const insertedLink = await insertLink(body as { code: string; url: string });
27+
const finalCode = await validateCode(body.code);
28+
const insertedLink = await insertLink({ code: finalCode, url: body.url });
3229
set.status = 201;
3330
return insertedLink;
3431
},
@@ -50,7 +47,8 @@ export const linksRoutes = new Elysia({ prefix: "/links" })
5047
set.status = 400;
5148
return { status: 400, message: "Invalid URL" };
5249
}
53-
const updatedLink = await editLink(params.code, body as { code: string; url: string });
50+
const finalCode = await validateCode(body.code);
51+
const updatedLink = await editLink(params.code, { code: finalCode, url: body.url });
5452
set.status = 200;
5553
return updatedLink;
5654
},

0 commit comments

Comments
 (0)