Compare commits
	
		
			10 Commits
		
	
	
		
			b68462a815
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ce72dd40a8 | |||
| 0b3a178715 | |||
| d61797a748 | |||
| 82b1a82aac | |||
| 5b1307a662 | |||
| 8f1461ce2b | |||
| 6195f80c4a | |||
| fce1a6aa71 | |||
| 934f8960cd | |||
| e55b42fdac | 
@@ -1,6 +1,6 @@
 | 
			
		||||
<mxfile host="Electron" modified="2023-11-04T11:34:00.376Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="zK0hocMrlzpD75Dksgz9" version="22.0.2" type="device">
 | 
			
		||||
<mxfile host="Electron" modified="2023-11-16T17:33:19.287Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="ZbU5Z7KAwFtN-KRkF2Te" version="22.0.2" type="device">
 | 
			
		||||
  <diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">
 | 
			
		||||
    <mxGraphModel dx="1669" dy="479" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0" extFonts="Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker">
 | 
			
		||||
    <mxGraphModel dx="1645" dy="474" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0" extFonts="Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker">
 | 
			
		||||
      <root>
 | 
			
		||||
        <mxCell id="0" />
 | 
			
		||||
        <mxCell id="1" parent="0" />
 | 
			
		||||
@@ -62,21 +62,21 @@
 | 
			
		||||
            <mxRectangle width="220" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-1" value="" style="shape=partialRectangle;collapsible=0;dropTarget=0;pointerEvents=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="C-vyLk0tnHw3VtMMgP7b-23">
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-1" value="" style="shape=partialRectangle;collapsible=0;dropTarget=0;pointerEvents=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="C-vyLk0tnHw3VtMMgP7b-23" vertex="1">
 | 
			
		||||
          <mxGeometry y="120" width="250" height="30" as="geometry" />
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-2" value="" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;" vertex="1" parent="lzMxBIVNoJ9j-Thf5CWH-1">
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-2" value="" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;" parent="lzMxBIVNoJ9j-Thf5CWH-1" vertex="1">
 | 
			
		||||
          <mxGeometry width="30" height="30" as="geometry">
 | 
			
		||||
            <mxRectangle width="30" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-3" value="settings JSON NOT NULL" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;" vertex="1" parent="lzMxBIVNoJ9j-Thf5CWH-1">
 | 
			
		||||
        <mxCell id="lzMxBIVNoJ9j-Thf5CWH-3" value="settings JSON NOT NULL" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;" parent="lzMxBIVNoJ9j-Thf5CWH-1" vertex="1">
 | 
			
		||||
          <mxGeometry x="30" width="220" height="30" as="geometry">
 | 
			
		||||
            <mxRectangle width="220" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="IqcACfFecUyFIL0cjSVW-16" value="post" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;" parent="1" vertex="1">
 | 
			
		||||
          <mxGeometry x="470" y="290" width="250" height="210" as="geometry" />
 | 
			
		||||
          <mxGeometry x="470" y="282" width="250" height="240" as="geometry" />
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="IqcACfFecUyFIL0cjSVW-17" value="" style="shape=partialRectangle;collapsible=0;dropTarget=0;pointerEvents=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="IqcACfFecUyFIL0cjSVW-16" vertex="1">
 | 
			
		||||
          <mxGeometry y="30" width="250" height="30" as="geometry" />
 | 
			
		||||
