feat: server and fix inline code

This commit is contained in:
Artemy 2023-04-02 10:53:32 +03:00
parent 8bdc53f45d
commit bff9c3875c
9 changed files with 1391 additions and 59 deletions

2
.gitignore vendored
View file

@ -125,3 +125,5 @@ dist
.yarn/build-state.yml .yarn/build-state.yml
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
notes/

61
index.js Normal file
View file

@ -0,0 +1,61 @@
const sha3 = require("js-sha3").sha3_512;
const express = require("express");
const bodyParser = require("body-parser");
const isValidNote = require("./note_validator");
const fs = require("fs");
const path = require("path");
const cryptojs = require("crypto-js");
require("dotenv").config();
const app = express();
app.use(bodyParser.json());
app.post("/publish", function (req, res) {
if (isValidNote(req.body)) {
let hash = sha3(JSON.stringify(req.body));
req.body.time = Date.now();
try {
fs.writeFileSync(
`./notes/${hash}.json`,
cryptojs.AES.encrypt(
JSON.stringify(req.body),
process.env.KEY
).toString()
);
res.send({ id: hash });
} catch {
res.status(500).send("Failed to write file");
}
} else {
res.status(403).send("Invalid body!");
}
});
app.get("/get-note/:delorno/:id", function (req, res) {
let path = `./notes/${req.params.id}.json`;
try {
let data = JSON.parse(
cryptojs.AES.decrypt(
fs.readFileSync(path, "utf-8"),
process.env.KEY
).toString(cryptojs.enc.Utf8)
);
res.send(data);
if (req.params.delorno === "del") fs.unlinkSync(path);
} catch {
res.status(404).send("There is no such note");
}
});
app.use(express.static("dist"));
app.get("*", function (req, res) {
res.sendFile(path.join(__dirname, "./dist", "index.html"));
});
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}`);
});

15
note_validator.js Normal file
View file

@ -0,0 +1,15 @@
const Ajv = require("ajv");
const ajv = new Ajv();
const note_schema = {
type: "object",
properties: {
name: { type: "string", maxLength: 64 },
text: { type: "string", maxLength: 5000 },
},
required: ["name", "text"],
additionalProperties: false,
};
module.exports = (data) => {
return ajv.validate(note_schema, data);
};

1249
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,15 @@
{ {
"name": "anopaper", "name": "anopaper",
"private": true, "private": true,
"version": "0.0.0", "version": "1.0.0",
"type": "module", "author": "anopaper",
"license": "MIT",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build --emptyOutDir", "build": "vite build --emptyOutDir",
"preview": "vite preview" "preview": "vite preview",
"start": "node index",
"bstart": "npm run build && npm run start"
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.0.12", "@heroicons/react": "^2.0.12",
@ -16,7 +19,13 @@
"react-router-dom": "^6.4.2", "react-router-dom": "^6.4.2",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"rehype-mathjax": "^4.0.2", "rehype-mathjax": "^4.0.2",
"remark-math": "^5.1.1" "remark-math": "^5.1.1",
"ajv": "^8.11.0",
"body-parser": "^1.20.1",
"crypto-js": "^4.1.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"js-sha3": "^0.8.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.17", "@types/react": "^18.0.17",

View file

@ -33,7 +33,7 @@ function CopyToClipboard(props) {
); );
} }
function CodeCopyBtn(props) { function CodeCopyBtn({ text }) {
let [copied, setCopied] = useState(false); let [copied, setCopied] = useState(false);
useEffect(() => { useEffect(() => {
@ -48,7 +48,7 @@ function CodeCopyBtn(props) {
<div <div
className="code-copy-btn" className="code-copy-btn"
onClick={() => { onClick={() => {
navigator.clipboard.writeText(props.children[0].props.children[0]); navigator.clipboard.writeText(text);
setCopied(true); setCopied(true);
}} }}
> >

View file

@ -12,14 +12,19 @@ let theme = window.matchMedia("(prefers-color-scheme: dark)").matches
: "light"; : "light";
function CodeBlock(props) { function CodeBlock(props) {
return ( let text = props.children[0];
<SyntaxHighlighter let oneline = text.indexOf("\n") <= 1;
className={`md-code ${props.className}`} if (oneline) {
style={theme == "light" ? github : darcula} return <code className="md-code">{text}</code>;
> } else
{props.children[0]} return (
</SyntaxHighlighter> <SyntaxHighlighter
); className={`md-code ${props.className}`}
style={theme == "light" ? github : darcula}
>
{text}
</SyntaxHighlighter>
);
} }
function RenderMarkdown(props) { function RenderMarkdown(props) {
@ -37,9 +42,12 @@ function RenderMarkdown(props) {
} }
function Pre({ children }) { function Pre({ children }) {
let text = children[0].props.children[0];
let oneline = text.indexOf("\n") <= 1;
console.log(oneline);
return ( return (
<pre className="blog-pre"> <pre className={oneline ? "" : "blog-pre"}>
<CodeCopyBtn>{children}</CodeCopyBtn> {!oneline && <CodeCopyBtn text={text} />}
{children} {children}
</pre> </pre>
); );

View file

@ -3,41 +3,45 @@ import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
import printDate from "../components/utils"; import printDate from "../components/utils";
function Notes() { function Notes() {
return ( let notes = Object.entries(localStorage.getObj("Notes"))
<div> .sort((a, b) => {
{Object.entries(localStorage.getObj("Notes")) return b[1].time - a[1].time;
.sort((a, b) => { })
return b[1].time - a[1].time; .map((val) => {
}) return (
.map((val) => { <div
return ( className="grid grid-cols-1 lg:grid-cols-2 border border-blue-300 rounded-lg m-2 p-2 justify-items-start"
<div key={val[0]}
className="grid grid-cols-1 lg:grid-cols-2 border border-blue-300 rounded-lg m-2 p-2 justify-items-start" >
key={val[0]} <div className="font-medium leading-tight text-4xl mt-0 mb-2">
> {val[1].name}
<div className="font-medium leading-tight text-4xl mt-0 mb-2"> </div>
{val[1].name} <div className="grid grid-cols-1 lg:grid-cols-2 justify-self-center lg:justify-self-end">
</div> <div className="text-center">{printDate(val[1].time)}</div>
<div className="grid grid-cols-1 lg:grid-cols-2 justify-self-center lg:justify-self-end"> <div className="">
<div className="text-center">{printDate(val[1].time)}</div> <Button className="" href={`/notes/${val[0]}`}>
<div className=""> <IconWithText
<Button className="" href={`/notes/${val[0]}`}> reverse={true}
<IconWithText icon={
reverse={true} <ChevronDoubleRightIcon className="transform translate-z-0 h-7 w-7" />
icon={ }
<ChevronDoubleRightIcon className="transform translate-z-0 h-7 w-7" /> >
} Перейти
> </IconWithText>
Перейти </Button>
</IconWithText>
</Button>
</div>
</div>
</div> </div>
); </div>
})} </div>
</div> );
); });
if (notes.length === 0)
return (
<div className="md">
<h3>Заметок пока нет</h3>
</div>
);
return notes;
} }
export default Notes; export default Notes;

View file

@ -5,6 +5,6 @@ import react from "@vitejs/plugin-react";
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
build: { build: {
outDir: "D:/node-js/anopaper-server/public", outDir: "./dist",
}, },
}); });