1
0
mirror of https://github.com/barthuijgen/factorio-sites.git synced 2025-03-06 16:16:30 +02:00

add handler wrappers for getServerSideProps are api routes to clean up code reuse

This commit is contained in:
Bart Huijgen 2021-01-17 22:15:41 +01:00
parent e3bc1c8f57
commit fe51aa2376
10 changed files with 154 additions and 157 deletions

View File

@ -1,23 +1,13 @@
import { NextApiHandler } from "next";
import {
init,
createBlueprint,
createBlueprintPage,
createBlueprintBook,
getSessionByToken,
} from "@factorio-sites/database";
import { getSessionToken, parseBlueprintString } from "@factorio-sites/node-utils";
import { parseBlueprintString } from "@factorio-sites/node-utils";
import { parseSequelizeError } from "../../../utils/api.utils";
import { apiHandler } from "../../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
await init();
const token = getSessionToken(req);
if (!token) {
return res.status(401).json({ status: "Not authenticated" });
}
const session = await getSessionByToken(token);
const handler = apiHandler(async (req, res, { session }) => {
if (!session) {
return res.status(401).json({ status: "Not authenticated" });
}
@ -66,6 +56,6 @@ const handler: NextApiHandler = async (req, res) => {
}
res.status(500).json({ status: "Failed to create blueprint" });
};
});
export default handler;

View File

@ -1,13 +1,14 @@
import { NextApiHandler } from "next";
import { init, loginUserWithEmail } from "@factorio-sites/database";
import { loginUserWithEmail } from "@factorio-sites/database";
import { setUserToken } from "@factorio-sites/node-utils";
import { apiHandler } from "../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
await init();
const handler = apiHandler(async (req, res, { session, ip, useragent }) => {
if (session) {
return res.status(400).json({ status: "Already logged in" });
}
const { email, password } = req.body;
const ip = (req.headers["x-forwarded-for"] || (req as any).ip) as string;
const useragent = req.headers["user-agent"] as string;
const user = await loginUserWithEmail({ email, password, useragent, ip });
if (user && user.session) {
@ -16,6 +17,6 @@ const handler: NextApiHandler = async (req, res) => {
} else {
res.status(401).json({ status: "Invalid email and password combination" });
}
};
});
export default handler;

View File

@ -1,22 +1,14 @@
import { NextApiHandler } from "next";
import { init, getSessionByToken } from "@factorio-sites/database";
import { deleteSessionToken, getSessionToken } from "@factorio-sites/node-utils";
import { deleteSessionToken } from "@factorio-sites/node-utils";
import { apiHandler } from "../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
await init();
const token = getSessionToken(req);
if (token) {
const session = await getSessionByToken(token);
if (session) {
await session.destroy();
}
const handler = apiHandler(async (req, res, { session }) => {
if (session) {
await session.destroy();
deleteSessionToken(res);
}
res.setHeader("Location", req.query.redirect || "/");
res.status(302).end();
};
});
export default handler;

View File

@ -1,14 +1,14 @@
import { NextApiHandler } from "next";
import { init, createUserWithEmail, createSession } from "@factorio-sites/database";
import { createUserWithEmail, createSession } from "@factorio-sites/database";
import { setUserToken } from "@factorio-sites/node-utils";
import { parseSequelizeError } from "../../utils/api.utils";
import { apiHandler } from "../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
await init();
const handler = apiHandler(async (req, res, { session, ip, useragent }) => {
if (session) {
return res.status(400).json({ status: "Already logged in" });
}
const { email, username, password, password_confirm } = req.body;
const ip = (req.headers["x-forwarded-for"] || (req as any).ip) as string;
const useragent = req.headers["user-agent"] as string;
// Validation
const errors: Record<string, string> = {};
@ -37,6 +37,6 @@ const handler: NextApiHandler = async (req, res) => {
}
res.status(401).json({ status: "Failed to register account" });
};
});
export default handler;

View File

@ -1,32 +1,21 @@
import { NextApiHandler } from "next";
import { deleteSessionToken, getSessionToken } from "@factorio-sites/node-utils";
import { init, getSessionByToken } from "@factorio-sites/database";
import { AuthContextProps } from "../../providers/auth";
import { apiHandler } from "../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
await init();
const session_token = getSessionToken(req);
if (session_token) {
const session = await getSessionByToken(session_token);
if (session) {
return res.status(200).json({
auth: {
user_id: session.user.get("id"),
username: session.user.get("username"),
email: session.user.get("email"),
steam_id: session.user.get("steam_id"),
} as AuthContextProps,
});
} else {
deleteSessionToken(res);
}
const handler = apiHandler(async (_, res, { session }) => {
if (session) {
return res.status(200).json({
auth: {
user_id: session.user.get("id"),
username: session.user.get("username"),
email: session.user.get("email"),
steam_id: session.user.get("steam_id"),
} as AuthContextProps,
});
}
res.status(404).json({
auth: null,
});
};
});
export default handler;