@@ -156,6 +156,19 @@
 | 
			
		||||
            <mxRectangle width="220" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="Z5fAuwPoposVQ_Fsqkp6-1" value="" style="shape=partialRectangle;collapsible=0;dropTarget=0;pointerEvents=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="IqcACfFecUyFIL0cjSVW-16">
 | 
			
		||||
          <mxGeometry y="210" width="250" height="30" as="geometry" />
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="Z5fAuwPoposVQ_Fsqkp6-2" value="" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;" vertex="1" parent="Z5fAuwPoposVQ_Fsqkp6-1">
 | 
			
		||||
          <mxGeometry width="30" height="30" as="geometry">
 | 
			
		||||
            <mxRectangle width="30" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="Z5fAuwPoposVQ_Fsqkp6-3" value="options VARCHAR(255)" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;" vertex="1" parent="Z5fAuwPoposVQ_Fsqkp6-1">
 | 
			
		||||
          <mxGeometry x="30" width="220" height="30" as="geometry">
 | 
			
		||||
            <mxRectangle width="220" height="30" as="alternateBounds" />
 | 
			
		||||
          </mxGeometry>
 | 
			
		||||
        </mxCell>
 | 
			
		||||
        <mxCell id="IqcACfFecUyFIL0cjSVW-23" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERoneToMany;rounded=0;exitX=0.998;exitY=0.676;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="IqcACfFecUyFIL0cjSVW-12" target="C-vyLk0tnHw3VtMMgP7b-3" edge="1">
 | 
			
		||||
          <mxGeometry width="100" height="100" relative="1" as="geometry">
 | 
			
		||||
            <mxPoint x="440" y="270" as="sourcePoint" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,18 @@
 | 
			
		||||
