yaml-admin-api 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -0
- package/src/crud/api-generator.js +118 -0
- package/src/crud/login-api-generator.js +10 -0
- package/src/index.js +3 -0
- package/src/login/auth.js +86 -0
- package/src/member/member.js +32 -0
- package/src/yml-admin-api.js +57 -0
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yaml-admin-api",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "YAML Admin API package",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"bcryptjs": "^3.0.2",
|
|
13
|
+
"jsonwebtoken": "^9.0.2",
|
|
14
|
+
"mongodb": "^6.18.0",
|
|
15
|
+
"request": "^2.88.2",
|
|
16
|
+
"yaml": "^2.8.1"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const isAuthenticated = (jwt_secret) => {
|
|
2
|
+
return async (req, res, next) => {
|
|
3
|
+
const token = req.headers['x-access-token'] || req.query.token || req.cookies.token;
|
|
4
|
+
if (token == null)
|
|
5
|
+
res.json({ r: false, err: { code: 666 }, msg: '로그인 필요' });
|
|
6
|
+
else {
|
|
7
|
+
jwt.verify(token, jwt_secret, (err, decoded) => {
|
|
8
|
+
if (err) {
|
|
9
|
+
res.json({ r: false, err: { code: 666 }, msg: '로그인 필요' });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
req.user = decoded;
|
|
13
|
+
delete req.user.password;
|
|
14
|
+
next();
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const generateList = async ({app, db, name, entity, jwt_secret}) => {
|
|
21
|
+
//list
|
|
22
|
+
app.get(`/${name}`, isAuthenticated(jwt_secret), async function (req, res) {
|
|
23
|
+
var s = {};
|
|
24
|
+
var _sort = req.query._sort;
|
|
25
|
+
var _order = req.query._order;
|
|
26
|
+
if (_sort != null)
|
|
27
|
+
s[_sort] = (_order == 'ASC' ? 1 : -1);
|
|
28
|
+
|
|
29
|
+
var _end = req.query._end;
|
|
30
|
+
var _start = req.query._start;
|
|
31
|
+
var l = _end - _start;
|
|
32
|
+
|
|
33
|
+
//검색 파라미터
|
|
34
|
+
var f = {};
|
|
35
|
+
var id = req.query.id;
|
|
36
|
+
if (id) {
|
|
37
|
+
if (Array.isArray(id))
|
|
38
|
+
f['id'] = { $in: id.map(m => parseInt(m)) };
|
|
39
|
+
else
|
|
40
|
+
f['id'] = parseInt(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ${
|
|
44
|
+
// entity.property.filter(f=>f.search && f.type != 'divider').map(m=>{
|
|
45
|
+
// if(m.type == 'bool') {
|
|
46
|
+
// return `
|
|
47
|
+
// if (req.query.${m.name})
|
|
48
|
+
// f['${m.name}'] = req.query.${m.name} == 'true'
|
|
49
|
+
// `
|
|
50
|
+
// } else if(m.type == 'text') {
|
|
51
|
+
// return `
|
|
52
|
+
// if (req.query.${m.name})
|
|
53
|
+
// f['${m.name}'] = { $regex: ".*" + req.query.${m.name} + ".*" }
|
|
54
|
+
// `
|
|
55
|
+
// } else if(m.type == 'reference') {
|
|
56
|
+
// //reference의 키는 integer인지 string인지 모른다. 그리고 member_no같이 string으로 취급되어야할 integer도 있다
|
|
57
|
+
// return `
|
|
58
|
+
// if (req.query.${m.name})
|
|
59
|
+
// f['${m.name}'] = isNaN(Number(req.query.${m.name}))|| parseInt(req.query.${m.name}) > 1569340492095?req.query.${m.name}:parseInt(req.query.${m.name})
|
|
60
|
+
// `
|
|
61
|
+
// } else if(m.type == 'date') {
|
|
62
|
+
// return `
|
|
63
|
+
// if (req.query.date) {
|
|
64
|
+
// if(req.query.date.replace(/-/g, '').length == 8) {
|
|
65
|
+
// //해당 날짜의 시작Date와 끝Date로 검색
|
|
66
|
+
// f['date'] = {
|
|
67
|
+
// $gte: moment(req.query.date).startOf('day').toDate(),
|
|
68
|
+
// $lte: moment(req.query.date).endOf('day').toDate()
|
|
69
|
+
// }
|
|
70
|
+
// }
|
|
71
|
+
// }
|
|
72
|
+
// `
|
|
73
|
+
// } else {
|
|
74
|
+
// return `
|
|
75
|
+
// if (req.query.${m.name})
|
|
76
|
+
// f['${m.name}'] = req.query.${m.name}
|
|
77
|
+
// `
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// }).join('\n')
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
var name = req.query.name;
|
|
84
|
+
if (name == null && req.query.q)
|
|
85
|
+
name = req.query.q;
|
|
86
|
+
if (name != null)
|
|
87
|
+
f['name'] = { $regex: ".*" + name + ".*" };
|
|
88
|
+
f.remove = { $ne: true }
|
|
89
|
+
|
|
90
|
+
//Custom f list Start
|
|
91
|
+
|
|
92
|
+
//Custom f list End
|
|
93
|
+
|
|
94
|
+
var count = await db.collection('${name}').find(f).project({ _id: false }).sort(s).count();
|
|
95
|
+
let list = await db.collection('${name}').find(f).project({ _id: false }).sort(s).skip(parseInt(_start)).limit(l).toArray()
|
|
96
|
+
console.log('entity', entity)
|
|
97
|
+
list.map(m => {
|
|
98
|
+
m.id = entity.key
|
|
99
|
+
})
|
|
100
|
+
//Custom list Start
|
|
101
|
+
|
|
102
|
+
//Custom list End
|
|
103
|
+
await addInfo(db, list)
|
|
104
|
+
res.header('X-Total-Count', count);
|
|
105
|
+
res.json(list);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const generateEntityApi = async ({app, db, name, entity, jwt_secret}) => {
|
|
110
|
+
const { fields } = entity;
|
|
111
|
+
console.log('generateEntityApi', name)
|
|
112
|
+
|
|
113
|
+
await generateList({app, db, name, entity, jwt_secret})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
generateEntityApi
|
|
118
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const bcrypt = require('bcryptjs');
|
|
2
|
+
const jwt = require('jsonwebtoken');
|
|
3
|
+
|
|
4
|
+
const withConfig = (config) => {
|
|
5
|
+
const { db, jwt_secret } = config;
|
|
6
|
+
|
|
7
|
+
const comparePassword = function (plainPass, hashword, callback) {
|
|
8
|
+
bcrypt.compare(plainPass, hashword, function (err, isPasswordMatch) {
|
|
9
|
+
return err == null ?
|
|
10
|
+
callback(null, isPasswordMatch) :
|
|
11
|
+
callback(err);
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const authenticateSuccess = function (req, res, user, next) {
|
|
16
|
+
jwt.sign(
|
|
17
|
+
user,
|
|
18
|
+
jwt_secret,
|
|
19
|
+
{
|
|
20
|
+
expiresIn: '1000d',
|
|
21
|
+
subject: 'userInfo'
|
|
22
|
+
}, (err, token) => {
|
|
23
|
+
if (err) res.json({ r: false, msg: '알 수 없는 이유로 토큰 생성에 실패하였습니다.' });
|
|
24
|
+
|
|
25
|
+
req.token = token;
|
|
26
|
+
delete user.password;
|
|
27
|
+
req.user = user;
|
|
28
|
+
next();
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const isAuthenticated = function (req, res, next) {
|
|
34
|
+
const token = req.headers['x-access-token'] || req.query.token || req.cookies.token;
|
|
35
|
+
|
|
36
|
+
if (token == null)
|
|
37
|
+
res.json({ r: false, err: { code: 666 }, msg: '로그인 필요' });
|
|
38
|
+
else
|
|
39
|
+
jwt.verify(token, jwt_secret, (err, decoded) => {
|
|
40
|
+
if (err) {
|
|
41
|
+
res.json({ r: false, err: { code: 666 }, msg: '로그인 필요' });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
req.user = decoded;
|
|
45
|
+
next();
|
|
46
|
+
})
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const authenticate = async (req, res, next) => {
|
|
50
|
+
const email = req.query.email || req.body.email;
|
|
51
|
+
const password = req.query.pass || req.body.pass;
|
|
52
|
+
const type = req.query.type || req.body.type || "email";
|
|
53
|
+
if (email === 'admin' && password === '5756') {
|
|
54
|
+
authenticateSuccess(req, res,
|
|
55
|
+
{ id: '1111111', email: 'admin', name: 'admin', type: 'email' },
|
|
56
|
+
next);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
var memberProjection = { projection: { _id: false, name: true, email: true, password: true, super: true, id: true } };
|
|
60
|
+
if (type === 'email') {
|
|
61
|
+
memberProjection['password'] = true;
|
|
62
|
+
let member = await db.collection('admin').findOne({ email: email }, memberProjection)
|
|
63
|
+
if (member != null) {
|
|
64
|
+
comparePassword(password, member.password, async function (err, isPasswordMatch) {
|
|
65
|
+
if (isPasswordMatch) {
|
|
66
|
+
authenticateSuccess(req, res, member, next);
|
|
67
|
+
await db.collection('admin').updateOne({ email: email }, { $set: { login_date: new Date() } }, { upsert: false })
|
|
68
|
+
} else
|
|
69
|
+
res.json({ r: false, msg: '비밀번호가 일치하지 않습니다.' });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
res.json({ r: false, msg: '존재하지 않는 사용자입니다.' });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
isAuthenticated,
|
|
80
|
+
authenticate,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
withConfig,
|
|
86
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const {withConfig} = require('../login/auth.js');
|
|
2
|
+
|
|
3
|
+
module.exports = async function (app, db, yml, delegate) {
|
|
4
|
+
const auth = withConfig({ db, jwt_secret: yml.login["jwt-secret"] });
|
|
5
|
+
|
|
6
|
+
app.get('/member/login',
|
|
7
|
+
auth.authenticate,
|
|
8
|
+
function (req, res) {
|
|
9
|
+
res.json({ r: true, token: req.token, member: req.user });
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
app.get('/member/islogin',
|
|
14
|
+
auth.isAuthenticated,
|
|
15
|
+
async function (req, res) {
|
|
16
|
+
res.json({ r: true, member: req.user });
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
app.post('/member/login',
|
|
21
|
+
auth.authenticate,
|
|
22
|
+
function (req, res) {
|
|
23
|
+
res.json({ r: true, token: req.token, member: req.user });
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
app.get('/member/logout', async (req, res) => {
|
|
28
|
+
req.logout();
|
|
29
|
+
res.json({ r: true });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { MongoClient } = require('mongodb');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const yaml = require('yaml');
|
|
4
|
+
const { generateEntityApi } = require('./crud/api-generator');
|
|
5
|
+
const { generateLoginApi } = require('./crud/login-api-generator');
|
|
6
|
+
|
|
7
|
+
async function registerRoutes(app, options = {}) {
|
|
8
|
+
const { yamlPath, yamlString } = options;
|
|
9
|
+
let yml;
|
|
10
|
+
if(yamlPath) {
|
|
11
|
+
yml = await readYml(yamlPath);
|
|
12
|
+
} else if(yamlString) {
|
|
13
|
+
yml = yaml.parse(yamlString);
|
|
14
|
+
} else {
|
|
15
|
+
let yamlString = await fs.readFile('./admin.yml', 'utf8');
|
|
16
|
+
if(!yamlString) {
|
|
17
|
+
throw new Error('admin.yml is not found. yamlPath or yamlString is required.')
|
|
18
|
+
}
|
|
19
|
+
yamlString = yamlString.replace('${JWT_SECRET}', process.env.JWT_SECRET);
|
|
20
|
+
yamlString = yamlString.replace('${MONGODB_URL}', process.env.MONGODB_URL);
|
|
21
|
+
yml = yaml.parse(yamlString);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {database, entity} = yml;
|
|
25
|
+
let db = null;
|
|
26
|
+
if(database) {
|
|
27
|
+
const {mongodb} = database;
|
|
28
|
+
if(mongodb) {
|
|
29
|
+
const {uri} = mongodb;
|
|
30
|
+
db = await MongoClient.connect(uri, {}).then(client => client.db())
|
|
31
|
+
console.log('db connected')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const jwt_secret = yml.login["jwt-secret"]
|
|
35
|
+
app.set('jwt-secret', jwt_secret);
|
|
36
|
+
|
|
37
|
+
await generateLoginApi(app, db, yml)
|
|
38
|
+
entity && Object.keys(entity).forEach(async (entityName) => {
|
|
39
|
+
await generateEntityApi({
|
|
40
|
+
app, db,
|
|
41
|
+
name:entityName,
|
|
42
|
+
entity:entity[entityName],
|
|
43
|
+
jwt_secret
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function readYml(path) {
|
|
49
|
+
let yml = await fs.readFile(path, 'utf8');
|
|
50
|
+
yml = yml.replace('${JWT_SECRET}', process.env.JWT_SECRET);
|
|
51
|
+
yml = yml.replace('${MONGODB_URL}', process.env.MONGODB_URL);
|
|
52
|
+
|
|
53
|
+
return yaml.parse(yml);
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = registerRoutes;
|