Авторизация через админку
This commit is contained in:
parent
e4d170e545
commit
c457e1465b
37
.eslintrc.json
Normal file
37
.eslintrc.json
Normal 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 }]
|
||||||
|
}
|
||||||
|
}
|
10
README.md
10
README.md
@ -9,11 +9,15 @@
|
|||||||
|
|
||||||
Пример:
|
Пример:
|
||||||
|
|
||||||
```.env
|
```
|
||||||
RED_CODER_BH_PORT=8045
|
RED_CODER_BH_PORT=8045
|
||||||
SESSION_SECRET="asdas a dasd sad as dasd as"
|
SESSION_SECRET="session secret string"
|
||||||
COOKIE_SESSION="sdfsfdjkbhjvav dbw fsfnnsdonan dlansln dlasnldnsa da"
|
COOKIE_SESSION="some secret string"
|
||||||
JWT_SECRET_STRING="wow wow it is a secret shhhhhhh....."
|
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. Установка зависимостей
|
### 2. Установка зависимостей
|
||||||
|
4
index.d.ts
vendored
4
index.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
declare module '*.json' {
|
declare module '*.json' {
|
||||||
const data: any;
|
const data: any
|
||||||
export default data;
|
export default data
|
||||||
}
|
}
|
||||||
|
@ -28,18 +28,24 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cookie-session": "^2.0.44",
|
"@types/cookie-session": "^2.0.44",
|
||||||
|
"axios": "^0.26.1",
|
||||||
"cookie-session": "^2.0.0",
|
"cookie-session": "^2.0.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-jwt": "^6.1.1",
|
"express-jwt": "^6.1.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"jwt-middleware": "^0.3.0",
|
"jwt-middleware": "^0.3.0",
|
||||||
|
"mongodb": "^4.5.0",
|
||||||
"pbkdf2-password": "^1.2.1",
|
"pbkdf2-password": "^1.2.1",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^17.0.24",
|
"@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",
|
"nodemon": "^2.0.15",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.7.0",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.3"
|
||||||
|
2
src/__data__/constants.ts
Normal file
2
src/__data__/constants.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
export const usersCollection = 'USERS_COLLECTIONS'
|
@ -1,3 +1,3 @@
|
|||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv'
|
||||||
|
|
||||||
config();
|
config()
|
||||||
|
26
src/main.ts
26
src/main.ts
@ -1,20 +1,20 @@
|
|||||||
import express from 'express';
|
import express from 'express'
|
||||||
import cookieSession from 'cookie-session';
|
import cookieSession from 'cookie-session'
|
||||||
|
|
||||||
import './config';
|
import './config'
|
||||||
import { errorHandle } from './utils/error-handling';
|
import { errorHandle } from './utils/error-handling'
|
||||||
import { router } from './routes';
|
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(express.json())
|
||||||
app.use(cookieSession({ secret: process.env.COOKIE_SESSION }));
|
app.use(cookieSession({ secret: process.env.COOKIE_SESSION }))
|
||||||
|
|
||||||
app.use(router);
|
app.use(router)
|
||||||
|
|
||||||
app.use(errorHandle);
|
app.use(errorHandle)
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`listen on http://localhost:${port}`);
|
console.log(`listen on http://localhost:${port}`)
|
||||||
});
|
})
|
||||||
|
7
src/model/roles.ts
Normal file
7
src/model/roles.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum Roles {
|
||||||
|
Admin = 'Admin',
|
||||||
|
User = 'User'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const allAccess = [Roles.Admin, Roles.User]
|
||||||
|
export const adminonly = Roles.Admin
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
@ -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)
|
|
||||||
})
|
|
@ -1,9 +1,9 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express'
|
||||||
|
|
||||||
import { authRouter } from './auth';
|
import { authRouter } from './v1/auth'
|
||||||
import { bannerRouter } from './banner';
|
import { bannerRouter } from './v1/banner'
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router()
|
||||||
|
|
||||||
router.use(bannerRouter);
|
router.use(bannerRouter)
|
||||||
router.use('/auth', authRouter);
|
router.use('/v1/auth', authRouter)
|
||||||
|
40
src/routes/v1/auth.controller.ts
Normal file
40
src/routes/v1/auth.controller.ts
Normal 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
47
src/routes/v1/auth.ts
Normal 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' })
|
||||||
|
})
|
11
src/routes/v1/banner/index.ts
Normal file
11
src/routes/v1/banner/index.ts
Normal 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
11
src/utils/admin-axios.ts
Normal 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
35
src/utils/common.ts
Normal 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,
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
export const errorHandle = (error, req, res, next) => {
|
export const errorHandle = (error, req, res, next) => {
|
||||||
res.status(500).send({
|
res.status(500).send({
|
||||||
error: error.message || 'some error'
|
error: error.message || 'some error'
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
31
src/utils/mongo.ts
Normal file
31
src/utils/mongo.ts
Normal 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
9
src/utils/required.ts
Normal 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()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user