schema-dsl 1.0.0 → 1.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/CHANGELOG.md +218 -542
- package/README.md +772 -903
- package/STATUS.md +97 -2
- package/docs/INDEX.md +1 -2
- package/docs/api-reference.md +1 -292
- package/docs/custom-extensions-guide.md +411 -0
- package/docs/enum.md +475 -0
- package/docs/i18n.md +394 -0
- package/docs/performance-benchmark-report.md +179 -0
- package/docs/plugin-system.md +8 -8
- package/docs/validate-async.md +1 -1
- package/docs/validation-rules-v1.0.2.md +1601 -0
- package/examples/README.md +81 -0
- package/examples/enum.examples.js +324 -0
- package/examples/express-integration.js +54 -54
- package/examples/i18n-full-demo.js +15 -24
- package/examples/schema-utils-chaining.examples.js +2 -2
- package/examples/slug.examples.js +179 -0
- package/index.d.ts +5 -5
- package/index.js +30 -34
- package/lib/config/constants.js +1 -1
- package/lib/config/patterns/common.js +47 -0
- package/lib/config/patterns/index.js +2 -1
- package/lib/core/DslBuilder.js +500 -8
- package/lib/core/StringExtensions.js +31 -0
- package/lib/core/Validator.js +42 -15
- package/lib/errors/ValidationError.js +3 -3
- package/lib/locales/en-US.js +79 -19
- package/lib/locales/es-ES.js +60 -19
- package/lib/locales/fr-FR.js +84 -43
- package/lib/locales/ja-JP.js +83 -42
- package/lib/locales/zh-CN.js +32 -0
- package/lib/validators/CustomKeywords.js +405 -0
- package/package.json +1 -1
- package/.github/CODE_OF_CONDUCT.md +0 -45
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -57
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -45
- package/.github/ISSUE_TEMPLATE/question.md +0 -31
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -70
- package/.github/SECURITY.md +0 -184
- package/.github/workflows/ci.yml +0 -33
- package/plugins/custom-format.js +0 -101
- package/plugins/custom-validator.js +0 -200
package/.github/SECURITY.md
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
# 安全政策
|
|
2
|
-
|
|
3
|
-
## 支持的版本
|
|
4
|
-
|
|
5
|
-
我们目前支持以下版本的安全更新:
|
|
6
|
-
|
|
7
|
-
| 版本 | 支持状态 |
|
|
8
|
-
| ------- | ------------------ |
|
|
9
|
-
| 2.3.x | :white_check_mark: |
|
|
10
|
-
| 2.2.x | :white_check_mark: |
|
|
11
|
-
| 2.1.x | :white_check_mark: |
|
|
12
|
-
| < 2.0 | :x: |
|
|
13
|
-
|
|
14
|
-
## 报告安全漏洞
|
|
15
|
-
|
|
16
|
-
我们非常重视 SchemaIO 的安全性。如果你发现了安全漏洞,请**不要**公开披露,而是通过以下方式报告:
|
|
17
|
-
|
|
18
|
-
### 报告渠道
|
|
19
|
-
|
|
20
|
-
**优先方式**:发送邮件至 **rockyshi1993@gmail.com**
|
|
21
|
-
|
|
22
|
-
邮件标题格式:`[SECURITY] SchemaIO - 简短描述`
|
|
23
|
-
|
|
24
|
-
### 应包含的信息
|
|
25
|
-
|
|
26
|
-
请在报告中包含以下信息:
|
|
27
|
-
|
|
28
|
-
1. **漏洞描述**:清晰描述安全问题
|
|
29
|
-
2. **影响范围**:受影响的版本
|
|
30
|
-
3. **重现步骤**:详细的重现步骤
|
|
31
|
-
4. **PoC 代码**:如果可能,提供概念验证代码
|
|
32
|
-
5. **潜在影响**:漏洞可能造成的影响
|
|
33
|
-
6. **建议修复**:如果有修复建议
|
|
34
|
-
|
|
35
|
-
### 报告模板
|
|
36
|
-
|
|
37
|
-
```markdown
|
|
38
|
-
## 漏洞类型
|
|
39
|
-
[例如:注入攻击、拒绝服务、信息泄露]
|
|
40
|
-
|
|
41
|
-
## 影响版本
|
|
42
|
-
[例如:v2.0.0 - v2.3.0]
|
|
43
|
-
|
|
44
|
-
## 漏洞描述
|
|
45
|
-
[详细描述]
|
|
46
|
-
|
|
47
|
-
## 重现步骤
|
|
48
|
-
1. ...
|
|
49
|
-
2. ...
|
|
50
|
-
|
|
51
|
-
## PoC 代码
|
|
52
|
-
```javascript
|
|
53
|
-
// 你的 PoC 代码
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## 潜在影响
|
|
57
|
-
[描述可能的影响]
|
|
58
|
-
|
|
59
|
-
## 建议修复
|
|
60
|
-
[如果有]
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### 响应时间
|
|
64
|
-
|
|
65
|
-
- **初步确认**:1-2 个工作日
|
|
66
|
-
- **漏洞评估**:3-5 个工作日
|
|
67
|
-
- **修复发布**:根据严重程度,7-30 天
|
|
68
|
-
|
|
69
|
-
### 严重程度分级
|
|
70
|
-
|
|
71
|
-
我们使用 CVSS 3.1 评分系统:
|
|
72
|
-
|
|
73
|
-
- **严重**(9.0-10.0):立即修复,< 7 天
|
|
74
|
-
- **高危**(7.0-8.9):优先修复,< 14 天
|
|
75
|
-
- **中危**(4.0-6.9):计划修复,< 30 天
|
|
76
|
-
- **低危**(0.1-3.9):常规修复,< 90 天
|
|
77
|
-
|
|
78
|
-
## 安全最佳实践
|
|
79
|
-
|
|
80
|
-
使用 SchemaIO 时,请遵循以下最佳实践:
|
|
81
|
-
|
|
82
|
-
### 1. 输入验证
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
// ✅ 推荐:使用 SchemaIO 验证所有外部输入
|
|
86
|
-
const schema = dsl({
|
|
87
|
-
username: 'string:3-32!'.pattern(/^[a-zA-Z0-9_]+$/),
|
|
88
|
-
email: 'email!',
|
|
89
|
-
age: 'number:0-150'
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const result = validate(schema, userInput);
|
|
93
|
-
if (!result.valid) {
|
|
94
|
-
throw new Error('Invalid input');
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 2. 避免动态执行
|
|
99
|
-
|
|
100
|
-
```javascript
|
|
101
|
-
// ❌ 危险:不要从不可信源动态执行代码
|
|
102
|
-
const schemaStr = req.body.schema; // 来自用户输入
|
|
103
|
-
eval(schemaStr); // 危险!
|
|
104
|
-
|
|
105
|
-
// ✅ 安全:使用预定义的 Schema
|
|
106
|
-
const allowedSchemas = {
|
|
107
|
-
user: userSchema,
|
|
108
|
-
post: postSchema
|
|
109
|
-
};
|
|
110
|
-
const schema = allowedSchemas[req.body.schemaType];
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### 3. 限制资源使用
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
// ✅ 推荐:限制数组大小,防止 DoS
|
|
117
|
-
const schema = dsl({
|
|
118
|
-
tags: 'array:1-100<string:1-50>' // 限制数组和元素大小
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### 4. 保持更新
|
|
123
|
-
|
|
124
|
-
定期更新到最新版本以获得安全补丁:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
npm update schema-dsl
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 5. 依赖审计
|
|
131
|
-
|
|
132
|
-
定期运行安全审计:
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npm audit
|
|
136
|
-
npm audit fix
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## 已知的安全问题
|
|
140
|
-
|
|
141
|
-
### 已修复的漏洞
|
|
142
|
-
|
|
143
|
-
目前没有已知的安全漏洞。
|
|
144
|
-
|
|
145
|
-
查看历史安全公告:[Security Advisories](https://github.com/vextjs/schema-dsl/security/advisories)
|
|
146
|
-
|
|
147
|
-
## 安全相关配置
|
|
148
|
-
|
|
149
|
-
### ReDoS 防护
|
|
150
|
-
|
|
151
|
-
SchemaIO 内置了对正则表达式拒绝服务(ReDoS)的防护:
|
|
152
|
-
|
|
153
|
-
```javascript
|
|
154
|
-
// 内部会检测和缓存安全的正则表达式
|
|
155
|
-
const schema = dsl({
|
|
156
|
-
username: 'string'.pattern(/^[a-zA-Z0-9_]+$/)
|
|
157
|
-
});
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 自定义验证器安全
|
|
161
|
-
|
|
162
|
-
使用自定义验证器时注意:
|
|
163
|
-
|
|
164
|
-
```javascript
|
|
165
|
-
// ⚠️ 注意:自定义验证器中的异步操作
|
|
166
|
-
const schema = dsl({
|
|
167
|
-
email: 'email!'.custom(async (value) => {
|
|
168
|
-
// 确保设置超时,防止挂起
|
|
169
|
-
const exists = await checkEmailExists(value, { timeout: 5000 });
|
|
170
|
-
if (exists) return '邮箱已被占用';
|
|
171
|
-
})
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## 致谢
|
|
176
|
-
|
|
177
|
-
我们感谢所有负责任地披露安全问题的研究人员。
|
|
178
|
-
|
|
179
|
-
如果你报告了有效的安全漏洞,我们会在修复后的发布说明中致谢(除非你要求匿名)。
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
**最后更新**:2025-12-29
|
|
184
|
-
**联系方式**:rockyshi1993@gmail.com
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main, master ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main, master ]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
build:
|
|
11
|
-
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
|
|
14
|
-
strategy:
|
|
15
|
-
matrix:
|
|
16
|
-
node-version: [16.x, 18.x, 20.x]
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v3
|
|
20
|
-
|
|
21
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
22
|
-
uses: actions/setup-node@v3
|
|
23
|
-
with:
|
|
24
|
-
node-version: ${{ matrix.node-version }}
|
|
25
|
-
cache: 'npm'
|
|
26
|
-
|
|
27
|
-
- name: Install dependencies
|
|
28
|
-
run: npm ci
|
|
29
|
-
|
|
30
|
-
- name: Run tests
|
|
31
|
-
run: npm test
|
|
32
|
-
|
|
33
|
-
|
package/plugins/custom-format.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 示例插件:自定义格式验证
|
|
3
|
-
*
|
|
4
|
-
* @description 添加常用的格式验证(手机号、邮编、身份证等)
|
|
5
|
-
* @module plugins/custom-format
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
name: 'custom-format',
|
|
10
|
-
version: '1.0.0',
|
|
11
|
-
description: '自定义格式验证插件',
|
|
12
|
-
|
|
13
|
-
install(schemaDsl, options = {}, context) {
|
|
14
|
-
// 获取默认 validator 实例
|
|
15
|
-
const validator = schemaDsl.getDefaultValidator();
|
|
16
|
-
const ajv = validator.getAjv();
|
|
17
|
-
|
|
18
|
-
// 添加自定义格式
|
|
19
|
-
this.addCustomFormats(ajv);
|
|
20
|
-
|
|
21
|
-
console.log('[Plugin] custom-format installed');
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
uninstall(schemaDsl, context) {
|
|
25
|
-
console.log('[Plugin] custom-format uninstalled');
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
addCustomFormats(ajv) {
|
|
29
|
-
// 1. 中国手机号
|
|
30
|
-
ajv.addFormat('phone-cn', {
|
|
31
|
-
validate: /^1[3-9]\d{9}$/
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// 2. 中国邮政编码
|
|
35
|
-
ajv.addFormat('postal-code-cn', {
|
|
36
|
-
validate: /^\d{6}$/
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// 3. IPv4 地址
|
|
40
|
-
ajv.addFormat('ipv4', {
|
|
41
|
-
validate: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 4. 微信号
|
|
45
|
-
ajv.addFormat('wechat', {
|
|
46
|
-
validate: /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// 5. QQ号
|
|
50
|
-
ajv.addFormat('qq', {
|
|
51
|
-
validate: /^[1-9][0-9]{4,10}$/
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// 6. 银行卡号(简单验证)
|
|
55
|
-
ajv.addFormat('bank-card', {
|
|
56
|
-
validate: (value) => {
|
|
57
|
-
if (!/^\d{16,19}$/.test(value)) return false;
|
|
58
|
-
|
|
59
|
-
// Luhn 算法验证
|
|
60
|
-
let sum = 0;
|
|
61
|
-
let shouldDouble = false;
|
|
62
|
-
|
|
63
|
-
for (let i = value.length - 1; i >= 0; i--) {
|
|
64
|
-
let digit = parseInt(value[i]);
|
|
65
|
-
|
|
66
|
-
if (shouldDouble) {
|
|
67
|
-
digit *= 2;
|
|
68
|
-
if (digit > 9) digit -= 9;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
sum += digit;
|
|
72
|
-
shouldDouble = !shouldDouble;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return sum % 10 === 0;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// 7. 车牌号(普通+新能源)
|
|
80
|
-
ajv.addFormat('license-plate', {
|
|
81
|
-
validate: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// 8. 统一社会信用代码
|
|
85
|
-
ajv.addFormat('credit-code', {
|
|
86
|
-
validate: /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// 9. 护照号(中国)
|
|
90
|
-
ajv.addFormat('passport-cn', {
|
|
91
|
-
validate: /^[EG]\d{8}$/
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// 10. 港澳通行证
|
|
95
|
-
ajv.addFormat('hk-macao-pass', {
|
|
96
|
-
validate: /^[HM]\d{8,10}$/
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 示例插件:自定义验证器
|
|
3
|
-
*
|
|
4
|
-
* @description 展示如何创建一个自定义验证器插件
|
|
5
|
-
* @module plugins/custom-validator
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
// 插件元信息
|
|
10
|
-
name: 'custom-validator',
|
|
11
|
-
version: '1.0.0',
|
|
12
|
-
description: '自定义验证器插件,添加业务特定的验证规则',
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 插件安装函数
|
|
16
|
-
*
|
|
17
|
-
* @param {Object} schemaDsl - SchemaIO 实例
|
|
18
|
-
* @param {Object} options - 插件选项
|
|
19
|
-
* @param {Object} context - 插件上下文
|
|
20
|
-
*/
|
|
21
|
-
install(schemaDsl, options = {}, context) {
|
|
22
|
-
// 1. 获取默认 validator 实例
|
|
23
|
-
const validator = schemaDsl.getDefaultValidator();
|
|
24
|
-
|
|
25
|
-
// 2. 添加自定义关键字
|
|
26
|
-
this.addCustomKeywords(validator);
|
|
27
|
-
|
|
28
|
-
// 3. 注册到全局
|
|
29
|
-
if (!global.__schemaDsl_plugins) {
|
|
30
|
-
global.__schemaDsl_plugins = {};
|
|
31
|
-
}
|
|
32
|
-
global.__schemaDsl_plugins['custom-validator'] = this;
|
|
33
|
-
|
|
34
|
-
console.log('[Plugin] custom-validator installed');
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 插件卸载函数
|
|
39
|
-
*/
|
|
40
|
-
uninstall(schemaDsl, context) {
|
|
41
|
-
// 清理全局注册
|
|
42
|
-
if (global.__schemaDsl_plugins) {
|
|
43
|
-
delete global.__schemaDsl_plugins['custom-validator'];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
console.log('[Plugin] custom-validator uninstalled');
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 添加自定义关键字
|
|
51
|
-
*/
|
|
52
|
-
addCustomKeywords(validator) {
|
|
53
|
-
const ajv = validator.getAjv();
|
|
54
|
-
|
|
55
|
-
// 示例1: 唯一性验证(需要异步检查数据库)
|
|
56
|
-
// 检查关键字是否已存在
|
|
57
|
-
if (!ajv.getKeyword('unique')) {
|
|
58
|
-
validator.addKeyword('unique', {
|
|
59
|
-
async: true,
|
|
60
|
-
type: 'string',
|
|
61
|
-
validate: async function validateUnique(schema, data, parentSchema, dataPath) {
|
|
62
|
-
// schema: { unique: { table: 'users', field: 'email' } }
|
|
63
|
-
// data: 实际要验证的值
|
|
64
|
-
|
|
65
|
-
if (!schema) return true;
|
|
66
|
-
|
|
67
|
-
const { table, field } = schema;
|
|
68
|
-
|
|
69
|
-
// 模拟数据库查询
|
|
70
|
-
// 实际使用时替换为真实的数据库查询
|
|
71
|
-
const exists = false; // await db.query(...)
|
|
72
|
-
|
|
73
|
-
if (exists) {
|
|
74
|
-
validateUnique.errors = [{
|
|
75
|
-
keyword: 'unique',
|
|
76
|
-
message: `${field} already exists in ${table}`,
|
|
77
|
-
params: { table, field }
|
|
78
|
-
}];
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 示例2: 密码强度验证
|
|
88
|
-
if (!ajv.getKeyword('passwordStrength')) {
|
|
89
|
-
validator.addKeyword('passwordStrength', {
|
|
90
|
-
type: 'string',
|
|
91
|
-
validate: function validatePasswordStrength(schema, data) {
|
|
92
|
-
if (!schema) return true;
|
|
93
|
-
|
|
94
|
-
// schema: { passwordStrength: 'medium' }
|
|
95
|
-
// 强度等级: weak, medium, strong
|
|
96
|
-
|
|
97
|
-
const strength = schema;
|
|
98
|
-
const value = data;
|
|
99
|
-
|
|
100
|
-
const rules = {
|
|
101
|
-
weak: /^.{6,}$/,
|
|
102
|
-
medium: /^(?=.*[a-z])(?=.*[A-Z]).{8,}$/,
|
|
103
|
-
strong: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$/
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const pattern = rules[strength];
|
|
107
|
-
if (!pattern) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!pattern.test(value)) {
|
|
112
|
-
validatePasswordStrength.errors = [{
|
|
113
|
-
keyword: 'passwordStrength',
|
|
114
|
-
message: `Password does not meet ${strength} strength requirements`,
|
|
115
|
-
params: { strength }
|
|
116
|
-
}];
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 示例3: 中国身份证号验证
|
|
126
|
-
if (!ajv.getKeyword('idCard')) {
|
|
127
|
-
validator.addKeyword('idCard', {
|
|
128
|
-
type: 'string',
|
|
129
|
-
validate: function validateIdCard(schema, data) {
|
|
130
|
-
if (!schema) return true;
|
|
131
|
-
|
|
132
|
-
const value = data;
|
|
133
|
-
|
|
134
|
-
// 身份证号正则
|
|
135
|
-
const pattern = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
|
136
|
-
|
|
137
|
-
if (!pattern.test(value)) {
|
|
138
|
-
validateIdCard.errors = [{
|
|
139
|
-
keyword: 'idCard',
|
|
140
|
-
message: 'Invalid ID card number',
|
|
141
|
-
params: {}
|
|
142
|
-
}];
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 验证校验码
|
|
147
|
-
if (!this._validateIdCardChecksum(value)) {
|
|
148
|
-
validateIdCard.errors = [{
|
|
149
|
-
keyword: 'idCard',
|
|
150
|
-
message: 'Invalid ID card checksum',
|
|
151
|
-
params: {}
|
|
152
|
-
}];
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* 验证身份证校验码
|
|
164
|
-
* @private
|
|
165
|
-
*/
|
|
166
|
-
_validateIdCardChecksum(idCard) {
|
|
167
|
-
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|
168
|
-
const checksums = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|
169
|
-
|
|
170
|
-
let sum = 0;
|
|
171
|
-
for (let i = 0; i < 17; i++) {
|
|
172
|
-
sum += parseInt(idCard[i]) * weights[i];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const checksum = checksums[sum % 11];
|
|
176
|
-
return idCard[17].toUpperCase() === checksum;
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* 生命周期钩子
|
|
181
|
-
*/
|
|
182
|
-
hooks: {
|
|
183
|
-
onBeforeValidate(schema, data) {
|
|
184
|
-
// 验证前钩子
|
|
185
|
-
// 可以在这里修改 schema 或 data
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
onAfterValidate(result) {
|
|
189
|
-
// 验证后钩子
|
|
190
|
-
// 可以在这里修改验证结果
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
onError(error, context) {
|
|
194
|
-
// 错误处理钩子
|
|
195
|
-
console.error('[custom-validator] Error:', error.message);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
|