move old to legacy folder

This commit is contained in:
Primakov Alexandr Alexandrovich
2025-01-19 21:09:17 +03:00
parent 8a2afc3f1b
commit 270fe51500
289 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
const router = require('express').Router()
const checkPwd = require('pbkdf2-password')()
const jwt = require('jsonwebtoken')
const { BASKET_JWT_TOKEN } = require('./key')
const { getResponse, signUp, getUser, _idToId, requiredFields } = require('./controller')
router.post('/sign-in', requiredFields(['email', 'password']), async (req, res) => {
try {
const user = await getUser(req.body)
// eslint-disable-next-line max-len
checkPwd({ password: req.body.password, salt: user.salt }, async (err, pass, salt, hash) => {
if (err) throw new Error(err)
if (user.pwd === hash) {
const { pwd, salt: _salt, ...rest } = user
const token = jwt.sign(_idToId(rest), BASKET_JWT_TOKEN)
res.send(getResponse(null, { token, user: _idToId(rest) }))
} else {
res.status(400).send(getResponse('Неправильный email или пароль'))
}
})
} catch (e) {
res.status(400).send(getResponse(e.message))
}
})
router.post('/sign-up', requiredFields(['email', 'login', 'password']), async (req, res) => {
let error = null
const data = await signUp(req.body).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, data))
})
module.exports = router

View File

@@ -0,0 +1,25 @@
const router = require('express').Router()
const { expressjwt } = require('express-jwt')
const ObjectId = require('mongodb').ObjectID
const { BASKET_JWT_TOKEN } = require('./key')
const { getResponse, getCategory, postCategory } = require('./controller')
router.use(expressjwt({ secret: BASKET_JWT_TOKEN, algorithms: ['HS256'] }))
router.get('/', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
const categoryData = await getCategory({ userId }).catch((e) => error = e.message)
res.send(getResponse(error, categoryData))
})
router.post('/', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
const categoryData = await postCategory({ userId, ...req.body }).catch((e) => error = e.message)
res.send(getResponse(error, categoryData))
})
module.exports = router

View File

@@ -0,0 +1,510 @@
const ObjectId = require('mongodb').ObjectID
const getHash = require('pbkdf2-password')()
const { getDB } = require('../../../utils/mongo')
const USERS_COLLECTION = 'users'
const LISTS_COLLECTION = 'lists'
const CATEGORY_COLLECTION = 'default_categories'
const USER_CATEGROY_COLLECTION = 'user_categories'
const ITEM_COLLECTION = 'items'
const fakeUserId = 'fakeUserId'
let db = null
const connect = async () => {
db = await getDB('basket')
}
const init = async () => {
await connect()
const categoriesCollection = db.collection(CATEGORY_COLLECTION)
const findData = await categoriesCollection.find({
}).toArray()
if (findData.length === 0) {
await categoriesCollection.insertMany([
{
name: 'Продукты',
color: '#08AE0F',
},
{
name: 'Одежда',
color: '#9D79B9',
},
{
name: 'Бытовая химия',
color: '#B11F1F',
},
{
name: 'Лекарства',
color: '#3414F5',
},
])
}
}
init()
const _idToId = (data) => {
const { _id, ...rest } = data
return {
id: _id,
...rest,
}
}
const _idToIdArray = (data) => {
const _idToIdMap = data.map((item) => _idToId(item))
return _idToIdMap
}
const getResponse = (error, data, success = true) => {
if (error) {
return {
success: false,
error,
}
}
return {
success,
data,
}
}
const signUp = async ({ email, login, password }) => {
if (db === null) throw new Error('no db connection')
try {
const usersCollection = db.collection(USERS_COLLECTION)
const userData = await usersCollection.findOne({
$or: [{
login,
}, {
email,
}],
})
if (userData?.login === login) {
throw new Error('Логин занят')
}
if (userData?.email === email) {
throw new Error('Email занят')
}
getHash({ password }, async (err, pass, salt, hash) => {
if (err) throw new Error(err)
// eslint-disable-next-line max-len
const { insertedCount } = await usersCollection.insertOne({ email, login, pwd: hash, salt })
if (!insertedCount) throw new Error('insert error')
})
return {}
} catch (e) {
throw new Error(e)
}
}
const getUser = async ({ email }) => {
if (db === null) throw new Error('no db connection')
try {
const usersCollection = db.collection(USERS_COLLECTION)
const userData = await usersCollection.findOne(
{
email,
},
)
if (userData) return userData
throw new Error('Неправильный email или пароль')
} catch (e) {
throw new Error(e)
}
}
const addList = async ({ userId = fakeUserId, ...data }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const insertData = await listsCollection.insertOne({
userId,
timeStamp: Date.now(),
...data,
})
const { insertedCount, ops } = insertData
if (insertedCount) { return _idToId(ops[0]) }
throw new Error('insert error')
} catch (e) {
throw new Error(e)
}
}
const getLists = async ({ userId = fakeUserId }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
let newLists = []
const data = await listsCollection.find({
userId,
}).toArray()
await Promise.all(data.map(async (element) => {
const total = await itemsCollection.countDocuments({
parentId: element._id,
})
const purchased = await itemsCollection.countDocuments({
parentId: element._id,
bought: true,
})
newLists.push({
...element, total, purchased,
})
}))
newLists.sort((a, b) => (b.timeStamp - a.timeStamp))
return _idToIdArray(newLists)
} catch (e) {
throw new Error(e)
}
}
/* добавил логику рекурсивного удаления дочерних документов */
const deleteDoc = async ({ id, tag = false }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
const findData = await itemsCollection.find({
parentId: new ObjectId(id),
}).toArray()
findData.forEach(async (element) => {
await deleteDoc({
id: element._id,
tag: true,
})
})
let delData = null
if (tag) {
delData = await itemsCollection.deleteOne({
_id: new ObjectId(id),
})
} else {
delData = await listsCollection.deleteOne({
_id: new ObjectId(id),
})
}
const { deletedCount } = delData
if (deletedCount) {
return {
}
} throw new Error('no data to delete')
} catch (e) {
throw new Error(e)
}
}
const renameList = async ({ id, listName }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const data = await listsCollection.updateOne({
_id: new ObjectId(id),
},
{
$set: {
listName,
},
})
const { matchedCount } = data
if (matchedCount) {
const findData = await listsCollection.findOne({
_id: new ObjectId(id),
})
return _idToId(findData)
} throw new Error('no data to rename')
} catch (e) {
throw new Error(e)
}
}
const duplicateList = async ({ id, parentId = null }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
let addListData = null
let newId = null
if (parentId) {
const findData = await itemsCollection.findOne(
{
_id: new ObjectId(id),
},
)
const { _id, ...item } = findData
item.parentId = parentId
const insertData = await itemsCollection.insertOne({
...item,
})
const { insertedCount } = insertData
if (!insertedCount) throw new Error('insert new item error')
} else {
const findData = await listsCollection.findOne(
{
_id: new ObjectId(id),
},
)
const { _id, timeStamp, ...item } = findData
item.listName = `(КОПИЯ) ${item.listName}`
addListData = await addList({
...item,
})
newId = addListData.id
}
const childData = await itemsCollection.find({
parentId: new ObjectId(id),
}).toArray()
childData.forEach(async (element) => {
await duplicateList({
id: element._id, parentId: newId,
})
})
if (addListData) return _idToId(addListData)
} catch (e) {
throw new Error(e)
}
}
const getCategory = async ({ userId }) => {
if (db === null) throw new Error('no db connection')
try {
const categoriesCollection = db.collection(CATEGORY_COLLECTION)
const defaultCategories = await categoriesCollection.find({
}).toArray()
const defaultCategoriesData = _idToIdArray(defaultCategories).map((dc) => ({
...dc, userId,
}))
const userCollection = db.collection(USER_CATEGROY_COLLECTION)
const userCategoriesFilter = {}
if (userId) {
userCategoriesFilter.userId = userId
}
const userFindData = await userCollection.find(userCategoriesFilter).toArray()
return [...defaultCategoriesData, ..._idToIdArray(userFindData)]
} catch (e) {
throw new Error(e)
}
}
const postCategory = async ({ userId = fakeUserId, ...categoryData }) => {
if (db === null) throw new Error('no db connection')
try {
const userCollection = db.collection(USER_CATEGROY_COLLECTION)
const insertData = await userCollection.insertOne({
userId, ...categoryData,
})
// const {insertedCount, ops} = insertData
// if (insertedCount)
// _idToId(ops[0])
// else
// throw new Error('insert error')
const userFindData = await userCollection.find({
userId,
}).toArray()
return _idToIdArray(userFindData)
} catch (e) {
throw new Error(e)
}
}
const getShoppingList = async ({ userId = fakeUserId, id }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(ITEM_COLLECTION)
const itemsList = await listsCollection.find({
parentId: new ObjectId(id),
}).toArray()
const categoryList = await getCategory({ })
const coloredItemsList = itemsList.map((item) => ({
...item,
// eslint-disable-next-line max-len
color: categoryList.find((category) => String(category.id) === String(item.categoryId))?.color,
}))
return _idToIdArray(coloredItemsList)
} catch (e) {
throw new Error(e)
}
}
const addListItem = async ({ userId = fakeUserId, listId, categoryId, text }) => {
if (db === null) throw new Error('no db connection')
try {
const dataToInsert = {
parentId: new ObjectId(listId),
categoryId: new ObjectId(categoryId),
text,
count: 1,
bought: false,
createdBy: userId,
createdDt: Date.now(),
modifiedBy: userId,
modifiedDt: Date.now(),
}
const itemCollection = db.collection(ITEM_COLLECTION)
await itemCollection.insertOne(dataToInsert)
return _idToId(dataToInsert)
} catch (e) {
throw new Error(e)
}
}
const boughtItem = async ({ userId = fakeUserId, itemId, bought }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const chengedData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
},
[{
$set: {
bought: { $eq: [false, '$bought'] },
modifiedBy: userId,
modifiedDt: Date.now(),
},
}])
return _idToId(chengedData)
} catch (e) {
throw new Error(e)
}
}
const incCountItem = async ({ userId = fakeUserId, itemId, count }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const chengedData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
},
{
$inc: {
count,
},
},
{
$set: {
modifiedBy: userId,
modifiedDt: Date.now(),
},
})
const chengeData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
count: {
$lt: 1,
},
},
{
$set: {
count: 1,
modifiedBy: userId,
modifiedDt: Date.now(),
},
})
return _idToId(chengedData || chengeData)
} catch (e) {
throw new Error(e)
}
}
const deleteItem = async ({ itemId }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const findItemData = await itemCollection.find({
_id: new ObjectId(itemId),
})
findItemData.forEach((item) => {
deleteItem({
id: item._id,
})
})
const deleteItemData = await itemCollection.deleteOne({
_id: new ObjectId(itemId),
})
const { deletedButton } = deleteItemData
if (deletedButton) {
return {
}
}
} catch (e) {
throw new Error(e)
}
}
const requiredFields = (fields) => (req, res, next) => {
// eslint-disable-next-line no-restricted-syntax
for (const fieldName of fields) {
if (!req.body[fieldName]) {
throw new Error(`Параметр ${fieldName} не установлен`)
}
}
next()
}
module.exports = {
getResponse,
addList,
getLists,
deleteDoc,
renameList,
duplicateList,
getCategory,
postCategory,
getShoppingList,
addListItem,
boughtItem,
deleteItem,
incCountItem,
signUp,
getUser,
_idToId,
requiredFields,
}

View File

@@ -0,0 +1,54 @@
const router = require('express').Router()
const { expressjwt } = require('express-jwt')
const ObjectId = require('mongodb').ObjectID
const { BASKET_JWT_TOKEN } = require('./key')
const {
getResponse, addList,
getLists, deleteDoc, renameList, duplicateList,
} = require('./controller')
const wait = (req, res, next) => setTimeout(next, 0)
router.use(expressjwt({ secret: BASKET_JWT_TOKEN, algorithms: ['HS256'] }))
/* получить списки покупок*/
router.get('/list', wait, async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
const listData = await getLists({ userId }).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, listData))
})
/* удалить список*/
router.delete('/list', wait, async (req, res) => {
let error = null
const listData = await deleteDoc(req.body).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, listData))
})
/* добавить новый список*/
router.post('/list', wait, async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
// eslint-disable-next-line max-len
const listData = await addList({ userId, ...req.body }).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, listData))
})
/* переименовать список*/
router.put('/list', wait, async (req, res) => {
let error = null
const listData = await renameList(req.body).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, listData))
})
/* дублировать список*/
router.post('/list/duplicate', wait, async (req, res) => {
let error = null
const listData = await duplicateList(req.body).catch((e) => error = e.message)
res.status(error ? 400 : 200).send(getResponse(error, listData))
})
module.exports = router

View File

@@ -0,0 +1,9 @@
const router = require('express').Router()
router.use('/dashboard', require('./dashboard'))
router.use('/landing', require('./landing'))
router.use('/categories', require('./categories'))
router.use('/shoppingList', require('./listItem'))
router.use('/auth', require('./auth'))
module.exports = router

View File

