Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"test": "vitest run",
"test": "vitest run && npm run build && node tests/route-smoke.mjs",
"test:unit": "vitest run",
"test:routes": "npm run build && node tests/route-smoke.mjs",
"generate:umbraco-api-client": "tsx generateUmbracoApiClient.ts"
},
"dependencies": {
Expand Down
20 changes: 20 additions & 0 deletions src/lib/mobileApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { APPLE_APP_STORE_LINK, GOOGLE_PLAY_STORE_LINK } from '#lib/config.ts';

export function getMobileAppStoreRedirect(
userAgent: string,
isTouchMac = false,
hasMicrosoftStream = false,
): string | undefined {
if (/android/i.test(userAgent)) {
return GOOGLE_PLAY_STORE_LINK;
}

if (
(/iPad|iPhone|iPod/i.test(userAgent) && !hasMicrosoftStream) ||
(/Macintosh/.test(userAgent) && isTouchMac)
) {
return APPLE_APP_STORE_LINK;
}

return undefined;
}
25 changes: 25 additions & 0 deletions src/lib/server/slashRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export function redirectToTrailingSlash(url: URL): Response | undefined {
if (url.pathname.endsWith('/')) {
return undefined;
}

return new Response(null, {
status: 308,
headers: {
location: `${url.pathname}/${url.search}`,
},
});
}

export function redirectFromTrailingSlash(url: URL): Response | undefined {
if (!url.pathname.endsWith('/')) {
return undefined;
}

return new Response(null, {
status: 308,
headers: {
location: `${url.pathname.replace(/\/$/, '')}${url.search}`,
},
});
}
22 changes: 0 additions & 22 deletions src/routes/+page.svelte

This file was deleted.

18 changes: 18 additions & 0 deletions src/routes/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { error } from '@sveltejs/kit';
import { getDefaultConference } from '#lib/server/getDefaultConference.ts';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async () => {
const conference = await getDefaultConference();

if (!conference) {
error(404);
}

return new Response(null, {
status: 307,
headers: {
location: conference.route.path ?? '/',
},
});
};
29 changes: 29 additions & 0 deletions src/routes/[conference]/speak/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { error } from '@sveltejs/kit';
import { getConference } from '#lib/server/getConference.ts';
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import { parseUrl } from '#lib/util.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = async ({ params, url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

const conference = await getConference(params.conference);
const targetUrl = parseUrl(conference?.properties.callForSpeakersUrl);

if (!targetUrl) {
error(404);
}

return new Response(null, {
status: 307,
headers: {
location: targetUrl.toString(),
},
});
};
19 changes: 19 additions & 0 deletions src/routes/[conference]/tickets/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = ({ params, url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

return new Response(null, {
status: 308,
headers: {
location: `/${params.conference}/register/`,
},
});
};
59 changes: 59 additions & 0 deletions src/routes/cms-media/[...path]/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { getUmbracoBaseUrl } from '#lib/server/config.ts';
import { redirectFromTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

const FORWARDED_REQUEST_HEADERS = [
'accept',
'range',
'if-none-match',
'if-modified-since',
];

function getMediaPath(pathname: string): string {
return pathname
.replace(/^\/cms-media\/?/, '')
.split('/')
.filter(Boolean)
.map((segment) => encodeURIComponent(decodeURIComponent(segment)))
.join('/');
}

async function proxyCmsMedia({ request, url }: Parameters<RequestHandler>[0]) {
const canonicalRedirect = redirectFromTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

const upstreamUrl = new URL(
getMediaPath(url.pathname),
`${getUmbracoBaseUrl().toString().replace(/\/$/, '')}/`,
);
upstreamUrl.search = url.search;

const headers = new Headers();

for (const headerName of FORWARDED_REQUEST_HEADERS) {
const headerValue = request.headers.get(headerName);

if (headerValue) {
headers.set(headerName, headerValue);
}
}

const response = await fetch(upstreamUrl, {
method: request.method,
headers,
});

return new Response(request.method === 'HEAD' ? null : response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
}

export const GET: RequestHandler = proxyCmsMedia;
export const HEAD: RequestHandler = proxyCmsMedia;
25 changes: 25 additions & 0 deletions src/routes/mobile-app/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { redirect } from '@sveltejs/kit';
import { getMobileAppStoreRedirect } from '#lib/mobileApp.ts';
import type { PageServerLoad } from './$types';

export const trailingSlash = 'ignore';

export const load: PageServerLoad = ({ request, url }) => {
if (!url.pathname.endsWith('/')) {
redirect(308, `${url.pathname}/${url.search}`);
}

const userAgent = request.headers.get('user-agent');

if (!userAgent) {
redirect(307, '/');
}

const appStoreUrl = getMobileAppStoreRedirect(userAgent);

if (appStoreUrl) {
redirect(307, appStoreUrl, { external: true });
}

return {};
};
31 changes: 31 additions & 0 deletions src/routes/mobile-app/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { onMount } from 'svelte';
import { getMobileAppStoreRedirect } from '#lib/mobileApp.ts';

onMount(() => {
const browserWindow = globalThis as typeof globalThis & {
MSStream?: unknown;
};
const userAgent = globalThis.navigator.userAgent;
const isTouchMac =
/Macintosh/.test(userAgent) && 'ontouchend' in globalThis.document;
const hasMicrosoftStream = Boolean(browserWindow.MSStream);
const appStoreUrl = getMobileAppStoreRedirect(
userAgent,
isTouchMac,
hasMicrosoftStream,
);

if (appStoreUrl) {
globalThis.location.href = appStoreUrl;
} else {
globalThis.location.replace('/');
}
});
</script>

<div class="content-container">
<p>
If you are not automatically redirected click <a href="/">here</a>
</p>
</div>
29 changes: 29 additions & 0 deletions src/routes/register/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { error } from '@sveltejs/kit';
import { getDefaultConference } from '#lib/server/getDefaultConference.ts';
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = async ({ url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

const conference = await getDefaultConference();

if (!conference) {
error(404);
}

const path = (conference.route.path ?? '/').replace(/\/$/, '');

return new Response(null, {
status: 308,
headers: {
location: `${path}/register/`,
},
});
};
21 changes: 21 additions & 0 deletions src/routes/robots.txt/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { isSearchIndexingEnabled } from '#lib/server/config.ts';
import { redirectFromTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = ({ url }) => {
const canonicalRedirect = redirectFromTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

const directive = isSearchIndexingEnabled() ? 'Allow' : 'Disallow';

return new Response(`User-Agent: *\n${directive}: /\n\n`, {
headers: {
'content-type': 'text/plain',
},
});
};
27 changes: 27 additions & 0 deletions src/routes/speak/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { error } from '@sveltejs/kit';
import { getDefaultConference } from '#lib/server/getDefaultConference.ts';
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = async ({ url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

const conference = await getDefaultConference();

if (!conference) {
error(404);
}

return new Response(null, {
status: 307,
headers: {
location: conference.route.path ?? '/',
},
});
};
19 changes: 19 additions & 0 deletions src/routes/tickets/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = ({ url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

return new Response(null, {
status: 307,
headers: {
location: '/register/',
},
});
};
20 changes: 20 additions & 0 deletions src/routes/umbraco/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getCmsPublicUrl } from '#lib/server/config.ts';
import { redirectToTrailingSlash } from '#lib/server/slashRedirect.ts';
import type { RequestHandler } from './$types';

export const trailingSlash = 'ignore';

export const GET: RequestHandler = ({ url }) => {
const canonicalRedirect = redirectToTrailingSlash(url);

if (canonicalRedirect) {
return canonicalRedirect;
}

return new Response(null, {
status: 307,
headers: {
location: getCmsPublicUrl().toString(),
},
});
};
Loading
Loading