feat(wip): create models to access db data

This commit is contained in:
Sonny
2023-05-31 19:27:48 +02:00
parent e7e7e0c950
commit a89fa471e0
19 changed files with 213 additions and 88 deletions

40
package-lock.json generated
View File

@@ -22,7 +22,8 @@
"react-select": "^5.7.3", "react-select": "^5.7.3",
"sass": "^1.62.1", "sass": "^1.62.1",
"sharp": "^0.32.1", "sharp": "^0.32.1",
"toastr": "^2.1.4" "toastr": "^2.1.4",
"yup": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.2.4", "@types/node": "^20.2.4",
@@ -6005,6 +6006,11 @@
"react-is": "^16.13.1" "react-is": "^16.13.1"
} }
}, },
"node_modules/property-expr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -6811,6 +6817,11 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true "dev": true
}, },
"node_modules/tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
},
"node_modules/tiny-glob": { "node_modules/tiny-glob": {
"version": "0.2.9", "version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
@@ -6848,6 +6859,11 @@
"jquery": ">=1.12.0" "jquery": ">=1.12.0"
} }
}, },
"node_modules/toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
},
"node_modules/tsconfig-paths": { "node_modules/tsconfig-paths": {
"version": "3.14.1", "version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@@ -7161,6 +7177,28 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/yup": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz",
"integrity": "sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==",
"dependencies": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
}
},
"node_modules/yup/node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": { "node_modules/zod": {
"version": "3.21.4", "version": "3.21.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",

View File

@@ -24,7 +24,8 @@
"react-select": "^5.7.3", "react-select": "^5.7.3",
"sass": "^1.62.1", "sass": "^1.62.1",
"sharp": "^0.32.1", "sharp": "^0.32.1",
"toastr": "^2.1.4" "toastr": "^2.1.4",
"yup": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.2.4", "@types/node": "^20.2.4",

View File

@@ -0,0 +1,17 @@
import { User } from "@prisma/client";
import prisma from "utils/prisma";
export default async function getUserCategories(user: User) {
return await prisma.category.findMany({
where: {
authorId: user.id,
},
include: {
links: {
where: {
authorId: user.id,
},
},
},
});
}

View File

@@ -0,0 +1,14 @@
import { Link, User } from "@prisma/client";
import prisma from "utils/prisma";
export default async function getUserLink(user: User, id: Link["id"]) {
return await prisma.link.findFirst({
where: {
id,
authorId: user.id,
},
include: {
category: true,
},
});
}

View File

@@ -0,0 +1,10 @@
import { User } from "@prisma/client";
import prisma from "utils/prisma";
export default async function getUserLinks(user: User) {
return await prisma.link.findMany({
where: {
authorId: user.id,
},
});
}

View File

@@ -0,0 +1,10 @@
import { Session } from "next-auth";
import prisma from "utils/prisma";
export default async function getUserOrThrow(session: Session) {
return await prisma.user.findFirstOrThrow({
where: {
email: session?.user?.email,
},
});
}

View File

@@ -33,6 +33,7 @@ export const authOptions = {
"[AUTH]", "[AUTH]",
"User", "User",
profile.name, profile.name,
profile.sub,
"attempt to log in with", "attempt to log in with",
accountParam.provider accountParam.provider
); );

View File

@@ -1,64 +1,40 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
import { checkMethodAllowedOrThrow } from "utils/api";
import prisma from "utils/prisma"; import prisma from "utils/prisma";
import { getSessionOrThrow } from "utils/session";
export default async function POST(req: NextApiRequest, res: NextApiResponse) {
await checkMethodAllowedOrThrow(req, ["post"]);
const session = await getSessionOrThrow(req, res);
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions); // je sais plus d'où ça vient
console.log("session", session);
const name = req.body?.name as string; const name = req.body?.name as string;
if (!name) { if (!name) {
return res.status(400).send({ error: "Nom de catégorie manquant" }); throw new Error("Categorie name missing");
} }
try { const category = await prisma.category.findFirst({
const category = await prisma.category.findFirst({ where: { name },
where: { name }, });
}); if (category) {
throw new Error("Category name already used");
if (category) {
return res
.status(400)
.send({ error: "Une catégorie avec ce nom existe déjà" });
}
} catch (error) {
console.error(error);
return res.status(400).send({
error:
"Une erreur est survenue lors de la création de la catégorie (category/create->findCategory)",
});
} }
try { const { id: authorId } = await prisma.user.findFirstOrThrow({
const { id: authorId } = await prisma.user.findFirst({ where: {
where: { email: session.user.email,
email: session.user.email, },
}, select: {
select: { id: true,
id: true, },
}, });
});
if (!authorId) {
throw new Error("Unable to find user");
}
const category = await prisma.category.create({ const categoryCreated = await prisma.category.create({
data: { name, authorId }, data: { name, authorId },
}); });
return res.status(200).send({ console.log("là");
success: "Catégorie créée avec succès", return res.status(200).send({
categoryId: category.id, success: "Category successfully created",
}); categoryId: categoryCreated.id,
} catch (error) { });
console.error(error);
return res.status(400).send({
error:
"Une erreur est survenue lors de la création de la catégorie (category/create->createCategory)",
});
}
} }