@@ -0,0 +1,10 @@
{
"data": {
"token": "qwert",
"user": {
"id": "1234",
"login": "eldar",
"email": "www@www.ru"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"success": true,
"data":
{
"category":[
{
"id":1,
"name": "Продукты",
"color": "#08AE0F"
},
{
"id":2,
"name": "Бытовая химия",
"color": "#3414F5"
},
{
"id":3,
"name": "Одежда",
"color": "#FA8803"
}
]
}
}

View File

@@ -0,0 +1,18 @@
{
"success": true,
"data":
{
"category":[
{
"id":1,
"name": "Продукты",
"color": "#08AE0F"
},
{
"id":2,
"name": "Бытовая химия",
"color": "#3414F5"
}
]
}
}

View File

@@ -0,0 +1,3 @@
{
"error": "Не получилось..."
}

View File

@@ -0,0 +1,8 @@
{
"data": {
"id": "vrgbtrgbtrbryn",
"listName": "2 список",
"purchased": 1,
"total": 5
}
}

View File

@@ -0,0 +1,4 @@
{
"success": true,
"data": []
}

View File

@@ -0,0 +1,4 @@
{
"success": false,
"error": "Список не загрузился"
}

View File

@@ -0,0 +1,23 @@
{
"success": true,
"data": [
{
"id": "uuid1",
"listName": "Состоялась 94-я церемония вручения премии «Оскар»: награда за лучший фильм присуждена картине...",
"purchased": 0,
"total": 5
},
{
"id": "uuid2",
"listName": "Второй список",
"purchased": 1,
"total": 5
},
{
"id": "uuid3",
"listName": "Первый список",
"purchased": 5,
"total": 5
}
]
}

View File

@@ -0,0 +1,4 @@
{
"success": false,
"error": "Ошибка получения данных для Landing"
}

View File

@@ -0,0 +1,80 @@
{
"success": true,
"data": [
{
"features": [
{
"id": "1",
"nameImg": "image-shopping-list.png",
"altImg": "image-shopping-list",
"title": "Список покупок",
"text": "Отличный интерфейс, удобно и понятно для использования. Удобно! Если нет нужного в списке, можно добавить и оно сохраниться."
},
{
"id": "2",
"nameImg": "image-shared-access.png",
"altImg": "image-shared-access",
"title": "Общий доступ",
"text": "Делитесь списками покупок, чтобы планировать вместе с другими. Синхронизация для нескольких покупателей — это вещь!"
},
{
"id": "3",
"nameImg": "image-messaging.png",
"altImg": "image-messaging",
"title": "Обмен сообщениями",
"text": "Хотите что-то обсудить при составление списка покупок, то есть возможность обмениваться сообщениями в режиме реального времени."
},
{
"id": "4",
"nameImg": "image-share-photos.png",
"altImg": "mage-share-photos",
"title": "Делитесь фотографиями",
"text": "Не перепутайте товар при покупки — загружайте фотографии и обменивайтесь ими с другими пользователями. Это очень удобно!"
},
{
"id": "5",
"nameImg": "image-prices.png",
"altImg": "image-prices",
"title": "Цены",
"text": "Расходы под контролем — вводите цены в спиок покупок. Приложение оценит стоимость и будет известно, чего ожидать на кассе."
},
{
"id": "6",
"nameImg": "image-ecology.png",
"altImg": "image-ecology",
"title": "Экология",
"text": "Бумажные списки покупок — это деревья, которые могли бы еще расти. Вместо тысяч слов — возьми с собой приложение!"
},
{
"id": "7",
"nameImg": "image-buy.png",
"altImg": "image-buy",
"title": "Покупайте",
"text": "97% пользователей поробывали приложение и теперь совершают меньше лишних и ненужных покупок. Покупайте нужное!"
}
],
"helps": [
{
"id": "1",
"title": "Как работает вМагазин?",
"text": "Задача организации, в особенности же консультация с широким активом обеспечивает широкому кругу специалистов новых принципов формирования материально-технической и кадровой базы."
},
{
"id": "2",
"title": "Условия оплаты",
"text": "Задача организации, в особенности же консультация с широким активом обеспечивает широкому кругу специалистов новых принципов формирования материально-технической и кадровой базы."
},
{
"id": "3",
"title": "Как с нами связаться",
"text": "Задача организации, в особенности же консультация с широким активом обеспечивает широкому кругу специалистов новых принципов формирования материально-технической и кадровой базы."
},
{
"id": "4",
"title": "Текст",
"text": "Задача организации, в особенности же консультация с широким активом обеспечивает широкому кругу специалистов новых принципов формирования материально-технической и кадровой базы."
}
]
}
]
}

View File

@@ -0,0 +1,3 @@
{
"error": "Не удалось изменить..."
}

View File

@@ -0,0 +1,15 @@
{
"success": true,
"data":
{
"id": 23,
"categoryId": 1,
"text": "Курица",
"count": 17,
"bought": false,
"createdBy": "",
"createdDt": "",
"modifiedBy": "",
"modifiedDt": ""
}
}

View File

@@ -0,0 +1,4 @@
{
"success": false,
"error": "Ошибка получения данных"
}

View File

@@ -0,0 +1,34 @@
{
"success": true,
"data":
{
"id": 8,
"listName":"Мой список",
"data":[
{
"id": 1,
"categoryId": 1,
"text": "Курица",
"count": 2,
"bought": true,
"delete":false
},
{
"id": 2,
"categoryId":1,
"text": "Хлеб",
"count": 1,
"bought":false,
"delete":true
},
{
"id": 3,
"categoryId": "3",
"text": "Шампунь",
"count": 1,
"bought":true,
"delete":false
}
]
}
}

View File

@@ -0,0 +1,5 @@
const BASKET_JWT_TOKEN = 'super super secret key'
module.exports = {
BASKET_JWT_TOKEN,
}

View File

@@ -0,0 +1,7 @@
const router = require('express').Router()
router.get('/', (req, res) => {
res.send(require('./json/landing/success.json'))
})
module.exports = router

View File

@@ -0,0 +1,67 @@
const router = require('express').Router()
const { expressjwt } = require('express-jwt')
const ObjectId = require('mongodb').ObjectID
const { BASKET_JWT_TOKEN } = require('./key')
const { getShoppingList, deleteItem, boughtItem, incCountItem, getResponse, addListItem } = require('./controller')
router.use(expressjwt({ secret: BASKET_JWT_TOKEN, algorithms: ['HS256'] }))
router.get('/:id', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
// eslint-disable-next-line no-return-assign
const { id } = req.params
// eslint-disable-next-line no-return-assign
const listData = await getShoppingList({ userId, id }).catch((e) => error = e.message)
res.send(getResponse(error, listData))
})
router.post('/item/:id', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
// eslint-disable-next-line no-return-assign
const { id } = req.params
const { categoryId, text } = req.body
const shoppingListData = await addListItem({
userId, listId: id, categoryId, text,
// eslint-disable-next-line no-return-assign
}).catch((e) => error = e.message)
res.send(getResponse(error, shoppingListData))
})
router.patch('/item/:id', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
// eslint-disable-next-line no-return-assign
const { id } = req.params
const { bought } = req.body.item
// eslint-disable-next-line no-return-assign
// eslint-disable-next-line max-len
const itemData = await boughtItem({ userId, itemId: id, bought }).catch((e) => error = e.message)
res.send(getResponse(error, itemData))
})
router.put('/item/:id', async (req, res) => {
const userId = new ObjectId(req.auth.id)
let error = null
// eslint-disable-next-line no-return-assign
const { id } = req.params
const { count } = req.body
// eslint-disable-next-line no-return-assign
// eslint-disable-next-line max-len
const itemData = await incCountItem({ userId, itemId: id, count }).catch((e) => error = e.message)
res.send(getResponse(error, itemData))
})
router.delete('/item/:id', async (req, res) => {
let error = null
// eslint-disable-next-line no-return-assign
const { id } = req.params
// eslint-disable-next-line no-return-assign
const itemData = await deleteItem({ itemId: id }).catch((e) => error = e.message)
res.send(getResponse(error, itemData))
})
module.exports = router

View File