View File

@ -1,56 +1,45 @@
import { NextApiHandler } from "next";
import { deleteSessionToken, getSessionToken } from "@factorio-sites/node-utils";
import { init, getSessionByToken } from "@factorio-sites/database";
import { AuthContextProps } from "../../../providers/auth";
import { parseSequelizeError } from "../../../utils/api.utils";
import { apiHandler } from "../../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
const handler = apiHandler(async (req, res, { session }) => {
if (req.method !== "POST") return res.status(400).json({ error: "method must be POST" });
await init();
const { username, email } = req.body;
const session_token = getSessionToken(req);
if (session_token) {
const session = await getSessionByToken(session_token);
if (session) {
const user = session.user;
if (username) {
user.set("username", username);
}
if (email) {
user.set("email", email);
} else if (user.get("email") && user.get("steam_id")) {
// User currently has email but wants to delete it, allow if steam_id exists
user.set("email", null);
}
try {
await user.save();
} catch (reason) {
const insert_errors = parseSequelizeError(reason);
if (insert_errors) {
return res.status(400).json({ errors: insert_errors });
}
}
return res.status(200).json({
auth: {
user_id: session.user.get("id"),
username: session.user.get("username"),
email: session.user.get("email"),
steam_id: session.user.get("steam_id"),
} as AuthContextProps,
});
} else {
deleteSessionToken(res);
if (session) {
const user = session.user;
if (username) {
user.set("username", username);
}
if (email) {
user.set("email", email);
} else if (user.get("email") && user.get("steam_id")) {
// User currently has email but wants to delete it, allow if steam_id exists
user.set("email", null);
}
try {
await user.save();
} catch (reason) {
const insert_errors = parseSequelizeError(reason);
if (insert_errors) {
return res.status(400).json({ errors: insert_errors });
}
}
return res.status(200).json({
auth: {
user_id: session.user.get("id"),
username: session.user.get("username"),
email: session.user.get("email"),
steam_id: session.user.get("steam_id"),
} as AuthContextProps,
});
}
res.status(404).json({
auth: null,
});
};
});
export default handler;

View File

@ -1,45 +1,30 @@
import { NextApiHandler } from "next";
import { deleteSessionToken, getSessionToken } from "@factorio-sites/node-utils";
import {
init,
getSessionByToken,
isBlueprintPageUserFavorite,
createUserFavorite,
} from "@factorio-sites/database";
import { isBlueprintPageUserFavorite, createUserFavorite } from "@factorio-sites/database";
import { apiHandler } from "../../../utils/api-handler";
const handler: NextApiHandler = async (req, res) => {
const handler = apiHandler(async (req, res, { session }) => {
if (req.method !== "POST") return res.status(400).json({ error: "method must be POST" });
await init();
const { blueprint_page_id } = req.body;
const session_token = getSessionToken(req);
if (session) {
const user = session.user;
if (session_token) {
const session = await getSessionByToken(session_token);
if (session) {
const user = session.user;
const existing = await isBlueprintPageUserFavorite(user.id, blueprint_page_id);
const existing = await isBlueprintPageUserFavorite(user.id, blueprint_page_id);
if (existing) {
await existing.destroy();
} else {
await createUserFavorite(user.id, blueprint_page_id);
}
return res.status(200).json({
favorite: !existing,
});
if (existing) {
await existing.destroy();
} else {
deleteSessionToken(res);
await createUserFavorite(user.id, blueprint_page_id);
}
return res.status(200).json({
favorite: !existing,
});
}
res.status(404).json({
auth: null,
});
};
});
export default handler;

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { NextPage, NextPageContext } from "next";
import { NextPage } from "next";
import BBCode from "bbcode-to-react";
import { Button, Grid, Image, Box } from "@chakra-ui/react";
import {
@ -9,8 +9,6 @@ import {
getBlueprintBookById,
getBlueprintById,
getBlueprintPageById,
init,
getSessionByToken,
isBlueprintPageUserFavorite,
} from "@factorio-sites/database";
import { BlueprintStringData, timeLogger } from "@factorio-sites/common-utils";
@ -20,7 +18,8 @@ import { Markdown } from "../../components/Markdown";
import { BookChildTree } from "../../components/BookChildTree";
import { CopyButton } from "../../components/CopyButton";
import { ImageEditor } from "../../components/ImageEditor";
import { getSessionToken } from "@factorio-sites/node-utils";
import { useAuth } from "../../providers/auth";
import { pageHandler } from "../../utils/page-handler";
type Selected =
| { type: "blueprint"; data: Pick<Blueprint, "id" | "blueprint_hash" | "image_hash"> }
@ -43,6 +42,7 @@ export const Index: NextPage<IndexProps> = ({
blueprint_page,
favorite,
}) => {
const auth = useAuth();
// const [imageZoom, setImageZoom] = useState(false);
const [blueprintString, setBlueprintString] = useState<string | null>(null);
const [data, setData] = useState<BlueprintStringData | null>(null);
@ -121,11 +121,13 @@ export const Index: NextPage<IndexProps> = ({
gap={6}
>
<Panel title={blueprint_page.title} gridColumn="1">
<Box>
<Button colorScheme="green" onClick={onClickFavorite}>
Favorite ({isFavorite ? "yes" : "no"})
</Button>
</Box>
{auth && (
<Box>
<Button colorScheme="green" onClick={onClickFavorite}>
Favorite ({isFavorite ? "yes" : "no"})
</Button>
</Box>
)}
{blueprint_book ? (
<>
<div>This string contains a blueprint book </div>
@ -271,14 +273,12 @@ export const Index: NextPage<IndexProps> = ({
);
};
export async function getServerSideProps(context: NextPageContext) {
await init();
export const getServerSideProps = pageHandler(async (context, { session }) => {
const throwError = (message: string) => {
if (!blueprint_page && context.res) {
context.res.statusCode = 404;
context.res.end(JSON.stringify({ error: message }));
return {};
return { props: {} };
}
};
@ -339,14 +339,9 @@ export async function getServerSideProps(context: NextPageContext) {
// const image_exists =
// selected.type === "blueprint" ? await hasBlueprintImage(selected.data.image_hash) : false;
let favorite = false;
const session_token = getSessionToken(context.req);
if (session_token) {
const session = await getSessionByToken(session_token);
favorite = session
? !!(await isBlueprintPageUserFavorite(session.user.id, blueprint_page.id))
: false;
}
const favorite = session
? !!(await isBlueprintPageUserFavorite(session.user.id, blueprint_page.id))
: false;
return {
props: {
@ -358,6 +353,6 @@ export async function getServerSideProps(context: NextPageContext) {
favorite,
} as IndexProps,
};
}
});
export default Index;

View File

@ -0,0 +1,29 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getSessionByToken, init } from "@factorio-sites/database";
import { deleteSessionToken, getSessionToken } from "@factorio-sites/node-utils";
type Await<T> = T extends PromiseLike<infer U> ? Await<U> : T;
interface CustomContext {
session: Await<ReturnType<typeof getSessionByToken>>;
ip: string;
useragent: string;
}
export const apiHandler = (
fn: (req: NextApiRequest, res: NextApiResponse, ctx: CustomContext) => Promise<any>
) => async (req: NextApiRequest, res: NextApiResponse) => {
await init();
const ip = (req.headers["x-forwarded-for"] || (req as any).ip) as string;
const useragent = req.headers["user-agent"] as string;
const session_token = getSessionToken(req);
const session = session_token ? await getSessionByToken(session_token) : null;
if (session_token && !session) {
deleteSessionToken(res);
}
return fn(req, res, { session, ip, useragent });
};

View File

@ -0,0 +1,27 @@
import { NextPageContext } from "next";
import { getSessionByToken, init } from "@factorio-sites/database";
import { getSessionToken } from "@factorio-sites/node-utils";
interface GetServerSidePropsReturn {
props: Record<string, any>;
}
type Await<T> = T extends PromiseLike<infer U> ? Await<U> : T;
interface CustomContext {
session: Await<ReturnType<typeof getSessionByToken>>;
}
export const pageHandler = (
fn: (
context: NextPageContext,
ctx: CustomContext
) => Promise<GetServerSidePropsReturn | undefined>
) => async (context: NextPageContext) => {
await init();
const session_token = getSessionToken(context.req);
const session = session_token ? await getSessionByToken(session_token) : null;
return fn(context, { session });
};