mirror of
https://github.com/DerTyp7/explainegy-nextjs.git
synced 2025-10-28 20:32:14 +01:00
auth
This commit is contained in:
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import styles from "@/styles/modules/AdminNav.module.scss";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
|
||||
function AdminNav() {
|
||||
return (
|
||||
<div className={styles.adminNav}>
|
||||
<Link href={"/admin/editor/article/0"}>New article</Link>
|
||||
<Link href={"/admin/editor/category/0"}>New category</Link>
|
||||
</div>
|
||||
);
|
||||
const { data: session } = useSession();
|
||||
|
||||
if (session) {
|
||||
return (
|
||||
<div className={styles.adminNav}>
|
||||
<Link href={"/admin/editor/article/0"}>New article</Link>
|
||||
<Link href={"/admin/editor/category/0"}>New category</Link>
|
||||
<Link href={"/api/auth/signout"}>Logout</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminNav;
|
||||
|
||||
@@ -2,8 +2,12 @@ import React from "react";
|
||||
import { apiUrl } from "@/global";
|
||||
import urlJoin from "url-join";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { isElementAccessExpression } from "typescript";
|
||||
|
||||
export default function ArticleControl({ articleId }: { articleId: string }) {
|
||||
const { data: session } = useSession();
|
||||
|
||||
const router = useRouter();
|
||||
async function deleteArticle() {
|
||||
await fetch(urlJoin(apiUrl, `articles/${articleId}`), { method: "DELETE" })
|
||||
@@ -17,15 +21,18 @@ export default function ArticleControl({ articleId }: { articleId: string }) {
|
||||
function editArticle() {
|
||||
router.push("/admin/editor/article/" + articleId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="danger" onClick={deleteArticle}>
|
||||
Delete
|
||||
</button>
|
||||
<button className="warning" onClick={editArticle}>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
if (session) {
|
||||
return (
|
||||
<div>
|
||||
<button className="danger" onClick={deleteArticle}>
|
||||
Delete
|
||||
</button>
|
||||
<button className="warning" onClick={editArticle}>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ import React from "react";
|
||||
import { apiUrl } from "@/global";
|
||||
import urlJoin from "url-join";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
export default function CategoryControl({ categoryId }: { categoryId: string }) {
|
||||
const { data: session } = useSession();
|
||||
|
||||
const router = useRouter();
|
||||
async function deleteCategory() {
|
||||
await fetch(urlJoin(apiUrl, `categories/${categoryId}`), { method: "DELETE" })
|
||||
@@ -18,14 +21,18 @@ export default function CategoryControl({ categoryId }: { categoryId: string })
|
||||
router.push("/admin/editor/category/" + categoryId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="danger" onClick={deleteCategory}>
|
||||
Delete
|
||||
</button>
|
||||
<button className="warning" onClick={editCategory}>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
if (session) {
|
||||
return (
|
||||
<div>
|
||||
<button className="danger" onClick={deleteCategory}>
|
||||
Delete
|
||||
</button>
|
||||
<button className="warning" onClick={editCategory}>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function Nav() {
|
||||
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z" />
|
||||
</svg>
|
||||
</div>
|
||||
<input onInput={handleSearchInput} type="text" name="" id="" />
|
||||
<input onChange={handleSearchInput} type="text" name="" id="" />
|
||||
</div>
|
||||
<div className={styles.searchResults}>
|
||||
<div className={styles.content}>
|
||||
|
||||
297
package-lock.json
generated
297
package-lock.json
generated
@@ -9,13 +9,14 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.6",
|
||||
"@prisma/client": "^4.9.0",
|
||||
"@prisma/client": "^4.10.0",
|
||||
"@types/node": "18.11.19",
|
||||
"@types/react": "18.0.27",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"eslint": "8.33.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"next": "13.1.6",
|
||||
"next-auth": "^4.19.2",
|
||||
"next-superjson-plugin": "^0.5.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@@ -26,13 +27,14 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-stringify": "^10.0.2",
|
||||
"sass": "^1.58.0",
|
||||
"swr": "^2.0.3",
|
||||
"typescript": "4.9.5",
|
||||
"url-join": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-select": "^5.0.1",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"prisma": "^4.9.0"
|
||||
"prisma": "^4.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@@ -964,6 +966,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@panva/hkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/utils": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz",
|
||||
@@ -984,12 +994,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
|
||||
"integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.10.0.tgz",
|
||||
"integrity": "sha512-sBmYb1S6SMKFIESaLMfKqWSalv3pH73cMCsFt9HslJvYjIIcKQCA6PDL2O4SZGWvc4JBef9cg5Gd7d9x3AtKjw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5"
|
||||
"@prisma/engines-version": "4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
@@ -1004,16 +1014,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz",
|
||||
"integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.10.0.tgz",
|
||||
"integrity": "sha512-ZPPo7q+nQZdTlPFedS7mFXPE3oZ2kWtTh3GO4sku0XQ8ikLqEyinuTPJbQCw/8qel2xglIEQicsK6yI4Jgh20A==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz",
|
||||
"integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA=="
|
||||
"version": "4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d.tgz",
|
||||
"integrity": "sha512-UVpmVlvSaGfY4ue+hh8CTkIesbuXCFUfrr8zk//+u85WwkKfWMtt6nLB2tNSzR1YO8eAA8+HqNf8LM7mnXIq5w=="
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
@@ -1660,6 +1670,14 @@
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-anything": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz",
|
||||
@@ -3414,6 +3432,14 @@
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.11.4.tgz",
|
||||
"integrity": "sha512-94FdcR8felat4vaTJyL/WVdtlWLlsnLMZP8v+A0Vru18K3bQ22vn7TtpVh3JlgBFNIlYOUlGqwp/MjRPOnIyCQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
|
||||
@@ -4474,6 +4500,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-auth": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.19.2.tgz",
|
||||
"integrity": "sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@panva/hkdf": "^1.0.1",
|
||||
"cookie": "^0.5.0",
|
||||
"jose": "^4.9.3",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.1.0",
|
||||
"preact": "^10.6.3",
|
||||
"preact-render-to-string": "^5.1.19",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12.2.5 || ^13",
|
||||
"nodemailer": "^6.6.5",
|
||||
"react": "^17.0.2 || ^18",
|
||||
"react-dom": "^17.0.2 || ^18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"nodemailer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-superjson-plugin": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/next-superjson-plugin/-/next-superjson-plugin-0.5.4.tgz",
|
||||
@@ -4500,6 +4553,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -4508,6 +4566,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
@@ -4613,6 +4679,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/oidc-token-hash": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz",
|
||||
"integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==",
|
||||
"engines": {
|
||||
"node": "^10.13.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -4637,6 +4711,20 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/openid-client": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.0.tgz",
|
||||
"integrity": "sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==",
|
||||
"dependencies": {
|
||||
"jose": "^4.10.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.0.1",
|
||||
"oidc-token-hash": "^5.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -4811,6 +4899,26 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.12.0",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.0.tgz",
|
||||
"integrity": "sha512-+w8ix+huD8CNZemheC53IPjMUFk921i02o30u0K6h53spMX41y/QhVDnG/nU2k42/69tvqWmVsgNLIiwRAcmxg==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/preact-render-to-string": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
|
||||
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
|
||||
"dependencies": {
|
||||
"pretty-format": "^3.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"preact": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -4819,14 +4927,19 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
||||
"integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.10.0.tgz",
|
||||
"integrity": "sha512-xUHcF3Glc8QGgW8x0rfPITvyyTo04fskUdG7pI4kQbvDX/rhzDP4046x/FvazYqYHXMLR5/KTIi2p2Gth5vKOQ==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.9.0"
|
||||
"@prisma/engines": "4.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
@@ -5491,6 +5604,20 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/swr": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.0.3.tgz",
|
||||
"integrity": "sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": "7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
|
||||
@@ -5803,6 +5930,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/uvu": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
|
||||
@@ -6616,6 +6759,11 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@panva/hkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA=="
|
||||
},
|
||||
"@pkgr/utils": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz",
|
||||
@@ -6630,23 +6778,23 @@
|
||||
}
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
|
||||
"integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.10.0.tgz",
|
||||
"integrity": "sha512-sBmYb1S6SMKFIESaLMfKqWSalv3pH73cMCsFt9HslJvYjIIcKQCA6PDL2O4SZGWvc4JBef9cg5Gd7d9x3AtKjw==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5"
|
||||
"@prisma/engines-version": "4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz",
|
||||
"integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.10.0.tgz",
|
||||
"integrity": "sha512-ZPPo7q+nQZdTlPFedS7mFXPE3oZ2kWtTh3GO4sku0XQ8ikLqEyinuTPJbQCw/8qel2xglIEQicsK6yI4Jgh20A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz",
|
||||
"integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA=="
|
||||
"version": "4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.10.0-84.ca7fcef713137fa11029d519a9780db130cca91d.tgz",
|
||||
"integrity": "sha512-UVpmVlvSaGfY4ue+hh8CTkIesbuXCFUfrr8zk//+u85WwkKfWMtt6nLB2tNSzR1YO8eAA8+HqNf8LM7mnXIq5w=="
|
||||
},
|
||||
"@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
@@ -7103,6 +7251,11 @@
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||
},
|
||||
"copy-anything": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz",
|
||||
@@ -8345,6 +8498,11 @@
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"jose": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.11.4.tgz",
|
||||
"integrity": "sha512-94FdcR8felat4vaTJyL/WVdtlWLlsnLMZP8v+A0Vru18K3bQ22vn7TtpVh3JlgBFNIlYOUlGqwp/MjRPOnIyCQ=="
|
||||
},
|
||||
"js-sdsl": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
|
||||
@@ -9015,6 +9173,22 @@
|
||||
"styled-jsx": "5.1.1"
|
||||
}
|
||||
},
|
||||
"next-auth": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.19.2.tgz",
|
||||
"integrity": "sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@panva/hkdf": "^1.0.1",
|
||||
"cookie": "^0.5.0",
|
||||
"jose": "^4.9.3",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.1.0",
|
||||
"preact": "^10.6.3",
|
||||
"preact-render-to-string": "^5.1.19",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"next-superjson-plugin": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/next-superjson-plugin/-/next-superjson-plugin-0.5.4.tgz",
|
||||
@@ -9034,11 +9208,21 @@
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||
},
|
||||
"oauth": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
@@ -9108,6 +9292,11 @@
|
||||
"es-abstract": "^1.20.4"
|
||||
}
|
||||
},
|
||||
"oidc-token-hash": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz",
|
||||
"integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -9126,6 +9315,17 @@
|
||||
"is-wsl": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"openid-client": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.0.tgz",
|
||||
"integrity": "sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==",
|
||||
"requires": {
|
||||
"jose": "^4.10.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.0.1",
|
||||
"oidc-token-hash": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -9239,18 +9439,36 @@
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.12.0",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.0.tgz",
|
||||
"integrity": "sha512-+w8ix+huD8CNZemheC53IPjMUFk921i02o30u0K6h53spMX41y/QhVDnG/nU2k42/69tvqWmVsgNLIiwRAcmxg=="
|
||||
},
|
||||
"preact-render-to-string": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
|
||||
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
|
||||
"requires": {
|
||||
"pretty-format": "^3.8.0"
|
||||
}
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
||||
},
|
||||
"pretty-format": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
||||
"integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.10.0.tgz",
|
||||
"integrity": "sha512-xUHcF3Glc8QGgW8x0rfPITvyyTo04fskUdG7pI4kQbvDX/rhzDP4046x/FvazYqYHXMLR5/KTIi2p2Gth5vKOQ==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.9.0"
|
||||
"@prisma/engines": "4.10.0"
|
||||
}
|
||||
},
|
||||
"prismjs": {
|
||||
@@ -9698,6 +9916,14 @@
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
},
|
||||
"swr": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.0.3.tgz",
|
||||
"integrity": "sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==",
|
||||
"requires": {
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"synckit": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
|
||||
@@ -9907,6 +10133,17 @@
|
||||
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"requires": {}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
},
|
||||
"uvu": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
|
||||
|
||||
@@ -10,13 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.6",
|
||||
"@prisma/client": "^4.9.0",
|
||||
"@prisma/client": "^4.10.0",
|
||||
"@types/node": "18.11.19",
|
||||
"@types/react": "18.0.27",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"eslint": "8.33.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"next": "13.1.6",
|
||||
"next-auth": "^4.19.2",
|
||||
"next-superjson-plugin": "^0.5.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@@ -27,12 +28,13 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-stringify": "^10.0.2",
|
||||
"sass": "^1.58.0",
|
||||
"swr": "^2.0.3",
|
||||
"typescript": "4.9.5",
|
||||
"url-join": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-select": "^5.0.1",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"prisma": "^4.9.0"
|
||||
"prisma": "^4.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,17 @@ import Footer from "@/components/Footer";
|
||||
import Nav from "@/components/Nav";
|
||||
import { Category } from "@prisma/client";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<SessionProvider session={session}>
|
||||
<header>
|
||||
<Nav />
|
||||
<AdminNav />
|
||||
</header>
|
||||
<Component {...pageProps} />
|
||||
<Footer />
|
||||
</>
|
||||
</SessionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@ import { apiUrl } from "@/global";
|
||||
import Markdown from "@/components/Markdown";
|
||||
import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma";
|
||||
import { CLIENT_RENEG_LIMIT } from "tls";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
export default function AdminArticlesEditorPage({ article, categories }: { article: ArticleWithIncludes | null; categories: CategoryWithIncludes[] }) {
|
||||
const router = useRouter();
|
||||
const { status } = useSession({
|
||||
required: true,
|
||||
});
|
||||
|
||||
const [title, setTitle] = useState<string>(article?.title ?? "");
|
||||
const [selectCategoriesOptions, setSelectCategoriesOptions] = useState<{ value: string; label: string }[]>(
|
||||
@@ -116,132 +120,136 @@ export default function AdminArticlesEditorPage({ article, categories }: { artic
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.adminArticlesCreate}>
|
||||
<h1>{article ? "Update article" : "Create new article"}</h1>
|
||||
<div className={styles.formControl}>
|
||||
<p className="text-error" ref={errorTextRef}></p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (article) {
|
||||
updateArticle();
|
||||
} else {
|
||||
createArticle();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{article ? "Update article" : "Create article"}
|
||||
</button>
|
||||
</div>
|
||||
if (status === "authenticated") {
|
||||
return (
|
||||
<div className={styles.adminArticlesCreate}>
|
||||
<h1>{article ? "Update article" : "Create new article"}</h1>
|
||||
<div className={styles.formControl}>
|
||||
<p className="text-error" ref={errorTextRef}></p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (article) {
|
||||
updateArticle();
|
||||
} else {
|
||||
createArticle();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{article ? "Update article" : "Create article"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.form}>
|
||||
<div className={styles.articleEditor}>
|
||||
<div className={styles.title}>
|
||||
<label htmlFor="title">Title</label>
|
||||
<div className={styles.form}>
|
||||
<div className={styles.articleEditor}>
|
||||
<div className={styles.title}>
|
||||
<label htmlFor="title">Title</label>
|
||||
|
||||
<div className={styles.titleInputs}>
|
||||
<div className={styles.titleInputs}>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value);
|
||||
}}
|
||||
className={!isValidText(title) && title ? "error" : ""}
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="title"
|
||||
ref={titleRef}
|
||||
defaultValue={title}
|
||||
/>
|
||||
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.category}>
|
||||
<label htmlFor="title">Category</label>
|
||||
<Select
|
||||
ref={categorySelectRef}
|
||||
className="react-select-container"
|
||||
classNamePrefix="react-select"
|
||||
options={selectCategoriesOptions}
|
||||
defaultValue={article ? { value: article.category.id, label: article.category.title } : {}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.introduction}>
|
||||
<label htmlFor="title">Introduction</label>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value);
|
||||
setIntroduction(e.target.value);
|
||||
}}
|
||||
className={!isValidText(title) && title ? "error" : ""}
|
||||
className={!isValidText(introduction) && introduction ? "error" : ""}
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="title"
|
||||
ref={titleRef}
|
||||
defaultValue={title}
|
||||
name="introduction"
|
||||
placeholder="Introduction"
|
||||
ref={introductionRef}
|
||||
defaultValue={introduction}
|
||||
/>
|
||||
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.category}>
|
||||
<label htmlFor="title">Category</label>
|
||||
<Select
|
||||
ref={categorySelectRef}
|
||||
className="react-select-container"
|
||||
classNamePrefix="react-select"
|
||||
options={selectCategoriesOptions}
|
||||
defaultValue={article ? { value: article.category.id, label: article.category.title } : {}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.introduction}>
|
||||
<label htmlFor="title">Introduction</label>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setIntroduction(e.target.value);
|
||||
}}
|
||||
className={!isValidText(introduction) && introduction ? "error" : ""}
|
||||
type="text"
|
||||
name="introduction"
|
||||
placeholder="Introduction"
|
||||
ref={introductionRef}
|
||||
defaultValue={introduction}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.markdown}>
|
||||
<label htmlFor="">Markdown Editor</label>
|
||||
<div className={styles.markdownEditor}>
|
||||
<textarea
|
||||
ref={markdownTextAreaRef}
|
||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setMarkdown(e.target.value);
|
||||
}}
|
||||
defaultValue={markdown}
|
||||
></textarea>
|
||||
<Markdown value={markdown} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.contentTable}>
|
||||
<label htmlFor="">Table of contents</label>
|
||||
<div className={styles.contentTableEditor}>
|
||||
<div className={styles.entries}>
|
||||
{contentTable?.map((entry: IContentTableEntry, i: number) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
changeContentTableEntryAnchor(i, e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder={"Anchor"}
|
||||
defaultValue={entry.anchor}
|
||||
/>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
changeContentTableEntryTitle(i, e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder={"Title"}
|
||||
defaultValue={entry.title}
|
||||
/>{" "}
|
||||
<button
|
||||
onClick={() => {
|
||||
removeEntry(i);
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
setContentTable([...contentTable, { title: "", anchor: "" }]);
|
||||
<div className={styles.markdown}>
|
||||
<label htmlFor="">Markdown Editor</label>
|
||||
<div className={styles.markdownEditor}>
|
||||
<textarea
|
||||
ref={markdownTextAreaRef}
|
||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setMarkdown(e.target.value);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
defaultValue={markdown}
|
||||
></textarea>
|
||||
<Markdown value={markdown} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.contentTable}>
|
||||
<label htmlFor="">Table of contents</label>
|
||||
<div className={styles.contentTableEditor}>
|
||||
<div className={styles.entries}>
|
||||
{contentTable?.map((entry: IContentTableEntry, i: number) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
changeContentTableEntryAnchor(i, e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder={"Anchor"}
|
||||
defaultValue={entry.anchor}
|
||||
/>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
changeContentTableEntryTitle(i, e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder={"Title"}
|
||||
defaultValue={entry.title}
|
||||
/>{" "}
|
||||
<button
|
||||
onClick={() => {
|
||||
removeEntry(i);
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
setContentTable([...contentTable, { title: "", anchor: "" }]);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
<Markdown value={markdown} />
|
||||
</div>
|
||||
<Markdown value={markdown} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
|
||||
@@ -9,8 +9,13 @@ import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { apiUrl } from "@/global";
|
||||
import prisma, { CategoryWithIncludes } from "@/lib/prisma";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
export default function AdminCategoriesEditor({ category }: { category: CategoryWithIncludes | null }) {
|
||||
const { status } = useSession({
|
||||
required: true,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const [title, setTitle] = useState<string>(category?.title ?? "");
|
||||
const [color, setColor] = useState<string>(category?.color ?? "");
|
||||
@@ -85,80 +90,84 @@ export default function AdminCategoriesEditor({ category }: { category: Category
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.categoryEditor}>
|
||||
<h1>{category ? "Update category" : "Create new category"}</h1>
|
||||
<div className={styles.formControl}>
|
||||
<p className="text-error" ref={errorTextRef}></p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (category) {
|
||||
updateCategory();
|
||||
} else {
|
||||
createCategory();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{category ? "Update category" : "Create category"}
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.form}>
|
||||
<div className={styles.title}>
|
||||
<label htmlFor="title">Title</label>
|
||||
<div className={styles.titleInputs}>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value);
|
||||
}}
|
||||
className={!isValidText(title) && title ? "error" : ""}
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="title"
|
||||
ref={titleRef}
|
||||
defaultValue={title}
|
||||
/>
|
||||
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
|
||||
</div>
|
||||
<div className={styles.svg}>
|
||||
<label>SVG</label>
|
||||
<div className={styles.svgInputs}>
|
||||
if (status === "authenticated") {
|
||||
return (
|
||||
<div className={styles.categoryEditor}>
|
||||
<h1>{category ? "Update category" : "Create new category"}</h1>
|
||||
<div className={styles.formControl}>
|
||||
<p className="text-error" ref={errorTextRef}></p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (category) {
|
||||
updateCategory();
|
||||
} else {
|
||||
createCategory();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{category ? "Update category" : "Create category"}
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.form}>
|
||||
<div className={styles.title}>
|
||||
<label htmlFor="title">Title</label>
|
||||
<div className={styles.titleInputs}>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSvgPath(e.target.value);
|
||||
setTitle(e.target.value);
|
||||
}}
|
||||
className={!isValidText(title) && title ? "error" : ""}
|
||||
type="text"
|
||||
placeholder="svg path"
|
||||
ref={svgPathRef}
|
||||
defaultValue={svgPath}
|
||||
name="title"
|
||||
placeholder="title"
|
||||
ref={titleRef}
|
||||
defaultValue={title}
|
||||
/>
|
||||
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
|
||||
</div>
|
||||
<div className={styles.svg}>
|
||||
<label>SVG</label>
|
||||
<div className={styles.svgInputs}>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSvgPath(e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder="svg path"
|
||||
ref={svgPathRef}
|
||||
defaultValue={svgPath}
|
||||
/>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSvgViewbox(e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder="0 0 512 512"
|
||||
ref={svgViewboxRef}
|
||||
defaultValue={svgViewbox}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.color}>
|
||||
<label>Color</label>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSvgViewbox(e.target.value);
|
||||
setColor(e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
placeholder="0 0 512 512"
|
||||
ref={svgViewboxRef}
|
||||
defaultValue={svgViewbox}
|
||||
type="color"
|
||||
ref={colorRef}
|
||||
defaultValue={color}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.color}>
|
||||
<label>Color</label>
|
||||
<input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setColor(e.target.value);
|
||||
}}
|
||||
type="color"
|
||||
ref={colorRef}
|
||||
defaultValue={color}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
|
||||
@@ -6,66 +6,72 @@ import prisma, { ArticleWithIncludes } from "../../../lib/prisma";
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { UpdateArticle } from "../../../types/api";
|
||||
import { isValidText } from "../../../validators";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "")
|
||||
|
||||
if (req.method == "PUT") {//* PUT
|
||||
console.log("PUT")
|
||||
const session = await getServerSession(req, res, authOptions);
|
||||
if (session) {
|
||||
const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "")
|
||||
if (req.method == "PUT") {//* PUT
|
||||
console.log("PUT")
|
||||
|
||||
|
||||
console.log(`API articleId: ${articleId}`)
|
||||
const articleData: UpdateArticle = req.body;
|
||||
console.log(`API articleId: ${articleId}`)
|
||||
const articleData: UpdateArticle = req.body;
|
||||
|
||||
if (articleData.title && !isValidText(articleData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (articleData.introduction && !isValidText(articleData.introduction)) {
|
||||
res.json({ target: "introduction", error: "Not a valid introduction" });
|
||||
return;
|
||||
}
|
||||
|
||||
const newArticle: Prisma.ArticleUncheckedUpdateInput = {
|
||||
title: articleData.title ?? undefined,
|
||||
name: articleData.title ? formatTextToUrlName(articleData.title) : undefined,
|
||||
introduction: articleData.introduction ?? undefined,
|
||||
|
||||
categoryId: articleData.categoryId ?? undefined,
|
||||
contentTable: articleData.contentTable ?? undefined,
|
||||
markdown: articleData.markdown ?? undefined,
|
||||
imageUrl: articleData.imageUrl ?? undefined,
|
||||
}
|
||||
console.log(newArticle)
|
||||
await prisma.article.update({ data: newArticle, where: { id: articleId }, include: { category: true } })
|
||||
.then(
|
||||
(data) => {
|
||||
res.json({ success: true, data: data });
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
} else if (req.method == "DELETE") {
|
||||
console.log("DELETE article")
|
||||
prisma.article.delete({ where: { id: articleId }, include: { category: true } }).then((result: ArticleWithIncludes | null) => {
|
||||
if (result) {
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
res.status(500).json({ error: true, message: "No article found" })
|
||||
if (articleData.title && !isValidText(articleData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
res.status(500).end(err)
|
||||
})
|
||||
|
||||
if (articleData.introduction && !isValidText(articleData.introduction)) {
|
||||
res.json({ target: "introduction", error: "Not a valid introduction" });
|
||||
return;
|
||||
}
|
||||
|
||||
const newArticle: Prisma.ArticleUncheckedUpdateInput = {
|
||||
title: articleData.title ?? undefined,
|
||||
name: articleData.title ? formatTextToUrlName(articleData.title) : undefined,
|
||||
introduction: articleData.introduction ?? undefined,
|
||||
|
||||
categoryId: articleData.categoryId ?? undefined,
|
||||
contentTable: articleData.contentTable ?? undefined,
|
||||
markdown: articleData.markdown ?? undefined,
|
||||
imageUrl: articleData.imageUrl ?? undefined,
|
||||
}
|
||||
console.log(newArticle)
|
||||
await prisma.article.update({ data: newArticle, where: { id: articleId }, include: { category: true } })
|
||||
.then(
|
||||
(data) => {
|
||||
res.json({ success: true, data: data });
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
} else if (req.method == "DELETE") {
|
||||
console.log("DELETE article")
|
||||
prisma.article.delete({ where: { id: articleId }, include: { category: true } }).then((result: ArticleWithIncludes | null) => {
|
||||
if (result) {
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
res.status(500).json({ error: true, message: "No article found" })
|
||||
}
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
res.status(500).end(err)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
res.status(403).json({ error: true, message: "Authorization Required" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,56 +6,64 @@ import { isValidText } from "../../../validators";
|
||||
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { getServerSession } from 'next-auth';
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method == "POST") { //* POST
|
||||
console.log("API new article")
|
||||
const articleData: CreateArticle = req.body
|
||||
console.log(articleData)
|
||||
const session = await getServerSession(req, res, authOptions);
|
||||
if (session) {
|
||||
if (req.method == "POST") { //* POST
|
||||
console.log("API new article")
|
||||
const articleData: CreateArticle = req.body
|
||||
console.log(articleData)
|
||||
|
||||
|
||||
if (!isValidText(articleData.title)) {
|
||||
res.status(500).json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
if (!isValidText(articleData.title)) {
|
||||
res.status(500).json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidText(articleData.introduction)) {
|
||||
res.status(500).json({ target: "introduction", error: "Not a valid introduction" });
|
||||
return;
|
||||
}
|
||||
if (!isValidText(articleData.introduction)) {
|
||||
res.status(500).json({ target: "introduction", error: "Not a valid introduction" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!articleData.categoryId) {
|
||||
res.status(500).json({ target: "category", error: "Category is required" });
|
||||
return;
|
||||
}
|
||||
if (!articleData.categoryId) {
|
||||
res.status(500).json({ target: "category", error: "Category is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
const newArticle: Prisma.ArticleUncheckedCreateInput = {
|
||||
title: articleData.title,
|
||||
name: formatTextToUrlName(articleData.title),
|
||||
introduction: articleData.introduction,
|
||||
categoryId: articleData.categoryId,
|
||||
markdown: articleData.markdown ?? "",
|
||||
contentTable: articleData.contentTable ?? {},
|
||||
imageUrl: articleData.imageUrl ?? ""
|
||||
}
|
||||
const newArticle: Prisma.ArticleUncheckedCreateInput = {
|
||||
title: articleData.title,
|
||||
name: formatTextToUrlName(articleData.title),
|
||||
introduction: articleData.introduction,
|
||||
categoryId: articleData.categoryId,
|
||||
markdown: articleData.markdown ?? "",
|
||||
contentTable: articleData.contentTable ?? {},
|
||||
imageUrl: articleData.imageUrl ?? ""
|
||||
}
|
||||
|
||||
prisma.article
|
||||
.create({ data: newArticle, include: { category: true } })
|
||||
.then(
|
||||
(data: ArticleWithIncludes) => {
|
||||
res.json({ success: true, data: data });
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log("reason", errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
prisma.article
|
||||
.create({ data: newArticle, include: { category: true } })
|
||||
.then(
|
||||
(data: ArticleWithIncludes) => {
|
||||
res.json({ success: true, data: data });
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log("reason", errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.status(403).json({ error: true, message: "Authorization Required" });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
30
pages/api/auth/[...nextauth].ts
Normal file
30
pages/api/auth/[...nextauth].ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import NextAuth, { AuthOptions } from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
export const authOptions: AuthOptions = {
|
||||
// Configure one or more authentication providers
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: "1afc604704e6ac0149e3", //! env vars
|
||||
clientSecret: "b8f76990fc0a9181eaba23359a27b2d140ab67e7", //! env vars
|
||||
}),
|
||||
// ...add more providers here
|
||||
], callbacks: {
|
||||
async signIn({ user, account, profile, email, credentials }) {
|
||||
|
||||
if (user.id.toString() == "76851529") { //! env vars
|
||||
return true
|
||||
} else {
|
||||
// Return false to display a default error message
|
||||
return false
|
||||
// Or you can return a URL to redirect to:
|
||||
// return '/unauthorized'
|
||||
}
|
||||
}
|
||||
},
|
||||
secret: "@AWeFkHpv!jzVr^a9nRXS8^PcRFnDaLvt65mJb&*C^pcCgpbHFzzKN",
|
||||
|
||||
|
||||
|
||||
}
|
||||
export default NextAuth(authOptions)
|
||||
|
||||
@@ -7,76 +7,83 @@ import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { formatTextToUrlName } from "../../../utils";
|
||||
import { isValidText } from "../../../validators";
|
||||
import { UpdateCategory } from '../../../types/api';
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const categoryId: string = req.query.categoryId?.toString() ?? "";
|
||||
|
||||
if (req.method == "PUT") {
|
||||
const session = await getServerSession(req, res, authOptions);
|
||||
if (session) {
|
||||
if (req.method == "PUT") {
|
||||
|
||||
const categoryData: UpdateCategory = req.body;
|
||||
const categoryData: UpdateCategory = req.body;
|
||||
|
||||
if (categoryData.title && !isValidText(categoryData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
|
||||
const newSvg: Prisma.SvgUncheckedUpdateInput = {
|
||||
viewbox: categoryData.svg?.viewbox ?? undefined,
|
||||
path: categoryData.svg?.path ?? undefined,
|
||||
};
|
||||
|
||||
const newCategory: Prisma.CategoryUncheckedUpdateInput = {
|
||||
title: categoryData.title ?? undefined,
|
||||
name: categoryData.title ? formatTextToUrlName(categoryData.title) : undefined,
|
||||
color: categoryData.color ?? undefined,
|
||||
};
|
||||
|
||||
await prisma.category
|
||||
.update({
|
||||
data: newCategory,
|
||||
where: { id: categoryId },
|
||||
include: { svg: true },
|
||||
})
|
||||
.then(
|
||||
async (categoryData) => {
|
||||
await prisma.svg
|
||||
.update({ data: newSvg, where: { id: categoryData.svg.id } })
|
||||
.then(
|
||||
(svgData) => {
|
||||
console.log("3");
|
||||
res.json({ success: true, data: categoryData });
|
||||
},
|
||||
(errorReason) => {
|
||||
res.status(500).end(errorReason);
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason);
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
} else if (req.method == "DELETE") {
|
||||
console.log("DELETE category")
|
||||
prisma.category.delete({ where: { id: categoryId }, include: { articles: true, svg: true } }).then((result: CategoryWithIncludes | null) => {
|
||||
if (result) {
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
res.status(500).json({ error: true, message: "No category found" })
|
||||
if (categoryData.title && !isValidText(categoryData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
res.status(500).end(err)
|
||||
})
|
||||
|
||||
const newSvg: Prisma.SvgUncheckedUpdateInput = {
|
||||
viewbox: categoryData.svg?.viewbox ?? undefined,
|
||||
path: categoryData.svg?.path ?? undefined,
|
||||
};
|
||||
|
||||
const newCategory: Prisma.CategoryUncheckedUpdateInput = {
|
||||
title: categoryData.title ?? undefined,
|
||||
name: categoryData.title ? formatTextToUrlName(categoryData.title) : undefined,
|
||||
color: categoryData.color ?? undefined,
|
||||
};
|
||||
|
||||
await prisma.category
|
||||
.update({
|
||||
data: newCategory,
|
||||
where: { id: categoryId },
|
||||
include: { svg: true },
|
||||
})
|
||||
.then(
|
||||
async (categoryData) => {
|
||||
await prisma.svg
|
||||
.update({ data: newSvg, where: { id: categoryData.svg.id } })
|
||||
.then(
|
||||
(svgData) => {
|
||||
console.log("3");
|
||||
res.json({ success: true, data: categoryData });
|
||||
},
|
||||
(errorReason) => {
|
||||
res.status(500).end(errorReason);
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason);
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
} else if (req.method == "DELETE") {
|
||||
console.log("DELETE category")
|
||||
prisma.category.delete({ where: { id: categoryId }, include: { articles: true, svg: true } }).then((result: CategoryWithIncludes | null) => {
|
||||
if (result) {
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
res.status(500).json({ error: true, message: "No category found" })
|
||||
}
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
res.status(500).end(err)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
res.status(403).json({ error: true, message: "Authorization Required" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,12 @@ import { isValidText } from "../../../validators";
|
||||
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { CreateCategory } from "@/types/api";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await getServerSession(req, res, authOptions);
|
||||
|
||||
if (req.method == "GET") {
|
||||
console.log("API get categories")
|
||||
await prisma.category.findMany().then((result: Category[]) => {
|
||||
@@ -22,65 +26,69 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
})
|
||||
} else
|
||||
if (req.method == "POST") {
|
||||
console.log("API new category")
|
||||
const categoryData: CreateCategory = req.body;
|
||||
console.log(categoryData)
|
||||
if (session) {
|
||||
console.log("API new category")
|
||||
const categoryData: CreateCategory = req.body;
|
||||
console.log(categoryData)
|
||||
|
||||
if (!isValidText(categoryData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
if (!isValidText(categoryData.title)) {
|
||||
res.json({ target: "title", error: "Not a valid title" });
|
||||
return;
|
||||
}
|
||||
|
||||
categoryData.svg.viewbox = categoryData.svg.viewbox.length > 1 ? categoryData.svg.viewbox : "";
|
||||
categoryData.svg.viewbox = categoryData.svg.viewbox.length > 1 ? categoryData.svg.viewbox : "";
|
||||
|
||||
const newSvg: Prisma.SvgUncheckedCreateInput = {
|
||||
viewbox: categoryData.svg.viewbox,
|
||||
path: categoryData.svg.path
|
||||
}
|
||||
const newSvg: Prisma.SvgUncheckedCreateInput = {
|
||||
viewbox: categoryData.svg.viewbox,
|
||||
path: categoryData.svg.path
|
||||
}
|
||||
|
||||
await prisma.svg
|
||||
.create({ data: newSvg })
|
||||
.then(
|
||||
async (createdSvg: Svg) => {
|
||||
const newCategory: Prisma.CategoryUncheckedCreateInput = {
|
||||
title: categoryData.title,
|
||||
name: formatTextToUrlName(categoryData.title),
|
||||
color: categoryData.color ?? "teal",
|
||||
svgId: createdSvg.id,
|
||||
await prisma.svg
|
||||
.create({ data: newSvg })
|
||||
.then(
|
||||
async (createdSvg: Svg) => {
|
||||
const newCategory: Prisma.CategoryUncheckedCreateInput = {
|
||||
title: categoryData.title,
|
||||
name: formatTextToUrlName(categoryData.title),
|
||||
color: categoryData.color ?? "teal",
|
||||
svgId: createdSvg.id,
|
||||
}
|
||||
|
||||
await prisma.category
|
||||
.create({
|
||||
data: newCategory,
|
||||
include: { svg: true, articles: true },
|
||||
})
|
||||
.then(
|
||||
(createdCategory: CategoryWithIncludes | null) => {
|
||||
if (createdCategory) {
|
||||
res.json({ success: true, data: createdCategory });
|
||||
} else {
|
||||
res.json({ error: true, message: "Could not create category" });
|
||||
}
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
},
|
||||
(errorReason) => {
|
||||
res.status(500).end(errorReason);
|
||||
}
|
||||
|
||||
await prisma.category
|
||||
.create({
|
||||
data: newCategory,
|
||||
include: { svg: true, articles: true },
|
||||
})
|
||||
.then(
|
||||
(createdCategory: CategoryWithIncludes | null) => {
|
||||
if (createdCategory) {
|
||||
res.json({ success: true, data: createdCategory });
|
||||
} else {
|
||||
res.json({ error: true, message: "Could not create category" });
|
||||
}
|
||||
},
|
||||
(errorReason) => {
|
||||
console.log(errorReason)
|
||||
if (errorReason.code === "P2002") {
|
||||
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
},
|
||||
(errorReason) => {
|
||||
res.status(500).end(errorReason);
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).end();
|
||||
});
|
||||
} else {
|
||||
res.status(403).json({ error: true, message: "Authorization Required" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma"
|
||||
import ArticleControl from "../../../../components/ArticleControl";
|
||||
import { IContentTableEntry } from "@/types/contentTable";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { useSession, signIn, signOut } from "next-auth/react";
|
||||
import { CLIENT_RENEG_LIMIT } from "tls";
|
||||
|
||||
//* MAIN
|
||||
export default function ArticlePage({ article }: { article: ArticleWithIncludes }) {
|
||||
const dateUpdated: Date = new Date(article?.dateUpdated);
|
||||
const dateCreated: Date = new Date(article?.dateCreated);
|
||||
const dateOptions: Intl.DateTimeFormatOptions = { month: "long", day: "numeric", year: "numeric" };
|
||||
|
||||
return (
|
||||
<>
|
||||
<ArticleControl articleId={article.id} />
|
||||
|
||||
@@ -27,7 +27,7 @@ model Category {
|
||||
title String @unique
|
||||
color String
|
||||
svgId String
|
||||
svg Svg @relation(fields: [svgId], references: [id])
|
||||
svg Svg @relation(fields: [svgId], references: [id], onDelete: Cascade)
|
||||
articles Article[]
|
||||
dateCreated DateTime @default(now())
|
||||
dateUpdated DateTime @default(now())
|
||||
|
||||
Reference in New Issue
Block a user