View File

@@ -1,10 +1,19 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
import prisma from "utils/prisma"; import prisma from "utils/prisma";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse res: NextApiResponse
) { ) {
const session = await getServerSession(req, res, authOptions); // je sais plus d'où ça vient
console.log("session", session);
if (!session?.user) {
return res.status(400).send({ error: "You must be connected" });
}
const name = req.body?.name as string; const name = req.body?.name as string;
const url = req.body?.url as string; const url = req.body?.url as string;
const favorite = Boolean(req.body?.favorite) || false; const favorite = Boolean(req.body?.favorite) || false;
@@ -57,12 +66,16 @@ export default async function handler(
} }
try { try {
const { id: authorId } = await prisma.user.findFirstOrThrow({
where: { email: session.user.email },
});
await prisma.link.create({ await prisma.link.create({
data: { data: {
name, name,
url, url,
categoryId, categoryId,
favorite, favorite,
authorId,
}, },
}); });
return res return res

View File

@@ -79,7 +79,7 @@ export async function getServerSideProps({ query }) {
const { cid } = query; const { cid } = query;
const categoryDB = await prisma.category.findFirst({ const categoryDB = await prisma.category.findFirst({
where: { id: Number(cid) }, where: { id: Number(cid) },
include: { links: true }, include: { links: true, author: true },
}); });
if (!categoryDB) { if (!categoryDB) {

View File

@@ -84,7 +84,7 @@ export async function getServerSideProps({ query }) {
const { cid } = query; const { cid } = query;
const categoryDB = await prisma.category.findFirst({ const categoryDB = await prisma.category.findFirst({
where: { id: Number(cid) }, where: { id: Number(cid) },
include: { links: true }, include: { links: true, author: true },
}); });
if (!categoryDB) { if (!categoryDB) {

View File

@@ -16,6 +16,7 @@ import { Category, Link, SearchItem } from "types";
import { BuildCategory } from "utils/front"; import { BuildCategory } from "utils/front";
import { pushStateVanilla } from "utils/link"; import { pushStateVanilla } from "utils/link";
import prisma from "utils/prisma"; import prisma from "utils/prisma";
import { getSessionOrThrow } from "utils/session";
interface HomePageProps { interface HomePageProps {
categories: Category[]; categories: Category[];
@@ -185,10 +186,27 @@ function Home(props: HomePageProps) {
); );
} }
export async function getServerSideProps({ query }) { export async function getServerSideProps({ req, res, query }) {
const session = await getSessionOrThrow(req, res);
const queryCategoryId = (query?.categoryId as string) || ""; const queryCategoryId = (query?.categoryId as string) || "";
const user = await prisma.user.findFirstOrThrow({
where: {
email: session.user.email,
},
});
const categoriesDB = await prisma.category.findMany({ const categoriesDB = await prisma.category.findMany({
include: { links: true }, include: {
links: {
where: {
authorId: user.id,
},
},
author: true,
},
where: {
authorId: user.id,
},
}); });
if (categoriesDB.length === 0) { if (categoriesDB.length === 0) {

View File

@@ -111,7 +111,9 @@ CreateLink.authRequired = true;
export default CreateLink; export default CreateLink;
export async function getServerSideProps() { export async function getServerSideProps() {
const categoriesDB = await prisma.category.findMany(); const categoriesDB = await prisma.category.findMany({
include: { author: true },
});
const categories = categoriesDB.map((categoryDB) => const categories = categoriesDB.map((categoryDB) =>
BuildCategory(categoryDB) BuildCategory(categoryDB)
); );

View File

@@ -11,15 +11,13 @@ import TextBox from "components/TextBox";
import useAutoFocus from "hooks/useAutoFocus"; import useAutoFocus from "hooks/useAutoFocus";
import { Category, Link } from "types"; import { Category, Link } from "types";
import { import { HandleAxiosError, IsValidURL } from "utils/front";
BuildCategory,
BuildLink,
HandleAxiosError,
IsValidURL,
} from "utils/front";
import prisma from "utils/prisma";
import getUserCategories from "lib/category/getUserCategories";
import getUserLink from "lib/link/getUserLink";
import getUserOrThrow from "lib/user/getUserOrThrow";
import styles from "styles/create.module.scss"; import styles from "styles/create.module.scss";
import { getSessionOrThrow } from "utils/session";
function EditLink({ function EditLink({
link, link,
@@ -133,20 +131,15 @@ function EditLink({
EditLink.authRequired = true; EditLink.authRequired = true;
export default EditLink; export default EditLink;
export async function getServerSideProps({ query }) { export async function getServerSideProps({ req, res, query }) {
const { lid } = query; const { lid } = query;
const categoriesDB = await prisma.category.findMany(); const session = await getSessionOrThrow(req, res);
const categories = categoriesDB.map((categoryDB) => const user = await getUserOrThrow(session);
BuildCategory(categoryDB) const categories = await getUserCategories(user);
);
const linkDB = await prisma.link.findFirst({ const link = await getUserLink(user, Number(lid));
where: { id: Number(lid) }, if (!link) {
include: { category: true },
});
if (!linkDB) {
return { return {
redirect: { redirect: {
destination: "/", destination: "/",
@@ -154,10 +147,6 @@ export async function getServerSideProps({ query }) {
}; };
} }
const link = BuildLink(linkDB, {
categoryId: linkDB.categoryId,
categoryName: linkDB.category.name,
});
return { return {
props: { props: {
link: JSON.parse(JSON.stringify(link)), link: JSON.parse(JSON.stringify(link)),

View File

@@ -98,7 +98,7 @@ export async function getServerSideProps({ query }) {
const { lid } = query; const { lid } = query;
const linkDB = await prisma.link.findFirst({ const linkDB = await prisma.link.findFirst({
where: { id: Number(lid) }, where: { id: Number(lid) },
include: { category: true }, include: { category: true, author: true },
}); });
if (!linkDB) { if (!linkDB) {

8
src/types.d.ts vendored
View File

@@ -1,9 +1,12 @@
import { User } from "@prisma/client";
export interface Category { export interface Category {
id: number; id: number;
name: string; name: string;
links: Link[]; links: Link[];
nextCategoryId: number; authorId: User["id"];
author: User;
createdAt: Date; createdAt: Date;
updatedAt: Date; updatedAt: Date;
@@ -20,7 +23,8 @@ export interface Link {
name: string; name: string;
}; };
nextLinkId: number; authorId: User["id"];
author: User;
favorite: boolean; favorite: boolean;
createdAt: Date; createdAt: Date;

12
src/utils/api.ts Normal file
View File

@@ -0,0 +1,12 @@
import { NextApiRequest } from "next";
export function checkMethodAllowedOrThrow(
req: NextApiRequest,
methods: Array<RequestInit["method"]>
) {
const isMethodAllowed = methods.includes(req.method.toLowerCase());
if (!isMethodAllowed) {
throw new Error(`Method ${req.method} not allowed`);
}
return isMethodAllowed;
}

View File

@@ -5,7 +5,8 @@ import { Category, Link } from "types";
export function BuildCategory({ export function BuildCategory({
id, id,
name, name,
nextCategoryId, authorId,
author,
links = [], links = [],
createdAt, createdAt,
updatedAt, updatedAt,
@@ -16,14 +17,15 @@ export function BuildCategory({
links: links.map((link) => links: links.map((link) =>
BuildLink(link, { categoryId: id, categoryName: name }) BuildLink(link, { categoryId: id, categoryName: name })
), ),
nextCategoryId, authorId,
author,
createdAt, createdAt,
updatedAt, updatedAt,
}; };
} }
export function BuildLink( export function BuildLink(
{ id, name, url, nextLinkId, favorite, createdAt, updatedAt }, { id, name, url, authorId, author, favorite, createdAt, updatedAt },
{ categoryId, categoryName } { categoryId, categoryName }
): Link { ): Link {
return { return {
@@ -34,7 +36,8 @@ export function BuildLink(
id: categoryId, id: categoryId,
name: categoryName, name: categoryName,
}, },
nextLinkId, authorId,
author,
favorite, favorite,
createdAt, createdAt,
updatedAt, updatedAt,

17
src/utils/session.ts Normal file
View File

@@ -0,0 +1,17 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
import { authOptions } from "pages/api/auth/[...nextauth]";
export async function getSessionOrThrow(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions);
if (!session) {
throw new Error("You must be connected");
}
return session;
}