Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: redirect() #163

Merged
merged 6 commits into from
May 9, 2023
Merged
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
17 changes: 5 additions & 12 deletions routes/account/display-name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type User,
} from "@/utils/db.ts";
import type { AccountState } from "./_middleware.ts";
import { redirect } from "@/utils/http.ts";

interface DisplayNamePageData {
user: User;
Expand All @@ -37,19 +38,11 @@ export const handler: Handlers<DisplayNamePageData, AccountState> = {
}

await setUserDisplayName(ctx.state.session.user.id, displayName);
return new Response(null, {
headers: { location: "/account" },
status: 303,
});
return redirect("/account");
} catch (error) {
return new Response(null, {
headers: {
location: `/account/display-name?error=${
encodeURIComponent(error.message)
}`,
},
status: 303,
});
return redirect(
`/account/display-name?error=${encodeURIComponent(error.message)}`,
);
}
},
};
Expand Down
8 changes: 2 additions & 6 deletions routes/account/manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Handlers } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import type { AccountState } from "./_middleware.ts";
import { getOrCreateUser } from "@/utils/db.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, AccountState> = {
Expand All @@ -17,11 +18,6 @@ export const handler: Handlers<any, AccountState> = {
return_url: new URL(req.url).origin + "/account",
});

return new Response(null, {
headers: {
location: url,
},
status: 302,
});
return redirect(url);
},
};
6 changes: 2 additions & 4 deletions routes/account/reset-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
INPUT_STYLES,
NOTICE_STYLES,
} from "@/utils/constants.ts";
import { redirect } from "@/utils/http.ts";

export const handler: Handlers = {
async GET(request, ctx) {
Expand All @@ -16,10 +17,7 @@ export const handler: Handlers = {
.auth.getSession();

if (error) {
return new Response(null, {
headers: { location: `/reset-password?error=${error.message}` },
status: 302,
});
return redirect(`/reset-password?error=${error.message}`);
}

return ctx.render();
Expand Down
8 changes: 2 additions & 6 deletions routes/account/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Handlers } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import type { AccountState } from "./_middleware.ts";
import { getOrCreateUser } from "@/utils/db.ts";
import { redirect } from "@/utils/http.ts";

export const handler: Handlers<null, AccountState> = {
async GET(req, ctx) {
Expand All @@ -27,11 +28,6 @@ export const handler: Handlers<null, AccountState> = {
mode: "subscription",
});

return new Response(null, {
headers: {
location: url!,
},
status: 302,
});
return redirect(url);
},
};
17 changes: 5 additions & 12 deletions routes/forgot-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
NOTICE_STYLES,
} from "@/utils/constants.ts";
import type { State } from "./_middleware.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, State> = {
Expand All @@ -23,20 +24,12 @@ export const handler: Handlers<any, State> = {
);

if (error) {
return new Response(null, {
headers: {
location: `/forgot-password?error=${
encodeURIComponent(error.message)
}`,
},
status: 302,
});
return redirect(
`/forgot-password?error=${encodeURIComponent(error.message)}`,
);
}

return new Response(null, {
headers: { location: "/forgot-password?success=1" },
status: 302,
});
return redirect("/forgot-password?success=1");
},
};

Expand Down
15 changes: 4 additions & 11 deletions routes/item/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
type Item,
type User,
} from "@/utils/db.ts";
import { redirect } from "@/utils/http.ts";

interface ItemPageData extends State {
user: User;
Expand Down Expand Up @@ -62,13 +63,8 @@ export const handler: Handlers<ItemPageData, State> = {
},
async POST(req, ctx) {
if (!ctx.state.session) {
return new Response(null, {
headers: {
/** @todo Figure out `redirect_to` query */
location: "/login",
},
status: 302,
});
/** @todo Figure out `redirect_to` query */
return redirect("/login");
}

const form = await req.formData();
Expand All @@ -84,10 +80,7 @@ export const handler: Handlers<ItemPageData, State> = {
text,
});

return new Response(null, {
headers: { location: `/item/${ctx.params.id}` },
status: 302,
});
return redirect(`/item/${ctx.params.id}`);
},
};

Expand Down
14 changes: 3 additions & 11 deletions routes/login/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { NOTICE_STYLES } from "@/utils/constants.ts";
import { REDIRECT_PATH_AFTER_LOGIN } from "@/utils/constants.ts";
import type { State } from "@/routes/_middleware.ts";
import { BUTTON_STYLES, INPUT_STYLES } from "@/utils/constants.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, State> = {
Expand All @@ -19,13 +20,7 @@ export const handler: Handlers<any, State> = {
.getSession();

if (session) {
return new Response(null, {
headers: {
location: "/",
},
/** @todo Confirm whether this HTTP redirect status code is correct */
status: 302,
});
return redirect("/");
}

return ctx.render();
Expand All @@ -44,10 +39,7 @@ export const handler: Handlers<any, State> = {
redirectUrl = `/login?error=${encodeURIComponent(error.message)}`;
}

return new Response(null, {
headers: { location: redirectUrl },
status: 302,
});
return redirect(redirectUrl);
},
};

