expressa 1.2.2 → 1.2.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/db/postgres.js +100 -103
- package/middleware/permissions.js +53 -49
- package/modules/admin/.babelrc +12 -13
- package/modules/admin/src/utils/request.js +48 -47
- package/package.json +1 -1
package/db/postgres.js
CHANGED
|
@@ -1,103 +1,100 @@
|
|
|
1
|
-
const mongoToPostgres = require('mongo-query-to-postgres-jsonb')
|
|
2
|
-
const util = require('../util')
|
|
3
|
-
|
|
4
|
-
module.exports = function (settings, collectionId, collection) {
|
|
5
|
-
const pool = util.getPgPool(settings.postgresql_uri)
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
init: async function () {
|
|
9
|
-
if (collection.plainStringIds) {
|
|
10
|
-
await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id text primary key, data jsonb)')
|
|
11
|
-
} else {
|
|
12
|
-
await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id uuid primary key, data jsonb)')
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
all: async function () {
|
|
16
|
-
return this.find({})
|
|
17
|
-
},
|
|
18
|
-
find: async function (rawQuery, offset, limit, orderby, fields) {
|
|
19
|
-
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
20
|
-
const pgQuery = mongoToPostgres('data', rawQuery || {}, arrayFields)
|
|
21
|
-
const select = fields ? mongoToPostgres.convertSelect('data', fields, arrayFields) : '*'
|
|
22
|
-
let query = 'SELECT ' + select + ' FROM ' + collectionId + (pgQuery ? ' WHERE ' + pgQuery : '')
|
|
23
|
-
if (typeof orderby !== 'undefined') {
|
|
24
|
-
query += ' ORDER BY '
|
|
25
|
-
query += orderby.map((ordering) => {
|
|
26
|
-
return mongoToPostgres.convertDotNotation('data', ordering[0]) + (ordering[1] > 0 ? ' ASC' : ' DESC')
|
|
27
|
-
}).join(', ')
|
|
28
|
-
}
|
|
29
|
-
if (typeof offset !== 'undefined') {
|
|
30
|
-
query += ' OFFSET ' + offset
|
|
31
|
-
}
|
|
32
|
-
if (typeof limit !== 'undefined') {
|
|
33
|
-
query += ' LIMIT ' + limit
|
|
34
|
-
}
|
|
35
|
-
const result = await pool.query(query)
|
|
36
|
-
return result.rows.map((row) => row.data)
|
|
37
|
-
},
|
|
38
|
-
count: async function(rawQuery, offset, limit) {
|
|
39
|
-
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
40
|
-
const pgQuery = mongoToPostgres('data', rawQuery || {}, arrayFields)
|
|
41
|
-
let query = 'SELECT COUNT(*) FROM ' + collectionId + (pgQuery ? ' WHERE ' + pgQuery : '')
|
|
42
|
-
if (typeof offset !== 'undefined') {
|
|
43
|
-
query += ' OFFSET ' + offset
|
|
44
|
-
}
|
|
45
|
-
if (typeof limit !== 'undefined') {
|
|
46
|
-
query += ' LIMIT ' + limit
|
|
47
|
-
}
|
|
48
|
-
const result = await pool.query(query)
|
|
49
|
-
return parseInt(result.rows[0].count)
|
|
50
|
-
},
|
|
51
|
-
get: async function (id, fields) {
|
|
52
|
-
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
53
|
-
const select = fields ? mongoToPostgres.convertSelect('data', fields, arrayFields) : '*'
|
|
54
|
-
const result = await pool.query(`SELECT ${select} FROM ${collectionId} WHERE id = $1`, [id])
|
|
55
|
-
if (result.rowCount === 0) {
|
|
56
|
-
throw new util.ApiError(404, 'document not found')
|
|
57
|
-
}
|
|
58
|
-
return result.rows[0].data
|
|
59
|
-
},
|
|
60
|
-
create: async function (data) {
|
|
61
|
-
util.addIdIfMissing(data)
|
|
62
|
-
try {
|
|
63
|
-
await pool.query('INSERT INTO ' + collectionId + ' (id, data) VALUES ($1, $2)', [data._id, data])
|
|
64
|
-
} catch (e) {
|
|
65
|
-
if (e.message.includes('duplicate key')) {
|
|
66
|
-
throw new util.ApiError(409, 'document already exists')
|
|
67
|
-
}
|
|
68
|
-
throw e
|
|
69
|
-
}
|
|
70
|
-
return data._id
|
|
71
|
-
},
|
|
72
|
-
update: async function (id, data) {
|
|
73
|
-
if (typeof data._id === 'undefined') {
|
|
74
|
-
data._id = id
|
|
75
|
-
}
|
|
76
|
-
const result = await pool.query('UPDATE ' + collectionId + ' SET data=$2,id=$3 WHERE id=$1', [id, data, data._id])
|
|
77
|
-
if (result.rowCount === 0) {
|
|
78
|
-
throw new util.ApiError(404, 'document not found')
|
|
79
|
-
}
|
|
80
|
-
return data
|
|
81
|
-
},
|
|
82
|
-
// eslint-disable-next-line no-unused-vars
|
|
83
|
-
updateWithQuery: async function (query, update, options) {
|
|
84
|
-
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
85
|
-
const pgQuery = mongoToPostgres('data', query || {}, arrayFields)
|
|
86
|
-
const updateSql = mongoToPostgres.convertUpdate('data', update,false)
|
|
87
|
-
const result = await pool.query('UPDATE ' + collectionId + ' SET data=' + updateSql + ' WHERE ' + pgQuery)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
1
|
+
const mongoToPostgres = require('mongo-query-to-postgres-jsonb')
|
|
2
|
+
const util = require('../util')
|
|
3
|
+
|
|
4
|
+
module.exports = function (settings, collectionId, collection) {
|
|
5
|
+
const pool = util.getPgPool(settings.postgresql_uri)
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
init: async function () {
|
|
9
|
+
if (collection.plainStringIds) {
|
|
10
|
+
await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id text primary key, data jsonb)')
|
|
11
|
+
} else {
|
|
12
|
+
await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id uuid primary key, data jsonb)')
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
all: async function () {
|
|
16
|
+
return this.find({})
|
|
17
|
+
},
|
|
18
|
+
find: async function (rawQuery, offset, limit, orderby, fields) {
|
|
19
|
+
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
20
|
+
const pgQuery = mongoToPostgres('data', rawQuery || {}, arrayFields)
|
|
21
|
+
const select = fields ? mongoToPostgres.convertSelect('data', fields, arrayFields) : '*'
|
|
22
|
+
let query = 'SELECT ' + select + ' FROM ' + collectionId + (pgQuery ? ' WHERE ' + pgQuery : '')
|
|
23
|
+
if (typeof orderby !== 'undefined') {
|
|
24
|
+
query += ' ORDER BY '
|
|
25
|
+
query += orderby.map((ordering) => {
|
|
26
|
+
return mongoToPostgres.convertDotNotation('data', ordering[0]) + (ordering[1] > 0 ? ' ASC' : ' DESC')
|
|
27
|
+
}).join(', ')
|
|
28
|
+
}
|
|
29
|
+
if (typeof offset !== 'undefined') {
|
|
30
|
+
query += ' OFFSET ' + offset
|
|
31
|
+
}
|
|
32
|
+
if (typeof limit !== 'undefined') {
|
|
33
|
+
query += ' LIMIT ' + limit
|
|
34
|
+
}
|
|
35
|
+
const result = await pool.query(query)
|
|
36
|
+
return result.rows.map((row) => row.data)
|
|
37
|
+
},
|
|
38
|
+
count: async function(rawQuery, offset, limit) {
|
|
39
|
+
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
40
|
+
const pgQuery = mongoToPostgres('data', rawQuery || {}, arrayFields)
|
|
41
|
+
let query = 'SELECT COUNT(*) FROM ' + collectionId + (pgQuery ? ' WHERE ' + pgQuery : '')
|
|
42
|
+
if (typeof offset !== 'undefined') {
|
|
43
|
+
query += ' OFFSET ' + offset
|
|
44
|
+
}
|
|
45
|
+
if (typeof limit !== 'undefined') {
|
|
46
|
+
query += ' LIMIT ' + limit
|
|
47
|
+
}
|
|
48
|
+
const result = await pool.query(query)
|
|
49
|
+
return parseInt(result.rows[0].count)
|
|
50
|
+
},
|
|
51
|
+
get: async function (id, fields) {
|
|
52
|
+
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
53
|
+
const select = fields ? mongoToPostgres.convertSelect('data', fields, arrayFields) : '*'
|
|
54
|
+
const result = await pool.query(`SELECT ${select} FROM ${collectionId} WHERE id = $1`, [id])
|
|
55
|
+
if (result.rowCount === 0) {
|
|
56
|
+
throw new util.ApiError(404, 'document not found')
|
|
57
|
+
}
|
|
58
|
+
return result.rows[0].data
|
|
59
|
+
},
|
|
60
|
+
create: async function (data) {
|
|
61
|
+
util.addIdIfMissing(data)
|
|
62
|
+
try {
|
|
63
|
+
await pool.query('INSERT INTO ' + collectionId + ' (id, data) VALUES ($1, $2)', [data._id, data])
|
|
64
|
+
} catch (e) {
|
|
65
|
+
if (e.message.includes('duplicate key')) {
|
|
66
|
+
throw new util.ApiError(409, 'document already exists')
|
|
67
|
+
}
|
|
68
|
+
throw e
|
|
69
|
+
}
|
|
70
|
+
return data._id
|
|
71
|
+
},
|
|
72
|
+
update: async function (id, data) {
|
|
73
|
+
if (typeof data._id === 'undefined') {
|
|
74
|
+
data._id = id
|
|
75
|
+
}
|
|
76
|
+
const result = await pool.query('UPDATE ' + collectionId + ' SET data=$2,id=$3 WHERE id=$1', [id, data, data._id])
|
|
77
|
+
if (result.rowCount === 0) {
|
|
78
|
+
throw new util.ApiError(404, 'document not found')
|
|
79
|
+
}
|
|
80
|
+
return data
|
|
81
|
+
},
|
|
82
|
+
// eslint-disable-next-line no-unused-vars
|
|
83
|
+
updateWithQuery: async function (query, update, options) {
|
|
84
|
+
const arrayFields = util.getArrayPaths('', collection.schema)
|
|
85
|
+
const pgQuery = mongoToPostgres('data', query || {}, arrayFields)
|
|
86
|
+
const updateSql = mongoToPostgres.convertUpdate('data', update,false)
|
|
87
|
+
const result = await pool.query('UPDATE ' + collectionId + ' SET data=' + updateSql + ' WHERE ' + pgQuery)
|
|
88
|
+
return {
|
|
89
|
+
matchedCount: result.rowCount
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
delete: async function (id) {
|
|
93
|
+
const result = await pool.query('DELETE FROM ' + collectionId + ' WHERE id=$1', [id])
|
|
94
|
+
if (result.rowCount === 0) {
|
|
95
|
+
throw new util.ApiError(404, 'document not found')
|
|
96
|
+
}
|
|
97
|
+
return 'OK'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -1,49 +1,53 @@
|
|
|
1
|
-
const util = require('../util')
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
roleDocs.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
1
|
+
const util = require('../util')
|
|
2
|
+
let authenticatedRoleExists
|
|
3
|
+
|
|
4
|
+
async function addRolePermissions (req, roles) {
|
|
5
|
+
req.permissions = req.permissions || {}
|
|
6
|
+
const roleDocs = await Promise.all(roles.map((name) => req.db.role.get(name)))
|
|
7
|
+
roleDocs.forEach((roleDoc) => {
|
|
8
|
+
for (const permission in roleDoc.permissions) {
|
|
9
|
+
if (roleDoc.permissions[permission]) {
|
|
10
|
+
req.permissions[permission] = true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function doesAuthenticatedRoleExist(req) {
|
|
17
|
+
if (typeof authenticatedRoleExists === 'undefined') {
|
|
18
|
+
try {
|
|
19
|
+
await req.db.role.get('Authenticated')
|
|
20
|
+
authenticatedRoleExists = true
|
|
21
|
+
} catch (e) {
|
|
22
|
+
authenticatedRoleExists = false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return authenticatedRoleExists
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports.addRolePermissionsAsync = async function addRolePermissionsMiddlewareAsync(req) {
|
|
29
|
+
if (!req.settings || !req.settings.enforce_permissions) {
|
|
30
|
+
// Use a dummy permission getter
|
|
31
|
+
req.hasPermission = () => true
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
req.hasPermission = (permission) => req.permissions && req.permissions[permission]
|
|
35
|
+
let roles = ['Anonymous']
|
|
36
|
+
const isAuthenticatedRole = await doesAuthenticatedRoleExist(req)
|
|
37
|
+
if (req.uid) {
|
|
38
|
+
try {
|
|
39
|
+
const user = await req.db[req.ucollection].get(req.uid)
|
|
40
|
+
req.user = user
|
|
41
|
+
roles = (user.roles || []).concat(isAuthenticatedRole ? ['Authenticated'] : [])
|
|
42
|
+
} catch (e) {
|
|
43
|
+
throw new util.ApiError(404, 'User no longer exists')
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
req.user = {}
|
|
47
|
+
}
|
|
48
|
+
await addRolePermissions(req, roles)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports.middleware = function addRolePermissionsMiddleware (req, res, next) {
|
|
52
|
+
module.exports.addRolePermissionsAsync(req).then(next).catch(next)
|
|
53
|
+
}
|
package/modules/admin/.babelrc
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
{
|
|
2
|
-
"sourceType": "module",
|
|
3
|
-
"presets": [
|
|
4
|
-
["env", {
|
|
5
|
-
"targets": {
|
|
6
|
-
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"sourceType": "module",
|
|
3
|
+
"presets": [
|
|
4
|
+
["env", {
|
|
5
|
+
"targets": {
|
|
6
|
+
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
|
7
|
+
}
|
|
8
|
+
}],
|
|
9
|
+
"stage-2"
|
|
10
|
+
],
|
|
11
|
+
"plugins":["transform-vue-jsx", "transform-runtime"]
|
|
12
|
+
}
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
import { Message } from 'element-ui'
|
|
3
|
-
import store from '../store'
|
|
4
|
-
import { getToken } from '@/utils/auth'
|
|
5
|
-
|
|
6
|
-
const get = (o, p) =>
|
|
7
|
-
p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o)
|
|
8
|
-
|
|
9
|
-
const service = axios.create({
|
|
10
|
-
// eslint-disable-next-line
|
|
11
|
-
baseURL: (typeof settings !== 'undefined' ? settings.apiurl :
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { Message } from 'element-ui'
|
|
3
|
+
import store from '../store'
|
|
4
|
+
import { getToken } from '@/utils/auth'
|
|
5
|
+
|
|
6
|
+
const get = (o, p) =>
|
|
7
|
+
p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o)
|
|
8
|
+
|
|
9
|
+
const service = axios.create({
|
|
10
|
+
// eslint-disable-next-line
|
|
11
|
+
baseURL: (typeof settings !== 'undefined' ? settings.apiurl : process.env.BASE_API),
|
|
12
|
+
timeout: 5000 // 请求超时时间
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// request拦截器
|
|
16
|
+
service.interceptors.request.use(
|
|
17
|
+
config => {
|
|
18
|
+
if (store.getters.token) {
|
|
19
|
+
config.headers['x-access-token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
|
20
|
+
}
|
|
21
|
+
return config
|
|
22
|
+
},
|
|
23
|
+
error => {
|
|
24
|
+
// Do something with request error
|
|
25
|
+
console.log(error) // for debug
|
|
26
|
+
Promise.reject(error)
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// response 拦截器
|
|
31
|
+
service.interceptors.response.use(
|
|
32
|
+
response => {
|
|
33
|
+
return response
|
|
34
|
+
},
|
|
35
|
+
error => {
|
|
36
|
+
console.log(error) // for debug
|
|
37
|
+
const errorMessage = get(error, ['response', 'data', 'error']) || error.message
|
|
38
|
+
Message({
|
|
39
|
+
message: errorMessage,
|
|
40
|
+
type: 'error',
|
|
41
|
+
duration: 0,
|
|
42
|
+
showClose: true,
|
|
43
|
+
})
|
|
44
|
+
return Promise.reject(error)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
export default service
|