mirror of
https://github.com/wahyd4/heygo.git
synced 2026-08-08 21:05:48 +10:00
fix: prevent caching private shortlink responses
This commit is contained in:
@@ -187,6 +187,16 @@ async function userSession(token: string, userId: string): Promise<SessionRow> {
|
||||
};
|
||||
}
|
||||
|
||||
function expectPrivateNoStoreHeaders(response: Response): void {
|
||||
expect(response.headers.get('cache-control')).toBe('no-store');
|
||||
expect(
|
||||
response.headers
|
||||
.get('vary')
|
||||
?.split(',')
|
||||
.map((value) => value.trim().toLowerCase()),
|
||||
).toContain('cookie');
|
||||
}
|
||||
|
||||
describe('my.heygo.cc private shortlinks', () => {
|
||||
it('returns 404 with a login link when unauthenticated', async () => {
|
||||
const { response, db } = await fetchWorker('https://my.heygo.cc/foo', {
|
||||
@@ -207,6 +217,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.headers.get('content-type')).toContain('text/html');
|
||||
expectPrivateNoStoreHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body).toContain('Login to use your private links');
|
||||
expect(body).toContain('https://heygo.cc/app/login');
|
||||
@@ -237,6 +248,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
|
||||
expect(response.status).toBe(302);
|
||||
expect(response.headers.get('location')).toBe('https://example.com/foo-a');
|
||||
expectPrivateNoStoreHeaders(response);
|
||||
expect(ctx.promises).toHaveLength(1);
|
||||
await Promise.all(ctx.promises);
|
||||
expect(db.runCalls).toHaveLength(1);
|
||||
@@ -324,6 +336,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
});
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expectPrivateNoStoreHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body).toContain('Create this private link');
|
||||
// Must never query the public scope on my.heygo.cc.
|
||||
@@ -353,6 +366,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('content-type')).toContain('text/html');
|
||||
expectPrivateNoStoreHeaders(response);
|
||||
const html = await response.text();
|
||||
expect(html).toContain('Private Note');
|
||||
expect(html).not.toContain('<script>');
|
||||
@@ -369,6 +383,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.headers.get('content-type')).toContain('text/html');
|
||||
expectPrivateNoStoreHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body).toContain('Create this private link');
|
||||
});
|
||||
@@ -376,6 +391,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
it('handles the root path: unauthenticated 404 + login, authenticated 302 to app/private', async () => {
|
||||
const unauth = await fetchWorker('https://my.heygo.cc/');
|
||||
expect(unauth.response.status).toBe(404);
|
||||
expectPrivateNoStoreHeaders(unauth.response);
|
||||
expect(await unauth.response.text()).toContain('Login to use your private links');
|
||||
|
||||
const cookie = await sessionCookie('token-a');
|
||||
@@ -386,6 +402,7 @@ describe('my.heygo.cc private shortlinks', () => {
|
||||
});
|
||||
expect(auth.response.status).toBe(302);
|
||||
expect(auth.response.headers.get('location')).toBe('https://heygo.cc/app/private');
|
||||
expectPrivateNoStoreHeaders(auth.response);
|
||||
});
|
||||
|
||||
it('does not trigger D1 private alias lookup for reserved paths', async () => {
|
||||
|
||||
+45
-6
@@ -1,10 +1,49 @@
|
||||
export function htmlResponse(body: string, init: ResponseInit = {}): Response {
|
||||
const headers = new Headers(init.headers);
|
||||
if (!headers.has('content-type')) {
|
||||
headers.set('content-type', 'text/html; charset=utf-8');
|
||||
}
|
||||
|
||||
return new Response(body, {
|
||||
...init,
|
||||
headers: {
|
||||
'content-type': 'text/html; charset=utf-8',
|
||||
...init.headers,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
function withCookieVary(headers: Headers): Headers {
|
||||
const vary = headers.get('vary');
|
||||
const varies = vary?.split(',').map((value) => value.trim().toLowerCase()) ?? [];
|
||||
if (!varies.includes('cookie')) {
|
||||
headers.set('vary', vary ? `${vary}, Cookie` : 'Cookie');
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
function privateNoStoreHeaders(headersInit?: HeadersInit): Headers {
|
||||
const headers = new Headers(headersInit);
|
||||
headers.set('cache-control', 'no-store');
|
||||
return withCookieVary(headers);
|
||||
}
|
||||
|
||||
export function withPrivateNoStoreHeaders(response: Response): Response {
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: privateNoStoreHeaders(response.headers),
|
||||
});
|
||||
}
|
||||
|
||||
export function privateHtmlResponse(body: string, init: ResponseInit = {}): Response {
|
||||
return htmlResponse(body, {
|
||||
...init,
|
||||
headers: privateNoStoreHeaders(init.headers),
|
||||
});
|
||||
}
|
||||
|
||||
export function privateRedirectResponse(location: string, status = 302): Response {
|
||||
return new Response(null, {
|
||||
status,
|
||||
headers: privateNoStoreHeaders({ location }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,14 +65,14 @@ export const PRIVATE_LOGIN_URL = 'https://heygo.cc/app/login';
|
||||
export const PRIVATE_APP_URL = 'https://heygo.cc/app/private';
|
||||
|
||||
export function privateLoginRequiredResponse(): Response {
|
||||
return htmlResponse(
|
||||
return privateHtmlResponse(
|
||||
`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Login required</title></head><body><h1>Not found</h1><p><a href="${PRIVATE_LOGIN_URL}">Login to use your private links</a>.</p></body></html>`,
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
export function privateAliasNotFoundResponse(): Response {
|
||||
return htmlResponse(
|
||||
return privateHtmlResponse(
|
||||
`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Not found</title></head><body><h1>Not found</h1><p>This private link does not exist.</p><p><a href="${PRIVATE_APP_URL}">Create this private link</a>.</p></body></html>`,
|
||||
{ status: 404 },
|
||||
);
|
||||
|
||||
@@ -4,10 +4,12 @@ import { validateAlias } from '../lib/aliases';
|
||||
import { renderCustomLinkHtml } from '../lib/custom-link';
|
||||
import {
|
||||
PRIVATE_APP_URL,
|
||||
htmlResponse,
|
||||
privateAliasNotFoundResponse,
|
||||
privateHtmlResponse,
|
||||
privateLoginRequiredResponse,
|
||||
privateRedirectResponse,
|
||||
publicBadRequestResponse,
|
||||
withPrivateNoStoreHeaders,
|
||||
} from '../lib/responses';
|
||||
import { resolveTemplateUrl, TemplateResolutionError } from '../lib/templates';
|
||||
import { parseAliasPath } from './redirect';
|
||||
@@ -45,7 +47,7 @@ export async function handlePrivateShortlink(
|
||||
if (!user) {
|
||||
return privateLoginRequiredResponse();
|
||||
}
|
||||
return Response.redirect(PRIVATE_APP_URL, 302);
|
||||
return privateRedirectResponse(PRIVATE_APP_URL, 302);
|
||||
}
|
||||
|
||||
const pathParts = parseAliasPath(url.pathname);
|
||||
@@ -84,10 +86,10 @@ export async function handlePrivateShortlink(
|
||||
query: url.searchParams,
|
||||
});
|
||||
|
||||
return Response.redirect(location, 302);
|
||||
return privateRedirectResponse(location, 302);
|
||||
} catch (error) {
|
||||
if (error instanceof TemplateResolutionError) {
|
||||
return publicBadRequestResponse(error.message);
|
||||
return withPrivateNoStoreHeaders(publicBadRequestResponse(error.message));
|
||||
}
|
||||
|
||||
throw error;
|
||||
@@ -95,7 +97,7 @@ export async function handlePrivateShortlink(
|
||||
}
|
||||
|
||||
if (link.link_type === 'custom') {
|
||||
return htmlResponse(renderCustomLinkHtml(link), { status: 200 });
|
||||
return privateHtmlResponse(renderCustomLinkHtml(link), { status: 200 });
|
||||
}
|
||||
|
||||
return privateAliasNotFoundResponse();
|
||||
|
||||
Reference in New Issue
Block a user