@@ -0,0 +1,162 @@
[
{
"id": "6d6883d86aa44db784436894a0b30881",
"title": "听新闻学汉语 2021年03月31日",
"description": "中国-世卫组织新冠病毒溯源联合研究报告正式发布\n世界卫生组织30日在日内瓦正式发布中国-世卫组织新冠病毒溯源联合研究报告。报告认为新冠病毒“极不可能”通过实验室传人。今年1月14日至2月10日17名中方专家和17名外方专家组成联合专家组分为流行病学、分子溯源、动物与环境3个小组在武汉开展了为期28天的全球溯源研究中国部分工作在此基础上撰写了研究报告。联合专家组评估了关于病毒引入人类的4个路径认为新冠病毒“比较可能至非常可能”经中间宿主传人“可能至比较可能”直接传人“可能”通过冷链食品传人“极不可能”通过实验室传人。报告提出了联合专家组下步研究的建议包括建立全球统一的数据库在全球更广范围内继续寻找可能的早期病例由全球科学家在多国多地寻找可能成为病毒宿主的动物物种进一步了解冷链和冷冻食品在病毒传播过程中的作用等。\n\n货轮搁浅6天终于脱困 苏伊士运河恢复通航\n苏伊士运河因“长赐”号货轮搁浅被堵多日货轮解困进展牵动全球目光。经过了长达一周的救援当地时间3月29日搁浅货轮已经完全脱困苏伊士运河恢复通航。当地时间3月29日救援队成功让搁浅在苏伊士运河长达一周的巨型货轮脱困。过去7天这艘搁浅货轮阻止了价值数十亿美元的货物通过苏伊士运河。苏伊士运河是世界上最繁忙的海运通道之一。受雇参与救援的荷兰宝斯卡利斯公司的总监皮特•伯尔道斯基表示为了让货轮脱困挖掘机挖走了3万立方米的泥沙并动用了13艘拖船。\n\n威廉王子获评“世界最性感光头男人”\n上周六3月27日英国《太阳报》发布报道称威廉王子是“世界最性感的秃顶男人”该报道基于提供植发服务的整容公司的一项调查。据《太阳报》报道该调查是通过分析“博客、报道和谷歌搜索页面”中“性感”一词的出现频率来得出结果的。《太阳报》写道调查发现威廉王子的名字被提及1760万次。这篇文章还提到了调查结果中入选前十位的其他名人。迈克•泰森仅次于威廉王子排在第二位紧随其后的是《速度与激情》主演杰森•斯坦森、说唱歌手皮普保罗、迈克尔•乔丹、拳击手弗洛伊德•梅威瑟、约翰•特拉沃尔塔、布鲁斯•威利斯、“巨石”强森和范•迪塞尔。",
"pub_date_ms": 1617163200000,
"audio": "https://www.listennotes.com/e/p/6d6883d86aa44db784436894a0b30881/",
"audio_length_sec": 553,
"listennotes_url": "https://www.listennotes.com/e/6d6883d86aa44db784436894a0b30881/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/6d6883d86aa44db784436894a0b30881/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "b82bf190efbc59a11323fba954a88939"
},
{
"id": "e8f2f82ac8b645c18ba3cc401cc3b9e0",
"title": "中国人的问候方式",
"description": "问候是交际中最常用的日常礼节,人与人之间的交际都是从互致问候开始的。由于习俗不同,各个国家,各个民族的问候方式差异很大。\n首先在问候类别上中国人的问候方式包括问好型、询问型、也有评论型。问好型最常用的是早、你早。中国人现在也越来越多用“你好”来打招呼。这种问候方式比较正式。汉语中还有“干吗去啊”“吃了吗”等询问型问候语以表示对别人的关心并不是打探隐私。\n其次问候语涉及的话题也不同。汉语的问候语大多由包含信息的问句组成并且大多涉及别人的起居寒暖给人亲切关心的感觉。中国人往往会互相问起对方的年龄、收入、婚姻和健康等非常大众化的话题并且问得越详细越能体现出对别人的关心。\n除了语言问候方式中国人的非语言问候方式也有自己的特点。中国人尤其是年轻人之间通常会点头或挥手表示问候而在旧时人们见面还要鞠躬以示问候。中国人的问候方式一般比较含蓄即使是久别重逢也仅仅是握握手绝不会亲吻一般男女之间连拥抱都很少有最多握握手。",
"pub_date_ms": 1617076800000,
"audio": "https://www.listennotes.com/e/p/e8f2f82ac8b645c18ba3cc401cc3b9e0/",
"audio_length_sec": 945,
"listennotes_url": "https://www.listennotes.com/e/e8f2f82ac8b645c18ba3cc401cc3b9e0/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/e8f2f82ac8b645c18ba3cc401cc3b9e0/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "e0b0bedbc76befceae54d01abdfb4742"
},
{
"id": "a7fa065d4f3e43f4af68b642f2ae7331",
"title": "不求有功,但求无过",
"description": "张路:大家好,我是张路。\n珺慧大家好我是珺慧。欢迎大家按时收听我们的节目。\n张路珺慧今天你给大家带来的俗语是哪一个呢\n珺慧今天我想给大家讲讲“不求有功但求无过”这个俗语。\n张路好啊那么你先来说说它的意思吧。\n珺慧“功”在这里是功绩、立功的意思“过”是指错误、过错的意思“求”是指要求、希望。所以“不求有功但求无过”这个俗语的表面意思就是不要求立功只希望没有错误。\n张路我觉得这个俗语其实也反映了一些人对待事情的态度。\n珺慧什么态度呢\n张路比如说我吧我就非常希望成功做事情的时候即使要冒险我也不怕只要能成功就行。可是我姐姐跟我就不一样她就是那种“不求有功但求无过”的人她做事特别小心谨慎不太追求成功只要没有大的错误就行。\n珺慧没错所以这个俗语有时也比喻不求上进或安分守己的态度或做法。\n张路下面我们就来听三段对话一起来学习一下这个俗语的意思和用法。",
"pub_date_ms": 1616817600000,
"audio": "https://www.listennotes.com/e/p/a7fa065d4f3e43f4af68b642f2ae7331/",
"audio_length_sec": 404,
"listennotes_url": "https://www.listennotes.com/e/a7fa065d4f3e43f4af68b642f2ae7331/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/a7fa065d4f3e43f4af68b642f2ae7331/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "722cdf994f21362230a5081106b82a3a"
},
{
"id": "94f99709224a4cd98f92ae4399ee1327",
"title": "听新闻学汉语 2021年03月26日",
"description": "83天200亿件快递业务量增长再提速\n国家邮政局实时监测数据显示截至3月24日今年我国快递业务量已突破200亿件日均业务量超过2.4亿件日均服务用户接近5亿人次服务民生作用更加凸显。今年以来邮政快递业继续保持高速增长态势预计全年快递业务量将超过950亿件。值得注意的是今年快递业务量突破200亿件用时仅83天比2020年提前了45天又一次刷新了我国快递业发展纪录。今年邮政快递业更贴近民生七件实事提出要提高建制村快递服务通达率东部地区基本实现快递服务直投到村中、西部地区分别达到80%和60%。\n\n欧盟正式实施健康欧盟计划\n欧盟委员会26日宣布2021年至2027年健康欧盟计划即日起正式实施旨在增强欧盟有效应对未来卫生危机的能力。欧委会当天在一份声明中说欧盟将为该计划拨付51亿欧元的财政预算有助于新冠疫情后健全卫生系统增强有效应对跨境健康威胁及未来卫生危机的能力促进人口健康。根据声明健康欧盟计划指导小组将与欧盟成员国协商后推出2021年第一个工作方案。健康欧盟计划将由新成立的健康和数字执行机构负责实施该机构将于4月1日开始运行。欧盟委员会负责卫生和食品安全事务的委员基里亚基季斯表示健康欧盟计划为欧盟在公共卫生领域进行长期性变革提供了手段可以通过规模空前的财政预算进行有针对性的投资以加强危机准备建立更强大、更有韧性和更易获得的卫生系统。欧委会于去年5月提出2021年至2027年健康欧盟计划旨在向欧盟成员国、卫生组织和非政府组织等提供资金支持提高应对卫生领域各种威胁的能力。该计划于本月9日经欧洲议会表决通过17日获欧洲理事会批准。\n\n研究不爱运动的人更容易失眠\n日前中国睡眠研究会发布了《2021年运动与睡眠白皮书》。数据显示当下中国有超3亿人存在睡眠障碍而运动人群失眠困扰比例仅为10%。运动人群中以广东运动人数最多,并且睡眠充足比例位列各省份第一。研究报告指出,新冠疫情期间,不运动人群和定期运动人群睡眠状态两极分化,旅行限制令使得久坐人群减少锻炼,作息更不规律。中国睡眠研究会理事、北京朝阳医院呼吸睡眠中心主任郭兮恒称,随着国内疫情得到控制,回归常规生活的重要一步就是多出门走走,定期锻炼,以解决日益普遍的睡眠问题。",
"pub_date_ms": 1616731200000,
"audio": "https://www.listennotes.com/e/p/94f99709224a4cd98f92ae4399ee1327/",
"audio_length_sec": 617,
"listennotes_url": "https://www.listennotes.com/e/94f99709224a4cd98f92ae4399ee1327/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/94f99709224a4cd98f92ae4399ee1327/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "7ed62a5606b169765dcc29d339b6e2d2"
},
{
"id": "244fe98dbb8d47cdb090041db48db674",
"title": "我对摇滚乐不感兴趣",
"description": "林娜:音乐声太吵了,赶快关掉。\n表妹这首歌多好听啊听完再关吧。\n林娜这有什么好听的我真不明白你为什么这么喜欢摇滚乐。\n表妹这有什么不明白的原因很简单啊我一听摇滚乐就特别兴奋。\n林娜我对摇滚乐不感兴趣完全没办法欣赏。拜托你下次听摇滚乐的时候小声一点儿不要影响别人。\n表妹好吧好吧我戴上耳机听绝对不会影响到你你放心吧。",
"pub_date_ms": 1616644800001,
"audio": "https://www.listennotes.com/e/p/244fe98dbb8d47cdb090041db48db674/",
"audio_length_sec": 910,
"listennotes_url": "https://www.listennotes.com/e/244fe98dbb8d47cdb090041db48db674/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/244fe98dbb8d47cdb090041db48db674/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "7bda2555f80feee77ea3c2f0b48d3034"
},
{
"id": "38cef39a6f64477488fa0ff88bed6e17",
"title": "听新闻学汉语 2021年03月24日",
"description": "教育部印发《职业教育专业目录2021年》 共设置1349个专业\n记者22日从教育部获悉其近日印发的《职业教育专业目录2021年专业设置对接现代产业体系服务产业基础高级化、产业链现代化共包含1349个专业。此次新版目录面向集成电路技术、生物信息技术、新能源材料应用技术等9大重点领域服务国家战略性新兴产业发展此外回应社会民生关切加强紧缺领域人才培养如设置婴幼儿托育服务与管理、智慧健康养老服务与管理等专业。\n\n澳大利亚东部遭遇罕见暴雨洪灾\n澳大利亚官员21日说东部沿海地带最近几天连降暴雨部分区域遭遇50年来最严重洪灾数以千计居民紧急疏散数以百计房屋损毁。新南威尔士州有800万人口是澳大利亚人口最多的州。州长格拉迪丝•贝雷吉克利安21日在一场新闻发布会上说全州范围遭遇暴雨情形比先前预期得更糟糕州首府悉尼西北部地势低洼地区洪灾尤其严重。“我们昨天预期将迎来20年一遇的洪灾”她说“但如今看来这是50年一遇的洪灾。”贝雷吉克利安说由于暴雨洪水摧毁房屋不少居民20日夜间至21日凌晨紧急疏散另有1000人21日晚些时候接到疏散令今后几天可能还将有大约4000人接到疏散令。气象部门预测澳大利亚东部地区暴雨还将持续数日。据法新社报道自18日开始降雨以来澳大利亚紧急情况应对部门已经接到7000多个求助电话。\n\n日本孩子长大后最想干啥男孩公司职员 女孩:糕点师\n据日本《朝日新闻》中文网报道日本“第一生命保险”不久前公布了对小学3至6年级儿童询问“长大后最想成为什么”的调查结果发现男生首选“公司职员”女生选择最多的是“糕点师”。根据调查结果男生选择的第1位是“公司职员”“油管主播”(YouTuber)以微弱之差位居其后。女生选择的第1位是“糕点师”第2位是“教师”。该调查以日本全国的约1100人为对象于2020年12月实施。此次是第32次。男生选择的第1位从上次的“足球选手”转为了“公司职员”。“第一生命保险”的负责人对这一结果解释称“在因疫情而引入远程工作的情况下或许是因为看到父母在家里工作的样子而产生了一种亲近感吧”。",
"pub_date_ms": 1616558400002,
"audio": "https://www.listennotes.com/e/p/38cef39a6f64477488fa0ff88bed6e17/",
"audio_length_sec": 536,
"listennotes_url": "https://www.listennotes.com/e/38cef39a6f64477488fa0ff88bed6e17/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/38cef39a6f64477488fa0ff88bed6e17/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "f28e8823de7317903c9a994e562f02a6"
},
{
"id": "5b8527abc26a47aea5d79849c1577bbe",
"title": "寒食节的传说(二)",
"description": "晋文公多次请介子推出来做官,可介子推每次都不愿意。晋文公很想念他,最后就想了一个办法:他想,介子推藏在山里,我怎么也找不到他。如果我在山里放火,他怕被火烧死,一定会跑出来,这样我就一定能见到他了。可是他没想到,介子推宁愿烧死也不愿意出来做官,晋文公放了火,介子推最后被大火烧死了。晋文公万万没想到他得到的是这样一个悲剧的结果。他因为想念自己的朋友而杀死了自己的朋友,又难过又后悔,生了一场大病,自己也差一点儿死了。\n晋文公为了纪念自己的好朋友介子推下了一个命令在每年的农历三月初三这一天全国的老百姓都不能用火甚至不能做饭只能吃做好的食品。因为这一天是他放火烧死自己朋友的日子他永远后悔这一天。因为法律不准用火人们只能吃凉的东西所以历史上这一天就被称作“寒食节”。",
"pub_date_ms": 1616472000003,
"audio": "https://www.listennotes.com/e/p/5b8527abc26a47aea5d79849c1577bbe/",
"audio_length_sec": 913,
"listennotes_url": "https://www.listennotes.com/e/5b8527abc26a47aea5d79849c1577bbe/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/5b8527abc26a47aea5d79849c1577bbe/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "f57e66faffeff030727d85932435c52e"
},
{
"id": "8fdae88892734896b79395817c061221",
"title": "寒食节的传说(一)",
"description": "传说在中国古代有一个晋国。这个国家遇到了危险,国家的王子逃了出去。在王子的身边有一些很好的朋友和忠诚的读书人陪着他,这些人爱自己的国家,他们支持王子,希望他能努力奋斗,回去救自己的国家。他们在外边受了很多苦,一直逃亡了十九年。这就是历史上的晋文公。\n在国外逃亡期间晋文公有一个最忠诚的朋友叫介子推。他是一个优秀的读书人为了自己热爱的祖国为了帮助晋文公重新回国做国王做了很多努力。他们成功以后晋文公让伴随他逃亡的人都做了大官。可是没想到他最好的朋友介子推却坚决拒绝他的邀请这让晋文公感到很苦恼。无论他怎么劝说介子推就是不肯出来做官。为了表示自己不愿意出来做官的决心介子推背着自己的母亲到了山上去隐居。",
"pub_date_ms": 1616212800004,
"audio": "https://www.listennotes.com/e/p/8fdae88892734896b79395817c061221/",
"audio_length_sec": 863,
"listennotes_url": "https://www.listennotes.com/e/8fdae88892734896b79395817c061221/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/8fdae88892734896b79395817c061221/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "a2df1174ce43a2f18a71d288ecc646f7"
},
{
"id": "0932fa791c6243f3a03ba42da3397369",
"title": "听新闻学汉语 2021年03月19日",
"description": "外交部:疫苗好不好要看是否安全可靠 反对搞疫苗民族主义\n3月15日外交部发言人赵立坚主持外交部例行记者会。有记者提问新加坡总理李显龙接受媒体采访时针对日益政治化的新冠病毒疫苗问题他表示疫苗不分国籍没有任何依据可以凭生产国就断言中国疫苗必定好或者不好。他还表示中国有非常优秀的科学家、生物医药和疫苗研究人员相信他们有能力生产好的疫苗。中方有何回应赵立坚表示疫苗是抗击病毒的利器是拯救生命的希望应当服务全世界、造福全人类。无论是哪个国家的疫苗只要安全可靠就是好疫苗。中方将继续同各方一道反对搞疫苗民族主义不接受制造“免疫鸿沟”努力推进疫苗在全球范围内的公平分配携手各国共同战胜疫情。\n\n朝鲜宣布与马来西亚断交\n据朝中社19日报道朝鲜外务省当天发表声明正式宣布完全断绝与马来西亚的外交关系。声明表示马来西亚当局17日以涉嫌“非法洗钱”为由将一名无辜的朝鲜公民强行引渡至美国彻底破坏了两国关系中相互尊重主权的基础鉴于当前的严重事态朝方因此决定断绝与“屈服美国强权而对朝鲜做出特大敌对行为”的马来西亚的外交关系。声明称该朝鲜公民多年在新加坡从事合法对外贸易活动其涉嫌洗钱是荒唐无稽的捏造和阴谋马来西亚方面也从未提出过任何确凿证据。声明指出马来西亚当局盲目追随美国不当的压力甚至无视公认的国际法把朝鲜公民当作美国敌视政策的牺牲品。从现在起马来西亚将要对双方之间可能发生的任何后果负全部责任。声明还警告美国作为这起事件的幕后操纵者也将付出应有的代价。据悉朝鲜和马来西亚1973年正式建交。\n\n20天逾4万次地震冰岛人被震到失眠\n据外媒报道自2月24日冰岛西南部的雷克雅内斯半岛发生5.6级地震以来该地区余震不断。过去20天已记录下4万多次地震活动。据路透社16日报道自2月24日以来雷克雅内斯半岛发生了40000多次地震超过了2020年在那里记录的地震总数。冰岛气象局(IMO)火山灾害协调员萨拉•巴索蒂表示:“我们从未见过如此多的地震活动。”如此频繁的地震活动,严重影响了当地居民的睡眠。“这里的每个人都很累,”当地一位名叫古德蒙兹多蒂尔的教师说。“当我晚上上床睡觉的时候,我想的都是:我今晚能睡得着吗?”",
"pub_date_ms": 1616126400000,
"audio": "https://www.listennotes.com/e/p/0932fa791c6243f3a03ba42da3397369/",
"audio_length_sec": 580,
"listennotes_url": "https://www.listennotes.com/e/0932fa791c6243f3a03ba42da3397369/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/0932fa791c6243f3a03ba42da3397369/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "9563923aacc3c0ced1bde1af9c578a14"
},
{
"id": "7ea1b07350204827a31c9b0995d9ce22",
"title": "我想预订一个双人间",
"description": "服务员:早上好。这里是假日酒店,很高兴为您服务。\n林 娜:早上好。我想预订一个双人间,你们有下周的空房吗?\n服务员请稍等。让我查一下房间的预订情况。是的我们有剩余的双人间。请问您要什么样的房间\n林 娜:我想要一个能看到漂亮风景的房间。\n服务员我们有一个带漂亮花园风景的房间可以吗\n林 娜:棒极了,我就要这间。多少钱?\n服务员每晚880元包含早餐。\n林 娜:好的,我们下周一下午两点左右入住,周四早上离开。\n服务员知道了。请留下您的名字和电话号码。\n林 娜:林娜,一三九五八一六二五一二。",
"pub_date_ms": 1616040000000,
"audio": "https://www.listennotes.com/e/p/7ea1b07350204827a31c9b0995d9ce22/",
"audio_length_sec": 971,
"listennotes_url": "https://www.listennotes.com/e/7ea1b07350204827a31c9b0995d9ce22/",
"image": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"thumbnail": "https://cdn-images-1.listennotes.com/podcasts/learn-chinese-culture-imandarinpodcom-Ac3CZvRgnzL.300x300.jpg",
"maybe_audio_invalid": false,
"listennotes_edit_url": "https://www.listennotes.com/e/7ea1b07350204827a31c9b0995d9ce22/#edit",
"explicit_content": false,
"link": "http://www.imandarinpod.com?utm_source=listennotes.com&utm_campaign=Listen+Notes&utm_medium=website",
"guid_from_rss": "9a3124609346ebf3c52cafbe8d0d1b4a"
}
]

Binary file not shown.

View File

@@ -0,0 +1,13 @@
const router = require('express').Router()
router.get('/radicals', (req, res) => res.send(require('./radicals/success.json')))
router.get('/episodes', (req, res) => res.send(require('./episodes/success.json')))
router.get('/tokenize', (req, res) => res.send(require('./tokenize/success.json')))
router.get('/images/:name', (req, res) => {
res.sendFile(`${__dirname}/images/${encodeURIComponent(req.params.name)}`)
})
module.exports = router

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.use(require('./login'))
router.use(require('./registration'))
router.use(require('./logrec'))
router.use(require('./passrec'))
module.exports = router

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,16 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.post('/postAuthorization', (req, res) => {
if (req?.session?.[req.body.login]?.login) {
const { login } = req.session[req.body.login]
const { pass } = req.session[req.body.login]
if (login === req.body.login && pass === req.body.pass) {
return res.send(req.session[req.body.login])
}
}
res.send(require('./error.json'))
})
module.exports = router

View File

