move old to legacy folder
This commit is contained in:
34
.bzr/legacy/basket/auth.js
Normal file
34
.bzr/legacy/basket/auth.js
Normal 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
|
||||
25
.bzr/legacy/basket/categories.js
Normal file
25
.bzr/legacy/basket/categories.js
Normal 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
|
||||
510
.bzr/legacy/basket/controller.js
Normal file
510
.bzr/legacy/basket/controller.js
Normal 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,
|
||||
}
|
||||
54
.bzr/legacy/basket/dashboard.js
Normal file
54
.bzr/legacy/basket/dashboard.js
Normal 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
|
||||
9
.bzr/legacy/basket/index.js
Normal file
9
.bzr/legacy/basket/index.js
Normal 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
|
||||
10
.bzr/legacy/basket/json/auth/sign-in.json
Normal file
10
.bzr/legacy/basket/json/auth/sign-in.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"token": "qwert",
|
||||
"user": {
|
||||
"id": "1234",
|
||||
"login": "eldar",
|
||||
"email": "www@www.ru"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
.bzr/legacy/basket/json/categories/add/success.json
Normal file
23
.bzr/legacy/basket/json/categories/add/success.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"success": true,
|
||||
"data":
|
||||
{
|
||||
"category":[
|
||||
{
|
||||
"id":1,
|
||||
"name": "Продукты",
|
||||
"color": "#08AE0F"
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"name": "Бытовая химия",
|
||||
"color": "#3414F5"
|
||||
},
|
||||
{
|
||||
"id":3,
|
||||
"name": "Одежда",
|
||||
"color": "#FA8803"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
18
.bzr/legacy/basket/json/categories/current/success.json
Normal file
18
.bzr/legacy/basket/json/categories/current/success.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"success": true,
|
||||
"data":
|
||||
{
|
||||
"category":[
|
||||
{
|
||||
"id":1,
|
||||
"name": "Продукты",
|
||||
"color": "#08AE0F"
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"name": "Бытовая химия",
|
||||
"color": "#3414F5"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
.bzr/legacy/basket/json/dashboard/common/error.json
Normal file
3
.bzr/legacy/basket/json/dashboard/common/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"error": "Не получилось..."
|
||||
}
|
||||
8
.bzr/legacy/basket/json/dashboard/common/success.json
Normal file
8
.bzr/legacy/basket/json/dashboard/common/success.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"id": "vrgbtrgbtrbryn",
|
||||
"listName": "2 список",
|
||||
"purchased": 1,
|
||||
"total": 5
|
||||
}
|
||||
}
|
||||
4
.bzr/legacy/basket/json/dashboard/list/empty.json
Normal file
4
.bzr/legacy/basket/json/dashboard/list/empty.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"success": true,
|
||||
"data": []
|
||||
}
|
||||
4
.bzr/legacy/basket/json/dashboard/list/error.json
Normal file
4
.bzr/legacy/basket/json/dashboard/list/error.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"success": false,
|
||||
"error": "Список не загрузился"
|
||||
}
|
||||
23
.bzr/legacy/basket/json/dashboard/list/success.json
Normal file
23
.bzr/legacy/basket/json/dashboard/list/success.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.bzr/legacy/basket/json/landing/error.json
Normal file
4
.bzr/legacy/basket/json/landing/error.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"success": false,
|
||||
"error": "Ошибка получения данных для Landing"
|
||||
}
|
||||
80
.bzr/legacy/basket/json/landing/success.json
Normal file
80
.bzr/legacy/basket/json/landing/success.json
Normal 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": "Задача организации, в особенности же консультация с широким активом обеспечивает широкому кругу специалистов новых принципов формирования материально-технической и кадровой базы."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.bzr/legacy/basket/json/listItem/item/error.json
Normal file
3
.bzr/legacy/basket/json/listItem/item/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"error": "Не удалось изменить..."
|
||||
}
|
||||
15
.bzr/legacy/basket/json/listItem/item/success.json
Normal file
15
.bzr/legacy/basket/json/listItem/item/success.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"success": true,
|
||||
"data":
|
||||
{
|
||||
"id": 23,
|
||||
"categoryId": 1,
|
||||
"text": "Курица",
|
||||
"count": 17,
|
||||
"bought": false,
|
||||
"createdBy": "",
|
||||
"createdDt": "",
|
||||
"modifiedBy": "",
|
||||
"modifiedDt": ""
|
||||
}
|
||||
}
|
||||
4
.bzr/legacy/basket/json/listItem/shoppingList/error.json
Normal file
4
.bzr/legacy/basket/json/listItem/shoppingList/error.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"success": false,
|
||||
"error": "Ошибка получения данных"
|
||||
}
|
||||
34
.bzr/legacy/basket/json/listItem/shoppingList/success.json
Normal file
34
.bzr/legacy/basket/json/listItem/shoppingList/success.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
5
.bzr/legacy/basket/key.js
Normal file
5
.bzr/legacy/basket/key.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const BASKET_JWT_TOKEN = 'super super secret key'
|
||||
|
||||
module.exports = {
|
||||
BASKET_JWT_TOKEN,
|
||||
}
|
||||
7
.bzr/legacy/basket/landing.js
Normal file
7
.bzr/legacy/basket/landing.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const router = require('express').Router()
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
res.send(require('./json/landing/success.json'))
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
67
.bzr/legacy/basket/listItem.js
Normal file
67
.bzr/legacy/basket/listItem.js
Normal 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
|
||||
162
.bzr/legacy/bushou/episodes/success.json
Normal file
162
.bzr/legacy/bushou/episodes/success.json
Normal 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"
|
||||
}
|
||||
]
|
||||
BIN
.bzr/legacy/bushou/images.tar
Normal file
BIN
.bzr/legacy/bushou/images.tar
Normal file
Binary file not shown.
13
.bzr/legacy/bushou/index.js
Normal file
13
.bzr/legacy/bushou/index.js
Normal 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
|
||||
3941
.bzr/legacy/bushou/radicals/success.json
Normal file
3941
.bzr/legacy/bushou/radicals/success.json
Normal file
File diff suppressed because it is too large
Load Diff
1
.bzr/legacy/bushou/tokenize/success.json
Normal file
1
.bzr/legacy/bushou/tokenize/success.json
Normal file
File diff suppressed because one or more lines are too long
9
.bzr/legacy/car-wash/auth/index.js
Normal file
9
.bzr/legacy/car-wash/auth/index.js
Normal 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
|
||||
3
.bzr/legacy/car-wash/auth/login/error.json
Normal file
3
.bzr/legacy/car-wash/auth/login/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
16
.bzr/legacy/car-wash/auth/login/index.js
Normal file
16
.bzr/legacy/car-wash/auth/login/index.js
Normal 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
|
||||
3
.bzr/legacy/car-wash/auth/login/success.json
Normal file
3
.bzr/legacy/car-wash/auth/login/success.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
3
.bzr/legacy/car-wash/auth/logrec/error.json
Normal file
3
.bzr/legacy/car-wash/auth/logrec/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
40
.bzr/legacy/car-wash/auth/logrec/index.js
Normal file
40
.bzr/legacy/car-wash/auth/logrec/index.js
Normal 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
|
||||
3
.bzr/legacy/car-wash/auth/logrec/success.json
Normal file
3
.bzr/legacy/car-wash/auth/logrec/success.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
3
.bzr/legacy/car-wash/auth/passrec/error.json
Normal file
3
.bzr/legacy/car-wash/auth/passrec/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
44
.bzr/legacy/car-wash/auth/passrec/index.js
Normal file
44
.bzr/legacy/car-wash/auth/passrec/index.js
Normal 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
|
||||
3
.bzr/legacy/car-wash/auth/passrec/success.json
Normal file
3
.bzr/legacy/car-wash/auth/passrec/success.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
3
.bzr/legacy/car-wash/auth/registration/dooble.json
Normal file
3
.bzr/legacy/car-wash/auth/registration/dooble.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "dooble"
|
||||
}
|
||||
3
.bzr/legacy/car-wash/auth/registration/dooblephone.json
Normal file
3
.bzr/legacy/car-wash/auth/registration/dooblephone.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "dooblePhone"
|
||||
}
|
||||
3
.bzr/legacy/car-wash/auth/registration/error.json
Normal file
3
.bzr/legacy/car-wash/auth/registration/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
26
.bzr/legacy/car-wash/auth/registration/index.js
Normal file
26
.bzr/legacy/car-wash/auth/registration/index.js
Normal 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
|
||||
3
.bzr/legacy/car-wash/auth/registration/success.json
Normal file
3
.bzr/legacy/car-wash/auth/registration/success.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
40
.bzr/legacy/car-wash/carousel/carousel-en.json
Normal file
40
.bzr/legacy/car-wash/carousel/carousel-en.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
40
.bzr/legacy/car-wash/carousel/carousel-ru.json
Normal file
40
.bzr/legacy/car-wash/carousel/carousel-ru.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
.bzr/legacy/car-wash/carousel/index.js
Normal file
23
.bzr/legacy/car-wash/carousel/index.js
Normal 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
|
||||
9
.bzr/legacy/car-wash/index.js
Normal file
9
.bzr/legacy/car-wash/index.js
Normal 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
|
||||
23
.bzr/legacy/car-wash/map/index.js
Normal file
23
.bzr/legacy/car-wash/map/index.js
Normal 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
|
||||
37
.bzr/legacy/car-wash/map/map-info-en.json
Normal file
37
.bzr/legacy/car-wash/map/map-info-en.json
Normal 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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
37
.bzr/legacy/car-wash/map/map-info-ru.json
Normal file
37
.bzr/legacy/car-wash/map/map-info-ru.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"address": [
|
||||
{
|
||||
"id": "1",
|
||||
"x": "55.746971",
|
||||
"y": "48.743549",
|
||||
"name": "InnoWashing",
|
||||
"phone": "7654321",
|
||||
"address": "Россия, Республика Татарстан, Верхнеуслонский район, Иннополис, Спортивная улица, 106",
|
||||
"workingMode": "08:00–20: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:00–20: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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
22
.bzr/legacy/car-wash/prices/index.js
Normal file
22
.bzr/legacy/car-wash/prices/index.js
Normal 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
|
||||
59
.bzr/legacy/car-wash/prices/prices-en.json
Normal file
59
.bzr/legacy/car-wash/prices/prices-en.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
59
.bzr/legacy/car-wash/prices/prices-ru.json
Normal file
59
.bzr/legacy/car-wash/prices/prices-ru.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
136
.bzr/legacy/coder/catalog/cardData.json
Normal file
136
.bzr/legacy/coder/catalog/cardData.json
Normal 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"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
||||
3
.bzr/legacy/coder/catalog/error.json
Normal file
3
.bzr/legacy/coder/catalog/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
15
.bzr/legacy/coder/catalog/index.js
Normal file
15
.bzr/legacy/coder/catalog/index.js
Normal 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;
|
||||
424
.bzr/legacy/coder/controllers.js
Normal file
424
.bzr/legacy/coder/controllers.js
Normal 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,
|
||||
}
|
||||
49
.bzr/legacy/coder/forum/categories.json
Normal file
49
.bzr/legacy/coder/forum/categories.json
Normal 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": []
|
||||
}
|
||||
3
.bzr/legacy/coder/forum/error.json
Normal file
3
.bzr/legacy/coder/forum/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
96
.bzr/legacy/coder/forum/index.js
Normal file
96
.bzr/legacy/coder/forum/index.js
Normal 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;
|
||||
29
.bzr/legacy/coder/forum/topic-comments.json
Normal file
29
.bzr/legacy/coder/forum/topic-comments.json
Normal 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": []
|
||||
}
|
||||
235
.bzr/legacy/coder/forum/topic-list.json
Normal file
235
.bzr/legacy/coder/forum/topic-list.json
Normal 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": []
|
||||
}
|
||||
107
.bzr/legacy/coder/forum/topic-tags.json
Normal file
107
.bzr/legacy/coder/forum/topic-tags.json
Normal 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": []
|
||||
}
|
||||
20
.bzr/legacy/coder/forum/topic.json
Normal file
20
.bzr/legacy/coder/forum/topic.json
Normal 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": []
|
||||
}
|
||||
6
.bzr/legacy/coder/forum/users.json
Normal file
6
.bzr/legacy/coder/forum/users.json
Normal 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": []
|
||||
}
|
||||
8
.bzr/legacy/coder/index.js
Normal file
8
.bzr/legacy/coder/index.js
Normal 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
|
||||
27
.bzr/legacy/coder/topic/dynamic-programming.json
Normal file
27
.bzr/legacy/coder/topic/dynamic-programming.json
Normal 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>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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."}
|
||||
]
|
||||
}
|
||||
@@ -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, Vi−1 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 Vi−1 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."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"title" : "Linked list",
|
||||
"slug" : "linked-list",
|
||||
"content": [
|
||||
{"data":"# Hello markdown"},
|
||||
{"data":"# Hello markdown"}
|
||||
]
|
||||
}
|
||||
@@ -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."}
|
||||
]
|
||||
}
|
||||
3
.bzr/legacy/coder/topic/error.json
Normal file
3
.bzr/legacy/coder/topic/error.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"status": "error"
|
||||
}
|
||||
30
.bzr/legacy/coder/topic/index.js
Normal file
30
.bzr/legacy/coder/topic/index.js
Normal 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
|
||||
48
.bzr/legacy/easy-project/auth.js
Normal file
48
.bzr/legacy/easy-project/auth.js
Normal 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
|
||||
30
.bzr/legacy/easy-project/constants.js
Normal file
30
.bzr/legacy/easy-project/constants.js
Normal 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,
|
||||
}
|
||||
410
.bzr/legacy/easy-project/db.js
Normal file
410
.bzr/legacy/easy-project/db.js
Normal 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,
|
||||
}
|
||||
15
.bzr/legacy/easy-project/index.js
Normal file
15
.bzr/legacy/easy-project/index.js
Normal 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
|
||||
116
.bzr/legacy/easy-project/projects.js
Normal file
116
.bzr/legacy/easy-project/projects.js
Normal 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
|
||||
137
.bzr/legacy/easy-project/tasks.js
Normal file
137
.bzr/legacy/easy-project/tasks.js
Normal 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
|
||||
21
.bzr/legacy/easy-project/users.js
Normal file
21
.bzr/legacy/easy-project/users.js
Normal 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
|
||||
49
.bzr/legacy/easy-project/utils.js
Normal file
49
.bzr/legacy/easy-project/utils.js
Normal 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,
|
||||
}
|
||||
36
.bzr/legacy/edateam/auth.js
Normal file
36
.bzr/legacy/edateam/auth.js
Normal 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;
|
||||
217
.bzr/legacy/edateam/controllers.js
Normal file
217
.bzr/legacy/edateam/controllers.js
Normal 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
|
||||
};
|
||||
25
.bzr/legacy/edateam/index.js
Normal file
25
.bzr/legacy/edateam/index.js
Normal 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;
|
||||
76
.bzr/legacy/edateam/json/homepage-data/success.json
Normal file
76
.bzr/legacy/edateam/json/homepage-data/success.json
Normal 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": "Салат \"Весенний\""
|
||||
}
|
||||
]
|
||||
}
|
||||
58
.bzr/legacy/edateam/json/recipe-data/success.json
Normal file
58
.bzr/legacy/edateam/json/recipe-data/success.json
Normal 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" }
|
||||
]
|
||||
|
||||
}
|
||||
30
.bzr/legacy/edateam/json/userpage-data/success.json
Normal file
30
.bzr/legacy/edateam/json/userpage-data/success.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
.bzr/legacy/edateam/key.js
Normal file
3
.bzr/legacy/edateam/key.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const EDATEAM_JWT_TOKEN = 'secretyk token';
|
||||
|
||||
module.exports = {EDATEAM_JWT_TOKEN};
|
||||
51
.bzr/legacy/edateam/main.js
Normal file
51
.bzr/legacy/edateam/main.js
Normal 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;
|
||||
35
.bzr/legacy/edateam/user.js
Normal file
35
.bzr/legacy/edateam/user.js
Normal 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;
|
||||
26
.bzr/legacy/epja-2023-2/example/index.js
Normal file
26
.bzr/legacy/epja-2023-2/example/index.js
Normal 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
|
||||
25
.bzr/legacy/epja-2023-2/example/news.json
Normal file
25
.bzr/legacy/epja-2023-2/example/news.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
.bzr/legacy/epja-2023-2/index.js
Normal file
9
.bzr/legacy/epja-2023-2/index.js
Normal 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
|
||||
28
.bzr/legacy/epja-2023-2/pen-plotter/index.js
Normal file
28
.bzr/legacy/epja-2023-2/pen-plotter/index.js
Normal 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
|
||||
10
.bzr/legacy/epja-2023-2/pen-plotter/paths.js
Normal file
10
.bzr/legacy/epja-2023-2/pen-plotter/paths.js
Normal 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;
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
Reference in New Issue
Block a user