From c457e1465bb1f8886b5719091397d578e797963f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=BC=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Sat, 23 Apr 2022 19:45:45 +0300 Subject: [PATCH] =?UTF-8?q?=D0=90=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 37 ++++++++++++++ README.md | 10 ++-- index.d.ts | 4 +- package.json | 6 +++ src/__data__/constants.ts | 2 + src/config.ts | 4 +- src/main.ts | 26 +++++----- src/model/roles.ts | 7 +++ src/routes/auth.ts | 76 ---------------------------- src/routes/banner/index.ts | 11 ---- src/routes/index.ts | 12 ++--- src/routes/v1/auth.controller.ts | 40 +++++++++++++++ src/routes/v1/auth.ts | 47 +++++++++++++++++ src/routes/{ => v1}/banner/data.json | 0 src/routes/v1/banner/index.ts | 11 ++++ src/utils/admin-axios.ts | 11 ++++ src/utils/common.ts | 35 +++++++++++++ src/utils/error-handling.ts | 4 +- src/utils/mongo.ts | 31 ++++++++++++ src/utils/required.ts | 9 ++++ 20 files changed, 268 insertions(+), 115 deletions(-) create mode 100644 .eslintrc.json create mode 100644 src/__data__/constants.ts create mode 100644 src/model/roles.ts delete mode 100644 src/routes/auth.ts delete mode 100644 src/routes/banner/index.ts create mode 100644 src/routes/v1/auth.controller.ts create mode 100644 src/routes/v1/auth.ts rename src/routes/{ => v1}/banner/data.json (100%) create mode 100644 src/routes/v1/banner/index.ts create mode 100644 src/utils/admin-axios.ts create mode 100644 src/utils/common.ts create mode 100644 src/utils/mongo.ts create mode 100644 src/utils/required.ts diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..288b9e2 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,37 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "never" + ], + "no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": true }] + } +} diff --git a/README.md b/README.md index 214d854..99872cc 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,15 @@ Пример: -```.env +``` RED_CODER_BH_PORT=8045 -SESSION_SECRET="asdas a dasd sad as dasd as" -COOKIE_SESSION="sdfsfdjkbhjvav dbw fsfnnsdonan dlansln dlasnldnsa da" +SESSION_SECRET="session secret string" +COOKIE_SESSION="some secret string" JWT_SECRET_STRING="wow wow it is a secret shhhhhhh....." + +RC_ADMIN_APP_TOKEN="ccc37960-b118-4e11-b8a0-d67b0ff0e715" +ADMIN_SERVER_BASE_NAME="212.193.59.173:8091" + ``` ### 2. Установка зависимостей diff --git a/index.d.ts b/index.d.ts index 5c7cdf7..2c4f25f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,4 @@ declare module '*.json' { - const data: any; - export default data; + const data: any + export default data } diff --git a/package.json b/package.json index 135f63c..c5c1ba4 100644 --- a/package.json +++ b/package.json @@ -28,18 +28,24 @@ "license": "MIT", "dependencies": { "@types/cookie-session": "^2.0.44", + "axios": "^0.26.1", "cookie-session": "^2.0.0", + "cross-env": "^7.0.3", "dotenv": "^16.0.0", "express": "^4.17.3", "express-jwt": "^6.1.1", "jsonwebtoken": "^8.5.1", "jwt-middleware": "^0.3.0", + "mongodb": "^4.5.0", "pbkdf2-password": "^1.2.1", "uuid": "^8.3.2" }, "devDependencies": { "@types/express": "^4.17.13", "@types/node": "^17.0.24", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "eslint": "^8.13.0", "nodemon": "^2.0.15", "ts-node": "^10.7.0", "typescript": "^4.6.3" diff --git a/src/__data__/constants.ts b/src/__data__/constants.ts new file mode 100644 index 0000000..12b455c --- /dev/null +++ b/src/__data__/constants.ts @@ -0,0 +1,2 @@ + +export const usersCollection = 'USERS_COLLECTIONS' diff --git a/src/config.ts b/src/config.ts index 8902e23..5b5887c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,3 @@ -import { config } from 'dotenv'; +import { config } from 'dotenv' -config(); +config() diff --git a/src/main.ts b/src/main.ts index 083f944..1254d7d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,20 +1,20 @@ -import express from 'express'; -import cookieSession from 'cookie-session'; +import express from 'express' +import cookieSession from 'cookie-session' -import './config'; -import { errorHandle } from './utils/error-handling'; -import { router } from './routes'; +import './config' +import { errorHandle } from './utils/error-handling' +import { router } from './routes' -const app = express(); +const app = express() -const port = process.env.RED_CODER_BH_PORT; +const port = process.env.RED_CODER_BH_PORT -app.use(express.json()); -app.use(cookieSession({ secret: process.env.COOKIE_SESSION })); +app.use(express.json()) +app.use(cookieSession({ secret: process.env.COOKIE_SESSION })) -app.use(router); +app.use(router) -app.use(errorHandle); +app.use(errorHandle) app.listen(port, () => { - console.log(`listen on http://localhost:${port}`); -}); + console.log(`listen on http://localhost:${port}`) +}) diff --git a/src/model/roles.ts b/src/model/roles.ts new file mode 100644 index 0000000..70815f4 --- /dev/null +++ b/src/model/roles.ts @@ -0,0 +1,7 @@ +export enum Roles { + Admin = 'Admin', + User = 'User' +} + +export const allAccess = [Roles.Admin, Roles.User] +export const adminonly = Roles.Admin diff --git a/src/routes/auth.ts b/src/routes/auth.ts deleted file mode 100644 index 6c6a864..0000000 --- a/src/routes/auth.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Router } from 'express'; -import pbkdf2Password from 'pbkdf2-password'; -import { v4 as uuid } from 'uuid'; -import jwt from 'jsonwebtoken'; -import jwtMiddleware from 'express-jwt'; - -const makeHash = pbkdf2Password(); - -export const authRouter = Router(); - -const requiredFields = (fields: string[]) => (req, res, next) => { - for (const fieldName of fields) { - if (!req.body[fieldName]) { - throw new Error(`Field ${fieldName} does\'t set`) - } - } - - next(); -}; - -const users: any[] = []; - - -authRouter.get('/users', jwtMiddleware({ secret: process.env.JWT_SECRET_STRING, algorithms: ['HS256'] }), (req, res) => { - res.send(users); -}); - -authRouter.post('/sign-in', requiredFields(['password', 'login']), (req, res) => { - const { password, login } = req.body; - - const user = users.find(u => u.login === login); - - if (!user) { - res.status(400).send({ error: 'Login or password does\'t match' }); - return; - } - - makeHash({ password, salt: user.salt }, (err, pass, salt, hash) => { - if (err) throw err; - - if (user.hash === hash) { - const { hash: _hash, salt: _salt, ...cleanUser } = user - - req.session.user = cleanUser; - const token = jwt.sign(cleanUser, process.env.JWT_SECRET_STRING, { - - }); - - return res.send({ token, user: cleanUser }) - } - - res.status(400).send({ error: 'Login or password does\'t match' }); - }); -}); - -authRouter.post('/sign-up', requiredFields(['password', 'login', 'email']), (req, res, next) => { - const { password, login, ...rest } = req.body; - - makeHash({ password }, function (err, pass, salt, hash) { - if (err) throw err; - - const newUser = { - id: uuid(), - ...rest, - login, - salt, - hash - } - - users.push(newUser); - - const { hash: _hash, salt: _salt, ...cleanUser } = newUser - - res.send(cleanUser); - }); -}); diff --git a/src/routes/banner/index.ts b/src/routes/banner/index.ts deleted file mode 100644 index 72324fa..0000000 --- a/src/routes/banner/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router } from 'express'; - -import BannerData from './data.json'; - -export const bannerRouter = Router(); - -bannerRouter.get('/banner-data', (req, res) => { - setTimeout(() => { - res.send(BannerData) - }, 3000) -}) diff --git a/src/routes/index.ts b/src/routes/index.ts index e2f4c88..c2fea4c 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,9 +1,9 @@ -import { Router } from 'express'; +import { Router } from 'express' -import { authRouter } from './auth'; -import { bannerRouter } from './banner'; +import { authRouter } from './v1/auth' +import { bannerRouter } from './v1/banner' -export const router = Router(); +export const router = Router() -router.use(bannerRouter); -router.use('/auth', authRouter); +router.use(bannerRouter) +router.use('/v1/auth', authRouter) diff --git a/src/routes/v1/auth.controller.ts b/src/routes/v1/auth.controller.ts new file mode 100644 index 0000000..fd40558 --- /dev/null +++ b/src/routes/v1/auth.controller.ts @@ -0,0 +1,40 @@ +import { Db } from 'mongodb' + +import { mainDb } from '../../utils/mongo' +import { cleanId } from '../../utils/common' +import { usersCollection } from '../../__data__/constants' + +export const registerUser = async ({ username, regtime, role, email, id, ...rest }) => { + const db: Db = await mainDb + + const usersCl = db.collection(usersCollection) + const [registred] = await usersCl.find({ ijlId: id }).toArray() + + const user = { + ...(registred || {}), + ijlId: id, + ijlUser: { + username, + regtime, + role, + email, + ...rest + }, + regtime: regtime || Date.now(), + } + + let insertedId + + if (!registred) { + const inserted = await usersCl.insertOne(user) + + insertedId = inserted.insertedId + } else { + await usersCl.updateOne({ _id: registred._id }, { $set: user }) + } + + return cleanId({ + id: insertedId, + ...user + }) +} diff --git a/src/routes/v1/auth.ts b/src/routes/v1/auth.ts new file mode 100644 index 0000000..7072eae --- /dev/null +++ b/src/routes/v1/auth.ts @@ -0,0 +1,47 @@ +import { Router } from 'express' +import jwt from 'jsonwebtoken' + +import { requiredFields } from '../../utils/required' +import { adminAxios } from '../../utils/admin-axios' +import { registerUser } from './auth.controller' +import { getAnswer } from '../../utils/common' + +export const authRouter = Router() + +authRouter.post('/sign-in', requiredFields(['password', 'login']), (req, res, next) => { + const { password, login } = req.body + + adminAxios({ + url: '/auth/sign-in', + data: { + password, + login + } + }) + .then(answer => { + registerUser(answer?.data?.user) + .then((user) => { + const { id, ijlId, ijlUser: { username, role } } = user + const token = jwt.sign({ id, username, role, ijlId }, process.env.JWT_SECRET_STRING, { + expiresIn: '2 days' + }) + + res.send(getAnswer(null, { token, user })) + }) + }) + .catch(error => { + console.log(error) + next(new Error(error)) + }) + + // const { hash: _hash, salt: _salt, ...cleanUser } = user + + // req.session.user = cleanUser + // const token = jwt.sign(cleanUser, process.env.JWT_SECRET_STRING, { + // expiresIn: '2 days' + // }); + + // return res.send({ token, user: cleanUser }) + + // res.status(400).send({ error: 'Login or password does\'t match' }) +}) diff --git a/src/routes/banner/data.json b/src/routes/v1/banner/data.json similarity index 100% rename from src/routes/banner/data.json rename to src/routes/v1/banner/data.json diff --git a/src/routes/v1/banner/index.ts b/src/routes/v1/banner/index.ts new file mode 100644 index 0000000..480ac89 --- /dev/null +++ b/src/routes/v1/banner/index.ts @@ -0,0 +1,11 @@ +import { Router } from 'express' + +import BannerData from './data.json' + +export const bannerRouter = Router() + +bannerRouter.get('/banner-data', (req, res) => { + setTimeout(() => { + res.send(BannerData) + }, 1500) +}) diff --git a/src/utils/admin-axios.ts b/src/utils/admin-axios.ts new file mode 100644 index 0000000..331311b --- /dev/null +++ b/src/utils/admin-axios.ts @@ -0,0 +1,11 @@ +import baseAxios from 'axios' + +export const adminAxios = baseAxios.create({ + baseURL: `http://${process.env.ADMIN_SERVER_BASE_NAME}/api/out`, + method: 'POST', +}) + +adminAxios.interceptors.request.use(requestConfig => { + requestConfig.data.appToken = process.env.RC_ADMIN_APP_TOKEN + return requestConfig +}) diff --git a/src/utils/common.ts b/src/utils/common.ts new file mode 100644 index 0000000..ecd7843 --- /dev/null +++ b/src/utils/common.ts @@ -0,0 +1,35 @@ +const getAnswer = (errors, data, success = true) => { + if (errors) { + return { success: false, errors } + } else { + return { success, body: data } + } +} + +function generateCode(length) { + return Array.from(Array(length)).map(_un => Math.floor(Math.random() * 9)).join('') +} + +function cleanId(entity) { + if (Array.isArray(entity)) { + return entity.map(cleanId) + } + + const { _id, ...other } = entity + + return { ...other, id: _id } +} + +function withoutPassword(user) { + const { password, ...cleanUser } = user + + return cleanUser +} + + +export { + generateCode, + getAnswer, + cleanId, + withoutPassword, +} \ No newline at end of file diff --git a/src/utils/error-handling.ts b/src/utils/error-handling.ts index 2d60ec1..e2dce23 100644 --- a/src/utils/error-handling.ts +++ b/src/utils/error-handling.ts @@ -1,5 +1,5 @@ export const errorHandle = (error, req, res, next) => { res.status(500).send({ error: error.message || 'some error' - }); -}; + }) +} diff --git a/src/utils/mongo.ts b/src/utils/mongo.ts new file mode 100644 index 0000000..9d68106 --- /dev/null +++ b/src/utils/mongo.ts @@ -0,0 +1,31 @@ +import { MongoClient as MDBClient } from 'mongodb' + +// Connection URL +const url = `mongodb://${process.env.MONGO_ADDR || 'localhost'}:27017` + +const dbInstanses = {} +const mongoDBConnect = async () => { + try { + const MongoClient = new MDBClient(url) + return await MongoClient.connect() + } catch (error) { + console.error(error) + } +} + +const getDB = async (dbName) => { + try { + const cl = await mongoDBConnect() + dbInstanses[dbName] = await cl.db(dbName) + return dbInstanses[dbName] + } catch (error) { + console.error(error) + } +} + +const mainDb = getDB('red-coder-bh') + +export { + getDB, + mainDb +} diff --git a/src/utils/required.ts b/src/utils/required.ts new file mode 100644 index 0000000..ad8f77f --- /dev/null +++ b/src/utils/required.ts @@ -0,0 +1,9 @@ +export const requiredFields = (fields: string[]) => (req, res, next) => { + for (const fieldName of fields) { + if (!req.body[fieldName]) { + throw new Error(`Field ${fieldName} does't set`) + } + } + + next() +} \ No newline at end of file