@@ -0,0 +1,3 @@
{
"status": "success"
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,40 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.post('/postLoginRec', (req, res) => {
if (Object.getOwnPropertyNames(req.session).some((item) => item === req.body.phone)) {
const loginRec = req.session[req.body.phone].login
if (req.body.phoneRecForm === 0) {
if (req.session[loginRec].phone === req.body.phone) {
const code = Math.floor(Math.random() * (9999 - 1000 + 1)) + 1000
const ret1 = {
status: 'success',
phonecode: code,
}
req.session[loginRec].phonecode = ret1.phonecode
return res.send(ret1)
}
res.send(require('./error.json'))
}
if (req.body.phoneRecForm === 1) {
if (req.session[loginRec].phonecode === req.body.phonecode) {
const ret = {
login: req.session[loginRec].login,
phone: req.session[loginRec].phone,
status: 'success',
}
req.session[loginRec].phonecode = undefined
return res.send(ret)
}
const ret = {
phonecode: req.session[loginRec].phonecode,
status: 'error',
}
return res.send(ret)
}
}
res.send(require('./error.json'))
})
module.exports = router

View File

@@ -0,0 +1,3 @@
{
"status": "success"
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,44 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.post('/postPassRec', (req, res) => {
if (req.body.login in req.session) {
if (req.body.phoneRecForm === 0) {
const code = Math.floor(Math.random() * (9999 - 1000 + 1)) + 1000
const ret1 = {
status: 'success',
phonecode: code,
}
req.session[req.body.login].phonecode = ret1.phonecode
return res.send(ret1)
}
if (req.body.phoneRecForm === 1) {
if (req.session[req.body.login].phonecode === req.body.phonecode) {
const ret = {
login: req.session[req.body.login].login,
phone: req.session[req.body.login].phone,
phonecode: req.session[req.body.login].phonecode,
status: 'success',
}
return res.send(ret)
}
const ret = {
phonecode: req.session[req.body.login].phonecode,
status: 'err',
}
return res.send(ret)
}
if (req.body.phoneRecForm === 2) {
const ret = {
status: 'success',
}
req.session[req.body.login].phonecode = undefined
req.session[req.body.login].pass = req.body.phonenewpass
return res.send(ret)
}
}
res.send(require('./error.json'))
})
module.exports = router

View File

@@ -0,0 +1,3 @@
{
"status": "success"
}

View File

@@ -0,0 +1,3 @@
{
"status": "dooble"
}

View File

@@ -0,0 +1,3 @@
{
"status": "dooblePhone"
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,26 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.post('/postRegistration', (req, res) => {
if (req.body.phone && req.body.login && req.body.pass) {
if (!req.session[req.body.login]) {
const ret = {
login: req.body.login,
pass: req.body.pass,
phone: req.body.phone,
status: 'success',
}
req.session[req.body.login] = ret
req.session[req.body.phone] = ret
} else if (req.session[req.body.login]) {
return res.send(require('./dooble.json'))
} else if (req.session[req.body.phone]) {
return res.send(require('./dooblephone.json'))
}
return res.send(require('./success.json'))
// return res.send(req.session);
}
res.send(require('./error.json'))
})
module.exports = router

View File

@@ -0,0 +1,3 @@
{
"status": "success"
}

View File

@@ -0,0 +1,40 @@
{
"status": {
"code": 0
},
"banners": [
{
"img": "first-slide.gif",
"title": "Fast and quality service",
"description": "1",
"color": "black"
},
{
"img": "second-slide.gif",
"title": "Affordable prices",
"description": "2",
"color": "black"
},
{
"img": "third-slide.gif",
"title": "Courteous staff",
"description": "3",
"color": "black"
},
{
"img": "fourth-slide.gif",
"title": "Comfortable waiting area",
"description": "3",
"color": "black"
},
{
"img": "fifth-slide.gif",
"title": "Convenient operating hours",
"description": "3",
"color": "black"
}
]
}

View File

@@ -0,0 +1,40 @@
{
"status": {
"code": 0
},
"banners": [
{
"img": "first-slide.gif",
"title": "Быстрое и качественное обслуживание",
"description": "1",
"color": "black"
},
{
"img": "second-slide.gif",
"title": "Доступные цены",
"description": "2",
"color": "black"
},
{
"img": "third-slide.gif",
"title": "Вежливый персонал",
"description": "3",
"color": "black"
},
{
"img": "fourth-slide.gif",
"title": "Комфортная зона ожидания",
"description": "3",
"color": "black"
},
{
"img": "fifth-slide.gif",
"title": "Удобное время работы",
"description": "3",
"color": "black"
}
]
}

View File

@@ -0,0 +1,23 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.get('/getBanners', (req, res) => {
switch (req.query.lng) {
case 'en':
res.send(require('./carousel-en.json'))
break
case 'ru':
res.send(require('./carousel-ru.json'))
break
case 'en-En':
res.send(require('./carousel-en.json'))
break
case 'ru-Ru':
res.send(require('./carousel-ru.json'))
break
default:
console.log('Unknown language')
}
})
module.exports = router

View File

@@ -0,0 +1,9 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.use(require('./carousel'))
router.use(require('./map'))
router.use('/auth', require('./auth'))
router.use(require('./prices'))
module.exports = router

View File

@@ -0,0 +1,23 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.get('/get-map-info', (req, res) => {
switch (req.query.lng) {
case 'en':
res.send(require('./map-info-en.json'))
break
case 'ru':
res.send(require('./map-info-ru.json'))
break
case 'en-En':
res.send(require('./map-info-en.json'))
break
case 'ru-Ru':
res.send(require('./map-info-ru.json'))
break
default:
console.log('Unknown language')
}
})
module.exports = router

View File

@@ -0,0 +1,37 @@
{
"address": [
{
"id": "1",
"x": "55.746971",
"y": "48.743549",
"name": "InnoWashing",
"phone": "7654321",
"address": "Russia, Republic of Tatarstan, Verkhneuslonsky District, Innopolis, 106 Sportivnaya Street",
"workingMode": "08:00-20:00",
"services": ["Contactless car wash", "Salon wash"],
"availableTime": ["7.00-8.00", "11.00-12.00", "17.00-18.00", "19.00-20.00", "21.00-22.00"]
},
{
"id": "2",
"x": "55.754105",
"y": "48.743341",
"name": "Car Wash №1",
"phone": "1234567",
"address": "Russia, Republic of Tatarstan, Verkhneuslonsky District, Innopolis, 1 University Street",
"workingMode": "09:00-20:00",
"services": ["3-in-1 Wash", "Nano Wash", "Salon Wash", "Contactless Wash"],
"availableTime": ["9.00-10.00", "12.00-13.00", "15.00-16.00"]
},
{
"id": "3",
"x": "55.784105",
"y": "48.733341",
"name": "Hare's car wash",
"phone": "00000",
"address": "Russia, Republic of Tatarstan, Verkhneuslonsky district, Makarievsky forest",
"workingMode": "24 hours",
"services": ["Paw wash", "Juniper wash", "Hose wash"],
"availableTime": ["9.00-24.00"]
}
]
}

View File

@@ -0,0 +1,37 @@
{
"address": [
{
"id": "1",
"x": "55.746971",
"y": "48.743549",
"name": "InnoWashing",
"phone": "7654321",
"address": "Россия, Республика Татарстан, Верхнеуслонский район, Иннополис, Спортивная улица, 106",
"workingMode": "08:0020:00",
"services": ["Бесконтактная мойка", "Мойка салона"],
"availableTime": ["7.00-8.00", "11.00-12.00", "17.00-18.00", "19.00-20.00", "21.00-22.00"]
},
{
"id": "2",
"x": "55.754105",
"y": "48.743341",
"name": "Автомойка №1",
"phone": "1234567",
"address": "Россия, Республика Татарстан, Верхнеуслонский район, Иннополис, Университетская улица, 1",
"workingMode": "09:0020:00",
"services": ["Мойка 3 в 1", "Наномойка", "Мойка салона","Бесконтактная мойка"],
"availableTime": ["9.00-10.00", "12.00-13.00", "15.00-16.00"]
},
{
"id": "3",
"x": "55.784105",
"y": "48.733341",
"name": "Автомойка у зайца",
"phone": "00000",
"address": "Россия, Республика Татарстан, Верхнеуслонский район, Макарьевский лес",
"workingMode": "24 часа",
"services": ["Мойка лапами", "Мойка можжевеловой мочалкой", "Мойка из шланга"],
"availableTime": ["9.00-24.00"]
}
]
}

View File

@@ -0,0 +1,22 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.get('/get-prices', (req, res) => {
switch (req.query.lng) {
case 'en':
res.send(require('./prices-en.json'))
break
case 'ru':
res.send(require('./prices-ru.json'))
break
case 'en-En':
res.send(require('./prices-en.json'))
break
case 'ru-Ru':
res.send(require('./prices-en.json'))
break
default:
console.log('Unknown language')
}
})
module.exports = router

View File

@@ -0,0 +1,59 @@
{
"prices": [
{
"id": "1",
"name": "Contactless car wash",
"price": "459"
},
{
"id": "2",
"name": "Nanowash",
"price": "759"
},
{
"id": "3",
"name": "Deluxe wash",
"price": "1559"
},
{
"id": "4",
"name": "Comprehensive wash",
"price": "859"
},
{
"id": "5",
"name": "Comprehensive nano wash",
"price": "11559"
},
{
"id": "6",
"name": "Antireagent",
"price": "1400"
},
{
"id": "7",
"name": "Antireagent complex",
"price": "1800"
},
{
"id": "8",
"name": "Express wash",
"price": "250"
},
{
"id": "9",
"name": "Vacuum cleaner",
"price": "250"
},
{
"id": "10",
"name": "Glass",
"price": "250"
},
{
"id": "11",
"name": "Plastic polish",
"price": "250"
}
]
}

View File

@@ -0,0 +1,59 @@
{
"prices": [
{
"id": "1",
"name": "Бесконтактная мойка",
"price": "459"
},
{
"id": "2",
"name": "Наномойка",
"price": "759"
},
{
"id": "3",
"name": "Мойка люкс",
"price": "1559"
},
{
"id": "4",
"name": "Комплексная мойка",
"price": "859"
},
{
"id": "5",
"name": "Комплексная наномойка",
"price": "11559"
},
{
"id": "6",
"name": "Антиреагент",
"price": "1400"
},
{
"id": "7",
"name": "Антиреагент комплекс",
"price": "1800"
},
{
"id": "8",
"name": "Экспресс-мойка",
"price": "250"
},
{
"id": "9",
"name": "Пылесос",
"price": "250"
},
{
"id": "10",
"name": "Стекла",
"price": "250"
},
{
"id": "11",
"name": "Полироль пластика",
"price": "250"
}
]
}

View File

@@ -0,0 +1,136 @@
{ "status": "success",
"dataFeatured": [{
"title": "Featured",
"items": [{
"description":"Detailed Explanation of",
"title": "Dynamic Programming",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "/coder/topics/dynamic-programming/",
"progressCard": 0,
"primeContent": null,
"iconCard": "featured1",
"slug": "dynamic-programming",
"categorySlug": "featured"
},{
"description":"Detailed Explanation of",
"title": "Heap",
"chanptersCount": 4,
"itemCount":28,
"linkToPage": "/coder/topics/heap/",
"progressCard": 0,
"primeContent": null,
"iconCard":"featured2",
"slug": "heap"
},{
"description":"Detailed Explanation of",
"title": "Graph",
"chanptersCount": 6,
"itemCount":58,
"linkToPage": "/coder/topics/graph/",
"progressCard": 0,
"primeContent": null,
"iconCard":"featured3",
"slug": "graph"
},{
"description":"Introductuion to Data Structure",
"title": "Arrays 101",
"chanptersCount": 6,
"itemCount":31,
"linkToPage": "/coder/card/fun-with-arrays/",
"progressCard": 0,
"primeContent": null,
"iconCard":"featured4",
"slug": "fun-with-arrays"
}]
},{
"title": "Interview",
"items": [{
"description":"Get Well Prepared for",
"title": "Google Interview",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/interview/card/google/",
"progressCard": 0,
"primeContent": 1,
"iconCard": "interview1",
"slug": "google"
},{
"description":"Get Well Prepared for",
"title": "Facebook",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/interview/card/facebook/",
"progressCard": 0,
"primeContent": 1,
"iconCard": "interview2",
"slug": "facebook"
},{
"description":"Top Questions from",
"title": "Microsoft",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/interview/card/microsoft/",
"progressCard": 0,
"primeContent": 1,
"iconCard": "interview3",
"slug": "microsoft"
},{
"description":"Top Questions from",
"title": "Amazon",
"chanptersCount": 8,
"itemCount":68,
"linkToPage": "coder/interview/card/amazon/",
"progressCard": 0,
"primeContent": 1,
"iconCard": "interview4",
"slug": "amazon"
}]
},{
"title": "Learn",
"items": [{
"description":"Detailed Explanation of",
"title": "Graph",
"chanptersCount": 6,
"itemCount":58,
"linkToPage": "coder/learn/card/graph",
"progressCard": 0,
"primeContent": null,
"iconCard": "learn1",
"slug": "graph"
},{
"description":"Introductuion to Data Structure",
"title": "Arrays 101",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/learn/card/fun-with-arrays",
"progressCard": 0,
"primeContent": null,
"iconCard": "learn2",
"slug": "fun-with-arrays"
},{
"description":"Introductuion to Data Structure",
"title": "Linked List",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/learn/card/linked-list",
"progressCard": 0,
"primeContent": null,
"iconCard": "learn3",
"slug": "linked-list"
},{
"description":"Introductuion to Data Structure",
"title": "Binary Tree",
"chanptersCount": 6,
"itemCount":55,
"linkToPage": "coder/learn/card/data-structure-tree",
"progressCard": 0,
"primeContent": null,
"iconCard": "learn4",
"slug": "data-structure-tree"
}]
}]
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,15 @@
// eslint-disable-next-line new-cap
const router = require('express').Router();
router.get('/catalog', (request, response, next) => {
setTimeout(() => next());
}, (request, response) => {
const params = process.env.stub;
if (params === 'error') {
response.send(require('./error.json'));
} else {
response.send(require('./cardData.json'));
}
});
module.exports = router;

View File

@@ -0,0 +1,424 @@
const { getDB } = require('../../../utils/mongo')
let db = null
// Database Name
const dbName = 'coder'
// Collections constant
const TOPIC_TAGS_COLLECTION = 'topic-tags'
const TOPIC_CATEGORIES_COLLECTION = 'topic-categories'
const TOPIC_LIST_COLLECTION = 'topic-list'
const TOPIC_COMMENTS_COLLECTION = 'topic-comments'
const USERS_COLLECTION = 'users'
// page size
const PAGE_SIZE = 2
// TODO убрать из export?
const connect = async () => {
db = await getDB(dbName)
}
const getCategories = async () => {
// TODO при первом запросе db = null
if (db === null) {
throw new Error('no db connection')
}
const collection = db.collection(TOPIC_CATEGORIES_COLLECTION)
const categories = await collection.find({
}).project({
_id: 0,
}).toArray()
if (categories.length === 0) {
const newCategories = require('./forum/categories.json').body
collection.insertMany(newCategories)
return newCategories
}
return categories
}
const getCategoryByPath = async (path) => {
if (db === null) {
throw new Error('no db connection')
}
const collection = db.collection(TOPIC_CATEGORIES_COLLECTION)
const category = await collection.findOne({
path,
}, {
projection: {
_id: 0,
},
})
if (!category) {
throw new Error('No data')
}
return category
}
const getTopicById = async (id) => {
if (db === null) {
throw new Error('no db connection')
}
const topicsCollection = db.collection(TOPIC_LIST_COLLECTION)
const topic = await topicsCollection
.aggregate([
{
$match: {
id: Number(id),
},
},
{
$limit: 1,
},
{
$lookup: {
from: USERS_COLLECTION, localField: 'authorId', foreignField: 'id', as: 'author',
},
},
{
$unwind: '$author',
},
{
$lookup: {
from: TOPIC_TAGS_COLLECTION, localField: 'tagsId', foreignField: 'id', as: 'tags',
},
},
{
$project: {
id: 0,
categoryId: 0,
authorId: 0,
tagsId: 0,
'author.id': 0,
'author._id': 0,
'tags.id': 0,
'tags._id': 0,
_id: 0,
},
},
])
.toArray()
if (topic.length === 0) {
throw new Error('No data')
}
return topic[0]
}
const getCommentsByTopicId = async (id, page) => {
if (db === null) {
throw new Error('no db connection')
}
if (page === undefined) {
page = 1
}
const commentsCollection = db.collection(TOPIC_COMMENTS_COLLECTION)
let count = await commentsCollection.count({
}, {
limit: 1,
})
if (count === 0) {
const newComments = require('./forum/topic-comments.json').body
commentsCollection.insertMany(newComments)
}
const comments = await commentsCollection
.aggregate([
{
$match: {
topicId: Number(id),
},
},
{
$skip: PAGE_SIZE * Number(page) - PAGE_SIZE,
},
{
$limit: PAGE_SIZE,
},
{
$lookup: {
from: USERS_COLLECTION, localField: 'authorId', foreignField: 'id', as: 'author',
},
},
{
$unwind: '$author',
},
{
$project: {
topicId: 0,
authorId: 0,
'author.id': 0,
'author._id': 0,
_id: 0,
},
},
])
.toArray()
// pagination
count = await commentsCollection.count({
topicId: Number(id),
})
const result = {
page,
pageSize: PAGE_SIZE,
total: count,
totalPages: Math.ceil(count / PAGE_SIZE),
comments,
}
return result
}
const getTopicsByCategoryId = async (id, page) => {
if (db === null) {
throw new Error('no db connection')
}
if (page === undefined) {
page = 1
}
const topicsCollection = db.collection(TOPIC_LIST_COLLECTION)
let count = await topicsCollection.count({
}, {
limit: 1,
})
if (count === 0) {
const newTopics = require('./forum/topic-list.json').body.items
topicsCollection.insertMany(newTopics)
}
// TODO delete after auth implementation
const usersCollection = db.collection(USERS_COLLECTION)
const usersCount = await usersCollection.count({
}, {
limit: 1,
})
if (usersCount === 0) {
const newUsers = require('./forum/users.json').body
usersCollection.insertMany(newUsers)
}
const topics = await topicsCollection
.aggregate([
{
$match: {
categoryId: Number(id),
},
},
{
$skip: PAGE_SIZE * Number(page) - PAGE_SIZE,
},
{
$limit: PAGE_SIZE,
},
{
$lookup: {
from: USERS_COLLECTION, localField: 'authorId', foreignField: 'id', as: 'author',
},
},
{
$unwind: '$author',
},
{
$project: {
id: 1,
title: 1,
commentCount: 1,
viewCount: 1,
voteCount: 1,
creationDate: 1,
'author.name': 1,
'author.avatar': 1,
_id: 0,
},
},
])
.toArray()
if (topics.length === 0) {
throw new Error('No data')
}
// pagination
count = await topicsCollection.count({
categoryId: Number(id),
})
const result = {
page,
pageSize: PAGE_SIZE,
total: count,
totalPages: Math.ceil(count / PAGE_SIZE),
topics,
}
return result
}
const getTags = async () => {
if (db === null) {
throw new Error('no db connection')
}
const tagsCollection = db.collection(TOPIC_TAGS_COLLECTION)
const count = await tagsCollection.count({
}, {
limit: 1,
})
if (count === 0) {
const newTags = require('./forum/topic-tags.json').body
tagsCollection.insertMany(newTags)
}
const tags = await tagsCollection.find({
}).project({
_id: 0,
}).toArray()
if (tags.length === 0) {
throw new Error('No data')
}
return tags
}
const findTags = async (value) => {
if (db === null) {
throw new Error('no db connection')
}
const tagsCollection = db.collection(TOPIC_TAGS_COLLECTION)
const tags = await tagsCollection
.find({
name: {
$regex: `${value}`,
},
})
.project({
_id: 0,
})
.toArray()
return tags
}
const insertTag = async (value) => {
if (db === null) {
throw new Error('no db connection')
}
const tagsCollection = db.collection(TOPIC_TAGS_COLLECTION)
// TODO no fast, reimplement
const count = await tagsCollection.estimatedDocumentCount()
await tagsCollection.insertOne({
id: count + 1,
name: value,
numTopics: 0,
})
return count + 1
}
const insertComment = async (comment) => {
if (db === null) {
throw new Error('no db connection')
}
const commentsCollection = db.collection(TOPIC_COMMENTS_COLLECTION)
// TODO no fast, reimplement
// TODO может перейти на _id?
const count = await commentsCollection.estimatedDocumentCount()
const currentTime = Math.round(new Date().getTime() / 1000)
await commentsCollection.insertOne({
id: count + 1,
topicId: comment.topicId,
voteCount: 0,
content: comment.content,
updationDate: currentTime,
creationDate: currentTime,
authorId: comment.authorId,
authorIsModerator: false,
isOwnPost: false,
})
return comment
}
const insertTopic = async (topic) => {
if (db === null) {
throw new Error('no db connection')
}
const topicsCollection = db.collection(TOPIC_LIST_COLLECTION)
// TODO no fast, reimplement
// TODO может перейти на _id?
const count = await topicsCollection.estimatedDocumentCount()
const currentTime = Math.round(new Date().getTime() / 1000)
const tagsId = []
if (topic.tags) {
for (let index = 0; index < topic.tags.length; index++) {
const tag = topic.tags[index]
if (tag.id === 0) {
const tagId = await insertTag(tag.name)
tagsId.push(tagId)
} else {
tagsId.push(tag.id)
}
}
}
const result = await topicsCollection.insertOne({
id: count + 1,
categoryId: topic.categoryId,
title: topic.title,
commentCount: 0,
viewCount: 0,
tagsId,
voteCount: 0,
voteStatus: 0,
content: topic.content,
updationDate: currentTime,
creationDate: currentTime,
authorId: topic.authorId,
isOwnPost: true,
})
if (!result.insertedId) {
throw new Error('Insert data failed, try again later')
}
return count + 1
}
module.exports = {
connect,
getTags,
findTags,
insertTag,
getCategories,
getTopicsByCategoryId,
getTopicById,
getCommentsByTopicId,
insertComment,
insertTopic,
getCategoryByPath,
}

View File

@@ -0,0 +1,49 @@
{
"success": true,
"body": [
{
"id": 1,
"title": "Interview Question",
"description": "Share and discuss questions from real technical interviews",
"path": "interview-question"
},
{
"id": 2,
"title": "Interview Experience",
"description": "Share details about the interview processes you have been through",
"path": "interview-experience"
},
{
"id": 3,
"title": "Compensation",
"description": "What kind of offers have you received? Share it here!",
"path": "compensation"
},
{
"id": 4,
"title": "Career",
"description": "Question about working in the tech industry? Discuss your career questions here.",
"path": "career"
},
{
"id": 5,
"title": "Study Guide",
"description": "Share your study guide or summaries for certain topics/patterns",
"path": "study-guide"
},
{
"id": 6,
"title": "General Discussion",
"description": "Discuss anything not covered in other categories",
"path": "general-discussion"
},
{
"id": 7,
"title": "Support & Feedback",
"description": "Get help on issues or submit feedback regarding LeetCode",
"path": "feedback"
}
],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,96 @@
const router = require('express').Router();
const { getResponse } = require('../../../../utils/common');
const {
getTags,
connect,
findTags,
insertTag,
getCategories,
getTopicsByCategoryId,
getTopicById,
getCommentsByTopicId,
insertComment,
getCategoryByPath,
insertTopic,
} = require('../controllers');
connect();
router.get('/forum/topic-categories', async (req, res) => {
const errors = [];
const categories = await getCategories().catch((e) => errors.push(e.message));
res.send(getResponse(errors, categories));
});
router.get('/forum/topic-categories/:groupId', async (req, res) => {
const errors = [];
const category = await getCategoryByPath(req.params.groupId).catch((e) => errors.push(e.message));
res.send(getResponse(errors, category));
});
router.get('/forum/topic-comments/:topicId', async (req, res) => {
const errors = [];
const comments = await getCommentsByTopicId(req.params.topicId, req.query.page).catch((e) => errors.push(e.message));
res.send(getResponse(errors, comments));
});
router.post('/forum/topic-comments', async (req, res) => {
const errors = [];
if (!req.body) {
errors.push('Bad request, no body');
res.send(getResponse(errors, undefined));
} else {
const comment = await insertComment(req.body).catch((e) => errors.push(e.message));
res.send(getResponse(errors, comment));
}
});
router.get('/forum/topic-list/:id', async (req, res) => {
const errors = [];
const topics = await getTopicsByCategoryId(req.params.id, req.query.page).catch((e) => errors.push(e.message));
res.send(getResponse(errors, topics));
});
router.get('/forum/topic/:id', async (req, res) => {
const errors = [];
const topic = await getTopicById(req.params.id).catch((e) => errors.push(e.message));
res.send(getResponse(errors, topic));
});
router.post('/forum/topic', async (req, res) => {
const errors = [];
if (!req.body) {
errors.push('Bad request, no body');
res.send(getResponse(errors, undefined));
} else {
const topic = await insertTopic(req.body).catch((e) => errors.push(e.message));
res.send(getResponse(errors, topic));
}
});
router.get('/forum/topic-tags', async (req, res) => {
const errors = [];
if (req.query.search !== undefined) {
const tags = await findTags(req.query.search).catch((e) => errors.push(e.message));
res.send(getResponse(errors, tags));
} else {
const tags = await getTags().catch((e) => errors.push(e.message));
res.send(getResponse(errors, tags));
}
});
router.post('/forum/topic-tags', async (req, res) => {
const errors = [];
const tags = await insertTag(req.body?.name).catch((e) => errors.push(e.message));
res.send(getResponse(errors, tags));
});
router.get('/forum/stub/:data', (request, response) => {
console.log(request.params);
process.env.stub = request.params.data;
response.sendStatus(200);
});
module.exports = router;

View File

@@ -0,0 +1,29 @@
{
"success": true,
"body": [
{
"id": 1,
"topicId": 1,
"voteCount": 36,
"content": "wish for offer",
"updationDate": 1563064458,
"creationDate": 1563064458,
"authorId": 1,
"authorIsModerator": false,
"isOwnPost": false
},
{
"id": 2,
"topicId": 1,
"voteCount": 10,
"content": "Thank you for your explantion",
"updationDate": 1559050657,
"creationDate": 1559050657,
"authorId": 1,
"authorIsModerator": false,
"isOwnPost": false
}
],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,235 @@
{
"success": true,
"body": {
"totalNum": 15,
"items": [
{
"id": 1,
"categoryId": 1,
"title": "How to write an Interview Question post",
"commentCount": 2,
"viewCount": 0,
"tagsId": [1, 2],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1524865315,
"creationDate": 1524865315,
"authorId": 1,
"isOwnPost": true
},
{
"id": 2,
"categoryId": 1,
"title": "Microsoft Online Assessment Questions",
"commentCount": 0,
"viewCount": 0,
"tagsId": [1, 2],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1570318826,
"creationDate": 1570318826,
"authorId": 1,
"isOwnPost": true
},
{
"id": 3,
"categoryId": 2,
"title": "Google Online Assessment Questions",
"commentCount": 0,
"viewCount": 0,
"tagsId": [1],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1565090199,
"creationDate": 1565090199,
"authorId": 1,
"isOwnPost": true
},
{
"id": 4,
"categoryId": 2,
"title": "Meta | March 2022 | Onsite USA E4",
"commentCount": 0,
"viewCount": 0,
"tagsId": [3, 4],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649546823,
"creationDate": 1649546823,
"authorId": 1,
"isOwnPost": true
},
{
"id": 5,
"categoryId": 2,
"title": "Flipkart | SDE 2 | Machine Coding",
"commentCount": 0,
"viewCount": 0,
"tagsId": [4, 5, 6],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649599102,
"creationDate": 1649599102,
"authorId": 1,
"isOwnPost": true
},
{
"id": 6,
"categoryId": 3,
"title": "Google | SDE (L4/L5) | Virtual Onsite | PENDING",
"commentCount": 0,
"viewCount": 0,
"tagsId": [7, 8],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649537604,
"creationDate": 1649537604,
"authorId": 1,
"isOwnPost": true
},
{
"id": 7,
"categoryId": 3,
"title": "Leetcode not taking track of submission and not filled the active days box",
"commentCount": 0,
"viewCount": 0,
"tagsId": [11, 12],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649486170,
"creationDate": 1649486170,
"authorId": 1,
"isOwnPost": true
},
{
"id": 8,
"categoryId": 4,
"title": "Salesforce India || AMTS (2022 Graduate) || Online Assessment || Off Campus",
"commentCount": 0,
"viewCount": 0,
"tagsId": [10, 20],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649572752,
"creationDate": 1649572752,
"authorId": 1,
"isOwnPost": true
},
{
"id": 9,
"categoryId": 5,
"title": "Meta | February Phone Interview | E4 | USA",
"commentCount": 0,
"viewCount": 0,
"tagsId": [14, 15],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649462307,
"creationDate": 1649462307,
"authorId": 1,
"isOwnPost": true
},
{
"id": 10,
"categoryId": 5,
"title": "Maximize minimum array",
"commentCount": 0,
"viewCount": 0,
"tagsId": [11, 7],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649652616,
"creationDate": 1649652616,
"authorId": 1,
"isOwnPost": true
},
{
"id": 11,
"categoryId": 6,
"title": "Yelp | London | April 2022 | Screening + Virtual On-Site | Full Stack Role",
"commentCount": 0,
"viewCount": 0,
"tagsId": [7, 12],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649637943,
"creationDate": 1649637943,
"authorId": 1,
"isOwnPost": true
},
{
"id": 12,
"categoryId": 6,
"title": "[System Design Question] Design a Game Matching system",
"commentCount": 0,
"viewCount": 0,
"tagsId": [8, 11],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649637516,
"creationDate": 1649637516,
"authorId": 1,
"isOwnPost": true
},
{
"id": 13,
"categoryId": 6,
"title": "Uber | Data Analyst | SF | 2022-04",
"commentCount": 0,
"viewCount": 0,
"tagsId": [1, 12],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649625709,
"creationDate": 1649625709,
"authorId": 1,
"isOwnPost": true
},
{
"id": 14,
"categoryId": 7,
"title": "Google Online Assessment",
"commentCount": 0,
"viewCount": 0,
"tagsId": [11, 2],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649622307,
"creationDate": 1649622307,
"authorId": 1,
"isOwnPost": true
},
{
"id": 15,
"categoryId": 7,
"title": "Amazon || SDE 2 || OA",
"commentCount": 0,
"viewCount": 0,
"tagsId": [],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1649619969,
"creationDate": 1649619969,
"authorId": 1,
"isOwnPost": true
}
]
},
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,107 @@
{
"success": true,
"body": [
{
"id": 1,
"name": "google",
"numTopics": 0
},
{
"id": 2,
"name": "phone screen",
"numTopics": 0
},
{
"id": 3,
"name": "amazon",
"numTopics": 0
},
{
"id": 4,
"name": "facebook",
"numTopics": 0
},
{
"id": 5,
"name": "online assessment",
"numTopics": 0
},
{
"id": 6,
"name": "onsite",
"numTopics": 0
},
{
"id": 7,
"name": "system design",
"numTopics": 0
},
{
"id": 8,
"name": "microsoft",
"numTopics": 0
},
{
"id": 9,
"name": "online assesment",
"numTopics": 0
},
{
"id": 10,
"name": "graph",
"numTopics": 0
},
{
"id": 11,
"name": "uber",
"numTopics": 0
},
{
"id": 12,
"name": "intern",
"numTopics": 0
},
{
"id": 13,
"name": "amazon online assesment",
"numTopics": 0
},
{
"id": 14,
"name": "bloomberg",
"numTopics": 0
},
{
"id": 15,
"name": "amazon interview",
"numTopics": 0
},
{
"id": 16,
"name": "new grad",
"numTopics": 0
},
{
"id": 17,
"name": "interview",
"numTopics": 0
},
{
"id": 18,
"name": "amazon sde2",
"numTopics": 0
},
{
"id": 19,
"name": "array",
"numTopics": 0
},
{
"id": 20,
"name": "phone-interview",
"numTopics": 0
}
],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,20 @@
{
"success": true,
"body": {
"id": 1,
"categoryId": 1,
"title": "How to write an Interview Question post",
"commentCount": 2,
"viewCount": 0,
"tagsId": [1, 2],
"voteCount": 0,
"voteStatus": 0,
"content": "## Heading level 2\n#### Heading level 4\nI just love **bold text**.\nItalicized text is the _cat's meow_.\n- Revenue was off the chart.\n- Profits were higher than ever.\n",
"updationDate": 1648229493,
"creationDate": 1524865315,
"authorId": 1,
"isOwnPost": true
},
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,6 @@
{
"success": true,
"body": [{ "id": 1, "name": "Test", "avatar": "https://s3-us-west-1.amazonaws.com/s3-lc-upload/assets/default_avatar.jpg" }],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,8 @@
// eslint-disable-next-line new-cap
const router = require('express').Router()
router.use(require('./forum'))
router.use(require('./catalog'))
router.use(require('./topic'))
module.exports = router

View File

@@ -0,0 +1,27 @@
{
"status": "success",
"title" : "Dynamic Programming",
"slug":"dynamic-programming",
"overview":"In this explore card, we're going to go over the basics of DP, provide you with a framework for solving DP problems, learn about common patterns, and walk through many examples.",
"chapters": [
{
"title" : "Dynamic Programming",
"section" : "dynamic-programming",
"isLock" : false,
"content" : "<p>Динамическое программирование</p>"
},
{
"title" : "Arrays and Strings",
"section" : "arrays-and-strings",
"isLock" : false,
"content" : "<p>Массивы и строки</p>"
},
{
"title" : "Linked list",
"section" : "linked-list",
"isLock" : true,
"content" : "<p>Ссылочный список</p>"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"title" : "Arrays and Strings",
"slug" : "arrays-and-strings",
"content": [
{"data":"## Arrays and String"},
{"data":"An array is a collection of similar type of elements that are stored in a contiguous memory location. Arrays can contain primitives(int, char, etc) as well as object(non-primitives) references of a class depending upon the definition of the array. In the case of primitive data type, the actual values are stored in contiguous memory locations whereas in the case of objects of a class the actual objects are stored in the heap segment. In Java, all the arrays are allocated dynamically. The size of an array must be specified by an int value and not long or short. The array index starts from 0 and goes up to n-1 where n is the length of the array."},
{"data":"#### Array Declaration Syntax"},
{"data": "An array declaration has two components: the type and the var-name. The type declares the element type of the array. The element type determines the data type of each element that comprises the array. The var-name declares the name of the array variable. Like an array of int type, we can also create an array of other primitive data types like char, float, double…etc."}
]
}

View File

@@ -0,0 +1,10 @@
{
"title": "Dynamic programming",
"slug": "dynamic-programming",
"content": [
{"data":"## Hello markdown"},
{"data":"Dynamic programming is both a mathematical optimization method and a computer programming method. The method was developed by Richard Bellman in the 1950s and has found applications in numerous fields, from aerospace engineering to economics. In both contexts it refers to simplifying a complicated problem by breaking it down into simpler sub-problems in a recursive manner. While some decision problems cannot be taken apart this way, decisions that span several points in time do often break apart recursively. Likewise, in computer science, if a problem can be solved optimally by breaking it into sub-problems and then recursively finding the optimal solutions to the sub-problems, then it is said to have optimal substructure.If sub-problems can be nested recursively inside larger problems, so that dynamic programming methods are applicable, then there is a relation between the value of the larger problem and the values of the sub-problems.[1] In the optimization literature this relationship is called the Bellman equation."},
{"data":"#### Mathematical optimization"},
{"data": "In terms of mathematical optimization, dynamic programming usually refers to simplifying a decision by breaking it down into a sequence of decision steps over time. This is done by defining a sequence of value functions V1, V2, ..., Vn taking y as an argument representing the state of the system at times i from 1 to n. The definition of Vn(y) is the value obtained in state y at the last time n. The values Vi at earlier times i = n 1, n 2, ..., 2, 1 can be found by working backwards, using a recursive relationship called the Bellman equation. For i = 2, ..., n, Vi1 at any state y is calculated from Vi by maximizing a simple function (usually the sum) of the gain from a decision at time i 1 and the function Vi at the new state of the system if this decision is made. Since Vi has already been calculated for the needed states, the above operation yields Vi1 for those states. Finally, V1 at the initial state of the system is the value of the optimal solution. The optimal values of the decision variables can be recovered, one by one, by tracking back the calculations already performed."}
]
}

View File

@@ -0,0 +1,8 @@
{
"title" : "Linked list",
"slug" : "linked-list",
"content": [
{"data":"# Hello markdown"},
{"data":"# Hello markdown"}
]
}

View File

@@ -0,0 +1,8 @@
{
"title" : "Overview",
"slug" : "overview",
"content": [
{"data":"## Overview"},
{"data":"In this explore card, we're going to go over the basics of DP, provide you with a framework for solving DP problems, learn about common patterns, and walk through many examples.No prior knowledge of DP is required for this card, but you will need a solid understanding of recursion, a basic understanding of what greedy algorithms are, and general knowledge such as big O and hash tables. If you aren't familiar with recursion yet, check out the recursion explore card first."}
]
}

View File

@@ -0,0 +1,3 @@
{
"status": "error"
}

View File

@@ -0,0 +1,30 @@
const router = require('express').Router()
router.get('/gettopic/:topic/:section', (request, response) => {
const topic = request.params.topic
let section = request.params.section
if (!topic || !section) {
response.send(require('./error.json'))
} else {
response.send(
// require(`./${topic}/${section}.json`)
require(`./dynamic-programming/${section}.json`),
)
}
})
router.get('/gettopic/:topic', (request, response) => {
const topic = request.params.topic
if (!topic) {
response.send(require('./error.json'))
} else {
response.send(
// require(`./${topic}.json`)
require('./dynamic-programming.json'),
)
}
})
module.exports = router

View File

@@ -0,0 +1,48 @@
const router = require('express').Router()
const jwt = require('jsonwebtoken')
const { JWT_SECRET } = require('./constants')
const { registrationUser, signInUser } = require('./db')
const { requiredFields, responseWrapper } = require('./utils')
router.get('/healthcheck', (req, res) => {
res.send(true)
})
router.post('/registration', requiredFields(['email', 'login', 'password']), async (req, res, next) => {
const { email, login, password, ...rest } = req.body
try {
await registrationUser({
email, login, password, ...rest,
})
res.send(responseWrapper(undefined, {
}))
} catch (e) {
next(e)
}
})
router.post('/sign-in', requiredFields(['email', 'password']), async (req, res, next) => {
const { email, password } = req.body
try {
const user = await signInUser({
email, password,
})
req.session.user = user
const token = jwt.sign({
id: user._id,
}, JWT_SECRET)
res.send(responseWrapper(undefined, {
token, user,
}))
} catch (e) {
next(e)
}
})
module.exports = router

View File

@@ -0,0 +1,30 @@
const JWT_SECRET = `MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDsZZ+2H3Q0uvFh
JohVkLKGUGR0dJRo801QbH/UMQjsuIYPExn4wLHr06ALGb3WQe+hiERthsJu3veV
38AuQ3YHE1mcD5KRwkBflqc+lldp1JyZm8qPO1PLCJBc/553EPV16MbuDdmdxoY5
Uk2X5b3+UVjzDY83Pd9YFa3i42y7wREKj7csTxJPGXGvFDtCgyhhH3vjtd91pCg8
nuXuFShv5HI5S2ffBbxj/p8rjIK+a+DOw2zKEeYN3G5OLCVsmBow76KQZyVmmYAq
Qnlut0s0HbNX8Deo70iib35AEts00ey+2Y5gJh4Lq8tCpSeFlO0mQ13g/jN8jDxu
mnoluhQnAgMBAAECggEARG4KdHLYdacj1maMEpDHTOAapCdXCqZbXAt8WVU0aynj
DJwP6ZUGK+jfrDbwYARINK84d6gJwoRikQzrGblHgjbUurs7R1w+vCzlDtYASc9U
4ZZaZWncEKrS90i7e2X6V/5hD2oM84ITOqabdXv4qpSrtffX3DrZ2yqzYjlJMXPc
VT+qqG8pDEhluJWO+CQBr3L7kk69S1s6ehlVh7OFOvtCXTPfwM23NrwDSRp+kx9x
GikhoPUFZL6FZttJxzgonW10Yi4MvlCNHuWs5F1vfK0b3eG9FsGsvclBd9E0P75i
iQeWG+Ecf81XWaqmaKMvQv5upK0jwEvHBEa6IXhoiQKBgQD+XPv68a8TKlbdg0UY
YYKbs+U/7v0ZcRwkweYTPrhg1cO91nRk8e9OXnD+Boqk2zMojLI9FEF7qf13LY9X
Ue9XikgoDQAIg6bS92FUx5KoyxOnQcDdTenhZZ7dbF0qXn8KBz5EXgPxMVfS3hhk
uZ7PBWbeXtnVyvV+7ymjzX/PDQKBgQDt6wslvUrlLLPDE9bqO+Zi7sYPyQXphRWS
IvG4SisKa0Fo/MCgAP+0hfLBXgl+z+JQFmxpGaZIHikgwI0JA2jQLYdTAcjMiWHn
bxtw+dvOyp8xMjDg0cKuSs798zW1ylGdBvOKMcB4tJK66ahNPoGfd/iVpqkXCCJO
rptTRaODAwKBgDknn58li1ddHiOsCWyPDI/7/jk9dDWxSsefohkU8M0he1g+xeyg
yErtwanywliEwJwN8Ub0NDqbWc7bt+fBC2y6L7iAI0/GdUfWOlKYfYXcC9B7X+Da
TwFMmkPaw5xvm1AfjIhEL9KUac2CBi0r4FlYN2MkIQJ/FmatSsf5twxJAoGBALp8
5E3+pDgi4/zjDjCoJxwhISq6XWH/qTrVHTdhG3+dNrY+eBZy8fvkirW7KiI2fKRe
DzgKnOYmjgJYK7SG8Z9MMKiF24lHnnA0DQRvNTZQaJu9RcbTZJANn1Y7Tzxhi7tT
+Y23FbEHiYPtJrj1Z9FBzp4u9pdRDuMxxhptmY9VAoGANasQyyWaNmJNFqK2OZRk
HhKwV5AXk7Q0F3YbGXOUUmlsQw+wYVtQmyThYESMbS3T9ccjTO1avCDZNcBYnKir
xkMh3KPVALIbolNjaac7TZ+CCu4VL99hCQVerVZqMivXiqmjjioobX3fspQl9nZL
h+adTrBTkdlVVgHE8TNs88Q=`
module.exports = {
JWT_SECRET,
}

View File

@@ -0,0 +1,410 @@
const { v4: uuid } = require('uuid')
const ObjectId = require('mongodb').ObjectID
const { getDB } = require('../../../utils/mongo')
const { _idToId, checkDB, _idToIdArray, filterId } = require('./utils')
let db = null
const USERS_COLL = 'ep_users'
const PROJECTS_COLL = 'ep_all_projects'
const PROJECT_TASKS_COLL = 'ep_projects_tasks'
const PROJECT_MEMBERS_COLL = 'ep_projects_members'
const PROJECT_STATUSES_COLL = 'ep_projects_statuses'
const PROJECT_TYPES_COLL = 'ep_projects_types'
const connect = async () => {
db = await getDB('easy-project')
if (db === null) throw new Error('Нет подключения к базе данных')
}
connect()
const hashPassword = (password, salt = uuid(), salt2 = uuid(), salt3 = uuid()) => ({
password: (password.split('').join(salt) + salt2 + password.split('').join(salt3)).split('-').reverse().join('-'),
salt,
salt2,
salt3,
})
const registrationUser = async ({ email, login, password, ...rest }) => {
checkDB(db)
const usersCollection = db.collection(USERS_COLL)
const userExist = await usersCollection.find({
$or: [{
login,
}, {
email,
}],
}).toArray()
if (userExist.length) {
if (userExist[0].login === login) {
throw new Error('Логин уже занят')
}
if (userExist[0].email === email) {
throw new Error('Email уже занят')
}
}
const { password: hash, salt, salt2, salt3 } = hashPassword(password)
const user = {
salt,
salt2,
salt3,
hash,
login,
email,
...rest,
}
const { insertedId } = await usersCollection.insertOne(user)
user.id = insertedId
}
const signInUser = async ({ email, password }) => {
checkDB(db)
const usersCollection = db.collection(USERS_COLL)
const [userCandidate] = await usersCollection.find({
email,
}).toArray()
if (!userCandidate) {
throw new Error('Email или пароль не корректный')
}
const { salt, salt2, salt3, hash, ...cleanUser } = userCandidate
const { password: hashFromDb } = hashPassword(password, salt, salt2, salt3)
if (hash !== hashFromDb) {
throw new Error('Email или пароль не корректный')
}
return cleanUser
}
const getMyProjects = async (userId) => {
checkDB(db)
const userFilterById = filterId(userId)
const projectCollection = db.collection(PROJECTS_COLL)
const usersCollection = db.collection(USERS_COLL)
let projectList = await projectCollection.find({
$or: [{
userId,
}, {
members: {
$in: [userId],
},
}],
}).toArray()
if (projectList) {
const [userAuthor] = await usersCollection.find(userFilterById).toArray()
for (let index = 0; index < projectList.length; index++) {
projectList[index].author = userAuthor
projectList[index] = _idToId(projectList[index])
}
return projectList
}
return []
}
const deleteProjectById = async (projectId) => {
checkDB(db)
const projectCollection = db.collection(PROJECTS_COLL)
const projectFilterById = filterId(projectId)
const deleted = await projectCollection.deleteOne(projectFilterById)
return deleted
}
const getMyProjectById = async (userId, projectId) => {
checkDB(db)
const projectCollection = db.collection(PROJECTS_COLL)
const usersCollection = db.collection(USERS_COLL)
const userFilterById = filterId(userId)
const [userAuthor] = await usersCollection.find(userFilterById).toArray()
const projectFilter = filterId(projectId)
let [projectMyExist] = await projectCollection.find(projectFilter).toArray()
const members = await usersCollection.find({
_id: {
$in: projectMyExist.members.map((memberId) => new ObjectId(memberId)),
},
}).toArray()
projectMyExist.members = members.map((m) => ({
value: m._id, label: m.email,
}))
projectMyExist.author = userAuthor
projectMyExist = _idToId(projectMyExist)
return projectMyExist
}
const newProject = async ({
title,
code,
userId,
members,
}) => {
checkDB(db)
if (!title || !code) {
throw new Error('Fields can\'t be empty')
}
const projectCollection = db.collection(PROJECTS_COLL)
const project = {
title,
code,
userId,
created: Date.now(),
changed: Date.now(),
changedBy: userId,
taskIndex: 0,
members,
}
await projectCollection.insertOne(project)
return _idToId(project)
}
const updateProject = async ({ projectId, title, code, members }) => {
checkDB(db)
if (!title || !code) {
throw new Error('Fields can\'t be empty')
}
const projectCollection = db.collection(PROJECTS_COLL)
const projectFilterById = filterId(projectId)
const updatedProject = await projectCollection.updateOne(projectFilterById, {
$set: {
title,
code,
changed: Date.now(),
members,
},
})
return updatedProject
}
// TODO: Совмещение projectId с userId ИЛИ поиск по memberIds
const getProjectById = async ({ projectId }) => {
checkDB(db)
const projectFilterById = filterId(projectId)
const projectCollection = db.collection(PROJECTS_COLL)
const [projectExist] = await projectCollection.find(projectFilterById).toArray()
return projectExist
}
const getTaskById = async ({ taskId, userId }) => {
checkDB(db)
if (taskId) {
const userFilterById = filterId(userId)
const taskFilterById = filterId(taskId)
const taskCollection = db.collection(PROJECT_TASKS_COLL)
const usersCollection = db.collection(USERS_COLL)
let [taskExist] = await taskCollection.find(taskFilterById).toArray()
if (taskExist) {
const [userAuthor] = await usersCollection.find(userFilterById).toArray()
taskExist.author = userAuthor
return _idToId(taskExist)
}
}
return {
}
}
const createTask = async ({ taskData, authorId, projectId }) => {
checkDB(db)
const projectCollection = db.collection(PROJECTS_COLL)
const projectFilterById = filterId(projectId)
const [projectExist] = await projectCollection.find(projectFilterById).toArray()
if (!projectExist) {
throw new Error('The project not exists [createTask]')
}
const nextIndex = projectExist.taskIndex + 1
const taskCollection = db.collection(PROJECT_TASKS_COLL)
const { type, status, ...taskRest } = taskData
const task = {
...taskRest,
type: Number(type),
status: Number(status),
changed: Date.now(),
created: Date.now(),
taskIndex: nextIndex,
authorId,
projectId,
}
await projectCollection.updateOne(projectFilterById, {
$set: {
taskIndex: nextIndex,
projectChanged: Date.now(),
},
})
await taskCollection.insertOne(task)
return _idToId(task)
}
const getTaskListByProjectId = async ({ projectId }) => {
checkDB(db)
const projectFilterById = filterId(projectId)
const projectCollection = db.collection(PROJECTS_COLL)
const [projectExist] = await projectCollection.find(projectId).toArray()
if (!projectExist) {
throw new Error('The project not exists [getTaskListByProjectId]')
}
const taskCollection = db.collection(PROJECT_TASKS_COLL)
let taskListCandidate = await taskCollection.find({
projectId,
}).toArray()
// if (taskListCandidate.length > 0) {
// for (let index = 0; index < taskListCandidate.length; index++) {
// // const [userAuthor] = await usersCollection.find(userFilterById).toArray()
// // taskListCandidate[index].author = userAuthor
// projectExist[index] = _idToId(projectExist[index])
// }
// }
taskListCandidate = _idToIdArray(taskListCandidate)
return taskListCandidate
}
const editTask = async ({ taskData, projectId, authorId, taskId }) => {
checkDB(db)
const taskCollection = db.collection(PROJECT_TASKS_COLL)
const projectCollection = db.collection(PROJECTS_COLL)
const taskFilterById = filterId(taskId)
const projectFilterById = filterId(projectId)
const { type, status, ...taskRest } = taskData
const updatedTask = await taskCollection.updateOne(taskFilterById, {
$set: {
type: Number(type),
status: Number(status),
...taskRest,
},
})
await projectCollection.updateOne(projectFilterById, {
$set: {
projectChanged: Date.now(),
},
})
return updatedTask
}
const deleteTaskById = async (taskId) => {
checkDB(db)
const taskCollection = db.collection(PROJECT_TASKS_COLL)
const taskFilterById = filterId(taskId)
const deleted = await taskCollection.deleteOne(taskFilterById)
return deleted
}
const getAllUsers = async () => {
checkDB(db)
const usersCollection = db.collection(USERS_COLL)
let allUsers = await usersCollection.find().toArray()
return _idToIdArray(allUsers)
}
// const getProjectMember = async () => {
// checkDB(db)
// const usersCollection = db.collection(USERS_COLL)
// let allUsers = await usersCollection.find().toArray()
// return _idToIdArray(allUsers)
// }
const updateProjectMembers = async (projectData, members) => {
checkDB(db)
const memberCollection = db.collection(PROJECT_MEMBERS_COLL)
await memberCollection.deleteMany({
projectId: projectData.id,
})
const membersAdd = []
for (let memberIndex = 0; memberIndex < members.length; memberIndex++) {
const member = {
projectId: projectData.id,
memberId: members[memberIndex].value,
}
membersAdd.push(member)
}
const { insertedData } = await memberCollection.insertMany(membersAdd)
return insertedData
}
module.exports = {
connect,
registrationUser,
signInUser,
getMyProjects,
deleteProjectById,
newProject,
createTask,
editTask,
deleteTaskById,
getTaskListByProjectId,
getProjectById,
getMyProjectById,
getTaskById,
updateProject,
getAllUsers,
updateProjectMembers,
}

View File

@@ -0,0 +1,15 @@
const router = require('express').Router()
router.use('/', require('./auth'))
router.use((err, req, res, next) => {
res.status(400).send({
success: false, error: err.message || 'Что-то пошло не так',
})
})
router.use('/projects', require('./projects'))
router.use('/tasks', require('./tasks'))
router.use('/users', require('./users'))
module.exports = router

View File

@@ -0,0 +1,116 @@
const router = require('express').Router()
const { expressjwt: jwt } = require('express-jwt')
const { JWT_SECRET } = require('./constants')
const {
getMyProjects,
newProject,
getProjectById,
updateProject,
getMyProjectById,
deleteProjectById,
updateProjectMembers,
} = require('./db')
const { requiredFields, responseWrapper, _idToId } = require('./utils')
router.get('/healthcheck', (req, res) => {
res.send(true)
})
router.get('/', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const projectList = await getMyProjects(req.auth.id)
res.send(responseWrapper(undefined, projectList))
} catch (e) {
next(e)
}
})
router.get('/:projectId', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const { projectId } = req.params
const myProject = await getMyProjectById(req.auth.id, projectId)
res.send(responseWrapper(undefined, myProject))
} catch (e) {
next(e)
}
})
router.post('/new', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const userId = req.auth.id
const { title, code, members } = req.body
const project = await newProject({
title, code, userId, members,
})
res.send(responseWrapper(undefined, project))
} catch (e) {
next(e)
}
})
router.post('/delete', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const userId = req.auth.id
const { projectId } = req.body
const myProject = await getMyProjectById(userId, projectId)
if (myProject) {
const answered = await deleteProjectById(projectId)
if (answered.result.ok) {
res.send(responseWrapper(undefined))
}
}
} catch (e) {
next(e)
}
})
router.post('/edit', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const userId = req.auth.id
const { projectId, title, code, members } = req.body
const projectCandidate = await getProjectById({
projectId,
})
if (!projectCandidate) {
throw new Error('The project not exists [project.edit]')
}
await updateProject({
projectId, title, code, members,
})
const updatedProject = await getProjectById({
projectId,
})
const projectSummary = {
oldData: projectCandidate,
newData: updatedProject,
}
res.send(responseWrapper(undefined, projectSummary))
} catch (e) {
next(e)
}
})
module.exports = router

View File

@@ -0,0 +1,137 @@
const router = require('express').Router()
const { expressjwt: jwt } = require('express-jwt')
const ObjectId = require('mongodb').ObjectID
const { JWT_SECRET } = require('./constants')
const { requiredFields, responseWrapper } = require('./utils')
const { createTask, getTaskListByProjectId, getProjectById, editTask, getTaskById, deleteTaskById } = require('./db')
router.get('/healthcheck', (req, res) => {
res.send(true)
})
router.get('/:projectId', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
const { projectId } = req.params
const taskList = await getTaskListByProjectId({
projectId,
})
res.send(responseWrapper(undefined, taskList))
})
router.get('/:projectId/:taskId', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
const { projectId, taskId } = req.params
if (projectId && taskId !== undefined && taskId !== 'undefined') {
const userId = req.auth.id
const taskDetail = await getTaskById({
taskId, userId,
})
res.send(responseWrapper(undefined, taskDetail))
} else {
res.send(responseWrapper(undefined, []))
}
})
router.post('/create', requiredFields(['projectId', 'title', 'type', 'status']), jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const authorId = req.auth.id
const { projectId, ...taskData } = req.body
const taskCandidate = await createTask({
taskData, projectId, authorId,
})
res.send(responseWrapper(undefined, taskCandidate))
} catch (e) {
next(e)
}
})
router.post('/edit', requiredFields(['projectId', 'taskId', 'title', 'type', 'status']), jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const authorId = req.auth.id
const { projectId, taskId, ...taskData } = req.body
const projectCandidate = await getProjectById({
projectId,
})
if (!projectCandidate) {
throw new Error('The project not exists [task.edit.projectCandidate]')
}
const taskCandidate = await getTaskById({
taskId,
})
if (!taskCandidate) {
throw new Error('The project not exists [task.edit.taskCandidate]')
}
await editTask({
taskData, projectId, authorId, taskId,
})
const updatedTask = await getTaskById({
taskId,
})
const taskSummary = {
oldData: taskCandidate,
newData: updatedTask,
}
res.send(responseWrapper(undefined, taskSummary))
} catch (e) {
next(e)
}
})
router.post('/delete', requiredFields(['projectId', 'taskId']), jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
try {
const authorId = req.auth.id
const { projectId, taskId } = req.body
const projectCandidate = await getProjectById({
projectId,
})
if (!projectCandidate) {
throw new Error('The project not exists [task.edit.projectCandidate]')
}
const taskCandidate = await getTaskById({
taskId,
})
if (!taskCandidate) {
throw new Error('The project not exists [task.edit.taskCandidate]')
}
const answered = await deleteTaskById(taskId)
if (answered.result.ok) {
res.send(responseWrapper(undefined))
}
} catch (e) {
next(e)
}
})
module.exports = router

