diff --git a/app.js b/app.js
index 9beabb8..d5cd674 100644
--- a/app.js
+++ b/app.js
@@ -5,6 +5,9 @@ const os = require('os')
const mongoose = require('mongoose')
require('dotenv').config()
+/**
+ * подключение переменных среды
+ */
const devMode = process.env.NODE_ENV === "dev"
const PORT = process.env.PORT || 5000
const mongoUri = process.env.mongoUri
@@ -14,11 +17,17 @@ const app = express()
app.use(express.json({ extended: true }))
+/**
+ * подключение роутов
+ */
app.use('/api/auth', require('./routes/auth.routes'))
app.use('/api/notes', require('./routes/notes.routes'))
if (httpsRedirect) app.use(httpToHttps)
+/**
+ * подключение статической библиотеки клиента
+ */
if (!devMode) {
app.use('/', express.static(path.join(__dirname, 'client', 'build')))
app.get('*', (req, res) => {
@@ -30,6 +39,9 @@ if (!devMode) {
})
}
+/**
+ * запуск сервера
+ */
async function start() {
try {
connectMongo(mongoUri)
@@ -42,6 +54,10 @@ async function start() {
start()
+/**
+ * подключение к MongoDb
+ * @param {*} mongoUri
+ */
async function connectMongo(mongoUri) {
if (mongoUri) {
await mongoose.connect(mongoUri, {
@@ -54,6 +70,9 @@ async function connectMongo(mongoUri) {
}
}
+/**
+ * Вывод информации о сервере
+ */
function logServerStart() {
dns.lookup(os.hostname(), (err, address) => {
const [logName, sBef, sAft] = devMode ? ['Express server', ' ', ':'] : ['React Notes App', '-', '']
@@ -64,6 +83,9 @@ function logServerStart() {
})
}
+/**
+ * перенаправление с http на https для PWA
+ */
function httpToHttps(req, res, next) {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`)
diff --git a/client/src/App.js b/client/src/App.js
index e3779cc..c05b71e 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -12,10 +12,14 @@ import { PageContext } from './Context/PageContext'
import Header from './Pages/SharedComponents/Header'
function App() {
+ /**подключение хука авторизации */
const { token, login, logout, userId, email, ready } = useAuth()
const isAuthenticated = !!token
+
+ /**подключение хука роутов */
const routes = useRoutes(isAuthenticated)
+ /**хук обновления навбара */
const [nav, setNav] = React.useState()
if (!ready) {
@@ -26,20 +30,23 @@ function App() {
)
}
-
+ /**рендер */
return (
+ /**
+ * обертка в контексты авторизации и обноления хедера
+ * внутри роутер со статичным хедером и динамическим содержимым
+ * */
-
+
-
-
- {routes}
-
-
+
+
+ {routes}
+
+
-
)
}
diff --git a/client/src/Cards/AddCard.js b/client/src/Cards/AddCard.js
index db945a3..ac48005 100644
--- a/client/src/Cards/AddCard.js
+++ b/client/src/Cards/AddCard.js
@@ -4,12 +4,16 @@ import Palette, { colors } from './palette/palette'
import useInputValue from '../Hooks/useInputValue.hook'
import CardsContext from '../Context/CardsContext'
+/**Компонент добавления новой заметки */
function AddCard() {
+ /**контекст*/
const { addCard } = React.useContext(CardsContext)
+ /**хук инпута*/
const input = useInputValue('')
const defColor = colors[0]
const [color, setColor] = React.useState(defColor)
+ /**проверка темного фона */
const blackOnHover = () => {
switch (color) {
case colors[0]:
@@ -24,6 +28,7 @@ function AddCard() {
}
}
+ /**обработчик добавления заметки */
function submitHandler() {
if (String(input.value).trim() && String(color).trim()) {
addCard({ name: String(input.value).trim(), text: "", color: String(color) })
@@ -37,6 +42,7 @@ function AddCard() {
submitHandler()
}
+ /**рендер */
return (
diff --git a/client/src/Cards/CardItem.js b/client/src/Cards/CardItem.js
index c2bb4e1..c959fbc 100644
--- a/client/src/Cards/CardItem.js
+++ b/client/src/Cards/CardItem.js
@@ -5,17 +5,24 @@ import Card, { PropTypeCard } from './cardType/Card'
import ReactMarkdown from 'react-markdown'
import gfm from 'remark-gfm'
+/**исправление переноса строки в markdown */
function fixLineBreaks(mdStr) {
return String(mdStr).replace(/\n/gi, ' \n')
}
+/**
+ * Компонент заметки
+ * @param {*} param0
+ * @returns
+ */
function CardItem({ card = new Card(), index }) {
+ /**Подключение контекста */
const { removeCard, setEditCard } = useContext(CardsContext)
+
const lineClip = 12
const bgColor = card.color
return (
-
@@ -44,10 +51,10 @@ function CardItem({ card = new Card(), index }) {
-
)
}
+// Валидация
CardItem.propTypes = {
card: PropTypeCard.isRequired,
index: PropTypes.number
diff --git a/client/src/Cards/CardList.js b/client/src/Cards/CardList.js
index 7cf2dbf..8b41f45 100644
--- a/client/src/Cards/CardList.js
+++ b/client/src/Cards/CardList.js
@@ -5,6 +5,7 @@ import StackGrid, { transitions } from "react-stack-grid"
import sizeMe from 'react-sizeme'
const { scaleDown } = transitions
+/**расчет ширины столбцов */
function calcWidth() {
const small = 576
const middle = 768
@@ -19,14 +20,17 @@ function calcWidth() {
else return '100%'
}
+/** Компонент списка карточек */
function CardList(props) {
const grid = React.useRef(null)
React.useEffect(gridRedraw, [props.size])
+ /**обновление рендера */
function gridRedraw() {
setTimeout(() => { if (grid.current && grid.current.updateLayout) grid.current.updateLayout() }, 10)
}
+ /**параметры сетки */
const gridSettings = {
ref: grid,
columnWidth: calcWidth(),
@@ -40,6 +44,7 @@ function CardList(props) {
leaved: scaleDown.leaved
}
+ /**рендер */
return (
@@ -53,6 +58,7 @@ function CardList(props) {
)
}
+// Валидация
CardList.propTypes = {
cards: PropTypes.arrayOf(PropTypes.object).isRequired,
}
diff --git a/client/src/Cards/ModalCardEdit.js b/client/src/Cards/ModalCardEdit.js
index e06d22e..687f4e6 100644
--- a/client/src/Cards/ModalCardEdit.js
+++ b/client/src/Cards/ModalCardEdit.js
@@ -6,45 +6,58 @@ import Modal, { ModalProps } from "../Shared/Modal/Modal"
import Card, { PropTypeCard } from './cardType/Card'
import Palette from './palette/palette'
+/**расчет числа строк */
function calcMaxRows() {
const small = 576
const middle = 768
const large = 992
const winWidth = window.innerWidth
-
if (winWidth < small) return '7'
else if (winWidth < middle) return '8'
else if (winWidth < large) return '10'
else return '17'
}
+/**
+ * Модальное окно редактирования заметки
+ * @param {*} param0
+ * @returns
+ */
function ModalCardEdit({ card = new Card(), index }) {
+ /**получение контекста */
const { removeCard, changeCardColor, unsetEditCard, editCardContent } = React.useContext(CardsContext)
React.useEffect(() => { if (card !== null) open() }, [card])
+ /**хук состояния формы */
const [showForm, setShowForm] = React.useState(false)
+ /**создание параметров модального окна*/
const modalProps = new ModalProps()
modalProps.isOpen = showForm
modalProps.setOpenState = setShowForm
modalProps.sideClose = true
modalProps.onSideClick = unsetEditCard
+ /**открытие окна */
function open() {
setShowForm(true)
}
+ /**закрытие окна */
function close() {
setShowForm(false)
}
- /////
-
+ /**сохранение данных */
function save(name, text) {
editCardContent(index, name, text)
}
+ /**
+ * обраюотчик изменений инпута
+ * @param {*} e
+ */
function onInputChange(e) {
let name = card.name
let text = card.text
@@ -53,19 +66,32 @@ function ModalCardEdit({ card = new Card(), index }) {
save(name, text)
}
+ /**
+ * Изменение цвета
+ * @param {*} color
+ */
function tryChangeColor(color) {
changeCardColor(index, color)
}
+
+ /**
+ * удаление
+ */
function tryRemove() {
unsetEditCard();
close();
removeCard(index);
}
+
+ /**
+ * закрытие и сброс окна
+ */
function tryClose() {
unsetEditCard()
close()
}
+ /**рендер */
return (
@@ -135,6 +161,7 @@ function ModalCardEdit({ card = new Card(), index }) {
)
}
+// Валидация
ModalCardEdit.propTypes = {
card: PropTypeCard,
index: PropTypes.number,
diff --git a/client/src/Cards/cardType/Card.js b/client/src/Cards/cardType/Card.js
index 33e87c0..891aa9f 100644
--- a/client/src/Cards/cardType/Card.js
+++ b/client/src/Cards/cardType/Card.js
@@ -1,12 +1,14 @@
import PropTypes from 'prop-types'
+/**валидация пропсов заметки*/
export const PropTypeCard = PropTypes.shape({
- id: PropTypes.string,
+ id: PropTypes.string,
name: PropTypes.string,
color: PropTypes.string,
text: PropTypes.string,
})
+/**валидация заметки */
export function checkCard(card) {
return (
(typeof card.id === "string") &&
@@ -16,6 +18,7 @@ export function checkCard(card) {
)
}
+/**валидация массива заметок */
export function checkCardsArr(cardsArr) {
if (!Array.isArray(cardsArr)) return false
else if (cardsArr.length === 0) return true
@@ -29,6 +32,7 @@ export function checkCardsArr(cardsArr) {
}
}
+/**класс заметки */
export class Card {
constructor({ id, name, color, text }) {
this.id = String(id)
diff --git a/client/src/Cards/palette/palette.js b/client/src/Cards/palette/palette.js
index 5373f70..7ce7d60 100644
--- a/client/src/Cards/palette/palette.js
+++ b/client/src/Cards/palette/palette.js
@@ -2,6 +2,7 @@ import React from "react";
import PropTypes from 'prop-types'
import "./palette.css"
+/**набор цветов */
export const colors = [
"#f8f9fa",
"#F38181",
@@ -14,7 +15,11 @@ export const colors = [
"#6EF3D6"
];
-
+/**
+ * компонент палитры
+ * @param {*} param0
+ * @returns
+ */
function Palette({ setColor, style, className, disabled }) {
return (
@@ -59,6 +64,7 @@ function Palette({ setColor, style, className, disabled }) {
);
}
+// Валидация
Palette.propTypes = {
setColor: PropTypes.func,
style: PropTypes.object,
diff --git a/client/src/Context/AuthContext.js b/client/src/Context/AuthContext.js
index b302288..2260055 100644
--- a/client/src/Context/AuthContext.js
+++ b/client/src/Context/AuthContext.js
@@ -2,6 +2,7 @@ import {createContext} from 'react'
function noop() {}
+/**контекст авторизации */
export const AuthContext = createContext({
token: null,
userId: null,
diff --git a/client/src/Context/CardsContext.js b/client/src/Context/CardsContext.js
index 084eb5e..488ee50 100644
--- a/client/src/Context/CardsContext.js
+++ b/client/src/Context/CardsContext.js
@@ -1,5 +1,6 @@
import {createContext} from 'react'
+/**контекст заметок */
const CardsContext = createContext()
export default CardsContext
\ No newline at end of file
diff --git a/client/src/Context/PageContext.js b/client/src/Context/PageContext.js
index 0410d50..25112b4 100644
--- a/client/src/Context/PageContext.js
+++ b/client/src/Context/PageContext.js
@@ -2,4 +2,5 @@ import { createContext } from 'react'
function noop() { }
+/**контекст данных страницы */
export const PageContext = createContext({ setNav: noop })
diff --git a/client/src/Hooks/auth.hook.js b/client/src/Hooks/auth.hook.js
index 588548f..fddc1ff 100644
--- a/client/src/Hooks/auth.hook.js
+++ b/client/src/Hooks/auth.hook.js
@@ -1,36 +1,41 @@
-import {useState, useCallback, useEffect} from 'react'
+import { useState, useCallback, useEffect } from 'react'
const storageName = 'userData'
+/**
+ * Хук авторизации
+ * @returns
+ */
export const useAuth = () => {
const [token, setToken] = useState(null)
const [ready, setReady] = useState(false)
const [userId, setUserId] = useState(null)
const [email, setEmail] = useState(null)
+ /**обновление данных авторизации */
const login = useCallback((jwtToken, id, email) => {
setToken(jwtToken)
setUserId(id)
setEmail(email)
-
+ /**запись данных в кэш */
localStorage.setItem(storageName, JSON.stringify({
userId: id, token: jwtToken, email: email
}))
-
}, [])
-
+ /**очистка данных авторизации */
const logout = useCallback(() => {
-
setToken(null)
setUserId(null)
setEmail(null)
+ /**удаление данных из кэша */
localStorage.removeItem(storageName)
}, [])
+ /**считывание данных из кэша */
useEffect(() => {
+ /**данные из кэша */
const data = JSON.parse(localStorage.getItem(storageName))
-
if (data && data.token) {
login(data.token, data.userId, data.email)
}
diff --git a/client/src/Hooks/http.hook.js b/client/src/Hooks/http.hook.js
index 4c8fec0..950f0ca 100644
--- a/client/src/Hooks/http.hook.js
+++ b/client/src/Hooks/http.hook.js
@@ -1,5 +1,10 @@
-import {useState, useCallback} from 'react'
+import { useState, useCallback } from 'react'
+/**
+ * Хук обработки Http запросов
+ * Позволяет выводить результаты, ошибки и статус запроса, удобно совершать параметрические запросы
+ * @returns
+ */
export const useHttp = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
@@ -7,20 +12,19 @@ export const useHttp = () => {
const request = useCallback(async (url, method = 'GET', body = null, headers = {}) => {
setLoading(true)
try {
+ /**составление запроса */
if (body) {
body = JSON.stringify(body)
headers['Content-Type'] = 'application/json'
}
-
- const response = await fetch(url, {method, body, headers})
+ /**отправка запроса */
+ const response = await fetch(url, { method, body, headers })
+ /**получение ответа */
const data = await response.json()
-
if (!response.ok) {
throw new Error(data.message || 'Что-то пошло не так')
}
-
setLoading(false)
-
return data
} catch (e) {
setLoading(false)
diff --git a/client/src/Hooks/useDebouncedEffect.hook.js b/client/src/Hooks/useDebouncedEffect.hook.js
index 4217d32..04ea5a6 100644
--- a/client/src/Hooks/useDebouncedEffect.hook.js
+++ b/client/src/Hooks/useDebouncedEffect.hook.js
@@ -1,28 +1,33 @@
import { useState, useEffect } from 'react';
+/**
+ * Хук debounce
+ * @param {*} value
+ * @param {*} delay
+ * @returns
+ */
function useDebounce(value, delay) {
-
const [debouncedValue, setDebouncedValue] = useState(value);
-
useEffect(
() => {
-
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
-
-
return () => {
clearTimeout(handler);
};
},
-
[delay, value]
);
-
return debouncedValue;
}
+/**
+ * Хук debounced effect
+ * @param {*} func
+ * @param {*} deps
+ * @param {*} delay
+ */
export default function useDebouncedEffect(func, deps, delay) {
const debounced = useDebounce(deps, delay || 0)
const debDepsList = Array.isArray(debounced) ? debounced : debounced ? [debounced] : undefined
diff --git a/client/src/Hooks/useInputValue.hook.js b/client/src/Hooks/useInputValue.hook.js
index 45093e5..eb592a0 100644
--- a/client/src/Hooks/useInputValue.hook.js
+++ b/client/src/Hooks/useInputValue.hook.js
@@ -1,5 +1,10 @@
import { useState } from 'react'
+/**
+ * Хук для обработки форм
+ * @param {*} defaultValue
+ * @returns
+ */
export default function useInputValue(defaultValue) {
const [value, setValue] = useState(defaultValue)
return {
diff --git a/client/src/Pages/AuthPage.js b/client/src/Pages/AuthPage.js
index f1a1180..8990e81 100644
--- a/client/src/Pages/AuthPage.js
+++ b/client/src/Pages/AuthPage.js
@@ -1,32 +1,49 @@
import React, { useContext, useEffect, useState } from 'react'
import { useHttp } from '../Hooks/http.hook'
-
import { AuthContext } from '../Context/AuthContext'
import { PageContext } from '../Context/PageContext'
-
import { NavLink, useHistory } from 'react-router-dom'
import './AuthPage.css'
+/**
+ * Страница авторизации
+ * @returns
+ */
function AuthPage() {
+ /**подключение контекстов */
const auth = useContext(AuthContext)
const page = useContext(PageContext)
+
const history = useHistory()
+ /**подключение хука http запросов */
const { loading, request, error, clearError } = useHttp()
+
+ /**состояние формы */
const [form, setForm] = useState({
email: '', password: ''
})
+
+ /**хук сообщений от сервера */
const [message, setMessage] = useState(null)
+ /** очистка оштбок хука запросов и запись ошибки в сообщение*/
useEffect(() => {
if (error) setMessage([error, false])
clearError()
}, [error, clearError])
+ /**
+ * обработчик ввода данных в форму
+ * @param {*} event
+ */
const changeHandler = event => {
setForm({ ...form, [event.target.name]: event.target.value })
}
+ /**
+ * обработчик регистрации
+ */
const registerHandler = async () => {
try {
const data = await request('/api/auth/register', 'POST', { ...form })
@@ -34,6 +51,9 @@ function AuthPage() {
} catch (e) { }
}
+ /**
+ * Обработчик входа в систему
+ */
const loginHandler = async () => {
try {
const data = await request('/api/auth/login', 'POST', { ...form })
@@ -42,11 +62,17 @@ function AuthPage() {
} catch (e) { }
}
+ /**
+ * Обработчик выхода
+ */
const logoutHandler = event => {
event.preventDefault()
auth.logout()
}
+ /**
+ * Обновление навбара при переходе на эту страницу и изменениях
+ */
React.useEffect(() => {
page.setNav(auth.isAuthenticated &&
@@ -56,10 +82,11 @@ function AuthPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [auth.isAuthenticated, auth.token])
+ /**рендер */
return (
+ /**Здесь отрисовываются меню регистрации и авторизации */
-
@@ -76,14 +103,12 @@ function AuthPage() {
- {
- message &&
+ {message &&
{message[1] ? "Инфо" : "Ошибка"}
{String(message[0])}
}
-
e.key === 'Enter' && loginHandler()}
/>
-
{!auth.isAuthenticated ? (
-
-
-
) : (
-
-
)}
-
)
diff --git a/client/src/Pages/CardsPage.js b/client/src/Pages/CardsPage.js
index 53aa4a8..abefc9c 100644
--- a/client/src/Pages/CardsPage.js
+++ b/client/src/Pages/CardsPage.js
@@ -1,54 +1,67 @@
import React from 'react';
-
import './CardsPage.css';
import CardList from '../Cards/CardList'
import AddCard from '../Cards/AddCard'
import CardsContext from '../Context/CardsContext'
import Loader from '../Shared/Loader'
import ModalCardEdit from '../Cards/ModalCardEdit'
-
import Card, { checkCardsArr } from '../Cards/cardType/Card'
-
-
import { NavLink } from 'react-router-dom'
import { AuthContext } from '../Context/AuthContext'
import { PageContext } from '../Context/PageContext'
-
import { useHttp } from '../Hooks/http.hook'
+/**
+ * Хук использования массива заметок
+ * @param {*} defaultValue
+ * @returns
+ */
function useCardsArr(defaultValue) {
const [value, setValue] = React.useState(defaultValue)
-
function trySetValue(cardsArr) {
if (checkCardsArr(cardsArr) || cardsArr === null) setValue(cardsArr)
else console.error('Массив cardsArr не прошел проверку \n', cardsArr)
}
-
return [value, trySetValue]
}
+/**
+ * Хук-таймер для обновления данных с очисткой счетчика при ререндере
+ * @returns
+ */
function useUpdater() {
const [updaterVal, setUpdaterVal] = React.useState(null)
const timer = React.useRef()
React.useEffect(() => {
- if (timer.current) clearTimeout(timer.current)
+ if (timer.current) clearTimeout(timer.current) // сброс при переопределении таймера
timer.current = setTimeout(() => {
console.log("Timed update")
setUpdaterVal(Date.now())
}, 60 * 1000) // обновяем через минуту
- return () => clearTimeout(timer.current)
+ return () => clearTimeout(timer.current) // сброс при ререндере
}, []) // eslint-disable-line react-hooks/exhaustive-deps
return [updaterVal]
}
+/**
+ * Страница с заметками
+ * @returns
+ */
function CardsPage() {
-
+ /**подключение контекстов */
const auth = React.useContext(AuthContext)
const page = React.useContext(PageContext)
+ /**подключение хука http запросов */
const { loading, request, error, clearError } = useHttp()
+
+ /**хук сообщений от сервера */
+ const [message, setMessage] = React.useState(null)
+
+ /**Хук-функция для работы с базой данных заметок */
const fetchNotes = React.useCallback(async (url = "", method = "GET", body = null, resCallback = () => { }) => {
try {
+ /**запрос к серверу с определенными параметрами*/
const fetched = await request(`/api/notes${url ? ("/" + url) : ""}`, method, body, { Authorization: `Bearer ${auth.token}` })
resCallback(tryParce(fetched))
} catch (e) { }
@@ -61,42 +74,52 @@ function CardsPage() {
}
}, [auth.token, request])
- const [message, setMessage] = React.useState(null)
-
+ /** очистка оштбок хука запросов и запись ошибки в сообщение*/
React.useEffect(() => {
if (error) setMessage([error, false])
clearError()
}, [error, clearError])
-
-
+ /**Массив заметок */
const [cardsArr, setCardsArr] = useCardsArr(null)
+
+ /**Id редактируемой заметки */
const [editCardId, setEditCardId] = React.useState(null)
const [updaterVal] = useUpdater()
-
const updatingEnable = React.useRef(true)
+ /**
+ * хук обновления данных с сервера
+ * флаг updatingEnable позволяет избежать взаимодействия с устаревшей unmount версией компонента
+ */
React.useEffect(() => {
updatingEnable.current = true
loadDataFromServer()
return () => updatingEnable.current = false
}, [auth.isAuthenticated, auth.email, updaterVal]) // eslint-disable-line react-hooks/exhaustive-deps
- React.useEffect(clearOldData, [auth.isAuthenticated]) // eslint-disable-line react-hooks/exhaustive-deps
-
///////////
+ //очистка старых данных
+ React.useEffect(clearOldData, [auth.isAuthenticated]) // eslint-disable-line react-hooks/exhaustive-deps
function clearOldData() {
- //console.log("clearOldData, auth.isAuthenticated:", auth.isAuthenticated)
if (!auth.isAuthenticated) setCardsArr(null)
}
///////////
///////////
+
+ /**
+ * получение данных с сервера
+ */
function loadDataFromServer() {
fetchNotes("", "GET", null, setLoadedCards)
}
+ /**
+ * Внесение в полученных данных в массив
+ * @param {*} cards
+ */
function setLoadedCards(cards) {
if (updatingEnable.current) setCardsArr([...cards])
}
@@ -104,16 +127,29 @@ function CardsPage() {
///////////
+ /**
+ * Загрузка данных на сервер
+ * @param {*} card
+ * @param {*} target
+ */
function loadDataToServer(card = new Card(), target = 'set') {
fetchNotes(target, "POST", { card })
}
+ /**
+ * удаление карточки
+ * @param {*} index
+ */
function removeCard(index) {
const toDelete = cardsArr.splice(index, 1)[0]
setCardsArr([...cardsArr])
loadDataToServer(toDelete, "delete")
}
+ /**
+ * добавление карточки
+ * @param {*} cardData
+ */
function addCard(cardData = {}) {
const newId = String(auth.email) + String(Date.now()) + String(Math.random())
const newCard = new Card({ id: newId, name: cardData.name, color: cardData.color, text: cardData.text })
@@ -125,12 +161,23 @@ function CardsPage() {
}
+ /**
+ * Изменение цвета карточки
+ * @param {*} index
+ * @param {*} color
+ */
function changeCardColor(index, color) {
cardsArr[index].color = color
setCardsArr([...cardsArr])
loadDataToServer(cardsArr[index], "set")
}
+ /**
+ * Изменение текстового содержания карточки
+ * @param {*} index
+ * @param {*} name
+ * @param {*} text
+ */
function editCardContent(index, name, text) {
if (cardsArr[index]) {
let card = new Card(cardsArr[index])
@@ -144,17 +191,23 @@ function CardsPage() {
///////////
///////////
- function getCardByIndex(index) {
- return index !== null ? cardsArr[index] : null
- }
+ /**функция назначения редактируемой заметки для модального окна */
function setEditCard(index) {
setEditCardId(index)
}
+ /**функция сброса редактируемой заметки для модального окна */
function unsetEditCard() {
setEditCardId(null)
}
+ /**функция получения карточки по id */
+ function getCardByIndex(index) {
+ return index !== null ? cardsArr[index] : null
+ }
///////////
+ /**
+ * Обновление навбара при переходе на эту страницу и изменениях
+ */
React.useEffect(() => {
page.setNav(
@@ -162,7 +215,6 @@ function CardsPage() {
{loading ? : }
Update
-
{auth.email}
@@ -171,11 +223,13 @@ function CardsPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [auth.email, auth.token])
+ /**рендер */
return (
+ /**Здесь отрисовываются меню добавления и редактирования заметок и сам перечнь заметок в виде динамичной отзывчивой сетки */
-
+
@@ -197,6 +251,7 @@ function CardsPage() {
}
+
diff --git a/client/src/Pages/SharedComponents/Header.js b/client/src/Pages/SharedComponents/Header.js
index a877ed5..a1473ee 100644
--- a/client/src/Pages/SharedComponents/Header.js
+++ b/client/src/Pages/SharedComponents/Header.js
@@ -4,10 +4,9 @@ import './Header.css';
const brandText = "Notes"
+/**компонент хедера с логотипом и навбаром */
function Header(props) {
-
return (
-
-
);
}
diff --git a/client/src/Shared/Loader.js b/client/src/Shared/Loader.js
index d3badc4..df32e50 100644
--- a/client/src/Shared/Loader.js
+++ b/client/src/Shared/Loader.js
@@ -1,5 +1,6 @@
import React from 'react'
+/**просто вращающееся колесико загрузки */
export default function Loader({ className }) {
return (
diff --git a/client/src/Shared/Modal/Modal.js b/client/src/Shared/Modal/Modal.js
index 6113632..6d7bed4 100644
--- a/client/src/Shared/Modal/Modal.js
+++ b/client/src/Shared/Modal/Modal.js
@@ -7,7 +7,13 @@ const classes = {
modalBody: 'modal-window-body'
}
+/**
+ * Компонент-остнова для модального окна
+ * @param {{ isOpen, setOpenState, openButton, sideClose, onSideClick }} props
+ * @returns
+ */
function Modal(props) {
+ /**получение параметров */
const { isOpen, setOpenState, openButton, sideClose, onSideClick } = props
const wrapperRef = React.useRef(null)
@@ -21,16 +27,19 @@ function Modal(props) {
if (typeof onSideClick === "function") onSideClick()
}
+ /**обработчик клика по кнопке открытия */
function handleOpenButtonClick() {
open()
}
+ /**обработчик клика вне окна */
function handleWrapperClick(e) {
if (wrapperRef.current && wrapperRef.current === e.target) {
if (sideClose) close()
}
}
+ /**рендер */
return (
{openButton &&
@@ -57,6 +66,9 @@ function Modal(props) {
)
}
+/**
+ * Класс для удобного создания обьекта параметров модального окна
+ */
class ModalProps {
constructor() {
this.isOpen = false
@@ -77,9 +89,9 @@ class ModalProps {
}
}
+/**валидация параметров */
Modal.propTypes = {
children: PropTypes.object,
-
isOpen: PropTypes.bool,
setOpenState: PropTypes.func,
openButton: PropTypes.bool,
diff --git a/client/src/index.js b/client/src/index.js
index 688e033..9434a09 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -9,6 +9,7 @@ import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap-icons/font/bootstrap-icons.css';
import "bootstrap/dist/js/bootstrap.bundle.min";
+/**рендер приложения */
ReactDOM.render(
@@ -19,10 +20,18 @@ ReactDOM.render(
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
+/**
+ * Подключение ServiceWorker для PWA
+ */
serviceWorkerRegistration.register({
+ /**подключение обновителя */
onUpdate: updater
});
+/**
+ * Обновление кэша PWA
+ * @param {*} registration
+ */
function updater(registration) {
alert('New version available! Ready to update?');
if (registration && registration.waiting) {
diff --git a/client/src/routes.js b/client/src/routes.js
index 302cb6f..12eddf8 100644
--- a/client/src/routes.js
+++ b/client/src/routes.js
@@ -2,27 +2,26 @@ import React from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import CardsPage from './Pages/CardsPage'
-
import AuthPage from './Pages/AuthPage'
export const useRoutes = isAuthenticated => {
if (isAuthenticated) {
return (
+ /**Набор роутов в случае авторизации */
-
-
)
}
return (
+ /**Набор роутов в случае неавторизации */
diff --git a/middleware/auth.middleware.js b/middleware/auth.middleware.js
index 9358fbb..26713fa 100644
--- a/middleware/auth.middleware.js
+++ b/middleware/auth.middleware.js
@@ -1,19 +1,29 @@
const jwt = require('jsonwebtoken')
require('dotenv').config()
+/**
+ * Функция-Middleware для проверки авторизации пользователя
+ * @param {*} req
+ * @param {*} res
+ * @param {*} next
+ * @returns
+ */
module.exports = (req, res, next) => {
+ //проверка работы сервера
if (req.method === 'OPTIONS') {
return next()
}
try {
-
+ /**получение токена */
const token = req.headers.authorization.split(' ')[1] // "Bearer TOKEN"
+ /**проверка отсутствия токена */
if (!token) {
return res.status(401).json({ message: 'Нет авторизации' })
}
+ /**верификация токена */
const decoded = jwt.verify(token, process.env.jwtSecret)
req.user = decoded
next()
diff --git a/models/Note.js b/models/Note.js
index 4262ca8..fae8858 100644
--- a/models/Note.js
+++ b/models/Note.js
@@ -1,12 +1,14 @@
const { Schema, model, Types } = require('mongoose')
+/**
+ * Схема заметки для базы данных
+ */
const schema = new Schema({
- id: { type: String, required: true, unique: true /*, default: String(Date.now) + String(Math.random)*/ },
+ id: { type: String, required: true, unique: true },
name: { type: String },
text: { type: String },
color: { type: String },
image: { type: String },
- //date: { type: Date, default: Date.now },
owner: { type: Types.ObjectId, ref: 'User', required: true }
})
diff --git a/models/User.js b/models/User.js
index 3bc1240..a36a7a4 100644
--- a/models/User.js
+++ b/models/User.js
@@ -1,5 +1,8 @@
const {Schema, model, Types} = require('mongoose')
+/**
+ * Схема данных пользователя
+ */
const schema = new Schema({
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
diff --git a/routes/auth.routes.js b/routes/auth.routes.js
index 380f8c0..736e07a 100644
--- a/routes/auth.routes.js
+++ b/routes/auth.routes.js
@@ -6,18 +6,22 @@ const { check, validationResult } = require('express-validator')
const User = require('../models/User')
const router = Router()
-// /api/auth/register
+/**
+ * Регистрация
+ * /api/auth/register
+ */
router.post(
'/register',
[
+ /**валидация */
check('email', 'Некорректный email').isEmail(),
check('password', 'Минимальная длина пароля 6 символов')
.isLength({ min: 6 })
],
async (req, res) => {
try {
+ /**проверка данных */
const errors = validationResult(req)
-
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array(),
@@ -27,15 +31,15 @@ router.post(
const { email, password } = req.body
+ /**Проверка существования пользователя */
const candidate = await User.findOne({ email })
-
if (candidate) {
return res.status(400).json({ message: 'Такой пользователь уже существует' })
}
+ /**Хеширование пароля и сохранение пользователя */
const hashedPassword = await bcrypt.hash(password, 12)
const user = new User({ email, password: hashedPassword })
-
await user.save()
res.status(201).json({ message: 'Пользователь создан' })
@@ -45,17 +49,21 @@ router.post(
}
})
-// /api/auth/login
+/**
+ * Вход
+ * /api/auth/login
+ */
router.post(
'/login',
[
+ /**валидация */
check('email', 'Введите корректный email').normalizeEmail().isEmail(),
check('password', 'Введите пароль').exists()
],
async (req, res) => {
try {
+ /**проверка данных */
const errors = validationResult(req)
-
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array(),
@@ -65,18 +73,19 @@ router.post(
const { email, password } = req.body
+ /**Поиск пользователя в бд */
const user = await User.findOne({ email })
-
if (!user) {
return res.status(400).json({ message: 'Пользователь не найден' })
}
+ /**Проверка пароля */
const isMatch = await bcrypt.compare(password, user.password)
-
if (!isMatch) {
return res.status(400).json({ message: 'Неверный пароль, попробуйте снова' })
}
+ /**Создание токена */
const token = jwt.sign(
{ userId: user.id },
process.env.jwtSecret,
diff --git a/routes/notes.routes.js b/routes/notes.routes.js
index da913ac..18e1fa0 100644
--- a/routes/notes.routes.js
+++ b/routes/notes.routes.js
@@ -6,9 +6,13 @@ const router = Router()
const { checkCard } = require('../validation/CardCheck')
-
+/**
+ * Добавление и редактирование заметки
+ * /api/notes/set
+ */
router.post('/set', auth, async (req, res) => {
try {
+ /**получение данных о заметке и запись в бд */
const card = tryParce(req.body.card)
if (checkCard(card)) {
postNote(card)
@@ -20,18 +24,20 @@ router.post('/set', auth, async (req, res) => {
res.status(500).json({ message: 'Что-то пошло не так, попробуйте снова' })
}
+ /**Добавление или редактирование заметки в бд */
async function postNote(noteToSave) {
noteToSave.owner = req.user.userId
+ /**проверка существования заметки */
const existing = await Note.findOne({ id: noteToSave.id })
if (existing) {
- //console.log("EXITING");
+ /**Выполнится если такая заметка уже есть */
existing.overwrite(noteToSave)
existing.save()
} else {
- //console.log("NON EXITING")
+ /**Выполнится если нет такой заметки */
const note = new Note(noteToSave)
await note.save()
}
@@ -40,10 +46,13 @@ router.post('/set', auth, async (req, res) => {
})
-
+/**
+ * Удаление заметки
+ * /api/notes/delete
+ */
router.post('/delete', auth, async (req, res) => {
try {
-
+ /**получение данных о заметке и удаление */
const card = tryParce(req.body.card)
if (checkCard(card)) {
deleteNote(card)
@@ -55,32 +64,29 @@ router.post('/delete', auth, async (req, res) => {
res.status(500).json({ message: 'Что-то пошло не так, попробуйте снова' })
}
+ /**Удаление заметки в бд */
async function deleteNote(noteToSave) {
noteToSave.owner = req.user.userId
const existing = await Note.findOne({ id: noteToSave.id })
if (existing) {
- //console.log("EXITING");
-
existing.remove()
-
} else res.status(500).json({ message: 'уже удален' })
}
})
-
-
+/**
+ * Получение массива заметок
+ * /api/notes/
+ */
router.get('/', auth, async (req, res) => {
try {
- //console.log(req.user.userId);
+ /**Нахождение пользовательских заметок в бд */
const notes = await Note.find({ owner: req.user.userId })
- //console.log(notes);
- res.json(notes)
+ res.status(200).json(notes)
} catch (e) {
- //console.log("\n\n\n GET");
- //console.log(e);
- res.status(500).json({ message: '[GET] Что-то пошло не так, попробуйте снова' })
+ res.status(500).json({ message: 'Что-то пошло не так, попробуйте снова' })
}
})
diff --git a/validation/CardCheck.js b/validation/CardCheck.js
index 4e60bb1..c38d152 100644
--- a/validation/CardCheck.js
+++ b/validation/CardCheck.js
@@ -1,3 +1,8 @@
+/**
+ * Проверка рбьекта заметки
+ * @param {*} card
+ * @returns
+ */
function checkCard(card) {
return (
typeof card.id === "string" &&