CREATE TABLE boards (
 | 
			
		||||
    board_id VARCHAR(5) PRIMARY KEY,
 | 
			
		||||
    board_name VARCHAR(32) NOT NULL,
 | 
			
		||||
    threads_ids INT8[],
 | 
			
		||||
    options JSON
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE threads (
 | 
			
		||||
    thread_id SERIAL8 PRIMARY KEY,
 | 
			
		||||
    board_id VARCHAR(5) NOT NULL,
 | 
			
		||||
    thread_id BIGINT NOT NULL,
 | 
			
		||||
    thread_name VARCHAR(32),
 | 
			
		||||
    posts_ids BIGINT[],
 | 
			
		||||
    is_locked boolean NOT NULL,
 | 
			
		||||
    is_pinned boolean NOT NULL,
 | 
			
		||||
    options VARCHAR(255)
 | 
			
		||||
    options VARCHAR(255),
 | 
			
		||||
    PRIMARY KEY(board_id, thread_id)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE media (
 | 
			
		||||
@@ -19,12 +20,16 @@ CREATE TABLE media (
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE posts (
 | 
			
		||||
    post_id SERIAL8 PRIMARY KEY,
 | 
			
		||||
    board_id VARCHAR(5) NOT NULL,
 | 
			
		||||
    thread_id BIGINT NOT NULL,
 | 
			
		||||
    post_id BIGINT NOT NULL,
 | 
			
		||||
    options VARCHAR(255),
 | 
			
		||||
    content TEXT NOT NULL,
 | 
			
		||||
    media_ids VARCHAR(22)[],
 | 
			
		||||
    is_root BOOL NOT NULL,
 | 
			
		||||
    timestamp TIMESTAMP NOT NULL,
 | 
			
		||||
    user_ip CIDR NOT NULL 
 | 
			
		||||
    user_ip CIDR NOT NULL,
 | 
			
		||||
    PRIMARY KEY(board_id, thread_id, post_id)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE admins (
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <link rel="icon" href="/favicon.ico">
 | 
			
		||||
    <link rel="icon" href="/favicon.svg">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Vite App</title>
 | 
			
		||||
    <title>Dachan</title>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -12,6 +12,7 @@
 | 
			
		||||
        "@vitejs/plugin-vue": "^4.4.1",
 | 
			
		||||
        "axios": "^1.6.1",
 | 
			
		||||
        "bcryptjs": "^2.4.3",
 | 
			
		||||
        "cors": "^2.8.5",
 | 
			
		||||
        "dotenv": "^16.3.1",
 | 
			
		||||
        "ejs": "^3.1.9",
 | 
			
		||||
        "express": "^4.18.2",
 | 
			
		||||
@@ -779,6 +780,18 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
 | 
			
		||||
      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/cors": {
 | 
			
		||||
      "version": "2.8.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
 | 
			
		||||
      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "object-assign": "^4",
 | 
			
		||||
        "vary": "^1"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 0.10"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/csstype": {
 | 
			
		||||
      "version": "3.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
 | 
			
		||||
@@ -1531,6 +1544,14 @@
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/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==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/object-inspect": {
 | 
			
		||||
      "version": "1.13.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,8 @@
 | 
			
		||||
    "dev": "nodemon src/index.js & vite --host",
 | 
			
		||||
    "build": "vite build",
 | 
			
		||||
    "preview": "vite preview",
 | 
			
		||||
    "start": "node src/index.js & vite --host"
 | 
			
		||||
    "start": "node src/index.js & vite --host",
 | 
			
		||||
    "back": "nodemon src/index.js"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
@@ -22,6 +23,7 @@
 | 
			
		||||
    "@vitejs/plugin-vue": "^4.4.1",
 | 
			
		||||
    "axios": "^1.6.1",
 | 
			
		||||
    "bcryptjs": "^2.4.3",
 | 
			
		||||
    "cors": "^2.8.5",
 | 
			
		||||
    "dotenv": "^16.3.1",
 | 
			
		||||
    "ejs": "^3.1.9",
 | 
			
		||||
    "express": "^4.18.2",
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										80
									
								
								public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 8.8 KiB  | 
@@ -1,49 +0,0 @@
 | 
			
		||||
<script setup>
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <header>
 | 
			
		||||
      <div class="headerIneer">
 | 
			
		||||
          <div class="headerLogo"><h1 class="headerLogoTitle">Dach</h1></div>
 | 
			
		||||
          <div class="headerFaq"><a class="headerFaqTitle" href="faq.html">FAQ</a></div>
 | 
			
		||||
      </div>
 | 
			
		||||
  </header>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
header {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex: 0 0 100%;
 | 
			
		||||
    min-width: 100vh;
 | 
			
		||||
}
 | 
			
		||||
.headerIneer{
 | 
			
		||||
    display: flex;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    border-radius: 1em;
 | 
			
		||||
    background: #171515fb;  /* fallback for old browsers */
 | 
			
		||||
}
 | 
			
		||||
.headerLogo{
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    margin: 0 5%;
 | 
			
		||||
}
 | 
			
		||||
.headerLogoTitle{
 | 
			
		||||
    font-family: 'Tilt Neon', sans-serif;
 | 
			
		||||
    font-size: 2.5em;
 | 
			
		||||
    color: whitesmoke;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.headerFaq{
 | 
			
		||||
    margin: 2%;
 | 
			
		||||
    min-height: 2em;
 | 
			
		||||
    margin-left: auto;
 | 
			
		||||
    margin-right: 5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.headerFaqTitle{
 | 
			
		||||
    font-family: 'Tilt Neon', sans-serif;
 | 
			
		||||
    font-size: 1.5em;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    color: whitesmoke;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										85
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								src/index.js
									
									
									
									
									
								
							@@ -6,6 +6,7 @@ const MemoryStore = require('memorystore')(session);
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const bcrypt = require('bcryptjs');
 | 
			
		||||
// const fileupload = require('express-fileupload');
 | 
			
		||||
const cors = require('cors');
 | 
			
		||||
 | 
			
		||||
const app = express();
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +51,7 @@ let tokens = {};
 | 
			
		||||
 | 
			
		||||
app.use(express.urlencoded({ extended: true }))
 | 
			
		||||
app.use(express.json())
 | 
			
		||||
 | 
			
		||||
app.use(cors());
 | 
			
		||||
 | 
			
		||||
app.use(session({
 | 
			
		||||
    secret: process.env.SESSION_SECRET,
 | 
			
		||||
@@ -67,51 +68,82 @@ app.post('/api/uploadMedia', async (req, res) => {
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/api/getPosts/:boardId/:threadId', async (req, res) => {
 | 
			
		||||
    posts = [];
 | 
			
		||||
    (await db.query('SELECT post_id, content, timestamp, options FROM posts WHERE board_id = $1 AND thread_id = $2', [req.params.boardId, req.params.threadId])).rows
 | 
			
		||||
        .forEach((post) => posts.push(post))
 | 
			
		||||
    
 | 
			
		||||
    res.setHeader('Content-Type', 'application/json');
 | 
			
		||||
    res.end(JSON.stringify(posts));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post('/api/post', async (req, res) => {
 | 
			
		||||
    const { content,  } = req.body;
 | 
			
		||||
    let login = req.session.login, 
 | 
			
		||||
        token = req.session.token, 
 | 
			
		||||
        isAdmin = false;
 | 
			
		||||
    const {options, content, threadId, boardId} = req.body;
 | 
			
		||||
 | 
			
		||||
    if (!threadId || !boardId) return res.status(400).send("Не указано ID треда или доски");
 | 
			
		||||
 | 
			
		||||
    if (login && token) {
 | 
			
		||||
        if (authorize(login, token)) isAdmin = true;
 | 
			
		||||
        else res.status(403).send("Невалидный токен");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let postId = Number((await db.query('SELECT post_id FROM posts WHERE board_id = $1 ORDER BY post_id DESC LIMIT 1', [boardId])).rows[0].post_id) + 1
 | 
			
		||||
    await db.query('INSERT INTO posts(board_id, thread_id, post_id, options, content, media_ids, is_root, timestamp, user_ip) VALUES ($1, $2, $3, $4, $5, \'{}\', false, NOW(), $6)', [boardId, threadId, postId, options, content, req.socket.remoteAddress]);
 | 
			
		||||
    await db.query('UPDATE threads SET posts_ids = ARRAY_APPEND(posts_ids, $1) WHERE thread_id = $2 AND board_id = $3', [postId, threadId, boardId]);
 | 
			
		||||
 
 | 
			
		||||
    res.status(200).send("Пост отправлен"); 
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post('/api/createThread', async (req, res) => {
 | 
			
		||||
    let login, token, isLocked, isPinned
 | 
			
		||||
    let login = req.session.login,
 | 
			
		||||
        token = req.session.token,
 | 
			
		||||
        isLocked,
 | 
			
		||||
        isPinned,
 | 
			
		||||
        isAdmin = false;
 | 
			
		||||
    const { boardId, threadTitle, content, options} = req.body;
 | 
			
		||||
 | 
			
		||||
    isLocked = isLocked? isLocked : false;
 | 
			
		||||
    if (!boardId) return res.status(400).send("Не указано имя доски");
 | 
			
		||||
 | 
			
		||||
    isLocked = isLocked? isLocked : false; // if undefined then false
 | 
			
		||||
    isPinned = isPinned? isPinned : false;
 | 
			
		||||
 | 
			
		||||
    console.log(`Board id: ${boardId}\nThread name: ${threadTitle}\nIs locked: ${isLocked}\nIs pinned: ${isPinned}\nContent: ${content}\nOptions: ${options}`);
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
        let currentSession = req.session;
 | 
			
		||||
        token = currentSession.token;
 | 
			
		||||
        login = currentSession.login;
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        console.log(err);
 | 
			
		||||
    if (login && token) {
 | 
			
		||||
        if (authorize(login, token)) isAdmin = true;
 | 
			
		||||
        else res.status(403).send("Невалидный токен");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const boardOptions = (await db.query('SELECT options FROM boards WHERE board_id = $1', [boardId])).rows[0].options;
 | 
			
		||||
 | 
			
		||||
    if (login && token && token != tokens[login]) return res.status(403).send("Невалидный токен");
 | 
			
		||||
    let isAdmin = token? true : false;
 | 
			
		||||
 | 
			
		||||
    const boardOptions = (await db.query('SELECT * FROM boards WHERE board_id = $1', [boardId])).rows[0].options
 | 
			
		||||
    let postId = (await db.query('SELECT nextval(pg_get_serial_sequence(\'posts\', \'post_id\'))')).rows[0].nextval;
 | 
			
		||||
    let threadId = (await db.query('SELECT nextval(pg_get_serial_sequence(\'threads\', \'thread_id\'))')).rows[0].nextval;
 | 
			
		||||
    let threadId = (await db.query('SELECT EXISTS(SELECT FROM threads WHERE board_id = $1)', [boardId])).rows[0].exists?
 | 
			
		||||
        Number((await db.query('SELECT thread_id FROM threads WHERE board_id = $1 ORDER BY thread_id DESC LIMIT 1', [boardId])).rows[0].thread_id) + 1
 | 
			
		||||
        : 0
 | 
			
		||||
    let postId = (await db.query('SELECT EXISTS(SELECT FROM posts WHERE board_id = $1)', [boardId])).rows[0].exists?
 | 
			
		||||
        Number((await db.query('SELECT post_id FROM posts WHERE board_id = $1 ORDER BY post_id DESC LIMIT 1', [boardId])).rows[0].post_id) + 1
 | 
			
		||||
        : 0
 | 
			
		||||
    console.log(`ThreadId: ${threadId} postId: ${postId}`);
 | 
			
		||||
 | 
			
		||||
    let validateResults = validateThread(threadTitle, isLocked, 
 | 
			
		||||
                         isPinned, content, options, 
 | 
			
		||||
                         boardOptions, isAdmin);
 | 
			
		||||
    if (validateResults != "ok") return res.status(400).send(validateResults);
 | 
			
		||||
 | 
			
		||||
    await db.query('INSERT INTO posts (post_id, content, is_root, timestamp, user_ip) VALUES($1, $2, $3, NOW(), $4)', [postId, content, true, req.socket.remoteAddress]);
 | 
			
		||||
    await db.query('INSERT INTO threads (thread_id, thread_name, posts_ids, is_locked, is_pinned, options) VALUES ($1, $2, $3, $4, $5, $6)', [threadId, threadTitle, [postId], isLocked, isPinned, options]);
 | 
			
		||||
    await db.query('UPDATE boards SET threads_ids = ARRAY_APPEND(threads_ids, $1) WHERE board_id = $2', [threadId, boardId]);
 | 
			
		||||
    await db.query('INSERT INTO posts (board_id, thread_id, post_id, content, is_root, timestamp, user_ip) VALUES($1, $2, $3, $4, $5, NOW(), $6)', [boardId, threadId, postId, content, true, req.socket.remoteAddress]);
 | 
			
		||||
    await db.query('INSERT INTO threads (board_id, thread_id, thread_name, posts_ids, is_locked, is_pinned, options) VALUES ($1, $2, $3, $4, $5, $6, $7)', [boardId, threadId, threadTitle, [postId], isLocked, isPinned, options]);
 | 
			
		||||
    res.redirect(`/${boardId}/${postId}`);    
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/api/getThreads/:boardId', async (req, res) => {
 | 
			
		||||
    let queryRes = (await db.query('SELECT * FROM boards WHERE board_id = $1', [req.params.boardId])).rows[0];
 | 
			
		||||
 | 
			
		||||
    threads = [];
 | 
			
		||||
    (await db.query('SELECT thread_id FROM threads WHERE board_id = $1', [req.params.boardId])).rows
 | 
			
		||||
        .forEach((thread) => threads.push(thread.thread_id))
 | 
			
		||||
    
 | 
			
		||||
    res.setHeader('Content-Type', 'application/json');
 | 
			
		||||
    res.end(JSON.stringify(queryRes.threads_ids));
 | 
			
		||||
    res.end(JSON.stringify(threads));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/api/getBoards', async (req, res) => {
 | 
			
		||||
@@ -119,6 +151,7 @@ app.get('/api/getBoards', async (req, res) => {
 | 
			
		||||
 | 
			
		||||
    res.setHeader('Content-Type', 'application/json');
 | 
			
		||||
    res.end(JSON.stringify(queryRes.rows));
 | 
			
		||||
    // res.json(JSON.stringify(queryRes.rows));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post('/api/login', async (req, res) => {
 | 
			
		||||
@@ -178,8 +211,12 @@ app.listen(process.env.APP_PORT, () => {
 | 
			
		||||
 | 
			
		||||
const validateThread = (threadName, isLocked, isPinned, content, options, boardOptions, isAdmin) => {
 | 
			
		||||
    if ((isPinned || isLocked) && !isAdmin) return "Нет прав на выставление админских флагов";
 | 
			
		||||
    if (!content) return "Нельзя создать тред без текста";
 | 
			
		||||
    if (!content && boardOptions.requireContentForThreadCreation) return "Нельзя создать тред без текста";
 | 
			
		||||
 | 
			
		||||
    //TODO: check if image is required
 | 
			
		||||
    return 'ok'
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const authorize = (login, token) => {
 | 
			
		||||
    return tokens[login] == token? true : false;
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user