View File

@@ -0,0 +1,21 @@
const router = require('express').Router()
const { expressjwt: jwt } = require('express-jwt')
const ObjectId = require('mongodb').ObjectID
const { JWT_SECRET } = require('./constants')
const { requiredFields, responseWrapper } = require('./utils')
const { getAllUsers } = require('./db')
router.get('/healthcheck', (req, res) => {
res.send(true)
})
router.get('/getForSelect', jwt({
secret: JWT_SECRET, algorithms: ['HS256'],
}), async (req, res, next) => {
const userList = await getAllUsers()
res.send(responseWrapper(undefined, userList))
})
module.exports = router

View File

@@ -0,0 +1,49 @@
const ObjectId = require('mongodb').ObjectID
const requiredFields = (fields) => (req, res, next) => {
for (const fieldName of fields) {
if (!req.body[fieldName]) {
throw new Error(`Field ${fieldName} does't set`)
}
}
next()
}
const responseWrapper = (error, data, success = true) => ({
error, data, success,
})
const _idToId = (data) => {
const { _id, ...rest } = data
return {
id: _id,
...rest,
}
}
const _idToIdArray = (arrayData, setAuthor = false) => {
let newArray = []
for (let index = 0; index < arrayData.length; index++) {
newArray[index] = _idToId(arrayData[index])
}
return newArray
}
const checkDB = (db) => {
if (db === null) throw new Error('no db connection')
}
const filterId = (id) => ({
_id: new ObjectId(id),
})
module.exports = {
checkDB,
requiredFields,
responseWrapper,
_idToId,
_idToIdArray,
filterId,
}

