-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v2.0.0
- Loading branch information
Showing
66 changed files
with
1,912 additions
and
23,128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/node_modules/ | ||
/media/uploads/posts/preview | ||
.idea | ||
/media/uploads/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,77 @@ | ||
<p>Проект React</p> | ||
<p>ide: WebStorm -v 2022.3.2</p> | ||
<p>Выполненые задание находятся в ветке Releases</p> | ||
<div align="center"> | ||
<p>Проект ReactJS Backend</p> | ||
<p>ide: WebStorm -v 2022.3</p> | ||
<p>Выполненные задания находятся в ветке <span style="color: cyan">Releases</span></p> | ||
<p>Выполнили: <a href="https://github.com/TheKostVK">TheKostVK</a> и <a href="https://github.com/b1abb">b1abb</a></p> | ||
</div> | ||
|
||
|
||
<p align="center">Ссылки на проекты GitHub</p> | ||
<table align="center"> | ||
<tr> | ||
<th align="center"><a href="https://github.com/TheKostVK/react2023_frontend">Frontend</a></th> | ||
<th align="center"><a href="https://github.com/TheKostVK/react2023_backend">Backend</a></th> | ||
</tr> | ||
</table> | ||
|
||
<p align="center">Таблица используемых модулей</p> | ||
<table align="center"> | ||
<tr> | ||
<th style="color: blueviolet; font-size: large">Frontend</th> | ||
<th style="color: blueviolet; font-size: large">Backend</th> | ||
</tr> | ||
<tr> | ||
<td><p style="color: brown">ReactJS v18</p></td> | ||
<td><p style="color: brown">NodeJS/ES6</p></td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<p style="font-size: large; color: black">Redux Toolkit</p> | ||
<p style="color: brown">(глобальный стейт менеджер)</p> | ||
</td> | ||
<td> | ||
<p style="font-size: large; color: black">Express + Validator</p> | ||
<p style="color: brown">(веб-сервер/валидация запросов)</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<p style="font-size: large; color: black">React Hook Form</p> | ||
<p style="color: brown">(работа с формами)</p> | ||
</td> | ||
<td> | ||
<p style="font-size: large; color: black">MongoDB/Mongoose</p> | ||
<p style="color: brown">(работа с базой данных)</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<p style="font-size: large; color: black">Redux Router v6</p> | ||
<p style="color: brown">(навигация/роутинг)</p> | ||
</td> | ||
<td> | ||
<p style="font-size: large; color: black">JSON Web Token</p> | ||
<p style="color: brown">(аутентификация/авторизация)</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<p style="font-size: large; color: black">React Markdown/Simple Editor</p> | ||
<p style="color: brown">(редактор статей)</p> | ||
</td> | ||
<td> | ||
<p style="font-size: large; color: black">Multer</p> | ||
<p style="color: brown">(загрузка файлов/изображений)</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<p style="font-size: large; color: black">Axios</p> | ||
<p style="color: brown">(отправка запросов на сервер)</p> | ||
</td> | ||
<td> | ||
<p style="font-size: large; color: black">BCrypt</p> | ||
<p style="color: brown">(шифрование пароля)</p> | ||
</td> | ||
</tr> | ||
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import PostModel from '../models/Post.js'; | ||
|
||
export const getLastTags = async (req, res) => { | ||
try { | ||
|
||
const posts = await PostModel.find().limit(5).exec(); | ||
|
||
const tags = posts.map(obj => obj.tags).flat().slice(0, 5); | ||
|
||
res.json(tags); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось получить теги', | ||
}); | ||
} | ||
}; | ||
|
||
export const getAll = async (req, res) => { | ||
try { | ||
const posts = await PostModel.find().populate('user').exec(); | ||
|
||
res.json(posts); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось получить статьи', | ||
}); | ||
} | ||
}; | ||
|
||
export const getOne = async (req, res) => { | ||
try { | ||
const postId = req.params.id; | ||
|
||
const post = await PostModel.findOneAndUpdate( | ||
{ | ||
_id: postId, | ||
}, | ||
{ | ||
$inc: {viewsCount: 1}, | ||
}, | ||
{ | ||
returnDocument: 'after', | ||
} | ||
).populate('user').exec(); | ||
|
||
if (!post) { | ||
return res.status(404).json({ | ||
message: 'Статья не найдена', | ||
}); | ||
} | ||
|
||
res.json(post); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось получить статью', | ||
}); | ||
} | ||
}; | ||
|
||
|
||
export const create = async (req, res) => { | ||
try { | ||
const doc = new PostModel({ | ||
title: req.body.title, | ||
text: req.body.text, | ||
imageUrl: req.body.imageUrl, | ||
tags: req.body.tags, | ||
user: req.userId, | ||
}); | ||
|
||
const post = await doc.save(); | ||
|
||
res.json(post); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось создать статью', | ||
}); | ||
} | ||
}; | ||
|
||
export const remove = async (req, res) => { | ||
try { | ||
const postId = req.params.id; | ||
|
||
const post = await PostModel.findOneAndDelete( | ||
{ | ||
_id: postId, | ||
}); | ||
|
||
if (!post) { | ||
return res.status(404).json({ | ||
message: 'Статья не найдена', | ||
}); | ||
} | ||
|
||
res.json({ | ||
success: true, | ||
}); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось удалить статью', | ||
}); | ||
} | ||
}; | ||
|
||
export const update = async (req, res) => { | ||
try { | ||
const postId = req.params.id; | ||
|
||
await PostModel.updateOne({ | ||
_id: postId, | ||
}, | ||
{ | ||
title: req.body.title, | ||
text: req.body.text, | ||
imageUrl: req.body.imageUrl, | ||
user: req.userId, | ||
tags: req.body.tags, | ||
}, | ||
); | ||
res.json({ | ||
success: true, | ||
}); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось обновить статью', | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import bcrypt from "bcrypt"; | ||
import UserModel from "../models/User.js"; | ||
import jwt from "jsonwebtoken"; | ||
|
||
export const register = async (req, res) => { | ||
try { | ||
const password = req.body.password; | ||
const salt = await bcrypt.genSalt(10); | ||
const hash = await bcrypt.hash(password, salt); | ||
|
||
const doc = new UserModel({ | ||
email: req.body.email, | ||
userName: req.body.userName, | ||
passwordHash: hash, | ||
avatarUrl: req.body.avatarUrl, | ||
}); | ||
|
||
const user = await doc.save(); | ||
|
||
const token = jwt.sign( | ||
{ | ||
_id: user._id, | ||
}, | ||
'secret123', | ||
{ | ||
expiresIn: '30d', | ||
}, | ||
); | ||
|
||
const {passwordHash, ...userData} = user._doc; | ||
|
||
res.json({ | ||
...userData, | ||
token, | ||
}); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось зарегистрировать пользователя', | ||
}); | ||
} | ||
}; | ||
|
||
export const login = async (req, res) => { | ||
try { | ||
const user = await UserModel.findOne({email: req.body.email}); | ||
|
||
if (!user) { | ||
return res.status(404).json({ | ||
message: 'Некорректные данные для авторизации' | ||
}); | ||
} | ||
|
||
const isValidPass = await bcrypt.compare(req.body.password, user._doc.passwordHash); | ||
|
||
if (!isValidPass) { | ||
return res.status(400).json({ | ||
message: 'Некорректные данные для авторизации' | ||
}); | ||
} | ||
|
||
const token = jwt.sign( | ||
{ | ||
_id: user._id, | ||
}, | ||
'secret123', | ||
{ | ||
expiresIn: '30d', | ||
}, | ||
); | ||
|
||
const {passwordHash, ...userData} = user._doc; | ||
|
||
res.json({ | ||
...userData, | ||
token, | ||
}); | ||
|
||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Не удалось авторизовать пользователя', | ||
}); | ||
} | ||
}; | ||
|
||
export const getMe = async (req, res) => { | ||
try { | ||
const user = await UserModel.findById(req.userId); | ||
if (!user) { | ||
return res.status(404).json({ | ||
message: 'Пользователь не найден' | ||
}); | ||
} | ||
|
||
const {passwordHash, ...userData} = user._doc; | ||
|
||
res.json(userData); | ||
} catch (err) { | ||
console.log(err) | ||
res.status(500).json({ | ||
message: 'Отказано в доступе', | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * as UserController from "./UserController.js"; | ||
export * as PostController from "./PostController.js"; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import express from "express"; | ||
import mongoose from "mongoose"; | ||
import multer from "multer"; | ||
import cors from 'cors'; | ||
import * as path from "path"; | ||
|
||
import {PostController, UserController} from './controllers/index.js'; | ||
import {loginValidation, postCreateValidation, registerValidation} from "./validations.js"; | ||
import {checkAuth, handleValidationErrors} from './utils/index.js'; | ||
|
||
mongoose.connect( | ||
'mongodb+srv://TheKost:AD6-9PP-Vt9-n6D@cluster0.fkbk1nc.mongodb.net/blog?retryWrites=true&w=majority', | ||
).then(() => console.log('DB ok')).catch((err) => console.log('DB error', err)); | ||
|
||
const app = express(); | ||
|
||
|
||
// Настройки хранения загруженных файлов | ||
const storage = multer.diskStorage({ | ||
destination: (req, file, cb) => { | ||
cb(null, 'media/uploads/'); // Папка для хранения загруженных файлов | ||
}, | ||
filename: (req, file, cb) => { | ||
const extension = path.extname(file.originalname); | ||
const basename = path.basename(file.originalname, extension); | ||
cb(null, `${basename}-${Date.now()}${extension}`); | ||
}, | ||
}); | ||
|
||
// Загрузчик файла с настройками хранения | ||
const upload = multer({storage}); | ||
|
||
// Метод для загрузки файла | ||
app.post('/upload', cors(), upload.single('image'), (req, res) => { | ||
// Возвращаем URL загруженной картинки в качестве ответа на запрос | ||
const url = `${req.protocol}://${req.get('host')}/${req.file.path}`;`a` | ||
res.json({url: url}); | ||
}); | ||
|
||
app.use(cors()); | ||
app.use(express.json()); | ||
|
||
app.use('/media', express.static('media')); | ||
|
||
app.post('/auth/login', cors(), loginValidation, handleValidationErrors, UserController.login); | ||
app.post('/auth/registration', cors(), registerValidation, handleValidationErrors, UserController.register); | ||
app.get('/auth/me', cors(), checkAuth, UserController.getMe) | ||
|
||
// app.get('/tags',cors(), PostController.getLastTags); | ||
|
||
app.get('/posts', cors(), PostController.getAll); | ||
app.get('/posts/tags', cors(), PostController.getLastTags); | ||
app.get('/posts/:id', cors(), PostController.getOne); | ||
app.post('/posts', cors(), checkAuth, postCreateValidation, handleValidationErrors, PostController.create); | ||
app.delete('/posts/:id', cors(), checkAuth, PostController.remove); | ||
app.patch('/posts/:id', cors(), checkAuth, postCreateValidation, handleValidationErrors, PostController.update); | ||
|
||
app.listen(4444, (err) => { | ||
if (err) { | ||
return console.log(err); | ||
} | ||
console.log(`Server started`); | ||
}); |
Oops, something went wrong.