mirror of
				https://github.com/DerTyp7/explainegy-nextjs.git
				synced 2025-10-29 21:02:13 +01:00 
			
		
		
		
	asd
This commit is contained in:
		
							
								
								
									
										2
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| { | { | ||||||
|   "typescript.tsdk": "node_modules\\typescript\\lib", |   "typescript.tsdk": "node_modules\\typescript\\lib", | ||||||
|   "typescript.enablePromptUseWorkspaceTsdk": true |   "typescript.enablePromptUseWorkspaceTsdk": true | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,33 +1,30 @@ | |||||||
| "use client"; | "use client"; | ||||||
| 
 | 
 | ||||||
| import React from "react"; | import React from "react"; | ||||||
| 
 |  | ||||||
| import { useState, useRef, useEffect } from "react"; | import { useState, useRef, useEffect } from "react"; | ||||||
| import styles from "../../../../styles/modules/AdminArticlesCreate.module.scss"; | import styles from "../../../../../styles/modules/ArticleEditor.module.scss"; | ||||||
| import { PostArticle } from "../../../../types/postData"; | import { Prisma } from "@prisma/client"; | ||||||
| import Markdown from "../../../Markdown"; | import "../../../../../styles/inputs.scss"; | ||||||
| import { Article, Category, Prisma } from "@prisma/client"; | import "../../../../../styles/buttons.scss"; | ||||||
| import "../../../../styles/inputs.scss"; | import Select from "react-select"; | ||||||
| import "../../../../styles/buttons.scss"; |  | ||||||
| import Select, { GroupBase, OptionsOrGroups } from "react-select"; |  | ||||||
| import { apiUrl } from "../../../global"; |  | ||||||
| import urlJoin from "url-join"; |  | ||||||
| import { formatTextToUrlName } from "../../../../utils"; |  | ||||||
| import { isValidText } from "../../../../validators"; |  | ||||||
| import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||||
| import ContentTable from "../../../articles/[categoryName]/[articleName]/ContentTable"; | import urlJoin from "url-join"; | ||||||
| import { IContentTableEntry } from "../../../../types/contentTable"; | import { IContentTableEntry } from "../../../../../types/contentTable"; | ||||||
|  | import { CreateArticle, UpdateArticle } from "../../../../../types/api"; | ||||||
|  | import { formatTextToUrlName } from "../../../../../utils"; | ||||||
|  | import { isValidText } from "../../../../../validators"; | ||||||
|  | import { apiUrl } from "../../../../global"; | ||||||
|  | import Markdown from "../../../../Markdown"; | ||||||
| 
 | 
 | ||||||
| type ArticleWithCategory = Prisma.ArticleGetPayload<{ include: { category: true } }>; | type ArticleWithCategory = Prisma.ArticleGetPayload<{ include: { category: true } }>; | ||||||
| 
 | 
 | ||||||
| export default function AdminArticlesCreate() { | export default function ArticleEditor({ params }: { params: { articleId: string } }) { | ||||||
|   const router = useRouter(); |   const router = useRouter(); | ||||||
| 
 |  | ||||||
|   const [title, setTitle] = useState<string>(""); |   const [title, setTitle] = useState<string>(""); | ||||||
|   const [selectCategoriesOptions, setSelectCategoriesOptions] = useState<any>([]); |   const [selectCategoriesOptions, setSelectCategoriesOptions] = useState<any>([]); | ||||||
|   const [introduction, setIntroduction] = useState<string>(""); |   const [introduction, setIntroduction] = useState<string>(""); | ||||||
|   const [markdown, setMarkdown] = useState<string>(""); |   const [markdown, setMarkdown] = useState<string>(""); | ||||||
|   const [contentTable, setContentTable] = useState<IContentTableEntry[]>([]); |   const [contentTable, setContentTable] = useState<any>([]); | ||||||
| 
 | 
 | ||||||
|   const titleRef = useRef<HTMLInputElement>(null); |   const titleRef = useRef<HTMLInputElement>(null); | ||||||
|   const categorySelectRef = useRef(null); |   const categorySelectRef = useRef(null); | ||||||
| @@ -52,40 +49,102 @@ export default function AdminArticlesCreate() { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   function removeEntry(index: number) { | ||||||
|  |     let newArray = [...contentTable]; | ||||||
|  |     newArray.splice(index, 1); | ||||||
|  |     setContentTable(newArray); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   function handleFormChange() { |   function handleFormChange() { | ||||||
|     setMarkdown(markdownTextAreaRef.current.value); |     setMarkdown(markdownTextAreaRef.current.value); | ||||||
|     setTitle(titleRef.current.value); |     setTitle(titleRef.current.value); | ||||||
|     setIntroduction(introductionRef.current.value); |     setIntroduction(introductionRef.current.value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function postData() { |   // Create or update article
 | ||||||
|     const formData: PostArticle = { |   async function handleResponse(res: Response) { | ||||||
|  |     const json = await res.json(); | ||||||
|  |     errorTextRef.current.innerText = json.error ?? ""; | ||||||
|  |     if (json.success) { | ||||||
|  |       const newArticle: ArticleWithCategory = json.data; | ||||||
|  |       router.push(urlJoin(`/articles/`, newArticle.category.name, newArticle.name)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async function updateArticle() { | ||||||
|  |     console.log("Update article"); | ||||||
|  |     const payload: UpdateArticle = { | ||||||
|  |       id: params.articleId, | ||||||
|       title: titleRef.current.value, |       title: titleRef.current.value, | ||||||
|       introduction: introductionRef.current.value, |       introduction: introductionRef.current.value, | ||||||
|       markdown: markdown, |       markdown: markdown, | ||||||
|       categoryId: Number(categorySelectRef?.current?.getValue()[0]?.value), |       categoryId: Number(categorySelectRef?.current?.getValue()[0]?.value), | ||||||
|       contentTable: contentTable, |       contentTable: contentTable, | ||||||
|     }; |     }; | ||||||
|     console.log(formData); |     console.log(payload); | ||||||
|     const result = await fetch("/api/articles/create", { | 
 | ||||||
|  |     await fetch("/api/articles/", { | ||||||
|  |       method: "PUT", | ||||||
|  |       headers: { | ||||||
|  |         Accept: "application/json", | ||||||
|  |         "Content-Type": "application/json", | ||||||
|  |       }, | ||||||
|  |       cache: "no-cache", | ||||||
|  |       body: JSON.stringify(payload), | ||||||
|  |     }) | ||||||
|  |       .then(handleResponse) | ||||||
|  |       .catch(console.error); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async function createArticle() { | ||||||
|  |     console.log("Create article"); | ||||||
|  |     const payload: CreateArticle = { | ||||||
|  |       title: titleRef.current.value, | ||||||
|  |       introduction: introductionRef.current.value, | ||||||
|  |       markdown: markdown, | ||||||
|  |       categoryId: Number(categorySelectRef?.current?.getValue()[0]?.value), | ||||||
|  |       contentTable: contentTable, | ||||||
|  |     }; | ||||||
|  |     console.log(payload); | ||||||
|  | 
 | ||||||
|  |     await fetch("/api/articles/", { | ||||||
|       method: "POST", |       method: "POST", | ||||||
|       headers: { |       headers: { | ||||||
|         Accept: "application/json", |         Accept: "application/json", | ||||||
|         "Content-Type": "application/json", |         "Content-Type": "application/json", | ||||||
|       }, |       }, | ||||||
|       body: JSON.stringify(formData), |       cache: "no-cache", | ||||||
|     }); |       body: JSON.stringify(payload), | ||||||
| 
 |     }) | ||||||
|     const response = await result.json(); |       .then(handleResponse) | ||||||
|     console.log(response); |       .catch(console.error); | ||||||
|     errorTextRef.current.innerText = response.error ?? ""; |  | ||||||
|     if (response.success) { |  | ||||||
|       const newArticle: ArticleWithCategory = response.data; |  | ||||||
|       router.push(urlJoin(`/articles/`, newArticle.category.name, newArticle.name)); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // App
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  |     const fetchExistingArticle = async () => { | ||||||
|  |       const result: Response = await fetch(urlJoin(apiUrl, `articles/${params.articleId}`), { | ||||||
|  |         cache: "no-cache", | ||||||
|  |         next: { revalidate: 60 * 1 }, | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       const article = await result.json(); | ||||||
|  |       console.log(article); | ||||||
|  |       if (article.code == "404") { | ||||||
|  |         router.push(urlJoin(`/admin/articles/editor/0`)); | ||||||
|  |       } else { | ||||||
|  |         titleRef.current.value = article.title; | ||||||
|  |         introductionRef.current.value = article.introduction; | ||||||
|  |         markdownTextAreaRef.current.value = article.markdown; | ||||||
|  |         categorySelectRef.current.setValue({ value: article.category.id, label: article.category.title }); | ||||||
|  | 
 | ||||||
|  |         setTitle(article.title); | ||||||
|  |         setIntroduction(article.introduction); | ||||||
|  |         setMarkdown(article.markdown); | ||||||
|  |         setContentTable(article.contentTable); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     const fetchCategoryOptions = async () => { |     const fetchCategoryOptions = async () => { | ||||||
|       const result: Response = await fetch(urlJoin(apiUrl, `categories`), { |       const result: Response = await fetch(urlJoin(apiUrl, `categories`), { | ||||||
|         cache: "no-cache", |         cache: "no-cache", | ||||||
| @@ -100,23 +159,34 @@ export default function AdminArticlesCreate() { | |||||||
|       }); |       }); | ||||||
|       setSelectCategoriesOptions(newSelectCategoriesOptions); |       setSelectCategoriesOptions(newSelectCategoriesOptions); | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|     fetchCategoryOptions().catch((err) => { |     fetchCategoryOptions().catch((err) => { | ||||||
|       console.log(err); |       console.log(err); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     if (params.articleId != "0") { | ||||||
|  |       fetchExistingArticle().catch((err) => { | ||||||
|  |         console.log(err); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|   }, []); |   }, []); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.adminArticlesCreate}> |     <div className={styles.adminArticlesCreate}> | ||||||
|       <h1>Create a new article</h1> |       <h1>{params.articleId == "0" ? "Create new article" : "Update article"}</h1> | ||||||
|       <div className={styles.formControl}> |       <div className={styles.formControl}> | ||||||
|         <p className="text-error" ref={errorTextRef}></p> |         <p className="text-error" ref={errorTextRef}></p> | ||||||
|         <button |         <button | ||||||
|           type="button" |           type="button" | ||||||
|           onClick={() => { |           onClick={() => { | ||||||
|             postData(); |             if (params.articleId != "0") { | ||||||
|  |               updateArticle(); | ||||||
|  |             } else { | ||||||
|  |               createArticle(); | ||||||
|  |             } | ||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
|           Create Article |           {params.articleId == "0" ? "Create article" : "Update article"} | ||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
| @@ -177,7 +247,7 @@ export default function AdminArticlesCreate() { | |||||||
|             <label htmlFor="">Table of contents</label> |             <label htmlFor="">Table of contents</label> | ||||||
|             <div className={styles.contentTableEditor}> |             <div className={styles.contentTableEditor}> | ||||||
|               <div className={styles.entries}> |               <div className={styles.entries}> | ||||||
|                 {contentTable.map((entry: IContentTableEntry, i: number) => { |                 {contentTable?.map((entry: IContentTableEntry, i: number) => { | ||||||
|                   return ( |                   return ( | ||||||
|                     <div key={i}> |                     <div key={i}> | ||||||
|                       <input |                       <input | ||||||
| @@ -186,6 +256,7 @@ export default function AdminArticlesCreate() { | |||||||
|                         }} |                         }} | ||||||
|                         type="text" |                         type="text" | ||||||
|                         placeholder={"Anchor"} |                         placeholder={"Anchor"} | ||||||
|  |                         defaultValue={entry.anchor} | ||||||
|                       /> |                       /> | ||||||
|                       <input |                       <input | ||||||
|                         onChange={(e) => { |                         onChange={(e) => { | ||||||
| @@ -193,14 +264,22 @@ export default function AdminArticlesCreate() { | |||||||
|                         }} |                         }} | ||||||
|                         type="text" |                         type="text" | ||||||
|                         placeholder={"Title"} |                         placeholder={"Title"} | ||||||
|                       /> |                         defaultValue={entry.title} | ||||||
|  |                       />{" "} | ||||||
|  |                       <button | ||||||
|  |                         onClick={() => { | ||||||
|  |                           removeEntry(i); | ||||||
|  |                         }} | ||||||
|  |                       > | ||||||
|  |                         Remove | ||||||
|  |                       </button> | ||||||
|                     </div> |                     </div> | ||||||
|                   ); |                   ); | ||||||
|                 })} |                 })} | ||||||
| 
 | 
 | ||||||
|                 <button |                 <button | ||||||
|                   onClick={() => { |                   onClick={() => { | ||||||
|                     setContentTable([...contentTable, { title: "Title", anchor: "Anchor" }]); |                     setContentTable([...contentTable, { title: "", anchor: "" }]); | ||||||
|                   }} |                   }} | ||||||
|                 > |                 > | ||||||
|                   Add |                   Add | ||||||
| @@ -14,7 +14,7 @@ type ArticleWithIncludes = Prisma.ArticleGetPayload<{ | |||||||
| }>; | }>; | ||||||
|  |  | ||||||
| export async function GetArticle(articleName: string): Promise<any> { | export async function GetArticle(articleName: string): Promise<any> { | ||||||
|   const result: Response = await fetch(urlJoin(apiUrl, `articles/${articleName ?? ""}`), { |   const result: Response = await fetch(urlJoin(apiUrl, `articles/name/${articleName ?? ""}`), { | ||||||
|     cache: "force-cache", |     cache: "force-cache", | ||||||
|     next: { revalidate: 60 * 10 }, |     next: { revalidate: 60 * 10 }, | ||||||
|   }); |   }); | ||||||
|   | |||||||
| @@ -1,14 +1,14 @@ | |||||||
| { | { | ||||||
| 	"Servers": { |   "Servers": { | ||||||
| 		"1": { |     "1": { | ||||||
| 			"Name": "explainegy_postgres", |       "Name": "explainegy_postgres", | ||||||
| 			"Group": "explainegy_postgres_group", |       "Group": "explainegy_postgres_group", | ||||||
| 			"Host": "host.docker.internal", |       "Host": "host.docker.internal", | ||||||
| 			"Port": 5432, |       "Port": 5432, | ||||||
| 			"MaintenanceDB": "postgres", |       "MaintenanceDB": "postgres", | ||||||
| 			"Username": "postgres", |       "Username": "postgres", | ||||||
| 			"PassFile": "/pgpass", |       "PassFile": "/pgpass", | ||||||
| 			"SSLMode": "prefer" |       "SSLMode": "prefer" | ||||||
| 		} |     } | ||||||
| 	} |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								pages/api/articles/[articleId].ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								pages/api/articles/[articleId].ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | import { Prisma, Article } from "@prisma/client"; | ||||||
|  | import { Request, Response } from "express"; | ||||||
|  | import { ResponseError } from "../../../types/responseErrors"; | ||||||
|  | import { formatTextToUrlName } from "../../../utils"; | ||||||
|  | import prisma from "../../../lib/prisma"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | type ArticleWithIncludes = Prisma.ArticleGetPayload<{ include: { contentTableEntries: true, category: true, image: true } }> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export default async function handler(req: Request, res: Response) { | ||||||
|  |   res.setHeader("Content-Type", "application/json"); | ||||||
|  |  | ||||||
|  |   const articleId: string = formatTextToUrlName(req.query.articleId.toString()) | ||||||
|  |  | ||||||
|  |   await prisma.article | ||||||
|  |     .findUnique({ where: { id: articleId }, include: { category: true, image: true } }) | ||||||
|  |     .then((result: ArticleWithIncludes) => { | ||||||
|  |       if (result !== null) { | ||||||
|  |         res.end(JSON.stringify(result)); | ||||||
|  |       } else { | ||||||
|  |         const error: ResponseError = { | ||||||
|  |           code: "404", | ||||||
|  |           message: "No article with this name found!", | ||||||
|  |         }; | ||||||
|  |         res.status(404).send(JSON.stringify(error)); | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |     .catch((err) => { | ||||||
|  |  | ||||||
|  |       const error: ResponseError = { | ||||||
|  |         code: "500", | ||||||
|  |         message: err, | ||||||
|  |       }; | ||||||
|  |       res.status(500).send(JSON.stringify(error)); | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @@ -1,48 +0,0 @@ | |||||||
| import { Request, Response } from "express"; |  | ||||||
| import prisma from "../../../lib/prisma"; |  | ||||||
| import { Prisma } from "@prisma/client"; |  | ||||||
| import { Article, Category } from "@prisma/client"; |  | ||||||
| import { ResponseError } from "../../../types/responseErrors"; |  | ||||||
| import { PostArticle } from "../../../types/postData"; |  | ||||||
| import { isValidText } from "../../../validators"; |  | ||||||
| import { formatTextToUrlName } from "../../../utils"; |  | ||||||
| import { json } from "stream/consumers"; |  | ||||||
|  |  | ||||||
| export default async function handler(req: Request, res: Response) { |  | ||||||
|   res.setHeader("Content-Type", "application/json"); |  | ||||||
|  |  | ||||||
|   const postData: any = req.body; |  | ||||||
|   console.log(postData); |  | ||||||
|   if (!isValidText(postData.title)) { |  | ||||||
|     res.send(JSON.stringify({ target: "title", error: "Not a valid title" })); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (!isValidText(postData.introduction)) { |  | ||||||
|     res.send(JSON.stringify({ target: "introduction", error: "Not a valid introduction" })); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (!postData.categoryId) { |  | ||||||
|     res.send(JSON.stringify({ target: "category", error: "Category is required" })); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   postData.name = formatTextToUrlName(postData.title); |  | ||||||
|   prisma.article |  | ||||||
|     .create({ data: postData, include: { category: true } }) |  | ||||||
|     .then( |  | ||||||
|       (data) => { |  | ||||||
|         res.send(JSON.stringify({ success: true, data: data })); |  | ||||||
|       }, |  | ||||||
|       (errorReason) => { |  | ||||||
|         if (errorReason.code === "P2002") { |  | ||||||
|           res.send(JSON.stringify({ target: errorReason.meta.target[0], error: "Already exists" })); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     ) |  | ||||||
|     .catch((err) => { |  | ||||||
|       console.error(err); |  | ||||||
|       res.sendStatus(500).end(); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @@ -3,50 +3,135 @@ import prisma from "../../../lib/prisma"; | |||||||
| import { Prisma } from "@prisma/client"; | import { Prisma } from "@prisma/client"; | ||||||
| import { Article, Category } from "@prisma/client"; | import { Article, Category } from "@prisma/client"; | ||||||
| import { ResponseError } from "../../../types/responseErrors"; | import { ResponseError } from "../../../types/responseErrors"; | ||||||
|  | import { formatTextToUrlName } from "../../../utils"; | ||||||
|  | import { isValidText } from "../../../validators"; | ||||||
|  | import { title } from 'process'; | ||||||
|  | import { UpdateArticle } from "../../../types/api"; | ||||||
|  |  | ||||||
| export default async function handler(req: Request, res: Response) { | export default async function handler(req: Request, res: Response) { | ||||||
|   res.setHeader("Content-Type", "application/json"); |   res.setHeader("Content-Type", "application/json"); | ||||||
|  |  | ||||||
|   const categoryName: string = req.query.categoryName?.toString() ?? ""; |   if (req.method == "GET") { | ||||||
|   const limit: number = req.query.limit ? Number(req.query.limit) : undefined; |     const categoryName: string = req.query.categoryName?.toString() ?? ""; | ||||||
|   const orderBy: string = req.query.orderBy?.toString() ?? ""; |     const limit: number = req.query.limit ? Number(req.query.limit) : undefined; | ||||||
|  |     const orderBy: string = req.query.orderBy?.toString() ?? ""; | ||||||
|  |  | ||||||
|   const category = await prisma.category.findUnique({ where: { name: categoryName } }); |     const category = await prisma.category.findUnique({ where: { name: categoryName } }); | ||||||
|  |  | ||||||
|   let orderByObj: Prisma.Enumerable<Prisma.ArticleOrderByWithRelationInput>; |     let orderByObj: Prisma.Enumerable<Prisma.ArticleOrderByWithRelationInput>; | ||||||
|  |  | ||||||
|  |     if (orderBy === "recent") { | ||||||
|  |       orderByObj = { | ||||||
|  |         dateCreated: "desc" | ||||||
|  |       } | ||||||
|  |     } else if (orderBy === "popularity") { | ||||||
|  |  | ||||||
|   if (orderBy === "recent") { |  | ||||||
|     orderByObj = { |  | ||||||
|       dateCreated: "desc" |  | ||||||
|     } |     } | ||||||
|   } else if (orderBy === "popularity") { |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     await prisma.article | ||||||
|  |       .findMany({ | ||||||
|  |         where: { category: categoryName.length > 0 ? category : undefined }, | ||||||
|  |         include: { category: true }, | ||||||
|  |         take: limit, | ||||||
|  |         orderBy: orderByObj | ||||||
|  |       }) | ||||||
|  |       .then((result: Article[]) => { //! ContentTableEntries not sorted | ||||||
|  |         if (result !== null) { | ||||||
|  |           res.end(JSON.stringify(result)); | ||||||
|  |         } else { | ||||||
|  |           const error: ResponseError = { | ||||||
|  |             code: "404", | ||||||
|  |             message: "No articles found!", | ||||||
|  |           }; | ||||||
|  |           res.status(404).send(JSON.stringify(error)); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .catch((err) => { | ||||||
|  |         const error: ResponseError = { | ||||||
|  |           code: "500", | ||||||
|  |           message: err, | ||||||
|  |         }; | ||||||
|  |         res.status(500).send(JSON.stringify(error)); | ||||||
|  |       }); | ||||||
|  |   } else if (req.method == "POST") { | ||||||
|  |     const data: any = req.body; | ||||||
|  |  | ||||||
|  |     if (!isValidText(data.title)) { | ||||||
|  |       res.send(JSON.stringify({ target: "title", error: "Not a valid title" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!isValidText(data.introduction)) { | ||||||
|  |       res.send(JSON.stringify({ target: "introduction", error: "Not a valid introduction" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!data.categoryId) { | ||||||
|  |       res.send(JSON.stringify({ target: "category", error: "Category is required" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     data.name = formatTextToUrlName(data.title); | ||||||
|  |     prisma.article | ||||||
|  |       .create({ data: data, include: { category: true } }) | ||||||
|  |       .then( | ||||||
|  |         (data) => { | ||||||
|  |           res.send(JSON.stringify({ success: true, data: data })); | ||||||
|  |         }, | ||||||
|  |         (errorReason) => { | ||||||
|  |           if (errorReason.code === "P2002") { | ||||||
|  |             res.send(JSON.stringify({ target: errorReason.meta.target[0], error: "Already exists" })); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |       .catch((err) => { | ||||||
|  |         console.error(err); | ||||||
|  |         res.sendStatus(500).end(); | ||||||
|  |       }); | ||||||
|  |   } else if (req.method == "PUT") { | ||||||
|  |     const data: UpdateArticle = req.body; | ||||||
|  |  | ||||||
|  |     if (!isValidText(data.title)) { | ||||||
|  |       res.send(JSON.stringify({ target: "title", error: "Not a valid title" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!isValidText(data.introduction)) { | ||||||
|  |       res.send(JSON.stringify({ target: "introduction", error: "Not a valid introduction" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!data.categoryId) { | ||||||
|  |       res.send(JSON.stringify({ target: "category", error: "Category is required" })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const newArticle: Prisma.ArticleUncheckedUpdateInput = { | ||||||
|  |       title: data.title, | ||||||
|  |       name: formatTextToUrlName(data.title), | ||||||
|  |       introduction: data.introduction, | ||||||
|  |       categoryId: data.categoryId, | ||||||
|  |       contentTable: data.contentTable, | ||||||
|  |       markdown: data.markdown, | ||||||
|  |       imageId: data.imageId, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await prisma.article.update({ data: newArticle, where: { id: data.id }, include: { category: true } }) | ||||||
|  |       .then( | ||||||
|  |         (data) => { | ||||||
|  |           res.send(JSON.stringify({ success: true, data: data })); | ||||||
|  |         }, | ||||||
|  |         (errorReason) => { | ||||||
|  |           if (errorReason.code === "P2002") { | ||||||
|  |             res.send(JSON.stringify({ target: errorReason.meta.target[0], error: "Already exists" })); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |       .catch((err) => { | ||||||
|  |         console.error(err); | ||||||
|  |         res.sendStatus(500).end(); | ||||||
|  |       }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   await prisma.article |  | ||||||
|     .findMany({ |  | ||||||
|       where: { category: categoryName.length > 0 ? category : undefined }, |  | ||||||
|       include: { category: true }, |  | ||||||
|       take: limit, |  | ||||||
|       orderBy: orderByObj |  | ||||||
|     }) |  | ||||||
|     .then((result: Article[]) => { //! ContentTableEntries not sorted |  | ||||||
|       if (result !== null) { |  | ||||||
|         res.end(JSON.stringify(result)); |  | ||||||
|       } else { |  | ||||||
|         const error: ResponseError = { |  | ||||||
|           code: "404", |  | ||||||
|           message: "No articles found!", |  | ||||||
|         }; |  | ||||||
|         res.status(404).send(JSON.stringify(error)); |  | ||||||
|       } |  | ||||||
|     }) |  | ||||||
|     .catch((err) => { |  | ||||||
|       const error: ResponseError = { |  | ||||||
|         code: "500", |  | ||||||
|         message: err, |  | ||||||
|       }; |  | ||||||
|       res.status(500).send(JSON.stringify(error)); |  | ||||||
|     }); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| import { Request, Response } from "express"; | import { Request, Response } from "express"; | ||||||
| import prisma from "../../../lib/prisma"; | import prisma from "../../../../lib/prisma"; | ||||||
| import { Prisma } from '@prisma/client'; | import { Prisma } from '@prisma/client'; | ||||||
| import { ResponseError } from "../../../types/responseErrors"; | import { ResponseError } from "../../../../types/responseErrors"; | ||||||
| import { formatTextToUrlName } from "../../../utils"; | import { formatTextToUrlName } from "../../../../utils"; | ||||||
| 
 | 
 | ||||||
| type ArticleWithIncludes = Prisma.ArticleGetPayload<{ include: { contentTableEntries: true, category: true, image: true } }> | type ArticleWithIncludes = Prisma.ArticleGetPayload<{ include: { contentTableEntries: true, category: true, image: true } }> | ||||||
| 
 | 
 | ||||||
| @@ -27,6 +27,7 @@ export default async function handler(req: Request, res: Response) { | |||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|     .catch((err) => { |     .catch((err) => { | ||||||
|  | 
 | ||||||
|       const error: ResponseError = { |       const error: ResponseError = { | ||||||
|         code: "500", |         code: "500", | ||||||
|         message: err, |         message: err, | ||||||
| @@ -8,7 +8,7 @@ datasource db { | |||||||
| } | } | ||||||
|  |  | ||||||
| model Article { | model Article { | ||||||
|   id           Int      @id @default(autoincrement()) |   id           String   @id @default(uuid()) | ||||||
|   name         String   @unique |   name         String   @unique | ||||||
|   title        String   @unique |   title        String   @unique | ||||||
|   introduction String   @default("") |   introduction String   @default("") | ||||||
|   | |||||||
| @@ -57,6 +57,7 @@ | |||||||
|             border: 2px solid #3b3b3b80; |             border: 2px solid #3b3b3b80; | ||||||
|             background-color: transparent; |             background-color: transparent; | ||||||
|             min-height: 700px; |             min-height: 700px; | ||||||
|  |             max-height: 1500px; | ||||||
|             resize: none; |             resize: none; | ||||||
|             display: block; |             display: block; | ||||||
|             border-radius: 0px; |             border-radius: 0px; | ||||||
| @@ -68,7 +69,9 @@ | |||||||
| 
 | 
 | ||||||
|           & > div { |           & > div { | ||||||
|             max-width: 1000px; |             max-width: 1000px; | ||||||
|  |             max-height: 1500px; | ||||||
|             border: 2px solid #3b3b3b80; |             border: 2px solid #3b3b3b80; | ||||||
|  |             overflow: auto; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @@ -80,8 +83,9 @@ | |||||||
|           gap: 10px 10px; |           gap: 10px 10px; | ||||||
|           & > div { |           & > div { | ||||||
|             border: 2px solid #3b3b3b80; |             border: 2px solid #3b3b3b80; | ||||||
| 
 |             overflow: auto; | ||||||
|             max-width: 1000px; |             max-width: 1000px; | ||||||
|  |             max-height: 1000px; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
							
								
								
									
										21
									
								
								types/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								types/api.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import { IContentTableEntry } from "./contentTable"; | ||||||
|  | import { Prisma } from '@prisma/client'; | ||||||
|  |  | ||||||
|  | export interface CreateArticle { | ||||||
|  |   title: string; | ||||||
|  |   markdown: string; | ||||||
|  |   introduction: string; | ||||||
|  |   categoryId: number; | ||||||
|  |   contentTable: Prisma.JsonArray | ||||||
|  |   imageId?: number; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface UpdateArticle { | ||||||
|  |   id: string; | ||||||
|  |   title?: string; | ||||||
|  |   markdown?: string; | ||||||
|  |   introduction?: string; | ||||||
|  |   categoryId?: number; | ||||||
|  |   contentTable?: Prisma.JsonArray | ||||||
|  |   imageId?: number; | ||||||
|  | } | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| import { IContentTableEntry } from "./contentTable"; |  | ||||||
|  |  | ||||||
| export interface PostArticle { |  | ||||||
|   title: string; |  | ||||||
|   name?: string; |  | ||||||
|   markdown: string; |  | ||||||
|   introduction: string; |  | ||||||
|   categoryId: number; |  | ||||||
|   contentTable: IContentTableEntry[] |  | ||||||
|   imageId?: number; |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user
	 Janis
					Janis