Skip to content

Commit

Permalink
Migrated link shortener hook
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanThatOneKid committed Sep 14, 2022
1 parent fa086b9 commit d489f23
Show file tree
Hide file tree
Showing 9 changed files with 486 additions and 30 deletions.
402 changes: 373 additions & 29 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion package.json
Expand Up @@ -30,6 +30,7 @@
"@size-limit/file": "^8.1.0",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"@types/qrcode": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"eslint": "^8.16.0",
Expand All @@ -53,5 +54,8 @@
"limit": "256 MB"
}
],
"type": "module"
"type": "module",
"dependencies": {
"qrcode": "^1.5.1"
}
}
5 changes: 5 additions & 0 deletions src/hooks.server.ts
@@ -0,0 +1,5 @@
import { sequence } from '@sveltejs/kit/hooks';
import { shortener } from '$lib/links/shortener';

/** See: <https://kit.svelte.dev/docs/hooks#server-hooks> */
export const handle = sequence(shortener());
18 changes: 18 additions & 0 deletions src/lib/links/data.ts
@@ -0,0 +1,18 @@
export const LINKS = {
github: 'https://github.com/EthanThatOneKid/acmcsuf.com',
youtube: 'https://www.youtube.com/channel/UCDMBj0o4V8Cpt0jgsZLNVVg',
instagram: 'https://instagram.com/acmcsuf',
discord: 'https://discord.gg/eKRApG627b',
linkedin: 'https://linkedin.com/company/acm-at-csuf/',
tuffyhacks: 'https://tuffyhacks.com/',
'acm-w': 'https://sites.google.com/view/acm-w',
'covid-19':
'https://docs.google.com/document/d/1aEfH50N0fmeK3JiID968DoG41jmdk3nelDipQpF6yio/edit?usp=sharing',
bug: 'https://github.com/EthanThatOneKid/acmcsuf.com/issues/new?assignees=&labels=bug&template=bug_report.yaml',
enhance:
'https://github.com/EthanThatOneKid/acmcsuf.com/issues/new?assignees=&labels=enhancement&template=feature_request.yaml',
gfi: 'https://github.com/EthanThatOneKid/acmcsuf.com/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22',
devform: 'https://forms.gle/W7zE9DEhnE8Vhpcr9',
devdeck:
'https://docs.google.com/presentation/d/1I2iuN2h1awuqtd2osNo3kYuxdsYGOEwGYC1zn9zHtvs/edit?usp=sharing',
} as const;
22 changes: 22 additions & 0 deletions src/lib/links/shortener.ts
@@ -0,0 +1,22 @@
import { LINKS } from '$lib/links/data';
import { parseLink } from '$lib/links/utils';
import type { Handle } from '@sveltejs/kit';

export function shortener(): Handle {
return async ({ event, resolve }) => {
const { pathname } = new URL(event.request.url);
const link = parseLink(pathname);
if (!link) return resolve(event);

const destination = LINKS[link];
const message = `Redirecting to ${destination}...`;
return new Response(message, {
status: 302,
headers: {
Location: destination,
'Content-Type': 'text/plain',
'Content-Length': message.length.toString(),
},
});
};
}
7 changes: 7 additions & 0 deletions src/lib/links/utils.test.ts
@@ -0,0 +1,7 @@
import { test, expect } from 'vitest';
import { parseLink } from './utils';

test('parses shortlinks', () => {
expect(parseLink('missing-link')).toBe(undefined);
expect(parseLink('GitHub')).toBe('github');
});
22 changes: 22 additions & 0 deletions src/lib/links/utils.ts
@@ -0,0 +1,22 @@
import QRCode from 'qrcode';
import { LINKS } from './data';

export function parseLink(link: string): keyof typeof LINKS | undefined {
const cleanedLinkName = link
.replace(/^\/+|\/+$/g, '')
.replace(/[^a-zA-Z0-9-/]/g, '')
.toLowerCase();

if (!(cleanedLinkName in LINKS)) return;

return cleanedLinkName as keyof typeof LINKS;
}

export function genQRCodeSvg(url: string): Promise<string> {
return new Promise((resolve, reject) => {
QRCode.toString(url, { type: 'svg' }, (err, svg: string | PromiseLike<string>) => {
if (err) reject(err);
resolve(svg);
});
});
}
11 changes: 11 additions & 0 deletions src/params/link.ts
@@ -0,0 +1,11 @@
import type { ParamMatcher } from '@sveltejs/kit';
import { parseLink } from '$lib/links/utils';

function makeLinkMatcher(): ParamMatcher {
return (param: string): boolean => !!parseLink(param);
}

export function match(param: string): boolean {
const matcher = makeLinkMatcher();
return matcher(param);
}
23 changes: 23 additions & 0 deletions src/routes/[link=link].svg/+server.ts
@@ -0,0 +1,23 @@
import type { RequestHandler } from './$types';
import { LINKS } from '$lib/links/data';
import { parseLink, genQRCodeSvg } from '$lib/links/utils';

export const GET = handler();

function handler(): RequestHandler {
return async function (event) {
const link = parseLink(event.params.link);

if (!link) {
throw new Error('Invalid link');
}

const destination = LINKS[link];
const qrCodeSvg = await genQRCodeSvg(destination);

return new Response(qrCodeSvg, {
status: 200,
headers: { 'Content-Type': 'image/svg+xml' },
});
};
}

0 comments on commit d489f23

Please sign in to comment.