@qq33357486/oh-my-task 1.4.30 → 1.4.31
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/dist/api/middleware/security.d.ts +15 -0
- package/dist/api/middleware/security.d.ts.map +1 -0
- package/dist/api/middleware/security.js +103 -0
- package/dist/api/middleware/security.js.map +1 -0
- package/dist/api/routes/schedule.d.ts.map +1 -1
- package/dist/api/routes/schedule.js +130 -7
- package/dist/api/routes/schedule.js.map +1 -1
- package/dist/api/routes/users.d.ts.map +1 -1
- package/dist/api/routes/users.js +3 -2
- package/dist/api/routes/users.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +9 -1
- package/dist/api/server.js.map +1 -1
- package/dist/utils/password.d.ts.map +1 -1
- package/dist/utils/password.js +4 -0
- package/dist/utils/password.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +2 -1
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
type RateLimitOptions = {
|
|
3
|
+
windowMs: number;
|
|
4
|
+
max: number;
|
|
5
|
+
message: string;
|
|
6
|
+
keyGenerator?: (req: Request) => string;
|
|
7
|
+
};
|
|
8
|
+
export declare function securityHeaders(_req: Request, res: Response, next: NextFunction): void;
|
|
9
|
+
export declare function csrfOriginGuard(req: Request, res: Response, next: NextFunction): void;
|
|
10
|
+
export declare function createRateLimiter(options: RateLimitOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
11
|
+
export declare const loginRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
12
|
+
export declare const authEmailRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
13
|
+
export declare const authWriteRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/api/middleware/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACzC,CAAC;AAqCF,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAqBtF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAuBrF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,IAGjD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAqB/D;AAED,eAAO,MAAM,gBAAgB,QAvBd,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IA4B1D,CAAC;AAEH,eAAO,MAAM,oBAAoB,QA9BlB,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IAmC1D,CAAC;AAEH,eAAO,MAAM,oBAAoB,QArClB,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IA0C1D,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
2
|
+
function splitOrigins(value) {
|
|
3
|
+
return (value || '')
|
|
4
|
+
.split(',')
|
|
5
|
+
.map(origin => origin.trim())
|
|
6
|
+
.filter(Boolean);
|
|
7
|
+
}
|
|
8
|
+
function getRequestOrigin(req) {
|
|
9
|
+
const origin = req.headers.origin;
|
|
10
|
+
return typeof origin === 'string' && origin.length > 0 ? origin : null;
|
|
11
|
+
}
|
|
12
|
+
function isAllowedOrigin(req, origin) {
|
|
13
|
+
const configured = splitOrigins(process.env.FRONTEND_URL || 'http://localhost:5173');
|
|
14
|
+
const host = req.headers.host;
|
|
15
|
+
const sameHostOrigins = host ? [`http://${host}`, `https://${host}`] : [];
|
|
16
|
+
return [...configured, ...sameHostOrigins].includes(origin);
|
|
17
|
+
}
|
|
18
|
+
function getClientIp(req) {
|
|
19
|
+
return req.ip || req.socket.remoteAddress || 'unknown';
|
|
20
|
+
}
|
|
21
|
+
function getBodyEmail(req) {
|
|
22
|
+
const body = req.body;
|
|
23
|
+
return typeof body?.email === 'string' ? body.email.trim().toLowerCase() : '';
|
|
24
|
+
}
|
|
25
|
+
export function securityHeaders(_req, res, next) {
|
|
26
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
27
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
28
|
+
res.setHeader('Referrer-Policy', 'same-origin');
|
|
29
|
+
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
|
30
|
+
res.setHeader('Content-Security-Policy', [
|
|
31
|
+
"default-src 'self'",
|
|
32
|
+
"base-uri 'self'",
|
|
33
|
+
"object-src 'none'",
|
|
34
|
+
"frame-ancestors 'none'",
|
|
35
|
+
"script-src 'self'",
|
|
36
|
+
"style-src 'self' 'unsafe-inline'",
|
|
37
|
+
"img-src 'self' data:",
|
|
38
|
+
"font-src 'self' data:",
|
|
39
|
+
"connect-src 'self'",
|
|
40
|
+
"form-action 'self'",
|
|
41
|
+
].join('; '));
|
|
42
|
+
next();
|
|
43
|
+
}
|
|
44
|
+
export function csrfOriginGuard(req, res, next) {
|
|
45
|
+
if (SAFE_METHODS.has(req.method)) {
|
|
46
|
+
next();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (req.headers.authorization?.startsWith('Bearer ')) {
|
|
50
|
+
next();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (req.headers['sec-fetch-site'] === 'cross-site') {
|
|
54
|
+
res.status(403).json({ success: false, error: 'Cross-site request blocked' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const origin = getRequestOrigin(req);
|
|
58
|
+
if (origin && !isAllowedOrigin(req, origin)) {
|
|
59
|
+
res.status(403).json({ success: false, error: 'Invalid request origin' });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
next();
|
|
63
|
+
}
|
|
64
|
+
export function createRateLimiter(options) {
|
|
65
|
+
const hits = new Map();
|
|
66
|
+
return (req, res, next) => {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
const key = options.keyGenerator ? options.keyGenerator(req) : getClientIp(req);
|
|
69
|
+
const current = hits.get(key);
|
|
70
|
+
if (!current || current.resetAt <= now) {
|
|
71
|
+
hits.set(key, { count: 1, resetAt: now + options.windowMs });
|
|
72
|
+
next();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (current.count >= options.max) {
|
|
76
|
+
const retryAfter = Math.max(1, Math.ceil((current.resetAt - now) / 1000));
|
|
77
|
+
res.setHeader('Retry-After', String(retryAfter));
|
|
78
|
+
res.status(429).json({ success: false, error: options.message });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
current.count += 1;
|
|
82
|
+
next();
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export const loginRateLimiter = createRateLimiter({
|
|
86
|
+
windowMs: 15 * 60 * 1000,
|
|
87
|
+
max: Number(process.env.OMT_LOGIN_RATE_LIMIT_MAX || 20),
|
|
88
|
+
message: 'Too many login attempts, please try again later',
|
|
89
|
+
keyGenerator: req => `login:${getClientIp(req)}:${getBodyEmail(req)}`,
|
|
90
|
+
});
|
|
91
|
+
export const authEmailRateLimiter = createRateLimiter({
|
|
92
|
+
windowMs: 60 * 60 * 1000,
|
|
93
|
+
max: Number(process.env.OMT_EMAIL_RATE_LIMIT_MAX || 10),
|
|
94
|
+
message: 'Too many verification requests, please try again later',
|
|
95
|
+
keyGenerator: req => `email:${getClientIp(req)}:${getBodyEmail(req)}:${req.path}`,
|
|
96
|
+
});
|
|
97
|
+
export const authWriteRateLimiter = createRateLimiter({
|
|
98
|
+
windowMs: 15 * 60 * 1000,
|
|
99
|
+
max: Number(process.env.OMT_AUTH_WRITE_RATE_LIMIT_MAX || 300),
|
|
100
|
+
message: 'Too many requests, please try again later',
|
|
101
|
+
keyGenerator: req => `auth:${getClientIp(req)}:${req.path}`,
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/api/middleware/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../../src/api/routes/schedule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../../src/api/routes/schedule.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAwOxB,eAAe,MAAM,CAAC"}
|
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import * as scheduleService from '../../services/schedule.service.js';
|
|
3
|
+
import { getDb } from '../../db/connection.js';
|
|
3
4
|
const router = Router();
|
|
5
|
+
const TASK_STATUS = new Set(['planned', 'in_progress', 'done']);
|
|
6
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
7
|
+
const MAX_BULK_TASKS = 1000;
|
|
8
|
+
const MAX_HOLIDAYS = 500;
|
|
9
|
+
function isValidDateString(value) {
|
|
10
|
+
if (typeof value !== 'string' || !ISO_DATE.test(value)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const date = new Date(`${value}T00:00:00.000Z`);
|
|
14
|
+
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
|
|
15
|
+
}
|
|
16
|
+
function isValidDateOrDateTime(value) {
|
|
17
|
+
if (isValidDateString(value)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return typeof value === 'string' && value.length <= 40 && !Number.isNaN(Date.parse(value));
|
|
21
|
+
}
|
|
22
|
+
function isReasonableYear(year) {
|
|
23
|
+
return Number.isInteger(year) && year >= 1970 && year <= 2100;
|
|
24
|
+
}
|
|
25
|
+
function taskBelongsToUser(taskId, userId) {
|
|
26
|
+
const db = getDb();
|
|
27
|
+
const task = db.prepare(`
|
|
28
|
+
SELECT t.id
|
|
29
|
+
FROM tasks t
|
|
30
|
+
JOIN projects p ON t.project_id = p.id
|
|
31
|
+
WHERE t.id = ? AND p.owner_id = ? AND t.deleted_at IS NULL
|
|
32
|
+
`).get(taskId, userId);
|
|
33
|
+
return Boolean(task);
|
|
34
|
+
}
|
|
35
|
+
function projectBelongsToUser(projectId, userId) {
|
|
36
|
+
const db = getDb();
|
|
37
|
+
const project = db.prepare('SELECT id FROM projects WHERE id = ? AND owner_id = ?')
|
|
38
|
+
.get(projectId, userId);
|
|
39
|
+
return Boolean(project);
|
|
40
|
+
}
|
|
41
|
+
function versionBelongsToProject(versionId, projectId) {
|
|
42
|
+
const db = getDb();
|
|
43
|
+
const version = db.prepare('SELECT id FROM versions WHERE id = ? AND project_id = ?')
|
|
44
|
+
.get(versionId, projectId);
|
|
45
|
+
return Boolean(version);
|
|
46
|
+
}
|
|
47
|
+
function validateHolidayItems(holidays) {
|
|
48
|
+
if (holidays.length > MAX_HOLIDAYS) {
|
|
49
|
+
return `holidays cannot exceed ${MAX_HOLIDAYS} items`;
|
|
50
|
+
}
|
|
51
|
+
for (const holiday of holidays) {
|
|
52
|
+
if (!holiday || typeof holiday !== 'object') {
|
|
53
|
+
return 'holiday item must be an object';
|
|
54
|
+
}
|
|
55
|
+
const item = holiday;
|
|
56
|
+
if (!isValidDateString(item.date)) {
|
|
57
|
+
return 'holiday date is invalid';
|
|
58
|
+
}
|
|
59
|
+
if (typeof item.year !== 'number' || !isReasonableYear(item.year)) {
|
|
60
|
+
return 'holiday year is invalid';
|
|
61
|
+
}
|
|
62
|
+
if (item.is_workday !== 0 && item.is_workday !== 1 && typeof item.is_workday !== 'boolean') {
|
|
63
|
+
return 'holiday is_workday is invalid';
|
|
64
|
+
}
|
|
65
|
+
if (typeof item.name !== 'string' || item.name.length > 100) {
|
|
66
|
+
return 'holiday name is invalid';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
4
71
|
// POST /api/schedule/reschedule - 重新排期
|
|
5
72
|
router.post('/reschedule', async (req, res) => {
|
|
6
73
|
const { task_id, new_start_date } = req.body;
|
|
@@ -8,6 +75,14 @@ router.post('/reschedule', async (req, res) => {
|
|
|
8
75
|
res.status(400).json({ success: false, error: 'task_id and new_start_date are required' });
|
|
9
76
|
return;
|
|
10
77
|
}
|
|
78
|
+
if (typeof task_id !== 'string' || !isValidDateString(new_start_date)) {
|
|
79
|
+
res.status(400).json({ success: false, error: 'Invalid task_id or new_start_date' });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!taskBelongsToUser(task_id, req.auth.user.id)) {
|
|
83
|
+
res.status(404).json({ success: false, error: 'Task not found' });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
11
86
|
try {
|
|
12
87
|
const result = await scheduleService.rescheduleFromTask(task_id, new_start_date);
|
|
13
88
|
res.json({ success: true, data: result });
|
|
@@ -24,6 +99,22 @@ router.post('/auto', async (req, res) => {
|
|
|
24
99
|
res.status(400).json({ success: false, error: 'project_id and start_date are required' });
|
|
25
100
|
return;
|
|
26
101
|
}
|
|
102
|
+
if (typeof project_id !== 'string' || !isValidDateString(start_date)) {
|
|
103
|
+
res.status(400).json({ success: false, error: 'Invalid project_id or start_date' });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (version_id !== undefined && version_id !== null && typeof version_id !== 'string') {
|
|
107
|
+
res.status(400).json({ success: false, error: 'Invalid version_id' });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!projectBelongsToUser(project_id, req.auth.user.id)) {
|
|
111
|
+
res.status(404).json({ success: false, error: 'Project not found' });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (version_id && !versionBelongsToProject(version_id, project_id)) {
|
|
115
|
+
res.status(404).json({ success: false, error: 'Version not found' });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
27
118
|
try {
|
|
28
119
|
const result = await scheduleService.autoSchedule(project_id, start_date, version_id);
|
|
29
120
|
res.json({ success: true, data: result });
|
|
@@ -36,7 +127,7 @@ router.post('/auto', async (req, res) => {
|
|
|
36
127
|
// GET /api/schedule/holidays/:year - 获取年度节假日(自动从 API 获取)
|
|
37
128
|
router.get('/holidays/:year', async (req, res) => {
|
|
38
129
|
const year = parseInt(req.params.year, 10);
|
|
39
|
-
if (isNaN(year)) {
|
|
130
|
+
if (isNaN(year) || !isReasonableYear(year)) {
|
|
40
131
|
res.status(400).json({ success: false, error: 'Invalid year' });
|
|
41
132
|
return;
|
|
42
133
|
}
|
|
@@ -52,10 +143,19 @@ router.get('/holidays/:year', async (req, res) => {
|
|
|
52
143
|
// POST /api/schedule/holidays - 导入节假日(手动导入,一般不需要)
|
|
53
144
|
router.post('/holidays', (req, res) => {
|
|
54
145
|
const { holidays } = req.body;
|
|
146
|
+
if (req.auth.user.role !== 'admin') {
|
|
147
|
+
res.status(403).json({ success: false, error: 'Admin access required' });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
55
150
|
if (!Array.isArray(holidays)) {
|
|
56
151
|
res.status(400).json({ success: false, error: 'holidays array is required' });
|
|
57
152
|
return;
|
|
58
153
|
}
|
|
154
|
+
const validationError = validateHolidayItems(holidays);
|
|
155
|
+
if (validationError) {
|
|
156
|
+
res.status(400).json({ success: false, error: validationError });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
59
159
|
try {
|
|
60
160
|
const count = scheduleService.importHolidays(holidays);
|
|
61
161
|
res.json({ success: true, data: { imported: count } });
|
|
@@ -72,14 +172,37 @@ router.post('/calculate-end-dates', async (req, res) => {
|
|
|
72
172
|
res.status(400).json({ success: false, error: 'tasks array is required' });
|
|
73
173
|
return;
|
|
74
174
|
}
|
|
175
|
+
if (tasks.length > MAX_BULK_TASKS) {
|
|
176
|
+
res.status(400).json({ success: false, error: `tasks cannot exceed ${MAX_BULK_TASKS} items` });
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (start_date !== undefined && start_date !== null && !isValidDateString(start_date)) {
|
|
180
|
+
res.status(400).json({ success: false, error: 'Invalid start_date' });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
75
183
|
try {
|
|
76
184
|
// 转换前端格式到服务层格式
|
|
77
|
-
const input = tasks.map((t) =>
|
|
78
|
-
id
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
185
|
+
const input = tasks.map((t) => {
|
|
186
|
+
if (!t || typeof t.id !== 'string' || t.id.length > 128) {
|
|
187
|
+
throw new Error('Invalid task id');
|
|
188
|
+
}
|
|
189
|
+
if (!TASK_STATUS.has(t.status)) {
|
|
190
|
+
throw new Error('Invalid task status');
|
|
191
|
+
}
|
|
192
|
+
if (t.estimated_days !== undefined &&
|
|
193
|
+
(!Number.isFinite(t.estimated_days) || t.estimated_days < 0 || t.estimated_days > 365)) {
|
|
194
|
+
throw new Error('Invalid estimated_days');
|
|
195
|
+
}
|
|
196
|
+
if (t.actual_end !== undefined && t.actual_end !== null && !isValidDateOrDateTime(t.actual_end)) {
|
|
197
|
+
throw new Error('Invalid actual_end');
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
id: t.id,
|
|
201
|
+
estimatedDays: t.estimated_days || 1,
|
|
202
|
+
status: t.status,
|
|
203
|
+
actualEnd: t.actual_end,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
83
206
|
const results = await scheduleService.calculateEndDates(input, start_date);
|
|
84
207
|
res.json({ success: true, data: results });
|
|
85
208
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../../src/api/routes/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../../src/api/routes/schedule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../src/api/routes/users.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../src/api/routes/users.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA0CxB,eAAe,MAAM,CAAC"}
|
package/dist/api/routes/users.js
CHANGED
|
@@ -4,8 +4,9 @@ import * as userService from '../../services/user.service.js';
|
|
|
4
4
|
const router = Router();
|
|
5
5
|
// GET /api/users - 获取用户列表(仅管理员)
|
|
6
6
|
router.get('/', adminOnly, (req, res) => {
|
|
7
|
-
const page = parseInt(req.query.page) || 1;
|
|
8
|
-
const
|
|
7
|
+
const page = Math.max(1, parseInt(req.query.page) || 1);
|
|
8
|
+
const requestedPageSize = parseInt(req.query.page_size) || 10;
|
|
9
|
+
const pageSize = Math.min(Math.max(1, requestedPageSize), 100);
|
|
9
10
|
const { users, total } = userService.getAllUsers(page, pageSize);
|
|
10
11
|
const publicUsers = users.map(u => userService.toPublicUser(u));
|
|
11
12
|
res.json({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../src/api/routes/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,gCAAgC,CAAC;AAE9D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB,gCAAgC;AAChC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAc,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../src/api/routes/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,gCAAgC,CAAC;AAE9D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB,gCAAgC;AAChC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,MAAM,iBAAiB,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,SAAmB,CAAC,IAAI,EAAE,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,IAAI,EAAE;YACJ,KAAK,EAAE,WAAW;YAClB,UAAU,EAAE;gBACV,IAAI;gBACJ,SAAS,EAAE,QAAQ;gBACnB,KAAK;gBACL,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;aACzC;SACF;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,6BAA6B;AAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,2CAA2C;AAC3C,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC5C,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,IAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACxC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
package/dist/api/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AA4BA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAsEtB,wBAAgB,cAAc,IAAI,IAAI,CASrC;AAED,eAAe,GAAG,CAAC"}
|
package/dist/api/server.js
CHANGED
|
@@ -15,26 +15,34 @@ import versionsRouter from './routes/versions.js';
|
|
|
15
15
|
import scheduleRouter from './routes/schedule.js';
|
|
16
16
|
import configRouter from './routes/config.js';
|
|
17
17
|
import adminRouter from './routes/admin.js';
|
|
18
|
+
import { authEmailRateLimiter, authWriteRateLimiter, csrfOriginGuard, loginRateLimiter, securityHeaders, } from './middleware/security.js';
|
|
18
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
20
|
const __dirname = dirname(__filename);
|
|
20
21
|
const app = express();
|
|
21
22
|
const PORT = process.env.API_PORT || 17173;
|
|
22
23
|
// 允许反向代理透传 HTTPS 状态,否则生产环境 secure session cookie 不会写入
|
|
23
24
|
app.set('trust proxy', 1);
|
|
25
|
+
app.use(securityHeaders);
|
|
24
26
|
// CORS 配置
|
|
25
27
|
app.use(cors({
|
|
26
28
|
origin: process.env.FRONTEND_URL || 'http://localhost:5173',
|
|
27
29
|
credentials: true
|
|
28
30
|
}));
|
|
29
31
|
// JSON body parser
|
|
30
|
-
app.use(express.json());
|
|
32
|
+
app.use(express.json({ limit: '64kb' }));
|
|
31
33
|
// Session 中间件(SQLite session store)
|
|
32
34
|
app.use(createSessionMiddleware());
|
|
35
|
+
app.use(csrfOriginGuard);
|
|
33
36
|
// 健康检查端点(无需认证)
|
|
34
37
|
app.get('/api/health', (_req, res) => {
|
|
35
38
|
res.json({ success: true, data: { status: 'ok', timestamp: new Date().toISOString() } });
|
|
36
39
|
});
|
|
37
40
|
// 认证路由(无需认证)
|
|
41
|
+
app.use('/api/auth/login', loginRateLimiter);
|
|
42
|
+
app.use('/api/auth/send-code', authEmailRateLimiter);
|
|
43
|
+
app.use('/api/auth/forgot-password', authEmailRateLimiter);
|
|
44
|
+
app.use('/api/auth/reset-password', authEmailRateLimiter);
|
|
45
|
+
app.use('/api/auth/register', authWriteRateLimiter);
|
|
38
46
|
app.use('/api/auth', authRouter);
|
|
39
47
|
// API 路由(需要认证)
|
|
40
48
|
app.use('/api/tokens', authMiddleware, tokensRouter);
|
package/dist/api/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,WAAW,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,eAAe,GAChB,MAAM,0BAA0B,CAAC;AAElC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;AAE3C,sDAAsD;AACtD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;AAE1B,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAEzB,UAAU;AACV,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;IACX,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB;IAC3D,WAAW,EAAE,IAAI;CAClB,CAAC,CAAC,CAAC;AAEJ,mBAAmB;AACnB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAEzC,oCAAoC;AACpC,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC,CAAC;AAEnC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAEzB,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACnC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC,CAAC,CAAC;AAEH,aAAa;AACb,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAC7C,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;AACrD,GAAG,CAAC,GAAG,CAAC,2BAA2B,EAAE,oBAAoB,CAAC,CAAC;AAC3D,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,oBAAoB,CAAC,CAAC;AAC1D,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;AACpD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAEjC,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;AACrD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;AACnD,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;AACzD,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;AACzD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;AACnD,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;AACzD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;AACrD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;AAEnD,mBAAmB;AACnB,YAAY;AACZ,kDAAkD;AAClD,mEAAmE;AACnE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;AACnF,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IAC5B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IACrC,wCAAwC;IACxC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACzB,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS;AACT,GAAG,CAAC,GAAG,CAAC,CAAC,GAAU,EAAE,IAAqB,EAAE,GAAqB,EAAE,KAA2B,EAAE,EAAE;IAChG,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,UAAU,GAAI,GAAuC,CAAC,UAAU,IAAI,GAAG,CAAC;IAC9E,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,uBAAuB,EAAE,CAAC,CAAC;AACjG,CAAC,CAAC,CAAC;AAEH,QAAQ;AACR,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,CAAC;IAET,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,wCAAwC,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,eAAe,GAAG,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC1D,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAuBA"}
|
package/dist/utils/password.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import bcrypt from 'bcrypt';
|
|
2
2
|
const SALT_ROUNDS = 12;
|
|
3
|
+
const MAX_PASSWORD_LENGTH = 128;
|
|
3
4
|
/**
|
|
4
5
|
* 哈希密码
|
|
5
6
|
*/
|
|
@@ -20,6 +21,9 @@ export function validatePasswordStrength(password) {
|
|
|
20
21
|
if (password.length < 8) {
|
|
21
22
|
errors.push('密码至少 8 位');
|
|
22
23
|
}
|
|
24
|
+
if (password.length > MAX_PASSWORD_LENGTH) {
|
|
25
|
+
errors.push('Password cannot exceed 128 characters');
|
|
26
|
+
}
|
|
23
27
|
if (!/[A-Z]/.test(password)) {
|
|
24
28
|
errors.push('密码需要包含大写字母');
|
|
25
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,WAAW,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,IAAY;IAEZ,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IAIvD,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
package/dist/utils/validation.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2
|
+
const MAX_EMAIL_LENGTH = 254;
|
|
2
3
|
/**
|
|
3
4
|
* 验证邮箱格式
|
|
4
5
|
*/
|
|
5
6
|
export function validateEmail(email) {
|
|
6
|
-
return EMAIL_REGEX.test(email);
|
|
7
|
+
return typeof email === 'string' && email.length <= MAX_EMAIL_LENGTH && EMAIL_REGEX.test(email);
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
10
|
* 标准化邮箱(小写)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG,4BAA4B,CAAC;AACjD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,gBAAgB,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClG,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC"}
|