瞪眼法测试通过

This commit is contained in:
Kyaru
2026-06-25 21:18:13 +08:00
parent b9266dfc1f
commit 67c9330659
11 changed files with 919 additions and 49 deletions

18
db.js
View File

@@ -152,6 +152,24 @@ async function initDB() {
KEY idx_after_sales_status (handle_status) KEY idx_after_sales_status (handle_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后信息表' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后信息表'
`) `)
// ============ 2.2.1 添加缺失的字段(兼容已存在的表) ============
// contracts 表允许 customer_id 为空(采购合同可不关联客户)
try {
await pool.query(`ALTER TABLE contracts MODIFY COLUMN customer_id INT DEFAULT NULL COMMENT '客户ID (关联客户表)'`)
console.log('[init] contracts 表 customer_id 已允许为空')
} catch (e) {
// 修改失败则忽略
}
// contracts 表添加 supplier_id采购合同关联供应商
try {
await pool.query(`ALTER TABLE contracts ADD COLUMN supplier_id INT DEFAULT NULL COMMENT '供应商ID (关联供应商表)' AFTER customer_id`)
console.log('[init] contracts 表已添加 supplier_id 字段')
} catch (e) {
// 字段已存在则忽略
}
console.log('[init] 数据表初始化完成') console.log('[init] 数据表初始化完成')
// ============ 2.3 RBAC 权限相关表 ============ // ============ 2.3 RBAC 权限相关表 ============

View File

@@ -39,10 +39,10 @@ function getDataScope(user, resource) {
case 'contracts': case 'contracts':
if (roleName === 'finance') return {} if (roleName === 'finance') return {}
if (roleName === 'procurement_manager') { if (roleName === 'procurement_manager') {
return { where: 'c.type = ?', values: ['supply'] } return { where: 'type = ?', values: ['supply'] }
} }
if (roleName === 'franchise_manager' || roleName === 'operations_manager') { if (roleName === 'franchise_manager' || roleName === 'operations_manager') {
return { where: 'c.responsible_user_id = ? AND c.type = ?', values: [userId, 'franchise'] } return { where: 'responsible_user_id = ? AND type = ?', values: [userId, 'franchise'] }
} }
return {} return {}

View File

@@ -2,6 +2,24 @@
const { pool } = require('../db') const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions') const { getDataScope } = require('../middleware/permissions')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
if (!dateStr) return null
if (typeof dateStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return dateStr
}
try {
const date = new Date(dateStr)
if (isNaN(date.getTime())) return null
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch {
return null
}
}
function pagination(query) { function pagination(query) {
const page = Math.max(Number(query.page) || 1, 1) const page = Math.max(Number(query.page) || 1, 1)
const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100) const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100)
@@ -23,7 +41,7 @@ const LIST_SELECT = `
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { handle_status, customer_name } = req.query const { handle_status, customer_name, id, customer_id, feedback } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
@@ -35,11 +53,23 @@ async function list(req, res) {
} }
if (scope.where) { if (scope.where) {
// list 查询有 JOIN需要表别名前缀避免列名歧义 // list 查询有 JOIN需要表别名前缀避免列名歧义
const scopeCol = scope.tableAlias ? scope.where.replace('responsible_user_id', `${scope.tableAlias}.responsible_user_id`) : scope.where const scopeCol = scope.tableAlias ? scope.where.replace(/\bresponsible_user_id\b/g, `${scope.tableAlias}.responsible_user_id`) : scope.where
where += ' AND ' + scopeCol where += ' AND ' + scopeCol
params.push(...scope.values) params.push(...scope.values)
} }
if (id) {
where += ' AND a.id = ?'
params.push(id)
}
if (customer_id) {
where += ' AND a.customer_id = ?'
params.push(customer_id)
}
if (feedback) {
where += ' AND a.feedback LIKE ?'
params.push(`%${feedback}%`)
}
if (handle_status) { if (handle_status) {
where += ' AND a.handle_status = ?' where += ' AND a.handle_status = ?'
params.push(handle_status) params.push(handle_status)
@@ -145,7 +175,7 @@ async function create(req, res) {
employee_id || null, employee_id || null,
handle_method || null, handle_method || null,
handle_status || '待处理', handle_status || '待处理',
service_date || null, formatDate(service_date),
remark || null, remark || null,
ownerId] ownerId]
) )
@@ -201,7 +231,12 @@ async function update(req, res) {
for (const f of fields) { for (const f of fields) {
if (req.body[f] !== undefined) { if (req.body[f] !== undefined) {
sets.push(`${f} = ?`) sets.push(`${f} = ?`)
params.push(req.body[f]) // 日期字段需要格式化
if (f === 'service_date') {
params.push(formatDate(req.body[f]))
} else {
params.push(req.body[f])
}
} }
} }
if (sets.length === 0) { if (sets.length === 0) {

View File

@@ -2,6 +2,24 @@
const { pool } = require('../db') const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions') const { getDataScope } = require('../middleware/permissions')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
if (!dateStr) return null
if (typeof dateStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return dateStr
}
try {
const date = new Date(dateStr)
if (isNaN(date.getTime())) return null
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch {
return null
}
}
function pagination(query) { function pagination(query) {
const page = Math.max(Number(query.page) || 1, 1) const page = Math.max(Number(query.page) || 1, 1)
const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100) const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100)
@@ -9,14 +27,16 @@ function pagination(query) {
return { page, pageSize, offset } return { page, pageSize, offset }
} }
// 列表查询时 JOIN 客户名和员工名 // 列表查询时 JOIN 客户名、供应商名和员工名
const LIST_SELECT = ` const LIST_SELECT = `
SELECT SELECT
c.*, c.*,
cu.name AS customer_name, cu.name AS customer_name,
s.name AS supplier_name,
e.name AS employee_name e.name AS employee_name
FROM contracts c FROM contracts c
LEFT JOIN customers cu ON c.customer_id = cu.id LEFT JOIN customers cu ON c.customer_id = cu.id
LEFT JOIN suppliers s ON c.supplier_id = s.id
LEFT JOIN employees e ON c.employee_id = e.id LEFT JOIN employees e ON c.employee_id = e.id
` `
@@ -27,25 +47,35 @@ const DETAIL_SELECT = LIST_SELECT
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { status, customer_name, contract_no, employee_name } = req.query const { status, customer_name, contract_no, contract_name, employee_name, id, effective_date_start, effective_date_end } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
// 数据范围过滤 // 数据范围过滤(列表查询使用表别名 c需要加前缀
const scope = getDataScope(req.user, 'contracts') const scope = getDataScope(req.user, 'contracts')
if (scope.deny) { if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' }) return res.status(403).json({ code: 403, message: '无权访问此资源' })
} }
if (scope.where) { if (scope.where) {
where += ' AND ' + scope.where // 给 scope.where 中的字段加上表别名 c.
const scopedWhere = scope.where.replace(/\b(responsible_user_id|type)\b/g, 'c.$1')
where += ' AND ' + scopedWhere
params.push(...scope.values) params.push(...scope.values)
} }
if (id) {
where += ' AND c.id = ?'
params.push(id)
}
if (status) { if (status) {
where += ' AND c.status = ?' where += ' AND c.status = ?'
params.push(status) params.push(status)
} }
if (contract_name) {
where += ' AND c.contract_name LIKE ?'
params.push(`%${contract_name}%`)
}
if (customer_name) { if (customer_name) {
where += ' AND cu.name LIKE ?' where += ' AND cu.name LIKE ?'
params.push(`%${customer_name}%`) params.push(`%${customer_name}%`)
@@ -58,10 +88,19 @@ async function list(req, res) {
where += ' AND e.name LIKE ?' where += ' AND e.name LIKE ?'
params.push(`%${employee_name}%`) params.push(`%${employee_name}%`)
} }
if (effective_date_start) {
where += ' AND c.effective_date >= ?'
params.push(effective_date_start)
}
if (effective_date_end) {
where += ' AND c.effective_date <= ?'
params.push(effective_date_end)
}
const [[{ total }]] = await pool.query( const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM contracts c `SELECT COUNT(*) AS total FROM contracts c
LEFT JOIN customers cu ON c.customer_id = cu.id LEFT JOIN customers cu ON c.customer_id = cu.id
LEFT JOIN suppliers s ON c.supplier_id = s.id
LEFT JOIN employees e ON c.employee_id = e.id LEFT JOIN employees e ON c.employee_id = e.id
${where}`, ${where}`,
params params
@@ -100,7 +139,9 @@ async function detail(req, res) {
return res.status(403).json({ code: 403, message: '无权访问此资源' }) return res.status(403).json({ code: 403, message: '无权访问此资源' })
} }
if (scope.where) { if (scope.where) {
sql += ' AND ' + scope.where // detail 查询使用表别名 c需要给 scope.where 加上前缀
const scopedWhere = scope.where.replace(/\b(responsible_user_id|type)\b/g, 'c.$1')
sql += ' AND ' + scopedWhere
params.push(...scope.values) params.push(...scope.values)
} }
@@ -118,22 +159,43 @@ async function detail(req, res) {
// POST /api/contracts —— 新增 // POST /api/contracts —— 新增
async function create(req, res) { async function create(req, res) {
const { const {
customer_id, contract_name, contract_no, contract_content, customer_id, supplier_id, contract_name, contract_no, contract_content,
amount, effective_date, expiry_date, employee_id, status, remark, amount, effective_date, expiry_date, employee_id, status, remark,
} = req.body || {} } = req.body || {}
if (!customer_id) { // 根据角色限制合同类型
return res.status(400).json({ code: 400, message: '客户ID必填' }) const contractType = req.body.type || (req.user.roleName === 'procurement_manager' ? 'supply' : 'franchise')
if (req.user.roleName === 'procurement_manager' && contractType !== 'supply') {
return res.status(403).json({ code: 403, message: '采购经理只能创建采购合同' })
}
if (req.user.roleName === 'franchise_manager' && contractType !== 'franchise') {
return res.status(403).json({ code: 403, message: '招商经理只能创建加盟合同' })
}
if (contractType === 'supply' && !supplier_id) {
return res.status(400).json({ code: 400, message: '采购合同必须选择供应商' })
}
if (contractType !== 'supply' && !customer_id) {
return res.status(400).json({ code: 400, message: '加盟合同必须选择客户' })
} }
if (!contract_name) { if (!contract_name) {
return res.status(400).json({ code: 400, message: '合同名称必填' }) return res.status(400).json({ code: 400, message: '合同名称必填' })
} }
try { try {
// 校验客户是否存在 // 校验客户是否存在(非采购合同必填)
const [cust] = await pool.query('SELECT id FROM customers WHERE id = ?', [customer_id]) if (customer_id) {
if (cust.length === 0) { const [cust] = await pool.query('SELECT id FROM customers WHERE id = ?', [customer_id])
return res.status(400).json({ code: 400, message: '关联客户不存在' }) if (cust.length === 0) {
return res.status(400).json({ code: 400, message: '关联客户不存在' })
}
}
// 校验供应商是否存在(采购合同必填)
if (supplier_id) {
const [sup] = await pool.query('SELECT id FROM suppliers WHERE id = ?', [supplier_id])
if (sup.length === 0) {
return res.status(400).json({ code: 400, message: '关联供应商不存在' })
}
} }
// 校验业务员(如果填了) // 校验业务员(如果填了)
if (employee_id) { if (employee_id) {
@@ -145,18 +207,18 @@ async function create(req, res) {
const [result] = await pool.query( const [result] = await pool.query(
`INSERT INTO contracts `INSERT INTO contracts
(customer_id, contract_name, contract_no, contract_content, amount, effective_date, expiry_date, employee_id, status, remark, type, responsible_user_id) (customer_id, supplier_id, contract_name, contract_no, contract_content, amount, effective_date, expiry_date, employee_id, status, remark, type, responsible_user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[customer_id, contract_name, [customer_id || null, supplier_id || null, contract_name,
contract_no || null, contract_no || null,
contract_content || null, contract_content || null,
amount !== undefined ? amount : null, amount !== undefined ? amount : null,
effective_date || null, formatDate(effective_date),
expiry_date || null, formatDate(expiry_date),
employee_id || null, employee_id || null,
status || '生效', status || '生效',
remark || null, remark || null,
req.body.type || (req.user.roleName === 'procurement_manager' ? 'supply' : 'franchise'), contractType,
req.body.responsible_user_id || req.user.id] req.body.responsible_user_id || req.user.id]
) )
@@ -172,12 +234,12 @@ async function create(req, res) {
async function update(req, res) { async function update(req, res) {
const { id } = req.params const { id } = req.params
const fields = [ const fields = [
'customer_id', 'contract_name', 'contract_no', 'contract_content', 'customer_id', 'supplier_id', 'contract_name', 'contract_no', 'contract_content',
'amount', 'effective_date', 'expiry_date', 'employee_id', 'status', 'remark', 'type', 'responsible_user_id', 'amount', 'effective_date', 'expiry_date', 'employee_id', 'status', 'remark', 'type', 'responsible_user_id',
] ]
try { try {
// 确认记录存在且在数据范围内 // 确认记录存在且在数据范围内detail/update/delete 不用表别名)
let checkSql = 'SELECT id FROM contracts WHERE id = ?' let checkSql = 'SELECT id FROM contracts WHERE id = ?'
const checkParams = [id] const checkParams = [id]
const scope = getDataScope(req.user, 'contracts') const scope = getDataScope(req.user, 'contracts')
@@ -206,13 +268,24 @@ async function update(req, res) {
return res.status(400).json({ code: 400, message: '关联业务员不存在' }) return res.status(400).json({ code: 400, message: '关联业务员不存在' })
} }
} }
if (req.body.supplier_id) {
const [sup] = await pool.query('SELECT id FROM suppliers WHERE id = ?', [req.body.supplier_id])
if (sup.length === 0) {
return res.status(400).json({ code: 400, message: '关联供应商不存在' })
}
}
const sets = [] const sets = []
const params = [] const params = []
for (const f of fields) { for (const f of fields) {
if (req.body[f] !== undefined) { if (req.body[f] !== undefined) {
sets.push(`${f} = ?`) sets.push(`${f} = ?`)
params.push(req.body[f]) // 日期字段需要格式化
if (['effective_date', 'expiry_date'].includes(f)) {
params.push(formatDate(req.body[f]))
} else {
params.push(req.body[f])
}
} }
} }
if (sets.length === 0) { if (sets.length === 0) {

View File

@@ -14,7 +14,7 @@ function pagination(query) {
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { name, phone, province, city } = req.query const { name, phone, province, city, id } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
@@ -29,32 +29,40 @@ async function list(req, res) {
params.push(...scope.values) params.push(...scope.values)
} }
if (id) {
where += ' AND c.id = ?'
params.push(id)
}
if (name) { if (name) {
where += ' AND name LIKE ?' where += ' AND c.name LIKE ?'
params.push(`%${name}%`) params.push(`%${name}%`)
} }
if (phone) { if (phone) {
where += ' AND phone LIKE ?' where += ' AND c.phone LIKE ?'
params.push(`%${phone}%`) params.push(`%${phone}%`)
} }
if (province) { if (province) {
where += ' AND province = ?' where += ' AND c.province = ?'
params.push(province) params.push(province)
} }
if (city) { if (city) {
where += ' AND city = ?' where += ' AND c.city = ?'
params.push(city) params.push(city)
} }
// 查总数 // 查总数
const [[{ total }]] = await pool.query( const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM customers ${where}`, `SELECT COUNT(*) AS total FROM customers c ${where}`,
params params
) )
// 查分页数据 // 查分页数据JOIN users+employees 获取负责人姓名)
const [rows] = await pool.query( const [rows] = await pool.query(
`SELECT * FROM customers ${where} ORDER BY id DESC LIMIT ? OFFSET ?`, `SELECT c.*, e.name AS responsible_user_name
FROM customers c
LEFT JOIN users u ON c.responsible_user_id = u.id
LEFT JOIN employees e ON u.employee_id = e.id
${where} ORDER BY c.id DESC LIMIT ? OFFSET ?`,
[...params, pageSize, offset] [...params, pageSize, offset]
) )
@@ -201,6 +209,9 @@ async function remove(req, res) {
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
} catch (e) { } catch (e) {
console.error('[customers delete] error:', e) console.error('[customers delete] error:', e)
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该客户被合同或售后记录引用,无法删除' })
}
res.status(500).json({ code: 500, message: e.message }) res.status(500).json({ code: 500, message: e.message })
} }
} }

View File

@@ -2,6 +2,24 @@
const { pool } = require('../db') const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions') const { getDataScope } = require('../middleware/permissions')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
if (!dateStr) return null
if (typeof dateStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return dateStr // 已经是正确格式
}
try {
const date = new Date(dateStr)
if (isNaN(date.getTime())) return null
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch {
return null
}
}
function pagination(query) { function pagination(query) {
const page = Math.max(Number(query.page) || 1, 1) const page = Math.max(Number(query.page) || 1, 1)
const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100) const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100)
@@ -13,7 +31,7 @@ function pagination(query) {
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { name, department, status } = req.query const { id, name, status, department } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
@@ -28,18 +46,22 @@ async function list(req, res) {
params.push(...scope.values) params.push(...scope.values)
} }
if (id) {
where += ' AND id = ?'
params.push(Number(id))
}
if (name) { if (name) {
where += ' AND name LIKE ?' where += ' AND name LIKE ?'
params.push(`%${name}%`) params.push(`%${name}%`)
} }
if (department) {
where += ' AND department LIKE ?'
params.push(`%${department}%`)
}
if (status !== undefined && status !== '') { if (status !== undefined && status !== '') {
where += ' AND status = ?' where += ' AND status = ?'
params.push(Number(status)) params.push(Number(status))
} }
if (department) {
where += ' AND department LIKE ?'
params.push(`%${department}%`)
}
const [[{ total }]] = await pool.query( const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM employees ${where}`, `SELECT COUNT(*) AS total FROM employees ${where}`,
@@ -114,7 +136,7 @@ async function create(req, res) {
age !== undefined ? age : null, age !== undefined ? age : null,
education || null, education || null,
department || null, department || null,
entry_date || null, formatDate(entry_date),
position || null, position || null,
salary !== undefined ? salary : null, salary !== undefined ? salary : null,
phone || null, phone || null,
@@ -160,7 +182,12 @@ async function update(req, res) {
for (const f of fields) { for (const f of fields) {
if (req.body[f] !== undefined) { if (req.body[f] !== undefined) {
sets.push(`${f} = ?`) sets.push(`${f} = ?`)
params.push(req.body[f]) // 日期字段需要格式化
if (f === 'entry_date') {
params.push(formatDate(req.body[f]))
} else {
params.push(req.body[f])
}
} }
} }
if (sets.length === 0) { if (sets.length === 0) {
@@ -170,6 +197,11 @@ async function update(req, res) {
params.push(id) params.push(id)
await pool.query(`UPDATE employees SET ${sets.join(', ')} WHERE id = ?`, params) await pool.query(`UPDATE employees SET ${sets.join(', ')} WHERE id = ?`, params)
// 同步 department 到关联的 users 表
if (req.body.department !== undefined) {
await pool.query('UPDATE users SET department = ? WHERE employee_id = ?', [req.body.department, id])
}
const [rows] = await pool.query('SELECT * FROM employees WHERE id = ?', [id]) const [rows] = await pool.query('SELECT * FROM employees WHERE id = ?', [id])
res.json({ code: 0, message: 'ok', data: rows[0] }) res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) { } catch (e) {
@@ -196,10 +228,20 @@ async function remove(req, res) {
if (existing.length === 0) { if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '员工不存在或无权操作' }) return res.status(404).json({ code: 404, message: '员工不存在或无权操作' })
} }
// 禁止删除自己关联的员工
const [selfCheck] = await pool.query('SELECT id FROM users WHERE employee_id = ? AND id = ?', [id, req.user.id])
if (selfCheck.length > 0) {
return res.status(400).json({ code: 400, message: '不能删除自己的员工账号' })
}
// 同步删除关联的用户账号
await pool.query('DELETE FROM users WHERE employee_id = ?', [id])
await pool.query('DELETE FROM employees WHERE id = ?', [id]) await pool.query('DELETE FROM employees WHERE id = ?', [id])
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
} catch (e) { } catch (e) {
console.error('[employees delete] error:', e) console.error('[employees delete] error:', e)
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该员工被合同或售后记录引用,无法删除' })
}
res.status(500).json({ code: 500, message: e.message }) res.status(500).json({ code: 500, message: e.message })
} }
} }

View File

@@ -12,11 +12,15 @@ function pagination(query) {
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { name, type, supplier } = req.query const { name, type, supplier, id } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
if (id) {
where += ' AND id = ?'
params.push(id)
}
if (name) { if (name) {
where += ' AND name LIKE ?' where += ' AND name LIKE ?'
params.push(`%${name}%`) params.push(`%${name}%`)
@@ -150,6 +154,9 @@ async function remove(req, res) {
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
} catch (e) { } catch (e) {
console.error('[products delete] error:', e) console.error('[products delete] error:', e)
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该产品被引用,无法删除' })
}
res.status(500).json({ code: 500, message: e.message }) res.status(500).json({ code: 500, message: e.message })
} }
} }

View File

@@ -12,11 +12,15 @@ function pagination(query) {
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { name, type, status } = req.query const { id, name, type, status } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
if (id) {
where += ' AND id = ?'
params.push(Number(id))
}
if (name) { if (name) {
where += ' AND name LIKE ?' where += ' AND name LIKE ?'
params.push(`%${name}%`) params.push(`%${name}%`)
@@ -140,6 +144,9 @@ async function remove(req, res) {
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
} catch (e) { } catch (e) {
console.error('[suppliers delete] error:', e) console.error('[suppliers delete] error:', e)
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该供应商被合同引用,无法删除' })
}
res.status(500).json({ code: 500, message: e.message }) res.status(500).json({ code: 500, message: e.message })
} }
} }

View File

@@ -118,6 +118,21 @@ async function info(req, res) {
} }
} }
// GET /api/user/list —— 简易用户列表(用于下拉选择负责人,仅需登录)
async function simpleList(req, res) {
try {
const [rows] = await pool.query(
`SELECT u.id, u.username, e.name AS real_name
FROM users u LEFT JOIN employees e ON u.employee_id = e.id
WHERE u.is_active = 1 ORDER BY u.id`
)
res.json({ code: 0, message: 'ok', data: rows })
} catch (e) {
console.error('[user simpleList] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// POST /api/user/logout —— 登出(需要 token仅做应答 // POST /api/user/logout —— 登出(需要 token仅做应答
async function logout(req, res) { async function logout(req, res) {
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
@@ -162,7 +177,7 @@ async function changePassword(req, res) {
async function list(req, res) { async function list(req, res) {
try { try {
const { page, pageSize, offset } = pagination(req.query) const { page, pageSize, offset } = pagination(req.query)
const { username, role_id, is_active } = req.query const { username, real_name, id, role_id, is_active } = req.query
let where = 'WHERE 1=1' let where = 'WHERE 1=1'
const params = [] const params = []
@@ -171,6 +186,14 @@ async function list(req, res) {
where += ' AND u.username LIKE ?' where += ' AND u.username LIKE ?'
params.push(`%${username}%`) params.push(`%${username}%`)
} }
if (real_name) {
where += ' AND e.name LIKE ?'
params.push(`%${real_name}%`)
}
if (id) {
where += ' AND u.id = ?'
params.push(Number(id))
}
if (role_id) { if (role_id) {
where += ' AND u.role_id = ?' where += ' AND u.role_id = ?'
params.push(Number(role_id)) params.push(Number(role_id))
@@ -181,7 +204,9 @@ async function list(req, res) {
} }
const [[{ total }]] = await pool.query( const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM users u ${where}`, `SELECT COUNT(*) AS total FROM users u
LEFT JOIN employees e ON u.employee_id = e.id
${where}`,
params params
) )
@@ -369,11 +394,20 @@ async function remove(req, res) {
return res.status(400).json({ code: 400, message: '不能删除自己' }) return res.status(400).json({ code: 400, message: '不能删除自己' })
} }
const [existing] = await pool.query('SELECT id FROM users WHERE id = ?', [id]) const [existing] = await pool.query('SELECT id, role_id FROM users WHERE id = ?', [id])
if (existing.length === 0) { if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '用户不存在' }) return res.status(404).json({ code: 404, message: '用户不存在' })
} }
// 禁止删除最后一个管理员
const [adminRole] = await pool.query('SELECT id FROM roles WHERE name = ?', ['admin'])
if (adminRole.length > 0 && existing[0].role_id === adminRole[0].id) {
const [[{ cnt }]] = await pool.query('SELECT COUNT(*) AS cnt FROM users WHERE role_id = ?', [adminRole[0].id])
if (cnt <= 1) {
return res.status(400).json({ code: 400, message: '不能删除最后一个管理员' })
}
}
await pool.query('DELETE FROM users WHERE id = ?', [id]) await pool.query('DELETE FROM users WHERE id = ?', [id])
res.json({ code: 0, message: 'ok' }) res.json({ code: 0, message: 'ok' })
} catch (e) { } catch (e) {
@@ -382,4 +416,4 @@ async function remove(req, res) {
} }
} }
module.exports = { login, info, logout, changePassword, list, detail, create, update, remove } module.exports = { login, info, simpleList, logout, changePassword, list, detail, create, update, remove }

View File

@@ -35,6 +35,7 @@ function auth(req, res, next) {
// ============ 登录/登出/个人信息 ============ // ============ 登录/登出/个人信息 ============
app.post('/api/user/login', users.login) app.post('/api/user/login', users.login)
app.get('/api/user/info', auth, users.info) app.get('/api/user/info', auth, users.info)
app.get('/api/user/list', auth, checkPermission('user', 'manage'), users.simpleList)
app.post('/api/user/logout', auth, users.logout) app.post('/api/user/logout', auth, users.logout)
app.put('/api/user/password', auth, users.changePassword) app.put('/api/user/password', auth, users.changePassword)

642
yarn.lock Normal file
View File

@@ -0,0 +1,642 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895"
integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==
dependencies:
mime-types "^3.0.0"
negotiator "^1.0.0"
aws-ssl-profiles@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641"
integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==
bcryptjs@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/bcryptjs/-/bcryptjs-3.0.3.tgz#4b93d6a398c48bfc9f32ee65d301174a8a8ea56f"
integrity sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==
body-parser@^2.2.1:
version "2.3.0"
resolved "https://registry.npmmirror.com/body-parser/-/body-parser-2.3.0.tgz#6d8662f4d8c336028b8ac9aa24251b0ca64ba437"
integrity sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==
dependencies:
bytes "^3.1.2"
content-type "^2.0.0"
debug "^4.4.3"
http-errors "^2.0.1"
iconv-lite "^0.7.2"
on-finished "^2.4.1"
qs "^6.15.2"
raw-body "^3.0.2"
type-is "^2.1.0"
buffer-equal-constant-time@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
bytes@^3.1.2, bytes@~3.1.2:
version "3.1.2"
resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bound@^1.0.2:
version "1.0.4"
resolved "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
dependencies:
call-bind-apply-helpers "^1.0.2"
get-intrinsic "^1.3.0"
content-disposition@^1.0.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.1.0.tgz#f3db789c752d45564cc7e9e1e0b31790d4a38e17"
integrity sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==
content-type@^1.0.5:
version "1.0.5"
resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
content-type@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/content-type/-/content-type-2.0.0.tgz#2fb3ede69dffa0af78ca7c4ce7589680638b56df"
integrity sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==
cookie-signature@^1.2.1:
version "1.2.2"
resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793"
integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==
cookie@^0.7.1:
version "0.7.2"
resolved "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
debug@^4.4.0, debug@^4.4.3:
version "4.4.3"
resolved "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
denque@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
depd@^2.0.0, depd@~2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
dotenv@^17.4.2:
version "17.4.2"
resolved "https://registry.npmmirror.com/dotenv/-/dotenv-17.4.2.tgz#c07e54a746e11eba021dd9e1047ced5afdc1c034"
integrity sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.2"
resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b"
integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==
dependencies:
es-errors "^1.3.0"
escape-html@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@^1.8.1:
version "1.8.1"
resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^5.2.1:
version "5.2.1"
resolved "https://registry.npmmirror.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04"
integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==
dependencies:
accepts "^2.0.0"
body-parser "^2.2.1"
content-disposition "^1.0.0"
content-type "^1.0.5"
cookie "^0.7.1"
cookie-signature "^1.2.1"
debug "^4.4.0"
depd "^2.0.0"
encodeurl "^2.0.0"
escape-html "^1.0.3"
etag "^1.8.1"
finalhandler "^2.1.0"
fresh "^2.0.0"
http-errors "^2.0.0"
merge-descriptors "^2.0.0"
mime-types "^3.0.0"
on-finished "^2.4.1"
once "^1.4.0"
parseurl "^1.3.3"
proxy-addr "^2.0.7"
qs "^6.14.0"
range-parser "^1.2.1"
router "^2.2.0"
send "^1.1.0"
serve-static "^2.2.0"
statuses "^2.0.1"
type-is "^2.0.1"
vary "^1.1.2"
finalhandler@^2.1.0:
version "2.1.1"
resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099"
integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==
dependencies:
debug "^4.4.0"
encodeurl "^2.0.0"
escape-html "^1.0.3"
on-finished "^2.4.1"
parseurl "^1.3.3"
statuses "^2.0.1"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4"
integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
generate-function@^2.3.1:
version "2.3.1"
resolved "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
dependencies:
is-property "^1.0.2"
get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
version "1.3.0"
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.1.1"
function-bind "^1.1.2"
get-proto "^1.0.1"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.4"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003"
integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
dependencies:
function-bind "^1.1.2"
http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1:
version "2.0.1"
resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b"
integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==
dependencies:
depd "~2.0.0"
inherits "~2.0.4"
setprototypeof "~1.2.0"
statuses "~2.0.2"
toidentifier "~1.0.1"
iconv-lite@^0.7.2, iconv-lite@~0.7.0:
version "0.7.2"
resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e"
integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-promise@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
is-property@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==
jsonwebtoken@^9.0.3:
version "9.0.3"
resolved "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2"
integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==
dependencies:
jws "^4.0.1"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^7.5.4"
jwa@^2.0.1:
version "2.0.1"
resolved "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804"
integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==
dependencies:
buffer-equal-constant-time "^1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^4.0.1:
version "4.0.1"
resolved "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz#07edc1be8fac20e677b283ece261498bd38f0690"
integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==
dependencies:
jwa "^2.0.1"
safe-buffer "^5.0.1"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
long@^5.3.2:
version "5.3.2"
resolved "https://registry.npmmirror.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83"
integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==
lru.min@^1.1.0, lru.min@^1.1.4:
version "1.1.4"
resolved "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.4.tgz#6ea1737a8c1ba2300cc87ad46910a4bdffa0117b"
integrity sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
media-typer@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561"
integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==
merge-descriptors@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808"
integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==
mime-db@^1.54.0:
version "1.54.0"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
mime-types@^3.0.0, mime-types@^3.0.2:
version "3.0.2"
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab"
integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==
dependencies:
mime-db "^1.54.0"
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
mysql2@^3.22.5:
version "3.22.5"
resolved "https://registry.npmmirror.com/mysql2/-/mysql2-3.22.5.tgz#26c51c035ac577579ad239168015ad1eec321679"
integrity sha512-95uZ2TrPWAZdwpB3vvvDbmEMcNG8yIeNCyu6GUcr/QnWEE/wXm7+mhOCsdQfWQDTV7qYT/PDUZ4U4UPP4AsXqQ==
dependencies:
aws-ssl-profiles "^1.1.2"
denque "^2.1.0"
generate-function "^2.3.1"
iconv-lite "^0.7.2"
long "^5.3.2"
lru.min "^1.1.4"
named-placeholders "^1.1.6"
sql-escaper "^1.3.3"
named-placeholders@^1.1.6:
version "1.1.6"
resolved "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.6.tgz#c50c6920b43f258f59c16add1e56654f5cc02bb5"
integrity sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==
dependencies:
lru.min "^1.1.0"
negotiator@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
object-inspect@^1.13.3, object-inspect@^1.13.4:
version "1.13.4"
resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
on-finished@^2.4.1:
version "2.4.1"
resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
once@^1.4.0:
version "1.4.0"
resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
parseurl@^1.3.3:
version "1.3.3"
resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@^8.0.0:
version "8.4.2"
resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.4.2.tgz#795c420c4f7ca45c5b887366f622ee0c9852cccd"
integrity sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==
proxy-addr@^2.0.7:
version "2.0.7"
resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
qs@^6.14.0, qs@^6.15.2:
version "6.15.3"
resolved "https://registry.npmmirror.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b"
integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==
dependencies:
es-define-property "^1.0.1"
side-channel "^1.1.1"
range-parser@^1.2.1:
version "1.2.1"
resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@^3.0.2:
version "3.0.2"
resolved "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51"
integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==
dependencies:
bytes "~3.1.2"
http-errors "~2.0.1"
iconv-lite "~0.7.0"
unpipe "~1.0.0"
router@^2.2.0:
version "2.2.0"
resolved "https://registry.npmmirror.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef"
integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==
dependencies:
debug "^4.4.0"
depd "^2.0.0"
is-promise "^4.0.0"
parseurl "^1.3.3"
path-to-regexp "^8.0.0"
safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^7.5.4:
version "7.8.5"
resolved "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69"
integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==
send@^1.1.0, send@^1.2.0:
version "1.2.1"
resolved "https://registry.npmmirror.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed"
integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==
dependencies:
debug "^4.4.3"
encodeurl "^2.0.0"
escape-html "^1.0.3"
etag "^1.8.1"
fresh "^2.0.0"
http-errors "^2.0.1"
mime-types "^3.0.2"
ms "^2.1.3"
on-finished "^2.4.1"
range-parser "^1.2.1"
statuses "^2.0.2"
serve-static@^2.2.0:
version "2.2.1"
resolved "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9"
integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==
dependencies:
encodeurl "^2.0.0"
escape-html "^1.0.3"
parseurl "^1.3.3"
send "^1.2.0"
setprototypeof@~1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel-list@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz#c2e0b5a14a540aebee3bbc6c3f8666cc9b509127"
integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.4"
side-channel-map@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-weakmap@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-map "^1.0.1"
side-channel@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.1.tgz#ea02c62e05dc4bea67d4442f0fb71ee192f8e0ab"
integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.4"
side-channel-list "^1.0.1"
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
sql-escaper@^1.3.3:
version "1.3.3"
resolved "https://registry.npmmirror.com/sql-escaper/-/sql-escaper-1.3.3.tgz#65faf89f048d26bb9a75566b82b5990ddf8a5b7f"
integrity sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==
statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382"
integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==
toidentifier@~1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
type-is@^2.0.1, type-is@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/type-is/-/type-is-2.1.0.tgz#71d1a7053293582e16ac9f3ebaf1ab9aa49e5570"
integrity sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==
dependencies:
content-type "^2.0.0"
media-typer "^1.1.0"
mime-types "^3.0.0"
unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
vary@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
wrappy@1:
version "1.0.2"
resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==