View File

@@ -0,0 +1,36 @@
const router = require('express').Router();
const checkPassword = require('pbkdf2-password')();
const jwt = require('jsonwebtoken');
const {EDATEAM_JWT_TOKEN} = require('./key');
const {getUser, _idToId, getResponse, requiredFields, signUp} = require('./controllers');
router.post('/sign-in', requiredFields(['email','password']), async (req, res)=>{
try{
const user = await getUser(req.body);
checkPassword({password:req.body.password, salt:user.salt},async(err, pass, salt, hash )=>{
if(err){
throw new Error(err);
}
if(user.password === hash){
const {password, salt:_salt, ...rest} = user;
const token = jwt.sign(_idToId(rest), EDATEAM_JWT_TOKEN);
return res.send(getResponse(null, token));
}
return res.status(400).send(getResponse('Wrong email or password!'));
} )
}
catch(error){
res.status(400).send(getResponse(error.message));
}
});
router.post('/sign-up', requiredFields(['email', 'login', 'password']), async (req, res) => {
let error = null
const data = await signUp(req.body).catch((e) => error = e.message)
return res.status(error ? 400 : 200).send(getResponse(error, data))
})
module.exports = router;

View File

@@ -0,0 +1,217 @@
const ObjectId = require('mongodb').ObjectId;
const getHash = require('pbkdf2-password')();
const { getDB } = require('../../../utils/mongo');
const USERS_COLLECTION = 'users';
const RECIPES_COLLECTION = 'recipes_collection';
const FAVORITES_USER = 'favorites_user'
let db =null;
const connect = async () => {
db = await getDB('edateam');
};
const init = async () => {
await connect();
};
init();
const _idToId = (data) => {
const { _id, ...rest } = data;
return {
id: _id,
...rest
};
}
const _idToArray = (data) => {
const _idToMap = data.map((item) => _idToId(item));
return _idToMap;
}
const getResponse = (error, data, success = true) => {
if (error) {
return {
success: false,
error,
}
}
return {
success,
data,
}
}
const signUp = async ({ email, login, password }) => {
try {
db = await getDB('edateam');
const userCollection = db.collection(USERS_COLLECTION);
const userData = await userCollection.findOne({
$or: [
{ login },
{ email }
]
})
if (userData?.login === login) {
throw new Error('This login already in db!\nPlease come up with another login!');
}
if (userData?.email === email) {
throw new Error('This email already in db!\nPlease come up with another email!');
}
return new Promise((resolve, reject) => {
getHash({ password }, async (err, pass, salt, hash) => {
if (err) {
return reject(err);
}
const insertedCount = await userCollection.insertOne({ email, login, password: hash, salt });
if (!insertedCount) {
return reject(new Error('Insert error!'));
}
resolve({});
});
});
} catch (error) {
console.error(error);
throw error;
}
};
const getUser = async ({ email }) => {
if (db === null) {
throw new Error('no db connection :((');
}
try {
const userCollection = db.collection(USERS_COLLECTION);
const userData = await userCollection.findOne({ email });
if (userData) {
return userData;
}
throw new Error('Wrong email or password!');
} catch (error) {
throw new Error(error);
}
}
const getListRecipes = async () => {
try {
db = await getDB('edateam');
const recipesCollection = db.collection(RECIPES_COLLECTION);
const recipesData = await recipesCollection.find().toArray();
if (recipesData.length > 0) {
return _idToArray(recipesData);
} else {
throw new Error('No recipes found in the database!');
}
} catch (error) {
console.error('Error in getListRecipes:', error.message);
throw new Error(error.message);
}
};
const getRecipe = async (dishId ) => {
try {
db = await getDB('edateam');
const recipesCollection = db.collection(RECIPES_COLLECTION);
const id = dishId.id;
const recipeData = await recipesCollection.findOne({ _id :new ObjectId(id) } );
if (recipeData!=null) {
return _idToId(recipeData);
}
throw new Error('Not found recipe');
} catch (error) {
throw new Error(error);
}
}
const addRecipe = async (recipe) => {
try {
db = await getDB('edateam');
const recipesCollection = db.collection(RECIPES_COLLECTION);
const result = await recipesCollection.insertOne(recipe);
if (!result.insertedId) {
throw new Error('Recipe insertion failed');
}
return {
success: true,
id: result.insertedId
};
} catch (error) {
console.error('Error in addRecipe:', error.message);
throw new Error(error.message);
}
};
const requiredFields = (fields) => (req, res, next) => {
// eslint-disable-next-line no-restricted-syntax
for (const fieldName of fields) {
if (!req.body[fieldName]) {
throw new Error(`Параметр ${fieldName} не установлен`)
}
}
next()
}
const addFavorite = async (userId, recipeId) => {
try {
db = await getDB('edateam');
const favoritesCollection = db.collection(FAVORITES_USER);
const result = await favoritesCollection.updateOne(
{ userId: new ObjectId(userId) },
{ $addToSet: { favorites: new ObjectId(recipeId) } },
{ upsert: true }
);
return result;
} catch (error) {
throw new Error('Error adding favorite: ' + error.message);
}
};
const getFavorites = async (userId) => {
try {
db = await getDB('edateam');
const favoritesCollection = db.collection(FAVORITES_USER);
const userFavorites = await favoritesCollection.findOne({ userId: new ObjectId(userId) });
if (!userFavorites || !userFavorites.favorites || userFavorites.favorites.length === 0) {
return [];
}
const recipesCollection = db.collection(RECIPES_COLLECTION);
const favoriteRecipes = await recipesCollection.find({ _id: { $in: userFavorites.favorites } }).toArray();
return favoriteRecipes;
} catch (error) {
console.error(error);
throw new Error("Failed to get user favorites with recipes");
}
};
module.exports = {
getUser,
signUp,
getResponse,
_idToId,
_idToArray,
getListRecipes,
getRecipe,
addRecipe,
requiredFields,
getFavorites,
addFavorite
};