Expand Down
3 changes: 2 additions & 1 deletion routes/login/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { Handlers } from "$fresh/server.ts";
import type { Provider } from "@supabase/supabase-js";
import { State } from "@/routes/_middleware.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, State> = {
Expand All @@ -25,6 +26,6 @@ export const handler: Handlers<any, State> = {

if (error) throw error;

return new Response(null, { headers: { location: data.url }, status: 302 });
return redirect(data.url);
},
};
3 changes: 2 additions & 1 deletion routes/logout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { Handlers } from "$fresh/server.ts";
import { redirect } from "@/utils/http.ts";
import type { State } from "./_middleware.ts";

// deno-lint-ignore no-explicit-any
Expand All @@ -8,6 +9,6 @@ export const handler: Handlers<any, State> = {
const { error } = await ctx.state.supabaseClient.auth.signOut();
if (error) throw error;

return new Response(null, { headers: { location: "/" }, status: 302 });
return redirect("/");
},
};
14 changes: 4 additions & 10 deletions routes/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { State } from "./_middleware.ts";
import { stripe } from "@/utils/payments.ts";
import { createUser } from "@/utils/db.ts";
import { BUTTON_STYLES, INPUT_STYLES } from "@/utils/constants.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, State> = {
Expand All @@ -23,12 +24,7 @@ export const handler: Handlers<any, State> = {
.auth.signUp({ email, password });

if (error) {
return new Response(null, {
headers: {
location: `/signup?error=${encodeURIComponent(error.message)}`,
},
status: 302,
});
return redirect(`/signup?error=${encodeURIComponent(error.message)}`);
}

const { id } = await stripe.customers.create({ email });
Expand All @@ -40,10 +36,8 @@ export const handler: Handlers<any, State> = {

const redirectUrl = new URL(req.url).searchParams.get("redirect_url") ??
REDIRECT_PATH_AFTER_LOGIN;
return new Response(null, {
headers: { location: redirectUrl },
status: 302,
});

return redirect(redirectUrl);
},
};

Expand Down
14 changes: 3 additions & 11 deletions routes/submit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@ import Layout from "@/components/Layout.tsx";
import { BUTTON_STYLES, INPUT_STYLES } from "@/utils/constants.ts";
import type { State } from "@/routes/_middleware.ts";
import { createItem } from "@/utils/db.ts";
import { redirect } from "@/utils/http.ts";

export const handler: Handlers<State, State> = {
GET(req, ctx) {
if (!ctx.state.session) {
return new Response(null, {
headers: {
location: `/login?redirect_url=${encodeURIComponent(req.url)}`,
},
/** @todo Confirm whether this HTTP redirect status code is correct */
status: 302,
});
return redirect(`/login?redirect_url=${encodeURIComponent(req.url)}`);
}

return ctx.render(ctx.state);
Expand Down Expand Up @@ -47,10 +42,7 @@ export const handler: Handlers<State, State> = {
url,
});

return new Response(null, {
headers: { location: `/item/${item!.id}` },
status: 302,
});
return redirect(`/item/${item!.id}`);
},
};

Expand Down
9 changes: 2 additions & 7 deletions utils/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { MiddlewareHandlerContext } from "$fresh/server.ts";

import { createServerSupabaseClient } from "@supabase/auth-helpers-shared";
import { getCookies, setCookie } from "std/http/cookie.ts";
import { redirect } from "./http.ts";

export type SupabaseClient = ReturnType<typeof createSupabaseClient>;

Expand Down Expand Up @@ -37,13 +38,7 @@ export async function ensureLoggedInMiddleware(
ctx: MiddlewareHandlerContext,
) {
if (!ctx.state.session) {
return new Response(null, {
headers: {
location: `/login?redirect_url=${encodeURIComponent(req.url)}`,
},
/** @todo Confirm whether this HTTP redirect status code is correct */
status: 302,
});
return redirect(`/login?redirect_url=${encodeURIComponent(req.url)}`);
}

return await ctx.next();
Expand Down
23 changes: 23 additions & 0 deletions utils/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { RedirectStatus, Status } from "std/http/http_status.ts";

/**
* This function is used rather than `Response.redirect()`.
* `Response.redirect()` will throw if `Response.headers` is modified.
* `Response.headers` is modified when setting session cookies for Supabase Auth.
*
* @param location A relative (to the request URL) or absolute URL.
* @param status HTTP status
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location}
*/
export function redirect(
location: string,
status: Status.Created | RedirectStatus = 303,
) {
return new Response(null, {
headers: {
location,
},
status,
});
}
24 changes: 24 additions & 0 deletions utils/http_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import { redirect } from "./http.ts";
import { assert, assertEquals } from "std/testing/asserts.ts";

Deno.test("[http] redirect() defaults", () => {
const location = "/hello-there";

const response = redirect(location);
assert(!response.ok);
assertEquals(response.body, null);
assertEquals(response.headers.get("location"), location);
assertEquals(response.status, 303);
});

Deno.test("[http] redirect()", () => {
const location = "/hello-there";
const status = 302;

const response = redirect(location, status);
assert(!response.ok);
assertEquals(response.body, null);
assertEquals(response.headers.get("location"), location);
assertEquals(response.status, status);
});