Авторизация через админку

This commit is contained in:
Primakov Alexandr Alexandrovich 2022-04-23 19:45:45 +03:00
parent e4d170e545
commit c457e1465b
20 changed files with 268 additions and 115 deletions

37
.eslintrc.json Normal file
View File

@ -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 }]
}
}

View File

@ -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. Установка зависимостей

4
index.d.ts vendored
View File

@ -1,4 +1,4 @@
declare module '*.json' {
const data: any;
export default data;
const data: any
export default data
}

View File

@ -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"

View File

@ -0,0 +1,2 @@
export const usersCollection = 'USERS_COLLECTIONS'

View File

@ -1,3 +1,3 @@
import { config } from 'dotenv';
import { config } from 'dotenv'
config();
config()

View File

@ -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}`)
})

7
src/model/roles.ts Normal file
View File

@ -0,0 +1,7 @@
export enum Roles {
Admin = 'Admin',
User = 'User'
}
export const allAccess = [Roles.Admin, Roles.User]
export const adminonly = Roles.Admin

View File

@ -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);
});
});

View File

@ -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)
})

View File

@ -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)

View File

@ -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
})
}

47
src/routes/v1/auth.ts Normal file
View File

@ -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' })
})

View File

@ -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)
})

11
src/utils/admin-axios.ts Normal file
View File

@ -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
})

35
src/utils/common.ts Normal file
View File

@ -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,
}

View File

@ -1,5 +1,5 @@
export const errorHandle = (error, req, res, next) => {
res.status(500).send({
error: error.message || 'some error'
});
};
})
}

31
src/utils/mongo.ts Normal file
View File

@ -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
}

9
src/utils/required.ts Normal file
View File

@ -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()
}