View File

@@ -0,0 +1,25 @@
const router = require('express').Router();
router.get('/recipe-data', (request, response) => {
response.send(require('../json/recipe-data/success.json'));
});
router.get('/userpage-data', (req, res) => {
res.send(require('../json/userpage-data/success.json'));
});
router.post('/userpage-data', (req, res) => {
res.send(require('../json/userpage-data/success.json'));
});
router.get('/homepage-data', (req, res) => {
res.send(require('../json/homepage-data/success.json'));
});
router.use('/auth', require('./auth'));
router.use('/recipe', require('./user'));
router.use('/main', require('./main'));
module.exports = router;

View File

@@ -0,0 +1,76 @@
{
"data": [
{
"src": "pancakes_meat",
"alt": "Фотография блинчиков с мясом, сыром и луком",
"href": "?=dish01",
"name": "Блинчики с мясом, сыром и лучком"
},
{
"src": "cheesecakes",
"alt": "Фотография сырников из творога",
"href": "?=dish02",
"name": "Сырники из творога"
},
{
"src": "borsch",
"alt": "Фотография борща",
"href": "?=dish03",
"name": "Борщ"
},
{
"src": "vareniki",
"alt": "Фотография вареников",
"href": "?=dish04",
"name": "Ленивые вареники"
},
{
"src": "rice_porridge",
"alt": "Фотография рисовой каши",
"href": "?=dish05",
"name": "Рисовая каша"
},
{
"src": "cutlets",
"alt": "Фотография котлет по-киевски",
"href": "?=dish06",
"name": "Котлеты по-киевски"
},
{
"src": "draniki",
"alt": "Фотография драников",
"href": "?=dish07",
"name": "Драники"
},
{
"src": "meringue",
"alt": "Фотография безе",
"href": "?=dish08",
"name": "Безе"
},
{
"src": "goulash",
"alt": "Фотография гуляша",
"href": "?=dish09",
"name": "Гуляш"
},
{
"src": "pancakes_cherries",
"alt": "Фотография блинчиков с вишней и творожным сыром",
"href": "?=dish10",
"name": "Блинчики с вишней и творожным сыром"
},
{
"src": "canned_soup",
"alt": "Фотография супа из рыбных консервов",
"href": "?=dish11",
"name": "Суп из рыбных консервов"
},
{
"src": "salad",
"alt": "Фотография салата",
"href": "?=dish12",
"name": "Салат \"Весенний\""
}
]
}

