befly 0.1.26 → 0.2.0
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/apis/health/info.js +3 -3
- package/libs/xml/strnum.js +4 -2
- package/libs/xml/util.js +34 -34
- package/libs/xml/validator.js +316 -341
- package/package.json +2 -2
- package/plugins/cors.js +1 -1
- package/plugins/db.js +2 -2
- package/plugins/logger.js +1 -1
- package/plugins/redis.js +1 -1
- package/utils/api.js +4 -26
- package/utils/curd.js +4 -1
package/apis/health/info.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Env } from '../../config/env.js';
|
|
|
2
2
|
import { Api } from '../../utils/api.js';
|
|
3
3
|
import { RYes, RNo } from '../../utils/util.js';
|
|
4
4
|
|
|
5
|
-
export default Api.POST('健康检查', { auth: false }, {}, [], async (
|
|
5
|
+
export default Api.POST('健康检查', { auth: false }, {}, [], async (befly, ctx) => {
|
|
6
6
|
const info = {
|
|
7
7
|
status: 'ok',
|
|
8
8
|
timestamp: new Date().toISOString(),
|
|
@@ -15,9 +15,9 @@ export default Api.POST('健康检查', { auth: false }, {}, [], async (bunpii,
|
|
|
15
15
|
};
|
|
16
16
|
// 检查 Redis 连接状态
|
|
17
17
|
if (Env.REDIS_ENABLE === 1) {
|
|
18
|
-
if (
|
|
18
|
+
if (befly.redis) {
|
|
19
19
|
try {
|
|
20
|
-
await
|
|
20
|
+
await befly.redis.ping();
|
|
21
21
|
info.redis = '已连接';
|
|
22
22
|
} catch (error) {
|
|
23
23
|
info.redis = '未连接';
|
package/libs/xml/strnum.js
CHANGED
|
@@ -60,8 +60,10 @@ export default function toNumber(str, options = {}) {
|
|
|
60
60
|
else return str;
|
|
61
61
|
} else if (trimmedStr.indexOf('.') !== -1) {
|
|
62
62
|
//floating number
|
|
63
|
-
if (parsedStr === '0')
|
|
64
|
-
|
|
63
|
+
if (parsedStr === '0')
|
|
64
|
+
return num; //0.0
|
|
65
|
+
else if (parsedStr === numTrimmedByZeros)
|
|
66
|
+
return num; //0.456. 0.79000
|
|
65
67
|
else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;
|
|
66
68
|
else return str;
|
|
67
69
|
}
|
package/libs/xml/util.js
CHANGED
|
@@ -6,32 +6,32 @@ export const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';
|
|
|
6
6
|
const regexName = new RegExp('^' + nameRegexp + '$');
|
|
7
7
|
|
|
8
8
|
export function getAllMatches(string, regex) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const matches = [];
|
|
10
|
+
let match = regex.exec(string);
|
|
11
|
+
while (match) {
|
|
12
|
+
const allmatches = [];
|
|
13
|
+
allmatches.startIndex = regex.lastIndex - match[0].length;
|
|
14
|
+
const len = match.length;
|
|
15
|
+
for (let index = 0; index < len; index++) {
|
|
16
|
+
allmatches.push(match[index]);
|
|
17
|
+
}
|
|
18
|
+
matches.push(allmatches);
|
|
19
|
+
match = regex.exec(string);
|
|
17
20
|
}
|
|
18
|
-
matches
|
|
19
|
-
match = regex.exec(string);
|
|
20
|
-
}
|
|
21
|
-
return matches;
|
|
21
|
+
return matches;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export const isName = function(string) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
24
|
+
export const isName = function (string) {
|
|
25
|
+
const match = regexName.exec(string);
|
|
26
|
+
return !(match === null || typeof match === 'undefined');
|
|
27
|
+
};
|
|
28
28
|
|
|
29
29
|
export function isExist(v) {
|
|
30
|
-
|
|
30
|
+
return typeof v !== 'undefined';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function isEmptyObject(obj) {
|
|
34
|
-
|
|
34
|
+
return Object.keys(obj).length === 0;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -40,29 +40,29 @@ export function isEmptyObject(obj) {
|
|
|
40
40
|
* @param {*} a
|
|
41
41
|
*/
|
|
42
42
|
export function merge(target, a, arrayMode) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
if (a) {
|
|
44
|
+
const keys = Object.keys(a); // will return an array of own properties
|
|
45
|
+
const len = keys.length; //don't make it inline
|
|
46
|
+
for (let i = 0; i < len; i++) {
|
|
47
|
+
if (arrayMode === 'strict') {
|
|
48
|
+
target[keys[i]] = [a[keys[i]]];
|
|
49
|
+
} else {
|
|
50
|
+
target[keys[i]] = a[keys[i]];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
52
53
|
}
|
|
53
|
-
}
|
|
54
54
|
}
|
|
55
55
|
/* exports.merge =function (b,a){
|
|
56
56
|
return Object.assign(b,a);
|
|
57
57
|
} */
|
|
58
58
|
|
|
59
59
|
export function getValue(v) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
if (exports.isExist(v)) {
|
|
61
|
+
return v;
|
|
62
|
+
} else {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// const fakeCall = function(a) {return a;};
|
|
68
|
-
// const fakeCallNoReturn = function() {};
|
|
68
|
+
// const fakeCallNoReturn = function() {};
|
package/libs/xml/validator.js
CHANGED
|
@@ -1,197 +1,195 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import {getAllMatches, isName} from './util.js';
|
|
3
|
+
import { getAllMatches, isName } from './util.js';
|
|
4
4
|
|
|
5
5
|
const defaultOptions = {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
allowBooleanAttributes: false, //A tag can have attributes without any value
|
|
7
|
+
unpairedTags: []
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
|
11
11
|
export function validate(xmlData, options) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
|
15
|
-
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
|
16
|
-
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
|
|
17
|
-
const tags = [];
|
|
18
|
-
let tagFound = false;
|
|
19
|
-
|
|
20
|
-
//indicates that the root tag has been closed (aka. depth 0 has been reached)
|
|
21
|
-
let reachedRoot = false;
|
|
22
|
-
|
|
23
|
-
if (xmlData[0] === '\ufeff') {
|
|
24
|
-
// check for byte order mark (BOM)
|
|
25
|
-
xmlData = xmlData.substr(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
for (let i = 0; i < xmlData.length; i++) {
|
|
29
|
-
|
|
30
|
-
if (xmlData[i] === '<' && xmlData[i+1] === '?') {
|
|
31
|
-
i+=2;
|
|
32
|
-
i = readPI(xmlData,i);
|
|
33
|
-
if (i.err) return i;
|
|
34
|
-
}else if (xmlData[i] === '<') {
|
|
35
|
-
//starting of tag
|
|
36
|
-
//read until you reach to '>' avoiding any '>' in attribute value
|
|
37
|
-
let tagStartPos = i;
|
|
38
|
-
i++;
|
|
39
|
-
|
|
40
|
-
if (xmlData[i] === '!') {
|
|
41
|
-
i = readCommentAndCDATA(xmlData, i);
|
|
42
|
-
continue;
|
|
43
|
-
} else {
|
|
44
|
-
let closingTag = false;
|
|
45
|
-
if (xmlData[i] === '/') {
|
|
46
|
-
//closing tag
|
|
47
|
-
closingTag = true;
|
|
48
|
-
i++;
|
|
49
|
-
}
|
|
50
|
-
//read tagname
|
|
51
|
-
let tagName = '';
|
|
52
|
-
for (; i < xmlData.length &&
|
|
53
|
-
xmlData[i] !== '>' &&
|
|
54
|
-
xmlData[i] !== ' ' &&
|
|
55
|
-
xmlData[i] !== '\t' &&
|
|
56
|
-
xmlData[i] !== '\n' &&
|
|
57
|
-
xmlData[i] !== '\r'; i++
|
|
58
|
-
) {
|
|
59
|
-
tagName += xmlData[i];
|
|
60
|
-
}
|
|
61
|
-
tagName = tagName.trim();
|
|
62
|
-
//console.log(tagName);
|
|
63
|
-
|
|
64
|
-
if (tagName[tagName.length - 1] === '/') {
|
|
65
|
-
//self closing tag without attributes
|
|
66
|
-
tagName = tagName.substring(0, tagName.length - 1);
|
|
67
|
-
//continue;
|
|
68
|
-
i--;
|
|
69
|
-
}
|
|
70
|
-
if (!validateTagName(tagName)) {
|
|
71
|
-
let msg;
|
|
72
|
-
if (tagName.trim().length === 0) {
|
|
73
|
-
msg = "Invalid space after '<'.";
|
|
74
|
-
} else {
|
|
75
|
-
msg = "Tag '"+tagName+"' is an invalid name.";
|
|
76
|
-
}
|
|
77
|
-
return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
|
|
78
|
-
}
|
|
12
|
+
options = Object.assign({}, defaultOptions, options);
|
|
79
13
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
i = result.index;
|
|
86
|
-
|
|
87
|
-
if (attrStr[attrStr.length - 1] === '/') {
|
|
88
|
-
//self closing tag
|
|
89
|
-
const attrStrStart = i - attrStr.length;
|
|
90
|
-
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
91
|
-
const isValid = validateAttributeString(attrStr, options);
|
|
92
|
-
if (isValid === true) {
|
|
93
|
-
tagFound = true;
|
|
94
|
-
//continue; //text may presents after self closing tag
|
|
95
|
-
} else {
|
|
96
|
-
//the result from the nested function returns the position of the error within the attribute
|
|
97
|
-
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
98
|
-
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
99
|
-
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
|
|
100
|
-
}
|
|
101
|
-
} else if (closingTag) {
|
|
102
|
-
if (!result.tagClosed) {
|
|
103
|
-
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
|
104
|
-
} else if (attrStr.trim().length > 0) {
|
|
105
|
-
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
106
|
-
} else if (tags.length === 0) {
|
|
107
|
-
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
108
|
-
} else {
|
|
109
|
-
const otg = tags.pop();
|
|
110
|
-
if (tagName !== otg.tagName) {
|
|
111
|
-
let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
|
|
112
|
-
return getErrorObject('InvalidTag',
|
|
113
|
-
"Expected closing tag '"+otg.tagName+"' (opened in line "+openPos.line+", col "+openPos.col+") instead of closing tag '"+tagName+"'.",
|
|
114
|
-
getLineNumberForPosition(xmlData, tagStartPos));
|
|
115
|
-
}
|
|
14
|
+
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
|
15
|
+
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
|
16
|
+
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
|
|
17
|
+
const tags = [];
|
|
18
|
+
let tagFound = false;
|
|
116
19
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (isValid !== true) {
|
|
125
|
-
//the result from the nested function returns the position of the error within the attribute
|
|
126
|
-
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
127
|
-
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
128
|
-
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
//if the root level has been reached before ...
|
|
132
|
-
if (reachedRoot === true) {
|
|
133
|
-
return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
|
|
134
|
-
} else if(options.unpairedTags.indexOf(tagName) !== -1){
|
|
135
|
-
//don't push into stack
|
|
136
|
-
} else {
|
|
137
|
-
tags.push({tagName, tagStartPos});
|
|
138
|
-
}
|
|
139
|
-
tagFound = true;
|
|
140
|
-
}
|
|
20
|
+
//indicates that the root tag has been closed (aka. depth 0 has been reached)
|
|
21
|
+
let reachedRoot = false;
|
|
22
|
+
|
|
23
|
+
if (xmlData[0] === '\ufeff') {
|
|
24
|
+
// check for byte order mark (BOM)
|
|
25
|
+
xmlData = xmlData.substr(1);
|
|
26
|
+
}
|
|
141
27
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
28
|
+
for (let i = 0; i < xmlData.length; i++) {
|
|
29
|
+
if (xmlData[i] === '<' && xmlData[i + 1] === '?') {
|
|
30
|
+
i += 2;
|
|
31
|
+
i = readPI(xmlData, i);
|
|
32
|
+
if (i.err) return i;
|
|
33
|
+
} else if (xmlData[i] === '<') {
|
|
34
|
+
//starting of tag
|
|
35
|
+
//read until you reach to '>' avoiding any '>' in attribute value
|
|
36
|
+
let tagStartPos = i;
|
|
37
|
+
i++;
|
|
38
|
+
|
|
39
|
+
if (xmlData[i] === '!') {
|
|
40
|
+
i = readCommentAndCDATA(xmlData, i);
|
|
41
|
+
continue;
|
|
42
|
+
} else {
|
|
43
|
+
let closingTag = false;
|
|
44
|
+
if (xmlData[i] === '/') {
|
|
45
|
+
//closing tag
|
|
46
|
+
closingTag = true;
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
//read tagname
|
|
50
|
+
let tagName = '';
|
|
51
|
+
for (; i < xmlData.length && xmlData[i] !== '>' && xmlData[i] !== ' ' && xmlData[i] !== '\t' && xmlData[i] !== '\n' && xmlData[i] !== '\r'; i++) {
|
|
52
|
+
tagName += xmlData[i];
|
|
53
|
+
}
|
|
54
|
+
tagName = tagName.trim();
|
|
55
|
+
//console.log(tagName);
|
|
56
|
+
|
|
57
|
+
if (tagName[tagName.length - 1] === '/') {
|
|
58
|
+
//self closing tag without attributes
|
|
59
|
+
tagName = tagName.substring(0, tagName.length - 1);
|
|
60
|
+
//continue;
|
|
61
|
+
i--;
|
|
62
|
+
}
|
|
63
|
+
if (!validateTagName(tagName)) {
|
|
64
|
+
let msg;
|
|
65
|
+
if (tagName.trim().length === 0) {
|
|
66
|
+
msg = "Invalid space after '<'.";
|
|
67
|
+
} else {
|
|
68
|
+
msg = "Tag '" + tagName + "' is an invalid name.";
|
|
69
|
+
}
|
|
70
|
+
return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = readAttributeStr(xmlData, i);
|
|
74
|
+
if (result === false) {
|
|
75
|
+
return getErrorObject('InvalidAttr', "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i));
|
|
76
|
+
}
|
|
77
|
+
let attrStr = result.value;
|
|
78
|
+
i = result.index;
|
|
79
|
+
|
|
80
|
+
if (attrStr[attrStr.length - 1] === '/') {
|
|
81
|
+
//self closing tag
|
|
82
|
+
const attrStrStart = i - attrStr.length;
|
|
83
|
+
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
84
|
+
const isValid = validateAttributeString(attrStr, options);
|
|
85
|
+
if (isValid === true) {
|
|
86
|
+
tagFound = true;
|
|
87
|
+
//continue; //text may presents after self closing tag
|
|
88
|
+
} else {
|
|
89
|
+
//the result from the nested function returns the position of the error within the attribute
|
|
90
|
+
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
91
|
+
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
92
|
+
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
|
|
93
|
+
}
|
|
94
|
+
} else if (closingTag) {
|
|
95
|
+
if (!result.tagClosed) {
|
|
96
|
+
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
|
97
|
+
} else if (attrStr.trim().length > 0) {
|
|
98
|
+
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
99
|
+
} else if (tags.length === 0) {
|
|
100
|
+
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
101
|
+
} else {
|
|
102
|
+
const otg = tags.pop();
|
|
103
|
+
if (tagName !== otg.tagName) {
|
|
104
|
+
let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
|
|
105
|
+
return getErrorObject('InvalidTag', "Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ', col ' + openPos.col + ") instead of closing tag '" + tagName + "'.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//when there are no more tags, we reached the root level.
|
|
109
|
+
if (tags.length == 0) {
|
|
110
|
+
reachedRoot = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
const isValid = validateAttributeString(attrStr, options);
|
|
115
|
+
if (isValid !== true) {
|
|
116
|
+
//the result from the nested function returns the position of the error within the attribute
|
|
117
|
+
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
118
|
+
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
119
|
+
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//if the root level has been reached before ...
|
|
123
|
+
if (reachedRoot === true) {
|
|
124
|
+
return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
|
|
125
|
+
} else if (options.unpairedTags.indexOf(tagName) !== -1) {
|
|
126
|
+
//don't push into stack
|
|
127
|
+
} else {
|
|
128
|
+
tags.push({ tagName, tagStartPos });
|
|
129
|
+
}
|
|
130
|
+
tagFound = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//skip tag text value
|
|
134
|
+
//It may include comments and CDATA value
|
|
135
|
+
for (i++; i < xmlData.length; i++) {
|
|
136
|
+
if (xmlData[i] === '<') {
|
|
137
|
+
if (xmlData[i + 1] === '!') {
|
|
138
|
+
//comment or CADATA
|
|
139
|
+
i++;
|
|
140
|
+
i = readCommentAndCDATA(xmlData, i);
|
|
141
|
+
continue;
|
|
142
|
+
} else if (xmlData[i + 1] === '?') {
|
|
143
|
+
i = readPI(xmlData, ++i);
|
|
144
|
+
if (i.err) return i;
|
|
145
|
+
} else {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
} else if (xmlData[i] === '&') {
|
|
149
|
+
const afterAmp = validateAmpersand(xmlData, i);
|
|
150
|
+
if (afterAmp == -1) return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
151
|
+
i = afterAmp;
|
|
152
|
+
} else {
|
|
153
|
+
if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
|
|
154
|
+
return getErrorObject('InvalidXml', 'Extra text at the end', getLineNumberForPosition(xmlData, i));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} //end of reading tag text value
|
|
158
|
+
if (xmlData[i] === '<') {
|
|
159
|
+
i--;
|
|
160
|
+
}
|
|
156
161
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
161
|
-
i = afterAmp;
|
|
162
|
-
}else{
|
|
163
|
-
if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
|
|
164
|
-
return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
|
|
162
|
+
} else {
|
|
163
|
+
if (isWhiteSpace(xmlData[i])) {
|
|
164
|
+
continue;
|
|
165
165
|
}
|
|
166
|
-
|
|
167
|
-
} //end of reading tag text value
|
|
168
|
-
if (xmlData[i] === '<') {
|
|
169
|
-
i--;
|
|
166
|
+
return getErrorObject('InvalidChar', "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
170
167
|
}
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
if ( isWhiteSpace(xmlData[i])) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
177
168
|
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (!tagFound) {
|
|
181
|
-
return getErrorObject('InvalidXml', 'Start tag expected.', 1);
|
|
182
|
-
}else if (tags.length == 1) {
|
|
183
|
-
return getErrorObject('InvalidTag', "Unclosed tag '"+tags[0].tagName+"'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
|
|
184
|
-
}else if (tags.length > 0) {
|
|
185
|
-
return getErrorObject('InvalidXml', "Invalid '"+
|
|
186
|
-
JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\r?\n/g, '')+
|
|
187
|
-
"' found.", {line: 1, col: 1});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return true;
|
|
191
|
-
};
|
|
192
169
|
|
|
193
|
-
|
|
194
|
-
|
|
170
|
+
if (!tagFound) {
|
|
171
|
+
return getErrorObject('InvalidXml', 'Start tag expected.', 1);
|
|
172
|
+
} else if (tags.length == 1) {
|
|
173
|
+
return getErrorObject('InvalidTag', "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
|
|
174
|
+
} else if (tags.length > 0) {
|
|
175
|
+
return getErrorObject(
|
|
176
|
+
'InvalidXml',
|
|
177
|
+
"Invalid '" +
|
|
178
|
+
JSON.stringify(
|
|
179
|
+
tags.map((t) => t.tagName),
|
|
180
|
+
null,
|
|
181
|
+
4
|
|
182
|
+
).replace(/\r?\n/g, '') +
|
|
183
|
+
"' found.",
|
|
184
|
+
{ line: 1, col: 1 }
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isWhiteSpace(char) {
|
|
192
|
+
return char === ' ' || char === '\t' || char === '\n' || char === '\r';
|
|
195
193
|
}
|
|
196
194
|
/**
|
|
197
195
|
* Read Processing insstructions and skip
|
|
@@ -199,74 +197,56 @@ function isWhiteSpace(char){
|
|
|
199
197
|
* @param {*} i
|
|
200
198
|
*/
|
|
201
199
|
function readPI(xmlData, i) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
200
|
+
const start = i;
|
|
201
|
+
for (; i < xmlData.length; i++) {
|
|
202
|
+
if (xmlData[i] == '?' || xmlData[i] == ' ') {
|
|
203
|
+
//tagname
|
|
204
|
+
const tagname = xmlData.substr(start, i - start);
|
|
205
|
+
if (i > 5 && tagname === 'xml') {
|
|
206
|
+
return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
|
|
207
|
+
} else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
|
|
208
|
+
//check if valid attribut string
|
|
209
|
+
i++;
|
|
210
|
+
break;
|
|
211
|
+
} else {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
216
215
|
}
|
|
217
|
-
|
|
218
|
-
return i;
|
|
216
|
+
return i;
|
|
219
217
|
}
|
|
220
218
|
|
|
221
219
|
function readCommentAndCDATA(xmlData, i) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
220
|
+
if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
|
|
221
|
+
//comment
|
|
222
|
+
for (i += 3; i < xmlData.length; i++) {
|
|
223
|
+
if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
|
|
224
|
+
i += 2;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else if (xmlData.length > i + 8 && xmlData[i + 1] === 'D' && xmlData[i + 2] === 'O' && xmlData[i + 3] === 'C' && xmlData[i + 4] === 'T' && xmlData[i + 5] === 'Y' && xmlData[i + 6] === 'P' && xmlData[i + 7] === 'E') {
|
|
229
|
+
let angleBracketsCount = 1;
|
|
230
|
+
for (i += 8; i < xmlData.length; i++) {
|
|
231
|
+
if (xmlData[i] === '<') {
|
|
232
|
+
angleBracketsCount++;
|
|
233
|
+
} else if (xmlData[i] === '>') {
|
|
234
|
+
angleBracketsCount--;
|
|
235
|
+
if (angleBracketsCount === 0) {
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} else if (xmlData.length > i + 9 && xmlData[i + 1] === '[' && xmlData[i + 2] === 'C' && xmlData[i + 3] === 'D' && xmlData[i + 4] === 'A' && xmlData[i + 5] === 'T' && xmlData[i + 6] === 'A' && xmlData[i + 7] === '[') {
|
|
241
|
+
for (i += 8; i < xmlData.length; i++) {
|
|
242
|
+
if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
|
|
243
|
+
i += 2;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
248
246
|
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
} else if (
|
|
252
|
-
xmlData.length > i + 9 &&
|
|
253
|
-
xmlData[i + 1] === '[' &&
|
|
254
|
-
xmlData[i + 2] === 'C' &&
|
|
255
|
-
xmlData[i + 3] === 'D' &&
|
|
256
|
-
xmlData[i + 4] === 'A' &&
|
|
257
|
-
xmlData[i + 5] === 'T' &&
|
|
258
|
-
xmlData[i + 6] === 'A' &&
|
|
259
|
-
xmlData[i + 7] === '['
|
|
260
|
-
) {
|
|
261
|
-
for (i += 8; i < xmlData.length; i++) {
|
|
262
|
-
if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
|
|
263
|
-
i += 2;
|
|
264
|
-
break;
|
|
265
|
-
}
|
|
266
247
|
}
|
|
267
|
-
}
|
|
268
248
|
|
|
269
|
-
|
|
249
|
+
return i;
|
|
270
250
|
}
|
|
271
251
|
|
|
272
252
|
const doubleQuote = '"';
|
|
@@ -278,35 +258,35 @@ const singleQuote = "'";
|
|
|
278
258
|
* @param {number} i
|
|
279
259
|
*/
|
|
280
260
|
function readAttributeStr(xmlData, i) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
261
|
+
let attrStr = '';
|
|
262
|
+
let startChar = '';
|
|
263
|
+
let tagClosed = false;
|
|
264
|
+
for (; i < xmlData.length; i++) {
|
|
265
|
+
if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
|
|
266
|
+
if (startChar === '') {
|
|
267
|
+
startChar = xmlData[i];
|
|
268
|
+
} else if (startChar !== xmlData[i]) {
|
|
269
|
+
//if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
|
|
270
|
+
} else {
|
|
271
|
+
startChar = '';
|
|
272
|
+
}
|
|
273
|
+
} else if (xmlData[i] === '>') {
|
|
274
|
+
if (startChar === '') {
|
|
275
|
+
tagClosed = true;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
attrStr += xmlData[i];
|
|
280
|
+
}
|
|
281
|
+
if (startChar !== '') {
|
|
282
|
+
return false;
|
|
298
283
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
value: attrStr,
|
|
307
|
-
index: i,
|
|
308
|
-
tagClosed: tagClosed
|
|
309
|
-
};
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
value: attrStr,
|
|
287
|
+
index: i,
|
|
288
|
+
tagClosed: tagClosed
|
|
289
|
+
};
|
|
310
290
|
}
|
|
311
291
|
|
|
312
292
|
/**
|
|
@@ -317,109 +297,104 @@ const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s
|
|
|
317
297
|
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
|
318
298
|
|
|
319
299
|
function validateAttributeString(attrStr, options) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
300
|
+
//console.log("start:"+attrStr+":end");
|
|
301
|
+
|
|
302
|
+
//if(attrStr.trim().length === 0) return true; //empty string
|
|
303
|
+
|
|
304
|
+
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
|
305
|
+
const attrNames = {};
|
|
306
|
+
|
|
307
|
+
for (let i = 0; i < matches.length; i++) {
|
|
308
|
+
if (matches[i][1].length === 0) {
|
|
309
|
+
//nospace before attribute name: a="sd"b="saf"
|
|
310
|
+
return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]));
|
|
311
|
+
} else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
|
|
312
|
+
return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
|
|
313
|
+
} else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
|
|
314
|
+
//independent attribute: ab
|
|
315
|
+
return getErrorObject('InvalidAttr', "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
|
|
316
|
+
}
|
|
317
|
+
/* else if(matches[i][6] === undefined){//attribute without value: ab=
|
|
338
318
|
return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
|
|
339
319
|
} */
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
320
|
+
const attrName = matches[i][2];
|
|
321
|
+
if (!validateAttrName(attrName)) {
|
|
322
|
+
return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
|
|
323
|
+
}
|
|
324
|
+
if (!attrNames.hasOwnProperty(attrName)) {
|
|
325
|
+
//check for duplicate attribute.
|
|
326
|
+
attrNames[attrName] = 1;
|
|
327
|
+
} else {
|
|
328
|
+
return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i]));
|
|
329
|
+
}
|
|
349
330
|
}
|
|
350
|
-
}
|
|
351
331
|
|
|
352
|
-
|
|
332
|
+
return true;
|
|
353
333
|
}
|
|
354
334
|
|
|
355
335
|
function validateNumberAmpersand(xmlData, i) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
return -1;
|
|
336
|
+
let re = /\d/;
|
|
337
|
+
if (xmlData[i] === 'x') {
|
|
338
|
+
i++;
|
|
339
|
+
re = /[\da-fA-F]/;
|
|
340
|
+
}
|
|
341
|
+
for (; i < xmlData.length; i++) {
|
|
342
|
+
if (xmlData[i] === ';') return i;
|
|
343
|
+
if (!xmlData[i].match(re)) break;
|
|
344
|
+
}
|
|
345
|
+
return -1;
|
|
368
346
|
}
|
|
369
347
|
|
|
370
348
|
function validateAmpersand(xmlData, i) {
|
|
371
|
-
|
|
372
|
-
i++;
|
|
373
|
-
if (xmlData[i] === ';')
|
|
374
|
-
return -1;
|
|
375
|
-
if (xmlData[i] === '#') {
|
|
349
|
+
// https://www.w3.org/TR/xml/#dt-charref
|
|
376
350
|
i++;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
351
|
+
if (xmlData[i] === ';') return -1;
|
|
352
|
+
if (xmlData[i] === '#') {
|
|
353
|
+
i++;
|
|
354
|
+
return validateNumberAmpersand(xmlData, i);
|
|
355
|
+
}
|
|
356
|
+
let count = 0;
|
|
357
|
+
for (; i < xmlData.length; i++, count++) {
|
|
358
|
+
if (xmlData[i].match(/\w/) && count < 20) continue;
|
|
359
|
+
if (xmlData[i] === ';') break;
|
|
360
|
+
return -1;
|
|
361
|
+
}
|
|
362
|
+
return i;
|
|
388
363
|
}
|
|
389
364
|
|
|
390
365
|
function getErrorObject(code, message, lineNumber) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
366
|
+
return {
|
|
367
|
+
err: {
|
|
368
|
+
code: code,
|
|
369
|
+
msg: message,
|
|
370
|
+
line: lineNumber.line || lineNumber,
|
|
371
|
+
col: lineNumber.col
|
|
372
|
+
}
|
|
373
|
+
};
|
|
399
374
|
}
|
|
400
375
|
|
|
401
376
|
function validateAttrName(attrName) {
|
|
402
|
-
|
|
377
|
+
return isName(attrName);
|
|
403
378
|
}
|
|
404
379
|
|
|
405
380
|
// const startsWithXML = /^xml/i;
|
|
406
381
|
|
|
407
382
|
function validateTagName(tagname) {
|
|
408
|
-
|
|
383
|
+
return isName(tagname) /* && !tagname.match(startsWithXML) */;
|
|
409
384
|
}
|
|
410
385
|
|
|
411
386
|
//this function returns the line number for the character at the given index
|
|
412
387
|
function getLineNumberForPosition(xmlData, index) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
388
|
+
const lines = xmlData.substring(0, index).split(/\r?\n/);
|
|
389
|
+
return {
|
|
390
|
+
line: lines.length,
|
|
416
391
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
392
|
+
// column number is last line's length + 1, because column numbering starts at 1:
|
|
393
|
+
col: lines[lines.length - 1].length + 1
|
|
394
|
+
};
|
|
420
395
|
}
|
|
421
396
|
|
|
422
397
|
//this function returns the position of the first character of match within attrStr
|
|
423
398
|
function getPositionFromMatch(match) {
|
|
424
|
-
|
|
399
|
+
return match.startIndex + match[1].length;
|
|
425
400
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"README.md",
|
|
50
50
|
"vitest.config.js"
|
|
51
51
|
],
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "38adc6b09eeec245f6a2ce6fae7d71f93940b617"
|
|
53
53
|
}
|
package/plugins/cors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
after: ['_redis', '_db'],
|
|
3
|
-
async onGet(
|
|
3
|
+
async onGet(befly, ctx, req) {
|
|
4
4
|
// 设置 CORS 头部
|
|
5
5
|
req.headers.set('Access-Control-Allow-Origin', req.headers.get('origin'));
|
|
6
6
|
req.headers.set('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
|
package/plugins/db.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Crud } from '../utils/curd.js';
|
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
after: ['_redis'],
|
|
7
|
-
async onInit(
|
|
7
|
+
async onInit(befly) {
|
|
8
8
|
try {
|
|
9
9
|
if (Env.MYSQL_ENABLE === 1) {
|
|
10
10
|
// 创建 MySQL 连接池
|
|
@@ -36,7 +36,7 @@ export default {
|
|
|
36
36
|
const result = await sql`SELECT VERSION() AS version`.execute(db);
|
|
37
37
|
if (result?.rows?.[0]?.version) {
|
|
38
38
|
// 扩展数据库实例
|
|
39
|
-
return new Crud(db,
|
|
39
|
+
return new Crud(db, befly.redis, sql);
|
|
40
40
|
} else {
|
|
41
41
|
return {};
|
|
42
42
|
}
|
package/plugins/logger.js
CHANGED
package/plugins/redis.js
CHANGED
package/utils/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Logger } from '
|
|
2
|
-
import { RYes, RNo } from '
|
|
1
|
+
import { Logger } from './logger.js';
|
|
2
|
+
import { RYes, RNo } from './util.js';
|
|
3
3
|
export class Api {
|
|
4
4
|
// GET 方法
|
|
5
5
|
static GET(name, auth = false, fields = {}, required = [], handler) {
|
|
@@ -9,7 +9,7 @@ export class Api {
|
|
|
9
9
|
auth: auth,
|
|
10
10
|
fields: fields,
|
|
11
11
|
required: required,
|
|
12
|
-
handler:
|
|
12
|
+
handler: await handler(bp, ctx, req)
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -21,29 +21,7 @@ export class Api {
|
|
|
21
21
|
auth: auth,
|
|
22
22
|
fields: fields,
|
|
23
23
|
required: required,
|
|
24
|
-
handler:
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// 包装处理器,自动处理异常和响应格式
|
|
29
|
-
static wrapHandler(handler) {
|
|
30
|
-
return async (bp, ctx, req) => {
|
|
31
|
-
try {
|
|
32
|
-
const result = await handler(bp, ctx, req);
|
|
33
|
-
|
|
34
|
-
return result;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
Logger.error({
|
|
37
|
-
msg: '内部服务器错误',
|
|
38
|
-
error: error.message,
|
|
39
|
-
stack: error.stack,
|
|
40
|
-
url: req?.url || '',
|
|
41
|
-
user: ctx?.user || {}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 返回错误响应
|
|
45
|
-
return RNo('内部服务器错误');
|
|
46
|
-
}
|
|
24
|
+
handler: await handler(bp, ctx, req)
|
|
47
25
|
};
|
|
48
26
|
}
|
|
49
27
|
}
|
package/utils/curd.js
CHANGED
|
@@ -64,8 +64,11 @@ export class Crud {
|
|
|
64
64
|
|
|
65
65
|
// 增强的更新方法 - 自动添加 updated_at,支持链式调用
|
|
66
66
|
updData(tableName, data) {
|
|
67
|
+
// 剔除 undefined 值
|
|
68
|
+
const filteredData = Object.fromEntries(Object.entries(data).filter(([key, value]) => value !== undefined));
|
|
69
|
+
|
|
67
70
|
const updateData = {
|
|
68
|
-
...omitFields(
|
|
71
|
+
...omitFields(filteredData, ['id', 'created_at', 'deleted_at']),
|
|
69
72
|
updated_at: Date.now()
|
|
70
73
|
};
|
|
71
74
|
|