init
这个提交包含在:
父节点
9e8571402d
当前提交
e2e7bb066b
235
database/database.js
普通文件
235
database/database.js
普通文件
@ -0,0 +1,235 @@
|
|||||||
|
const sqlite3 = require('sqlite3')
|
||||||
|
const path = require('path')
|
||||||
|
const { open } = require('sqlite')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
|
||||||
|
const dbPath = path.join(process.cwd(), 'database/files.db')
|
||||||
|
|
||||||
|
// 确保数据库目录存在
|
||||||
|
const dbDir = path.dirname(dbPath)
|
||||||
|
fs.ensureDirSync(dbDir)
|
||||||
|
|
||||||
|
async function initDatabase() {
|
||||||
|
const db = await open({
|
||||||
|
filename: dbPath,
|
||||||
|
driver: sqlite3.Database
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置数据库编码为 UTF-8
|
||||||
|
await db.exec('PRAGMA encoding = "UTF-8"')
|
||||||
|
await db.exec('PRAGMA foreign_keys = ON')
|
||||||
|
|
||||||
|
await db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS files (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
original_name TEXT NOT NULL,
|
||||||
|
file_name TEXT NOT NULL,
|
||||||
|
file_path TEXT NOT NULL,
|
||||||
|
file_size INTEGER NOT NULL,
|
||||||
|
mime_type TEXT NOT NULL,
|
||||||
|
md5 TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// 新增 OCR 结果表
|
||||||
|
await db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS ocr_results (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
file_id INTEGER NOT NULL,
|
||||||
|
ocr_data TEXT NOT NULL,
|
||||||
|
confidence REAL,
|
||||||
|
processing_time INTEGER,
|
||||||
|
recognized_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (file_id) REFERENCES files (id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(file_id)
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileService {
|
||||||
|
async getDb() {
|
||||||
|
const db = await open({
|
||||||
|
filename: dbPath,
|
||||||
|
driver: sqlite3.Database
|
||||||
|
})
|
||||||
|
|
||||||
|
// 确保每次连接都使用 UTF-8
|
||||||
|
await db.exec('PRAGMA encoding = "UTF-8"')
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFile(fileData) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
|
||||||
|
// 确保文件名正确存储
|
||||||
|
const result = await db.run(
|
||||||
|
`INSERT INTO files (original_name, file_name, file_path, file_size, mime_type, md5)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
|
[
|
||||||
|
fileData.originalName,
|
||||||
|
fileData.fileName,
|
||||||
|
fileData.filePath,
|
||||||
|
fileData.fileSize,
|
||||||
|
fileData.mimeType,
|
||||||
|
fileData.md5
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const file = await db.get(
|
||||||
|
'SELECT * FROM files WHERE id = ?',
|
||||||
|
result.lastID
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
|
||||||
|
return this.mapDatabaseToFileRecord(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFilesPaginated(page, pageSize) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
const offset = (page - 1) * pageSize
|
||||||
|
|
||||||
|
const files = await db.all(
|
||||||
|
'SELECT * FROM files ORDER BY created_at DESC LIMIT ? OFFSET ?',
|
||||||
|
[pageSize, offset]
|
||||||
|
)
|
||||||
|
|
||||||
|
const totalResult = await db.get('SELECT COUNT(*) as count FROM files')
|
||||||
|
const total = totalResult.count
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
|
||||||
|
return {
|
||||||
|
files: files.map(file => this.mapDatabaseToFileRecord(file)),
|
||||||
|
pagination: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
total,
|
||||||
|
totalPages: Math.ceil(total / pageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileById(id) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
const file = await db.get('SELECT * FROM files WHERE id = ?', [id])
|
||||||
|
await db.close()
|
||||||
|
return file ? this.mapDatabaseToFileRecord(file) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateFileMD5(id, md5) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
await db.run(
|
||||||
|
'UPDATE files SET md5 = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||||
|
[md5, id]
|
||||||
|
)
|
||||||
|
await db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
mapDatabaseToFileRecord(dbFile) {
|
||||||
|
// 确保从数据库读取时正确处理编码
|
||||||
|
let originalName = dbFile.original_name
|
||||||
|
try {
|
||||||
|
// 尝试解码,如果已经是正确编码则不会影响
|
||||||
|
originalName = decodeURIComponent(originalName)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('文件名解码失败,使用原值:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: dbFile.id,
|
||||||
|
originalName: originalName,
|
||||||
|
fileName: dbFile.file_name,
|
||||||
|
filePath: dbFile.file_path,
|
||||||
|
fileSize: dbFile.file_size,
|
||||||
|
mimeType: dbFile.mime_type,
|
||||||
|
md5: dbFile.md5,
|
||||||
|
createdAt: dbFile.created_at,
|
||||||
|
updatedAt: dbFile.updated_at
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async saveOcrResult(fileId, ocrData) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
|
||||||
|
// 将 OCR 数据转为 JSON 字符串存储
|
||||||
|
const ocrDataJson = JSON.stringify(ocrData)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 尝试更新已存在的记录
|
||||||
|
const result = await db.run(
|
||||||
|
`UPDATE ocr_results SET ocr_data = ?, confidence = ?, processing_time = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE file_id = ?`,
|
||||||
|
[ocrDataJson, ocrData.confidence, ocrData.processingTime, fileId]
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果没有更新任何行,则插入新记录
|
||||||
|
if (result.changes === 0) {
|
||||||
|
await db.run(
|
||||||
|
`INSERT INTO ocr_results (file_id, ocr_data, confidence, processing_time)
|
||||||
|
VALUES (?, ?, ?, ?)`,
|
||||||
|
[fileId, ocrDataJson, ocrData.confidence, ocrData.processingTime]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
return { success: true }
|
||||||
|
} catch (error) {
|
||||||
|
await db.close()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOcrResult(fileId) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
|
||||||
|
const result = await db.get(
|
||||||
|
'SELECT * FROM ocr_results WHERE file_id = ?',
|
||||||
|
[fileId]
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
ocr_data: JSON.parse(result.ocr_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateOcrText(fileId, newTextBlocks) {
|
||||||
|
const db = await this.getDb()
|
||||||
|
|
||||||
|
const existingResult = await this.getOcrResult(fileId)
|
||||||
|
if (!existingResult) {
|
||||||
|
throw new Error('没有找到OCR结果')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新文本块
|
||||||
|
const updatedOcrData = {
|
||||||
|
...existingResult.ocr_data,
|
||||||
|
textBlocks: newTextBlocks,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
manuallyCorrected: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const ocrDataJson = JSON.stringify(updatedOcrData)
|
||||||
|
|
||||||
|
await db.run(
|
||||||
|
'UPDATE ocr_results SET ocr_data = ?, updated_at = CURRENT_TIMESTAMP WHERE file_id = ?',
|
||||||
|
[ocrDataJson, fileId]
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.close()
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { initDatabase, FileService }
|
||||||
@ -11,13 +11,17 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"canvas": "^3.2.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-ts": "^1.0.2",
|
"crypto-ts": "^1.0.2",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
|
"node-tesseract-ocr": "^2.2.1",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"sqlite": "^5.1.1",
|
"sqlite": "^5.1.1",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
|
"tesseract.js": "^6.0.1",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
|
|||||||
317
server/server.js
317
server/server.js
@ -6,6 +6,11 @@ const fs = require('fs-extra')
|
|||||||
const { calculateFileMD5 } = require('./utils.js')
|
const { calculateFileMD5 } = require('./utils.js')
|
||||||
const { initDatabase, FileService } = require('../database/database.js')
|
const { initDatabase, FileService } = require('../database/database.js')
|
||||||
|
|
||||||
|
// 新增 OCR 相关依赖
|
||||||
|
const Tesseract = require('tesseract.js')
|
||||||
|
const sharp = require('sharp')
|
||||||
|
const { createCanvas, loadImage } = require('canvas')
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const PORT = 3000
|
const PORT = 3000
|
||||||
|
|
||||||
@ -13,9 +18,11 @@ const PORT = 3000
|
|||||||
initDatabase()
|
initDatabase()
|
||||||
const fileService = new FileService()
|
const fileService = new FileService()
|
||||||
|
|
||||||
// 确保上传目录存在
|
// 确保上传目录和临时目录存在
|
||||||
const uploadDir = path.join(process.cwd(), 'uploads')
|
const uploadDir = path.join(process.cwd(), 'uploads')
|
||||||
|
const tempDir = path.join(process.cwd(), 'temp')
|
||||||
fs.ensureDirSync(uploadDir)
|
fs.ensureDirSync(uploadDir)
|
||||||
|
fs.ensureDirSync(tempDir)
|
||||||
|
|
||||||
// 配置 multer - 修复中文文件名问题
|
// 配置 multer - 修复中文文件名问题
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
@ -93,23 +100,30 @@ app.post('/api/upload', upload.single('file'), async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取文件列表(分页)
|
// 修复获取文件列表接口 - 确保返回正确的数据结构
|
||||||
app.get('/api/files', async (req, res) => {
|
app.get('/api/files', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const page = parseInt(req.query.page) || 1
|
const page = parseInt(req.query.page) || 1
|
||||||
const pageSize = parseInt(req.query.pageSize) || 10
|
const pageSize = parseInt(req.query.pageSize) || 100
|
||||||
|
|
||||||
const result = await fileService.getFilesPaginated(page, pageSize)
|
const result = await fileService.getFilesPaginated(page, pageSize)
|
||||||
|
|
||||||
// 确保返回的数据使用 UTF-8 编码
|
// 返回统一的数据结构
|
||||||
res.json(result)
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result.files, // 直接返回文件数组
|
||||||
|
pagination: result.pagination
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Get files error:', error)
|
console.error('Get files error:', error)
|
||||||
res.status(500).json({ error: 'Failed to get files' })
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to get files: ' + error.message
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 其他接口保持不变...
|
// MD5 检查接口
|
||||||
app.post('/api/files/:id/check-md5', async (req, res) => {
|
app.post('/api/files/:id/check-md5', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const fileId = parseInt(req.params.id)
|
const fileId = parseInt(req.params.id)
|
||||||
@ -134,6 +148,7 @@ app.post('/api/files/:id/check-md5', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 更新 MD5 接口
|
||||||
app.put('/api/files/:id/update-md5', async (req, res) => {
|
app.put('/api/files/:id/update-md5', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const fileId = parseInt(req.params.id)
|
const fileId = parseInt(req.params.id)
|
||||||
@ -147,6 +162,294 @@ app.put('/api/files/:id/update-md5', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 新增 OCR 识别接口
|
||||||
|
app.post('/api/ocr/recognize', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { fileId, page } = req.body
|
||||||
|
|
||||||
|
if (!fileId) {
|
||||||
|
return res.status(400).json({ error: 'File ID is required' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = await fileService.getFileById(parseInt(fileId))
|
||||||
|
if (!file) {
|
||||||
|
return res.status(404).json({ error: 'File not found' })
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`开始OCR识别: ${file.originalName}`)
|
||||||
|
|
||||||
|
// 预处理图像
|
||||||
|
const processedImagePath = await preprocessImage(file.filePath)
|
||||||
|
|
||||||
|
// 使用 Tesseract 进行 OCR 识别
|
||||||
|
const result = await performOCR(processedImagePath)
|
||||||
|
|
||||||
|
// 清理临时文件
|
||||||
|
await fs.remove(processedImagePath)
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
textBlocks: result.textBlocks,
|
||||||
|
totalPages: result.totalPages || 1,
|
||||||
|
processingTime: result.processingTime,
|
||||||
|
confidence: result.confidence
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('OCR recognition error:', error)
|
||||||
|
res.status(500).json({ error: 'OCR recognition failed: ' + error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加 OCR 结果相关的 API 接口
|
||||||
|
|
||||||
|
// 保存 OCR 结果
|
||||||
|
app.post('/api/ocr/save-result', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { fileId, ocrData } = req.body
|
||||||
|
|
||||||
|
if (!fileId || !ocrData) {
|
||||||
|
return res.status(400).json({ error: '文件ID和OCR数据是必需的' })
|
||||||
|
}
|
||||||
|
|
||||||
|
await fileService.saveOcrResult(parseInt(fileId), ocrData)
|
||||||
|
|
||||||
|
res.json({ success: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存OCR结果失败:', error)
|
||||||
|
res.status(500).json({ error: '保存OCR结果失败: ' + error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取 OCR 结果
|
||||||
|
app.get('/api/ocr/result/:fileId', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const fileId = parseInt(req.params.fileId)
|
||||||
|
const result = await fileService.getOcrResult(fileId)
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result.ocr_data
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
error: '未找到OCR结果'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取OCR结果失败:', error)
|
||||||
|
res.status(500).json({ error: '获取OCR结果失败: ' + error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新 OCR 文本(人工纠错)
|
||||||
|
app.put('/api/ocr/update-text', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { fileId, textBlocks } = req.body
|
||||||
|
|
||||||
|
if (!fileId || !textBlocks) {
|
||||||
|
return res.status(400).json({ error: '文件ID和文本数据是必需的' })
|
||||||
|
}
|
||||||
|
|
||||||
|
await fileService.updateOcrText(parseInt(fileId), textBlocks)
|
||||||
|
|
||||||
|
res.json({ success: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新OCR文本失败:', error)
|
||||||
|
res.status(500).json({ error: '更新OCR文本失败: ' + error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 图像预处理函数
|
||||||
|
async function preprocessImage(imagePath) {
|
||||||
|
const tempOutputPath = path.join(tempDir, `preprocessed-${Date.now()}.png`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 sharp 进行图像预处理
|
||||||
|
await sharp(imagePath)
|
||||||
|
.grayscale() // 转为灰度图
|
||||||
|
.normalize() // 标准化图像
|
||||||
|
.linear(1.5, 0) // 增加对比度
|
||||||
|
.sharpen() // 锐化
|
||||||
|
.png()
|
||||||
|
.toFile(tempOutputPath)
|
||||||
|
|
||||||
|
return tempOutputPath
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Image preprocessing failed:', error)
|
||||||
|
// 如果预处理失败,返回原图
|
||||||
|
return imagePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCR 识别函数
|
||||||
|
async function performOCR(imagePath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const startTime = Date.now()
|
||||||
|
|
||||||
|
Tesseract.recognize(
|
||||||
|
imagePath,
|
||||||
|
'chi_sim+eng', // 中文简体 + 英文
|
||||||
|
{
|
||||||
|
logger: m => console.log(m),
|
||||||
|
tessedit_pageseg_mode: Tesseract.PSM.AUTO,
|
||||||
|
tessedit_char_whitelist: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\u4e00-\u9fa5,。!?;:"'/'()【】《》…—·'
|
||||||
|
}
|
||||||
|
).then(({ data: { text, confidence } }) => {
|
||||||
|
const processingTime = Date.now() - startTime
|
||||||
|
|
||||||
|
// 解析文本块
|
||||||
|
const textBlocks = parseOCRText(text)
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
textBlocks,
|
||||||
|
confidence,
|
||||||
|
processingTime
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 OCR 文本结果
|
||||||
|
function parseOCRText(text) {
|
||||||
|
const blocks = []
|
||||||
|
const lines = text.split('\n').filter(line => line.trim())
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim()
|
||||||
|
if (!trimmedLine) continue
|
||||||
|
|
||||||
|
// 检测参考文献
|
||||||
|
if (isReference(trimmedLine)) {
|
||||||
|
blocks.push({
|
||||||
|
type: 'reference',
|
||||||
|
content: trimmedLine
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 检测引用
|
||||||
|
else if (isCitation(trimmedLine)) {
|
||||||
|
blocks.push({
|
||||||
|
type: 'citation',
|
||||||
|
content: trimmedLine.replace(/^\[\d+\]\s*/, ''),
|
||||||
|
number: extractCitationNumber(trimmedLine)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 检测图片标记
|
||||||
|
else if (isImageMarker(trimmedLine)) {
|
||||||
|
blocks.push({
|
||||||
|
type: 'image',
|
||||||
|
content: trimmedLine
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 普通文本
|
||||||
|
else {
|
||||||
|
blocks.push({
|
||||||
|
type: 'text',
|
||||||
|
content: trimmedLine
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数
|
||||||
|
function isReference(text) {
|
||||||
|
const refPatterns = [
|
||||||
|
/^参考文献/i,
|
||||||
|
/^references/i,
|
||||||
|
/^bibliography/i,
|
||||||
|
/^\[?\d+\]?\s*\.?\s*[A-Za-z].*\.\s*\d{4}/
|
||||||
|
]
|
||||||
|
return refPatterns.some(pattern => pattern.test(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCitation(text) {
|
||||||
|
return /^\[\d+\]/.test(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractCitationNumber(text) {
|
||||||
|
const match = text.match(/^\[(\d+)\]/)
|
||||||
|
return match ? parseInt(match[1]) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageMarker(text) {
|
||||||
|
const imagePatterns = [
|
||||||
|
/^图\s*\d+/i,
|
||||||
|
/^figure\s*\d+/i,
|
||||||
|
/^图片\d*/i
|
||||||
|
]
|
||||||
|
return imagePatterns.some(pattern => pattern.test(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件预览接口
|
||||||
|
app.get('/api/files/:id/preview', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const fileId = parseInt(req.params.id)
|
||||||
|
const file = await fileService.getFileById(fileId)
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return res.status(404).json({ error: 'File not found' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!fs.existsSync(file.filePath)) {
|
||||||
|
return res.status(404).json({ error: 'File not found on disk' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置正确的 Content-Type
|
||||||
|
res.setHeader('Content-Type', file.mimeType)
|
||||||
|
|
||||||
|
// 直接发送文件
|
||||||
|
res.sendFile(path.resolve(file.filePath))
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('File preview error:', error)
|
||||||
|
res.status(500).json({ error: 'Failed to get file preview' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取文件缩略图接口
|
||||||
|
app.get('/api/files/:id/thumbnail', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const fileId = parseInt(req.params.id)
|
||||||
|
const file = await fileService.getFileById(fileId)
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return res.status(404).json({ error: 'File not found' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只对图片生成缩略图
|
||||||
|
if (!file.mimeType.startsWith('image/')) {
|
||||||
|
return res.status(400).json({ error: 'Not an image file' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const thumbnailPath = path.join(tempDir, `thumbnail-${fileId}.jpg`)
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
await sharp(file.filePath)
|
||||||
|
.resize(100, 100, {
|
||||||
|
fit: 'inside',
|
||||||
|
withoutEnlargement: true
|
||||||
|
})
|
||||||
|
.jpeg({ quality: 80 })
|
||||||
|
.toFile(thumbnailPath)
|
||||||
|
|
||||||
|
res.sendFile(path.resolve(thumbnailPath))
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Thumbnail generation error:', error)
|
||||||
|
// 如果缩略图生成失败,返回原图
|
||||||
|
res.sendFile(path.resolve(file.filePath))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 健康检查接口
|
// 健康检查接口
|
||||||
app.get('/api/health', (req, res) => {
|
app.get('/api/health', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
@ -18,6 +18,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>文件名</th>
|
<th>文件名</th>
|
||||||
<th>大小</th>
|
<th>大小</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>OCR状态</th>
|
||||||
<th>MD5</th>
|
<th>MD5</th>
|
||||||
<th>上传时间</th>
|
<th>上传时间</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
@ -25,11 +27,18 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="file in files" :key="file.id">
|
<tr v-for="file in files" :key="file.id">
|
||||||
<td class="filename-cell">{{ decodeFileName(file.originalName) }}</td>
|
<td class="filename-cell" :title="decodeFileName(file.originalName)">
|
||||||
|
{{ decodeFileName(file.originalName) }}
|
||||||
|
</td>
|
||||||
<td>{{ formatFileSize(file.fileSize) }}</td>
|
<td>{{ formatFileSize(file.fileSize) }}</td>
|
||||||
|
<td class="file-type">{{ getFileType(file.mimeType) }}</td>
|
||||||
|
<td class="ocr-status">
|
||||||
|
<span v-if="file.hasOcrResult" class="ocr-done">✓ 已识别</span>
|
||||||
|
<span v-else class="ocr-pending">待识别</span>
|
||||||
|
</td>
|
||||||
<td class="md5-cell">{{ file.md5 }}</td>
|
<td class="md5-cell">{{ file.md5 }}</td>
|
||||||
<td>{{ formatDate(file.createdAt) }}</td>
|
<td>{{ formatDate(file.createdAt) }}</td>
|
||||||
<td>
|
<td class="action-buttons">
|
||||||
<button
|
<button
|
||||||
@click="checkFileMD5(file)"
|
@click="checkFileMD5(file)"
|
||||||
class="check-button"
|
class="check-button"
|
||||||
@ -38,9 +47,10 @@
|
|||||||
{{ checkingFileId === file.id ? '检查中...' : '检查MD5' }}
|
{{ checkingFileId === file.id ? '检查中...' : '检查MD5' }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn-ocr"
|
class="ocr-button"
|
||||||
@click="handleOcr(file)"
|
@click="handleOcr(file)"
|
||||||
title="OCR识别"
|
title="OCR识别"
|
||||||
|
:disabled="!file.mimeType.startsWith('image/')"
|
||||||
>
|
>
|
||||||
🔍 OCR
|
🔍 OCR
|
||||||
</button>
|
</button>
|
||||||
@ -79,7 +89,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import apiManager from '../utils/apiManager.ts'
|
import apiManager from '../utils/apiManager'
|
||||||
|
|
||||||
interface FileRecord {
|
interface FileRecord {
|
||||||
id: number
|
id: number
|
||||||
@ -91,6 +101,7 @@ interface FileRecord {
|
|||||||
md5: string
|
md5: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
updatedAt: string
|
updatedAt: string
|
||||||
|
hasOcrResult?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PaginationInfo {
|
interface PaginationInfo {
|
||||||
@ -100,9 +111,11 @@ interface PaginationInfo {
|
|||||||
totalPages: number
|
totalPages: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileListResponse {
|
interface ApiResponse {
|
||||||
files: FileRecord[]
|
success: boolean
|
||||||
|
data: FileRecord[]
|
||||||
pagination: PaginationInfo
|
pagination: PaginationInfo
|
||||||
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MD5CheckResponse {
|
interface MD5CheckResponse {
|
||||||
@ -128,24 +141,26 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
// 监听网络状态变化
|
// 监听网络状态变化
|
||||||
const handleNetworkChange = (event: any) => {
|
const handleNetworkChange = (event: CustomEvent<{ baseUrl: string; isOnline: boolean }>) => {
|
||||||
isOnline.value = event.detail.isOnline
|
isOnline.value = event.detail.isOnline
|
||||||
console.log('文件列表: 网络状态变更', event.detail)
|
console.log('文件列表: 网络状态变更', event.detail)
|
||||||
|
|
||||||
// 网络状态变化时重新加载文件列表
|
// 网络状态变化时重新加载文件列表
|
||||||
loadFiles(1)
|
loadFiles(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOcr = (file: FileRecord): void => {
|
const handleOcr = (file: FileRecord): void => {
|
||||||
emit('ocrRecognize', file)
|
emit('ocrRecognize', file)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isOnline.value = apiManager.isOnlineMode()
|
isOnline.value = apiManager.isOnlineMode()
|
||||||
window.addEventListener('apiBaseUrlChanged', handleNetworkChange)
|
window.addEventListener('apiBaseUrlChanged', handleNetworkChange as EventListener)
|
||||||
loadFiles()
|
loadFiles()
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('apiBaseUrlChanged', handleNetworkChange)
|
window.removeEventListener('apiBaseUrlChanged', handleNetworkChange as EventListener)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 解码文件名显示
|
// 解码文件名显示
|
||||||
@ -158,17 +173,55 @@ const decodeFileName = (fileName: string): string => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取文件类型显示
|
||||||
|
const getFileType = (mimeType: string): string => {
|
||||||
|
if (mimeType.startsWith('image/')) return '图片'
|
||||||
|
if (mimeType.includes('pdf')) return 'PDF'
|
||||||
|
if (mimeType.includes('word') || mimeType.includes('document')) return 'Word'
|
||||||
|
if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'Excel'
|
||||||
|
if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return 'PPT'
|
||||||
|
if (mimeType.includes('text')) return '文本'
|
||||||
|
return '其他'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否有OCR结果
|
||||||
|
const checkOcrStatus = async (file: FileRecord): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const result = await apiManager.get<{ success: boolean; data: any }>(
|
||||||
|
`/api/ocr/result/${file.id}`
|
||||||
|
)
|
||||||
|
return result.success && result.data !== null
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadFiles = async (page: number = pagination.value.page): Promise<void> => {
|
const loadFiles = async (page: number = pagination.value.page): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const result: FileListResponse = await apiManager.request(
|
const result: ApiResponse = await apiManager.request<ApiResponse>(
|
||||||
`/api/files?page=${page}&pageSize=${pagination.value.pageSize}`
|
`/api/files?page=${page}&pageSize=${pagination.value.pageSize}`
|
||||||
)
|
)
|
||||||
|
|
||||||
files.value = result.files
|
if (result.success && result.data) {
|
||||||
|
// 检查每个文件的OCR状态
|
||||||
|
const filesWithOcrStatus = await Promise.all(
|
||||||
|
result.data.map(async (file) => {
|
||||||
|
const hasOcrResult = await checkOcrStatus(file)
|
||||||
|
return { ...file, hasOcrResult }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
files.value = filesWithOcrStatus
|
||||||
pagination.value = result.pagination
|
pagination.value = result.pagination
|
||||||
|
console.log(`成功加载 ${files.value.length} 个文件`)
|
||||||
|
} else {
|
||||||
|
console.error('获取文件列表失败:', result.error)
|
||||||
|
files.value = []
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载文件列表失败:', error)
|
console.error('加载文件列表失败:', error)
|
||||||
alert('加载文件列表失败,请检查服务是否正常运行')
|
alert('加载文件列表失败,请检查服务是否正常运行')
|
||||||
|
files.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +229,7 @@ const checkFileMD5 = async (file: FileRecord): Promise<void> => {
|
|||||||
checkingFileId.value = file.id
|
checkingFileId.value = file.id
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result: MD5CheckResponse = await apiManager.request(
|
const result: MD5CheckResponse = await apiManager.request<MD5CheckResponse>(
|
||||||
`/api/files/${file.id}/check-md5`,
|
`/api/files/${file.id}/check-md5`,
|
||||||
{ method: 'POST' }
|
{ method: 'POST' }
|
||||||
)
|
)
|
||||||
@ -233,6 +286,7 @@ defineExpose({
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden; /* 确保滚动正常工作 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-list-header {
|
.file-list-header {
|
||||||
@ -240,6 +294,7 @@ defineExpose({
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
flex-shrink: 0; /* 防止头部被压缩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-actions {
|
.header-actions {
|
||||||
@ -255,6 +310,7 @@ defineExpose({
|
|||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.refresh-button:hover {
|
.refresh-button:hover {
|
||||||
@ -285,19 +341,19 @@ defineExpose({
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-table {
|
.file-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
background: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-table th,
|
.file-table th,
|
||||||
.file-table td {
|
.file-table td {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-table th {
|
.file-table th {
|
||||||
@ -305,22 +361,52 @@ defineExpose({
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-table tr:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filename-cell {
|
.filename-cell {
|
||||||
max-width: 250px;
|
max-width: 200px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md5-cell {
|
.file-type {
|
||||||
font-family: monospace;
|
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
max-width: 200px;
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ocr-status {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ocr-done {
|
||||||
|
color: #28a745;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ocr-pending {
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md5-cell {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
max-width: 120px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-button {
|
.check-button {
|
||||||
@ -330,7 +416,8 @@ defineExpose({
|
|||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.875rem;
|
font-size: 0.75rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-button:hover:not(:disabled) {
|
.check-button:hover:not(:disabled) {
|
||||||
@ -342,11 +429,35 @@ defineExpose({
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ocr-button {
|
||||||
|
background: #3498db;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ocr-button:hover:not(:disabled) {
|
||||||
|
background: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ocr-button:disabled {
|
||||||
|
background: #bdc3c7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2rem;
|
padding: 3rem;
|
||||||
color: #666;
|
color: #6c757d;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
@ -356,6 +467,9 @@ defineExpose({
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
flex-shrink: 0; /* 防止分页被压缩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-button {
|
.page-button {
|
||||||
@ -365,6 +479,7 @@ defineExpose({
|
|||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-button:hover:not(:disabled) {
|
.page-button:hover:not(:disabled) {
|
||||||
@ -377,6 +492,7 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-info {
|
.page-info {
|
||||||
color: #666;
|
color: #6c757d;
|
||||||
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
文件差异内容过多而无法显示
加载差异
@ -90,6 +90,8 @@ const handleOcrRecognize = (file: FileRecord): void => {
|
|||||||
console.log('导航成功:', res)
|
console.log('导航成功:', res)
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('导航错误:', error)
|
console.error('导航错误:', error)
|
||||||
|
// 如果路由跳转失败,尝试使用 hash 方式
|
||||||
|
window.location.hash = `/ocr?fileId=${file.id}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -104,6 +106,8 @@ const handleOcrRecognize = (file: FileRecord): void => {
|
|||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
@ -120,6 +124,7 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
flex-shrink: 0; /* 防止头部被压缩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.network-indicator {
|
.network-indicator {
|
||||||
@ -143,6 +148,6 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
overflow: hidden;
|
overflow: hidden; /* 改为 hidden,内部组件自己处理滚动 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
289
yarn.lock
289
yarn.lock
@ -42,6 +42,13 @@
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
global-agent "^3.0.0"
|
global-agent "^3.0.0"
|
||||||
|
|
||||||
|
"@emnapi/runtime@^1.7.0":
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.0.tgz#d7ef3832df8564fe5903bf0567aedbd19538ecbe"
|
||||||
|
integrity sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@epic-web/invariant@^1.0.0":
|
"@epic-web/invariant@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813"
|
resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813"
|
||||||
@ -216,6 +223,153 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^11.0.2"
|
"@hapi/hoek" "^11.0.2"
|
||||||
|
|
||||||
|
"@img/colour@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311"
|
||||||
|
integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==
|
||||||
|
|
||||||
|
"@img/sharp-darwin-arm64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86"
|
||||||
|
integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-darwin-arm64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-darwin-x64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b"
|
||||||
|
integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-darwin-x64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-libvips-darwin-arm64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43"
|
||||||
|
integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-darwin-x64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc"
|
||||||
|
integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-arm64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318"
|
||||||
|
integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-arm@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d"
|
||||||
|
integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-ppc64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7"
|
||||||
|
integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-riscv64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de"
|
||||||
|
integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-s390x@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec"
|
||||||
|
integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-x64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce"
|
||||||
|
integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06"
|
||||||
|
integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64@1.2.4":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75"
|
||||||
|
integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==
|
||||||
|
|
||||||
|
"@img/sharp-linux-arm64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc"
|
||||||
|
integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-arm64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linux-arm@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d"
|
||||||
|
integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-arm" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linux-ppc64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813"
|
||||||
|
integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-ppc64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linux-riscv64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60"
|
||||||
|
integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-riscv64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linux-s390x@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7"
|
||||||
|
integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-s390x" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linux-x64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8"
|
||||||
|
integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linux-x64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linuxmusl-arm64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086"
|
||||||
|
integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-linuxmusl-x64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f"
|
||||||
|
integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64" "1.2.4"
|
||||||
|
|
||||||
|
"@img/sharp-wasm32@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0"
|
||||||
|
integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==
|
||||||
|
dependencies:
|
||||||
|
"@emnapi/runtime" "^1.7.0"
|
||||||
|
|
||||||
|
"@img/sharp-win32-arm64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a"
|
||||||
|
integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==
|
||||||
|
|
||||||
|
"@img/sharp-win32-ia32@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de"
|
||||||
|
integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==
|
||||||
|
|
||||||
|
"@img/sharp-win32-x64@0.34.5":
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8"
|
||||||
|
integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.5.5":
|
"@jridgewell/sourcemap-codec@^1.5.5":
|
||||||
version "1.5.5"
|
version "1.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||||
@ -762,6 +916,11 @@ bl@^4.0.3:
|
|||||||
inherits "^2.0.4"
|
inherits "^2.0.4"
|
||||||
readable-stream "^3.4.0"
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
|
bmp-js@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233"
|
||||||
|
integrity sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==
|
||||||
|
|
||||||
body-parser@^2.2.0:
|
body-parser@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa"
|
||||||
@ -878,6 +1037,14 @@ call-bound@^1.0.2:
|
|||||||
call-bind-apply-helpers "^1.0.2"
|
call-bind-apply-helpers "^1.0.2"
|
||||||
get-intrinsic "^1.3.0"
|
get-intrinsic "^1.3.0"
|
||||||
|
|
||||||
|
canvas@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/canvas/-/canvas-3.2.0.tgz#877c51aabdb99cbb5b2b378138a6cdd681e9d390"
|
||||||
|
integrity sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==
|
||||||
|
dependencies:
|
||||||
|
node-addon-api "^7.0.0"
|
||||||
|
prebuild-install "^7.1.3"
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||||
@ -1089,7 +1256,7 @@ depd@2.0.0, depd@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||||
|
|
||||||
detect-libc@^2.0.0:
|
detect-libc@^2.0.0, detect-libc@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
|
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
|
||||||
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
|
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
|
||||||
@ -1611,6 +1778,11 @@ iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||||
|
|
||||||
|
idb-keyval@^6.2.0:
|
||||||
|
version "6.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.2.tgz#b0171b5f73944854a3291a5cdba8e12768c4854a"
|
||||||
|
integrity sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==
|
||||||
|
|
||||||
ieee754@^1.1.13:
|
ieee754@^1.1.13:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
@ -1674,6 +1846,11 @@ is-promise@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
|
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
|
||||||
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
|
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
|
||||||
|
|
||||||
|
is-url@^1.2.4:
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
|
||||||
|
integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
@ -1975,6 +2152,13 @@ node-addon-api@^7.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||||
|
|
||||||
|
node-fetch@^2.6.9:
|
||||||
|
version "2.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||||
|
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-gyp@8.x:
|
node-gyp@8.x:
|
||||||
version "8.4.1"
|
version "8.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
|
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
|
||||||
@ -1991,6 +2175,11 @@ node-gyp@8.x:
|
|||||||
tar "^6.1.2"
|
tar "^6.1.2"
|
||||||
which "^2.0.2"
|
which "^2.0.2"
|
||||||
|
|
||||||
|
node-tesseract-ocr@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-tesseract-ocr/-/node-tesseract-ocr-2.2.1.tgz#465fea1a1acd6720efb582d5f6c2bcbb82cce94d"
|
||||||
|
integrity sha512-Q9cD79JGpPNQBxbi1fV+OAsTxYKLpx22sagsxSyKbu1u+t6UarApf5m32uVc8a5QAP1Wk7fIPN0aJFGGEE9DyQ==
|
||||||
|
|
||||||
nopt@^5.0.0:
|
nopt@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
|
||||||
@ -2042,6 +2231,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
|
opencollective-postinstall@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
|
||||||
|
integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
|
||||||
|
|
||||||
p-cancelable@^2.0.0:
|
p-cancelable@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
||||||
@ -2103,7 +2297,7 @@ postcss@^8.5.6:
|
|||||||
picocolors "^1.1.1"
|
picocolors "^1.1.1"
|
||||||
source-map-js "^1.2.1"
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
prebuild-install@^7.1.1:
|
prebuild-install@^7.1.1, prebuild-install@^7.1.3:
|
||||||
version "7.1.3"
|
version "7.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec"
|
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec"
|
||||||
integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==
|
integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==
|
||||||
@ -2206,6 +2400,11 @@ readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable
|
|||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.3:
|
||||||
|
version "0.13.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||||
|
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||||
|
|
||||||
require-directory@^2.1.1:
|
require-directory@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||||
@ -2316,7 +2515,7 @@ semver@^6.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.3.2, semver@^7.3.5:
|
semver@^7.3.2, semver@^7.3.5, semver@^7.7.3:
|
||||||
version "7.7.3"
|
version "7.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
||||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||||
@ -2365,6 +2564,40 @@ setprototypeof@1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||||
|
|
||||||
|
sharp@^0.34.5:
|
||||||
|
version "0.34.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0"
|
||||||
|
integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==
|
||||||
|
dependencies:
|
||||||
|
"@img/colour" "^1.0.0"
|
||||||
|
detect-libc "^2.1.2"
|
||||||
|
semver "^7.7.3"
|
||||||
|
optionalDependencies:
|
||||||
|
"@img/sharp-darwin-arm64" "0.34.5"
|
||||||
|
"@img/sharp-darwin-x64" "0.34.5"
|
||||||
|
"@img/sharp-libvips-darwin-arm64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-darwin-x64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-arm" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-arm64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-ppc64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-riscv64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-s390x" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linux-x64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64" "1.2.4"
|
||||||
|
"@img/sharp-linux-arm" "0.34.5"
|
||||||
|
"@img/sharp-linux-arm64" "0.34.5"
|
||||||
|
"@img/sharp-linux-ppc64" "0.34.5"
|
||||||
|
"@img/sharp-linux-riscv64" "0.34.5"
|
||||||
|
"@img/sharp-linux-s390x" "0.34.5"
|
||||||
|
"@img/sharp-linux-x64" "0.34.5"
|
||||||
|
"@img/sharp-linuxmusl-arm64" "0.34.5"
|
||||||
|
"@img/sharp-linuxmusl-x64" "0.34.5"
|
||||||
|
"@img/sharp-wasm32" "0.34.5"
|
||||||
|
"@img/sharp-win32-arm64" "0.34.5"
|
||||||
|
"@img/sharp-win32-ia32" "0.34.5"
|
||||||
|
"@img/sharp-win32-x64" "0.34.5"
|
||||||
|
|
||||||
shebang-command@^2.0.0:
|
shebang-command@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||||
@ -2594,6 +2827,26 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2:
|
|||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
tesseract.js-core@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz#6f25da94f70f8e8f02aff47a43be61d49e6f67c3"
|
||||||
|
integrity sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==
|
||||||
|
|
||||||
|
tesseract.js@^6.0.1:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-6.0.1.tgz#5b2ff39aae92d59cef79589a43a0f3ab963801cc"
|
||||||
|
integrity sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==
|
||||||
|
dependencies:
|
||||||
|
bmp-js "^0.1.0"
|
||||||
|
idb-keyval "^6.2.0"
|
||||||
|
is-url "^1.2.4"
|
||||||
|
node-fetch "^2.6.9"
|
||||||
|
opencollective-postinstall "^2.0.3"
|
||||||
|
regenerator-runtime "^0.13.3"
|
||||||
|
tesseract.js-core "^6.0.0"
|
||||||
|
wasm-feature-detect "^1.2.11"
|
||||||
|
zlibjs "^0.3.1"
|
||||||
|
|
||||||
tinyglobby@^0.2.15:
|
tinyglobby@^0.2.15:
|
||||||
version "0.2.15"
|
version "0.2.15"
|
||||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
||||||
@ -2607,6 +2860,11 @@ toidentifier@1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||||
|
|
||||||
|
tr46@~0.0.3:
|
||||||
|
version "0.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||||
|
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||||
|
|
||||||
tree-kill@1.2.2:
|
tree-kill@1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||||
@ -2617,7 +2875,7 @@ tslib@^1.7.1:
|
|||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
tslib@^2.1.0:
|
tslib@^2.1.0, tslib@^2.4.0:
|
||||||
version "2.8.1"
|
version "2.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
@ -2776,6 +3034,24 @@ wait-on@^9.0.1:
|
|||||||
minimist "^1.2.8"
|
minimist "^1.2.8"
|
||||||
rxjs "^7.8.2"
|
rxjs "^7.8.2"
|
||||||
|
|
||||||
|
wasm-feature-detect@^1.2.11:
|
||||||
|
version "1.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz#4e9f55b0a64d801f372fbb0324ed11ad3abd0c78"
|
||||||
|
integrity sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==
|
||||||
|
|
||||||
|
webidl-conversions@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
|
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||||
|
|
||||||
|
whatwg-url@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
|
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||||
|
dependencies:
|
||||||
|
tr46 "~0.0.3"
|
||||||
|
webidl-conversions "^3.0.0"
|
||||||
|
|
||||||
which@^2.0.1, which@^2.0.2:
|
which@^2.0.1, which@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||||
@ -2844,3 +3120,8 @@ yauzl@^2.10.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
fd-slicer "~1.1.0"
|
fd-slicer "~1.1.0"
|
||||||
|
|
||||||
|
zlibjs@^0.3.1:
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/zlibjs/-/zlibjs-0.3.1.tgz#50197edb28a1c42ca659cc8b4e6a9ddd6d444554"
|
||||||
|
integrity sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户