View File

@@ -0,0 +1,58 @@
{
"name":"Блинчики с вишней и творожным сыром",
"stages":
[
"Смешать муку, молоко, яйца, сахар и соль в миске",
"Добавить вишню в тесто и перемешать",
"Вылить тесто на разогретую сковороду и обжарить с двух сторон до золотистого цвета",
"Подавать блинчики, украсив творожным сыром сверху"
],
"table":
[
{ "ingredient": "1",
"weight": "500 гр",
"price1": "500р.",
"price2": "439р.",
"price3": "600р." },
{ "ingredient": "Ингредиент 2",
"weight": "2 шт",
"price1": "120р.",
"price2": "150р.",
"price3": "130р." },
{ "ingredient": "Ингредиент 3",
"weight": "500 гр",
"price1": "12р.",
"price2": "12.99р.",
"price3": "10р." },
{ "ingredient": "Ингредиент 4",
"weight": "500 гр",
"price1": "500р.",
"price2": "439р.",
"price3": "600р." },
{ "ingredient": "Ингредиент 5",
"weight": "500 гр",
"price1": "500р.",
"price2": "439р.",
"price3": "600р." },
{ "ingredient": "Ингредиент 6",
"weight": "500 гр",
"price1": "500р.",
"price2": "439р.",
"price3": "600р." }
],
"tags":
[
{ "name": "#блины", "href": "#01" },
{ "name": "#вишня", "href": "#02" },
{ "name": "#молоко"," href": "#03" }
]
}

View File

@@ -0,0 +1,30 @@
{
"data":{
"id":1,
"loginname":"Логин пользователя",
"datesignin":"2024/05/18",
"favoritedishes":
[
{"id":1,
"dishlink":"?=dish1",
"dishname":"Блюдо1"
},
{"id":2,
"dishlink":"?=dish2",
"dishname":"Блюдо2"
},
{"id":3,
"dishlink":"?=dish3",
"dishname":"Блюдо3"
},
{"id":4,
"dishlink":"?=dish4",
"dishname":"Блюдо4"
},
{"id":5,
"dishlink":"?=dish5",
"dishname":"Блюдо5"
}
]
}
}

View File

@@ -0,0 +1,3 @@
const EDATEAM_JWT_TOKEN = 'secretyk token';
module.exports = {EDATEAM_JWT_TOKEN};

View File

@@ -0,0 +1,51 @@
const { getListRecipes , getRecipe, addFavorite , getFavorites} = require('./controllers');
const router = require('express').Router();
router.get('/recipes', async (req, res) => {
try {
const result = await getListRecipes();
return res.status(200).json({ success: true, data: result });
} catch (error) {
console.error('Error in GET /recipes:', error.message);
return res.status(500).json({ success: false, message: error.message });
}
});
router.post('/recipe', async(req,res)=>{
try{
const result = await getRecipe(req.body);
return res.status(200).json({success:true, data: result});
}
catch(error){
console.error('Error in GET /recipes:', error.message);
return res.status(500).json({ success: false, message: error.message });
}
})
router.post('/favorites', async (req, res) => {
try {
const { userId, recipeId } = req.body;
const result = await addFavorite(userId, recipeId);
return res.status(200).json({ success: true, data: result });
} catch (error) {
console.error('Error in POST /favorites:', error.message);
return res.status(500).json({ success: false, message: error.message });
}
});
router.post('/get-favorites', async(req,res) =>{
try {
const { userId } = req.body;
const result = await getFavorites(userId);
console.log(result)
return res.status(200).json({ success: true, data: result });
} catch (error) {
console.error('Error in POST /get-favorites:', error.message);
return res.status(500).json({ success: false, message: error.message });
}
});
module.exports = router;

View File

@@ -0,0 +1,35 @@
const { requiredFields , getFavorites, addRecipe} = require('./controllers');
const router = require('express').Router();
router.post('/favorites', requiredFields('id'), async(req,res)=>{
try{
const recipes = await getFavorites(req.body);
res.status(200).send(getResponse(recipes));
}
catch(error){
res.status(400).send(getResponse(error.message));
}
})
router.post('/add-recept', async (req, res) => {
let error = null;
let result = null;
try {
result = await addRecipe(req.body);
} catch (e) {
error = e.message;
}
if (error) {
console.error(`Error in POST /add-recept: ${error}`);
}
return res.status(error ? 500 : 201).json({
message: error ? error : 'Recipe added successfully',
id: result?.id
});
});
module.exports = router;

View File

@@ -0,0 +1,26 @@
const express = require("express");
const router = express.Router()
const waitMiddleware = (req, res, next) => {
setTimeout(() => {
next()
}, 3000)
}
const listActivated = true
router.get('/list', waitMiddleware, (req, res) => {
req.user
if (listActivated) {
res.status(200).send(require('./news.json'))
} else {
res.status(500).send()
}
})
router.get('/list-activate-toggle', (req, res) => {
listActivated = !listActivated
res.send(listActivated ? 'Activated' : 'Deactivated')
})
module.exports = router

View File

@@ -0,0 +1,25 @@
{
"ok": true,
"data": [
{
"id": "1",
"name": "Some name 1",
"description": "Some long long long long description"
},
{
"id": "2",
"name": "Some name 2",
"description": "Some long long long long description"
},
{
"id": "3",
"name": "Some name 3",
"description": "Some long long long long description"
},
{
"id": "4",
"name": "Some name 4",
"description": "Some long long long long description"
}
]
}

View File

@@ -0,0 +1,9 @@
const express = require('express')
const router = express.Router()
router.use('/example', require('./example/index'))
router.use('/pen-plotter', require('./pen-plotter/index'))
router.use('/score-scout', require('./score-scout/index'))
module.exports = router

View File

@@ -0,0 +1,28 @@
const express = require('express')
const router = express.Router()
const fs = require("fs");
const path = require("path");
const { BASE_PATH } = require("./paths");
router.use("/profiles", express.static(path.join(BASE_PATH, "/profiles")));
router.use("/static", express.static(path.join(BASE_PATH, "/static")));
router.use('/api', require('./routes/api').default)
// Add the required directories
router.use((req, res, next) => {
const directories = ["/static", "/profiles"];
directories.forEach((dir) => {
if (!fs.existsSync(BASE_PATH + dir)) {
fs.mkdirSync(BASE_PATH + dir);
}
});
next();
});
router.get('/info', (req, res) => {
res.send('Pen-Plotter backend')
})
module.exports = router

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.STATIC_PATH = exports.PROFILES_PATH = exports.BASE_PATH = void 0;
const path = require("path");
const BASE_PATH = __dirname;
exports.BASE_PATH = BASE_PATH;
const PROFILES_PATH = path.join(BASE_PATH, "profiles");
exports.PROFILES_PATH = PROFILES_PATH;
const STATIC_PATH = path.join(BASE_PATH, "static");
exports.STATIC_PATH = STATIC_PATH;

View File

@@ -0,0 +1 @@
<g xmlns="http://www.w3.org/2000/svg" width="77" height="68"><path stroke="#000000" stroke-width="3" fill="none" d="M 3 37 L 3 37 L 7 38 L 22 41 L 49 41 L 62 37 L 71 31 L 73 25 L 74 18 L 73 10 L 71 6 L 69 4 L 63 3 L 58 3 L 49 3 L 39 5 L 33 9 L 28 13 L 24 18 L 21 29 L 21 37 L 21 43 L 29 53 L 33 58 L 37 61 L 39 63 L 41 65 L 44 65 L 46 63 L 49 61 L 52 59 L 53 58 L 53 58"/></g>

View File

@@ -0,0 +1 @@
<g xmlns="http://www.w3.org/2000/svg" width="86" height="88"><path stroke="#000000" stroke-width="3" fill="none" d="M 13 3 L 13 3 L 14 7 L 16 30 L 18 44 L 20 59 L 21 63 L 21 66 L 21 68 L 22 69 L 23 67 L 27 55 L 31 43 L 36 33 L 40 25 L 43 18 L 46 16 L 46 15 L 46 15 L 46 15"/><path stroke="#000000" stroke-width="3" fill="none" d="M 44 42 L 44 42 L 44 42"/><path stroke="#000000" stroke-width="3" fill="none" d="M 44 42 L 44 42 L 44 42 L 42 42 L 35 42 L 29 42 L 22 41 L 18 39 L 12 36 L 8 35 L 4 31 L 3 31 L 3 31 L 3 31"/><path stroke="#000000" stroke-width="3" fill="none" d="M 3 31 L 3 31 L 7 33 L 29 43 L 63 60 L 75 69 L 81 76 L 83 79 L 83 82 L 83 84 L 82 85 L 80 85 L 78 84 L 78 84"/></g>

View File

@@ -0,0 +1 @@
<g xmlns="http://www.w3.org/2000/svg" width="70" height="125"><path stroke="#000000" stroke-width="3" fill="none" d="M 3 27 L 3 27 L 3 31 L 9 38 L 15 43 L 21 46 L 27 46 L 31 42 L 35 35 L 39 23 L 41 15 L 41 9 L 41 4 L 42 3 L 42 3 L 43 5 L 44 10 L 45 14 L 46 23 L 47 32 L 48 40 L 49 51 L 50 69 L 50 84 L 50 99 L 46 112 L 44 115 L 40 119 L 33 122 L 27 122 L 22 121 L 16 118 L 15 115 L 15 110 L 15 103 L 22 91 L 31 85 L 39 79 L 45 76 L 52 71 L 55 70 L 59 68 L 59 66 L 62 63 L 64 62 L 66 59 L 67 58 L 67 58 L 67 57 L 67 57"/></g>

View File

@@ -0,0 +1 @@
<g xmlns="http://www.w3.org/2000/svg" width="37.20001220703125" height="80.4000244140625"><path stroke="#000000" stroke-width="3" fill="none" d="M 7 7.79998779296875 L 7 7.79998779296875 L 7 18.20001220703125 L 7 22.20001220703125 L 7 26.20001220703125 L 6.20001220703125 29.4000244140625 L 6.20001220703125 31 L 6.20001220703125 31.79998779296875 L 6.20001220703125 33.4000244140625 L 6.20001220703125 34.20001220703125 L 6.20001220703125 35 L 6.20001220703125 35.79998779296875 L 6.20001220703125 36.60003662109375 L 5.399993896484375 37.4000244140625 L 5.399993896484375 38.20001220703125 L 4.600006103515625 38.20001220703125 L 4.600006103515625 38.20001220703125"/><path stroke="#000000" stroke-width="3" fill="none" d="M 3.79998779296875 29.4000244140625 L 3.79998779296875 29.4000244140625 L 13.399993896484375 30.20001220703125 L 17.399993896484375 30.20001220703125 L 20.600006103515625 30.20001220703125 L 23.79998779296875 31 L 25.399993896484375 31 L 27.79998779296875 31 L 28.600006103515625 31 L 30.20001220703125 31 L 31 31 L 31.79998779296875 31 L 32.600006103515625 31.79998779296875 L 33.399993896484375 31.79998779296875 L 34.20001220703125 32.60003662109375 L 34.20001220703125 32.60003662109375"/><path stroke="#000000" stroke-width="3" fill="none" d="M 34.20001220703125 3 L 34.20001220703125 3 L 34.20001220703125 12.60003662109375 L 34.20001220703125 24.60003662109375 L 34.20001220703125 31.79998779296875 L 34.20001220703125 39.79998779296875 L 33.399993896484375 46.20001220703125 L 33.399993896484375 54.20001220703125 L 33.399993896484375 59.79998779296875 L 31.79998779296875 65.4000244140625 L 31.79998779296875 69.4000244140625 L 31.79998779296875 71.79998779296875 L 31 74.20001220703125 L 31 75 L 31 75.79998779296875 L 31 76.60003662109375 L 31 77.4000244140625 L 31 77.4000244140625"/><path stroke="#000000" stroke-width="3" fill="none" d="M 3 29.4000244140625 L 3 29.4000244140625 L 3 39 L 3 43 L 3 47 L 3 50.20001220703125 L 3 53.4000244140625 L 3 55.79998779296875 L 3 56.60003662109375 L 3 58.20001220703125 L 3 59 L 3 59.79998779296875 L 3 60.60003662109375 L 3 61.4000244140625 L 3 61.4000244140625"/></g>

View File

@@ -0,0 +1 @@
<g xmlns="http://www.w3.org/2000/svg" width="59" height="61"><path stroke="#000000" stroke-width="3" fill="none" d="M 3 47 L 3 47 L 4 36 L 4 24 L 5 17 L 5 9 L 5 7 L 5 5 L 5 4 L 5 3 L 6 3 L 7 4 L 9 8 L 14 15 L 16 20 L 21 26 L 23 27 L 26 29 L 28 30 L 33 29 L 37 27 L 40 23 L 46 18 L 48 14 L 50 11 L 52 10 L 54 9 L 54 8 L 54 7 L 54 10 L 55 16 L 55 23 L 56 37 L 56 46 L 56 50 L 55 53 L 55 56 L 55 57 L 55 58 L 55 58"/></g>

Some files were not shown because too many files have changed in this diff Show More