1
0
mirror of https://github.com/barthuijgen/factorio-sites.git synced 2025-02-08 14:28:29 +02:00

Fix leftover errors from prisma migration

This commit is contained in:
Bart Huijgen 2021-03-05 00:32:00 +01:00
parent a42927f336
commit 7e77230fac
21 changed files with 159 additions and 92 deletions

1
.gitignore vendored
View File

@ -43,5 +43,6 @@ Thumbs.db
/credentials
.env.local
.local.env
.env
local.readme.md
.yalc

View File

@ -1,4 +1,17 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
module.exports = {
poweredByHeader: false,
reactStrictMode: true,
webpack(config, options) {
const { dev, isServer } = options;
// Do not run type checking twice:
if (dev && isServer) {
config.plugins.push(new ForkTsCheckerWebpackPlugin());
}
return config;
},
};

View File

@ -72,7 +72,7 @@ model blueprint_page {
model session {
id String @id @default(uuid()) @db.Uuid
user_id String @db.Uuid
session_token String @unique @db.VarChar(255)
session_token String @unique @default(uuid()) @db.Uuid
useragent String @db.VarChar(255)
ip String @db.VarChar(255)
last_used DateTime

View File

@ -1,11 +1,13 @@
import React from 'react';
import { render } from '@testing-library/react';
import React from "react";
import { render } from "@testing-library/react";
import Index from '../pages/index';
import Index from "../src/pages/index";
describe('Index', () => {
it('should render successfully', () => {
const { baseElement } = render(<Index />);
describe("Index", () => {
it("should render successfully", () => {
const { baseElement } = render(
<Index totalItems={0} currentPage={0} totalPages={0} blueprints={[]} />
);
expect(baseElement).toBeTruthy();
});
});

View File

@ -39,7 +39,7 @@ export const ImageEditor: React.FC<{ string: string }> = ({ string }) => {
if (!editorLoaded || !FBE || !editor) return;
const bpOrBook = await FBE.getBlueprintOrBookFromSource(string);
const blueprint = bpOrBook instanceof FBE.Book ? bpOrBook.getBlueprint() : bpOrBook;
const blueprint = bpOrBook instanceof FBE.Book ? bpOrBook.selectBlueprint(0) : bpOrBook;
console.log({ blueprint });

View File

@ -67,8 +67,8 @@ export const Index: NextPage = () => {
<li>Add blueprint tags</li>
<li>Expand search on tags</li>
<li>Add tracking of blueprint views</li>
<li>Add favorites</li>
<li>Add sorting by views/favorites</li>
<li css={{ textDecoration: "line-through" }}>Add favorites</li>
<li css={{ textDecoration: "line-through" }}>Add sorting by views/favorites</li>
<li>Add thumbnails</li>
<li>Add tag fixing moderators</li>
<li>Improve modded blueprint support</li>

View File

@ -4,7 +4,7 @@ import {
createBlueprintBook,
} from "@factorio-sites/database";
import { parseBlueprintString } from "@factorio-sites/node-utils";
import { parseSequelizeError } from "../../../utils/api.utils";
import { parseDatabaseError } from "../../../utils/api.utils";
import { apiHandler } from "../../../utils/api-handler";
const handler = apiHandler(async (req, res, { session }) => {
@ -46,7 +46,7 @@ const handler = apiHandler(async (req, res, { session }) => {
return res.status(201).json({ success: true, id: page.id });
}
} catch (reason) {
const insert_errors = parseSequelizeError(reason);
const insert_errors = parseDatabaseError(reason);
if (insert_errors) {
if (insert_errors.blueprint_id || insert_errors.blueprint_book_id) {
insert_errors.string = "This string already exists";

View File

@ -1,15 +1,12 @@
import { NextApiHandler } from "next";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { init, BlueprintModel } from "@factorio-sites/database";
import { init, prisma } from "@factorio-sites/database";
const handler: NextApiHandler = async (_, res) => {
await init();
if (process.env.NODE_ENV === "development") {
const bp = await BlueprintModel()
.findByPk("uuid")
// eslint-disable-next-line @typescript-eslint/no-empty-function
.catch(() => {});
const bp = await prisma.blueprint.findFirst({ where: { id: "uuid" } });
console.log({ bp });
}

View File

@ -1,9 +1,12 @@
import { prisma } from "@factorio-sites/database";
import { deleteSessionToken } from "@factorio-sites/node-utils";
import { apiHandler } from "../../utils/api-handler";
const handler = apiHandler(async (req, res, { session }) => {
if (session) {
await session.destroy();
await prisma.session.delete({
where: { id: session.id },
});
deleteSessionToken(res);
}

View File

@ -1,6 +1,6 @@
import { createUserWithEmail, createSession } from "@factorio-sites/database";
import { setUserToken } from "@factorio-sites/node-utils";
import { parseSequelizeError } from "../../utils/api.utils";
import { parseDatabaseError } from "../../utils/api.utils";
import { apiHandler } from "../../utils/api-handler";
const handler = apiHandler(async (req, res, { session, ip, useragent }) => {
@ -30,7 +30,7 @@ const handler = apiHandler(async (req, res, { session, ip, useragent }) => {
setUserToken(res, session.session_token);
return res.status(201).json({ success: true });
} catch (reason) {
const insert_errors = parseSequelizeError(reason);
const insert_errors = parseDatabaseError(reason);
if (insert_errors) {
return res.status(400).json({ errors: insert_errors });
}

View File

@ -1,6 +1,8 @@
import { AuthContextProps } from "../../../providers/auth";
import { parseSequelizeError } from "../../../utils/api.utils";
import { parseDatabaseError } from "../../../utils/api.utils";
import { apiHandler } from "../../../utils/api-handler";
import { user } from "@prisma/client";
import { prisma } from "@factorio-sites/database";
const handler = apiHandler(async (req, res, { session }) => {
if (req.method !== "POST") return res.status(400).json({ error: "method must be POST" });
@ -8,20 +10,23 @@ const handler = apiHandler(async (req, res, { session }) => {
const { username, email } = req.body;
if (session) {
const user = session.user;
let user = session.user;
const update: Partial<user> = {};
if (username) {
user.set("username", username);
update.username = username;
}
if (email) {
user.set("email", email);
} else if (user.get("email") && user.get("steam_id")) {
update.email = email;
} else if (user.email && user.steam_id) {
// User currently has email but wants to delete it, allow if steam_id exists
user.set("email", null);
update.email = null;
}
try {
await user.save();
user = await prisma.user.update({ where: { id: user.id }, data: update });
} catch (reason) {
const insert_errors = parseSequelizeError(reason);
const insert_errors = parseDatabaseError(reason);
if (insert_errors) {
return res.status(400).json({ errors: insert_errors });
}
@ -29,10 +34,10 @@ const handler = apiHandler(async (req, res, { 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"),
user_id: user.id,
username: user.username,
email: user.email,
steam_id: user.steam_id,
} as AuthContextProps,
});
}

View File

@ -12,7 +12,7 @@ const handler = apiHandler(async (req, res, { session }) => {
const existing = await isBlueprintPageUserFavorite(user.id, blueprint_page_id);
if (existing) {
await prisma().user_favorites.delete({
await prisma.user_favorites.delete({
where: {
user_id_blueprint_page_id: {
user_id: existing.user_id,

View File

@ -28,7 +28,7 @@ export const UserBlueprintCreate: NextPage = () => {
const auth = useAuth();
const router = useRouter();
if (!auth) {
if (!auth && typeof window !== "undefined") {
router.push("/");
}

View File

@ -1,18 +1,12 @@
import { ValidationErrorItem } from "sequelize/types";
export const parseSequelizeError = (reason: any): Record<string, string> | null => {
export const parseDatabaseError = (reason: any): Record<string, string> | null => {
const errors: Record<string, string> = {};
console.log(reason);
if (Array.isArray(reason.errors)) {
reason.errors.forEach((error: ValidationErrorItem) => {
if (error.type === "unique violation") {
errors[error.path] = `${error.path} is alraedy taken.`;
}
if (reason.code === "P2002") {
reason.meta.target.forEach((field: string) => {
errors[field] = `${field} is already taken`;
});
if (Object.keys(errors).length) {
return errors;
}
}
return null;
return Object.keys(errors).length ? errors : null;
};

View File

@ -20,12 +20,12 @@ const mapBlueprintInstanceToEntry = (entity: BlueprintModel): Blueprint => ({
});
export async function getBlueprintById(id: string): Promise<Blueprint | null> {
const result = await prisma().blueprint.findUnique({ where: { id } });
const result = await prisma.blueprint.findUnique({ where: { id } });
return result ? mapBlueprintInstanceToEntry(result) : null;
}
export async function getBlueprintByHash(hash: string): Promise<Blueprint | null> {
const result = await prisma().blueprint.findUnique({ where: { blueprint_hash: hash } });
const result = await prisma.blueprint.findUnique({ where: { blueprint_hash: hash } });
return result ? mapBlueprintInstanceToEntry(result) : null;
}
@ -51,7 +51,7 @@ export async function createBlueprint(
// Write blueprint details to datastore
const result = await prisma().blueprint.create({
const result = await prisma.blueprint.create({
data: {
label: blueprint.label,
description: blueprint.description,

View File

@ -18,12 +18,12 @@ const mapBlueprintBookEntityToObject = (entity: blueprint_book): BlueprintBook =
});
export async function getBlueprintBookById(id: string): Promise<BlueprintBook | null> {
const result = await prisma().blueprint_book.findUnique({ where: { id } });
const result = await prisma.blueprint_book.findUnique({ where: { id } });
return result ? mapBlueprintBookEntityToObject(result) : null;
}
export async function getBlueprintBookByHash(hash: string): Promise<BlueprintBook | null> {
const result = await prisma().blueprint_book.findUnique({ where: { blueprint_hash: hash } });
const result = await prisma.blueprint_book.findUnique({ where: { blueprint_hash: hash } });
return result ? mapBlueprintBookEntityToObject(result) : null;
}
@ -75,7 +75,7 @@ export async function createBlueprintBook(
}
}
const result = await prisma().blueprint_book.create({
const result = await prisma.blueprint_book.create({
data: {
label: blueprintBook.label,
description: blueprintBook.description,

View File

@ -15,19 +15,19 @@ const mapBlueprintPageEntityToObject = (entity: blueprint_page): BlueprintPage =
});
export async function getBlueprintPageById(id: string): Promise<BlueprintPage | null> {
const result = await prisma().blueprint_page.findUnique({ where: { id } });
const result = await prisma.blueprint_page.findUnique({ where: { id } });
return result ? mapBlueprintPageEntityToObject(result) : null;
}
export async function getBlueprintPageByUserId(user_id: string): Promise<BlueprintPage[] | null> {
const results = await prisma().blueprint_page.findMany({ where: { user_id } });
const results = await prisma.blueprint_page.findMany({ where: { user_id } });
return results ? results.map((result) => mapBlueprintPageEntityToObject(result)) : null;
}
export async function getBlueprintPageByFactorioprintsId(
id: string
): Promise<BlueprintPage | null> {
const result = await prisma().blueprint_page.findFirst({ where: { factorioprints_id: id } });
const result = await prisma.blueprint_page.findFirst({ where: { factorioprints_id: id } });
return result ? mapBlueprintPageEntityToObject(result) : null;
}
@ -47,7 +47,7 @@ export async function getMostRecentBlueprintPages({
favorites: "favorite_count",
};
const result = (
await prisma().$queryRaw<(blueprint_page & { favorite_count: number })[]>(
await prisma.$queryRaw<(blueprint_page & { favorite_count: number })[]>(
`SELECT *, (SELECT COUNT(*) FROM user_favorites where user_favorites.blueprint_page_id = blueprint_page.id) AS favorite_count
FROM public.blueprint_page
WHERE blueprint_page.title ILIKE $1
@ -82,7 +82,7 @@ export async function createBlueprintPage(
factorioprints_id?: string;
}
) {
const page = await prisma().blueprint_page.create({
const page = await prisma.blueprint_page.create({
data: {
user_id: extraInfo.user_id,
title: extraInfo.title,

View File

@ -1,10 +1,9 @@
import * as bcrypt from "bcrypt";
import { session, user } from "@prisma/client";
import { prisma } from "../postgres/database";
import { SessionInstance } from "../postgres/models/Session";
import { UserInstance } from "../postgres/models/User";
export const getUserById = async (id: string) => {
const user = await prisma().user.findUnique({ where: { id } });
const user = await prisma.user.findUnique({ where: { id } });
return user ? user : null;
};
@ -15,7 +14,7 @@ export const createUserWithEmail = async (
ip: string
) => {
const hash = await bcrypt.hash(password, 10);
return prisma().user.create({
return prisma.user.create({
data: {
email,
username,
@ -27,7 +26,7 @@ export const createUserWithEmail = async (
};
export const createUserWithSteam = async (steam_id: string, username: string, ip: string) => {
return prisma().user.create({
return prisma.user.create({
data: {
steam_id,
username,
@ -46,30 +45,29 @@ export const loginUserWithEmail = async ({
password: string;
useragent: string;
ip: string;
}): Promise<(UserInstance & { session: SessionInstance }) | null> => {
const user = await prisma().user.findUnique({ where: { email } });
if (!user) {
}): Promise<(user & { session: session }) | null> => {
const user = await prisma.user.findUnique({ where: { email } });
if (!user || !user.password) {
return null;
}
const password_valid = await bcrypt.compare(password, user.password);
if (password_valid) {
await user.update({
last_login_ip: ip,
last_login_at: new Date(),
await prisma.user.update({
where: { id: user.id },
data: { last_login_ip: ip, last_login_at: new Date() },
});
const session = await createSession(user, useragent, ip);
(user as any).session = session;
return user as any;
return Object.assign(user, { session });
}
return null;
};
export const loginUserWithSteam = async (steam_id: string, ip: string) => {
const user = await prisma().user.findUnique({ where: { steam_id } });
const user = await prisma.user.findUnique({ where: { steam_id } });
if (!user) {
return false;
}
await prisma().user.update({
await prisma.user.update({
where: { steam_id },
data: {
last_login_ip: ip,
@ -79,9 +77,10 @@ export const loginUserWithSteam = async (steam_id: string, ip: string) => {
return user;
};
export const createSession = async (user: UserInstance, useragent: string, ip: string) => {
return prisma().session.create({
datta: {
export const createSession = async (user: user, useragent: string, ip: string) => {
return prisma.session.create({
data: {
last_used: new Date(),
user_id: user.id,
useragent,
ip,
@ -90,7 +89,7 @@ export const createSession = async (user: UserInstance, useragent: string, ip: s
};
export const getSessionByToken = async (token: string) => {
return await prisma().session.findUnique({
return await prisma.session.findUnique({
where: { session_token: token },
include: {
user: true,
@ -99,12 +98,12 @@ export const getSessionByToken = async (token: string) => {
};
export const isBlueprintPageUserFavorite = async (user_id: string, blueprint_page_id: string) => {
const { user_favorites } = prisma();
const { user_favorites } = prisma;
return user_favorites.findFirst({ where: { user_id, blueprint_page_id } });
};
export const createUserFavorite = async (user_id: string, blueprint_page_id: string) => {
const { user_favorites } = prisma();
const { user_favorites } = prisma;
return user_favorites.create({
data: {
user_id,

View File

@ -1,13 +1,15 @@
import { PrismaClient } from "@prisma/client";
import { getEnvOrThrow, getSecretOrThrow } from "../env.util";
export let prisma: PrismaClient;
const _init = async () => {
if (_prisma) return _prisma;
if (prisma) return prisma;
const databasePassword = getEnvOrThrow("POSTGRES_PASSWORD");
const prisma = new PrismaClient({
log: ["query", "info", "warn", "error"],
const prismaClient = new PrismaClient({
log: ["warn", "error"],
datasources: {
db: {
url: `postgresql://${getEnvOrThrow("POSTGRES_USER")}:${await getSecretOrThrow(
@ -17,24 +19,20 @@ const _init = async () => {
},
});
await prisma.$connect();
// const x = await prismaClient.user.create({ data: { username: 1 } });
return prisma;
await prismaClient.$connect();
return prismaClient;
};
const promise = _init()
.then((result) => {
_prisma = result;
prisma = result;
return result;
})
.catch((reason) => {
console.log("Database failed to init!", reason);
});
let _prisma: PrismaClient;
export const init = async () => {
return promise;
};
export const prisma = () => _prisma;
export const init = async () => promise;

View File

@ -102,6 +102,7 @@
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"fork-ts-checker-webpack-plugin": "6.1.1",
"jest": "26.6.3",
"prettier": "2.2.1",
"prisma": "2.18.0",

View File

@ -4383,6 +4383,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
"@types/json-schema@^7.0.4":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@ -6183,7 +6188,7 @@ check-more-types@^2.24.0:
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=
chokidar@3.5.1:
chokidar@3.5.1, chokidar@^3.4.2:
version "3.5.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
@ -8654,6 +8659,24 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
fork-ts-checker-webpack-plugin@6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.1.1.tgz#c6c3b6506bfb0c7b645cd5c377e82e670d7d71c9"
integrity sha512-H8cjLmIxbnAUgxhPOqCqx1Ji3mVHnhGDnKxORZIkkkSsZLJF2IIEUc/+bywPXcWfKSR9K2zJtknRlreCWwGv0Q==
dependencies:
"@babel/code-frame" "^7.8.3"
"@types/json-schema" "^7.0.5"
chalk "^4.1.0"
chokidar "^3.4.2"
cosmiconfig "^6.0.0"
deepmerge "^4.2.2"
fs-extra "^9.0.0"
memfs "^3.1.2"
minimatch "^3.0.4"
schema-utils "2.7.0"
semver "^7.3.2"
tapable "^1.0.0"
fork-ts-checker-webpack-plugin@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19"
@ -8756,6 +8779,16 @@ fs-extra@8.1.0, fs-extra@^8.1.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^9.0.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
@ -8780,6 +8813,11 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0:
dependencies:
minipass "^3.0.0"
fs-monkey@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.1.tgz#4a82f36944365e619f4454d9fff106553067b781"
integrity sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@ -11399,6 +11437,13 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
memfs@^3.1.2:
version "3.2.0"
resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.0.tgz#f9438e622b5acd1daa8a4ae160c496fdd1325b26"
integrity sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==
dependencies:
fs-monkey "1.0.1"
memory-fs@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@ -14605,6 +14650,15 @@ scheduler@^0.20.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
dependencies:
"@types/json-schema" "^7.0.4"
ajv "^6.12.2"
ajv-keywords "^3.4.1"
schema-utils@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"