fix(server): 使用 lossless-json 无损处理 summary.json Long 精度

This commit is contained in:
2026-03-10 16:10:25 +08:00
parent 42bc8605b4
commit 9a2bd5c43a
4 changed files with 31 additions and 4 deletions

View File

@@ -37,6 +37,7 @@
"@tanstack/react-start": "catalog:",
"drizzle-orm": "catalog:",
"jszip": "catalog:",
"lossless-json": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"systeminformation": "catalog:",

View File

@@ -10,12 +10,28 @@ import {
} from '@furtherverse/crypto'
import { ORPCError } from '@orpc/server'
import JSZip from 'jszip'
import {
isInteger,
isSafeNumber,
LosslessNumber,
parse as losslessParse,
stringify as losslessStringify,
} from 'lossless-json'
import { z } from 'zod'
import { extractSafeZipFiles, ZipValidationError } from '@/server/safe-zip'
import { getUxConfig } from '@/server/ux-config'
import { db } from '../middlewares'
import { os } from '../server'
const safeNumberParser = (value: string): number | string => {
if (isSafeNumber(value)) return Number(value)
if (isInteger(value)) return value
return Number(value)
}
const toLosslessNumber = (value: string): LosslessNumber | string =>
value !== '' && /^-?\d+$/.test(value) ? new LosslessNumber(value) : value
const summaryPayloadSchema = z
.object({
taskId: z.string().min(1, 'summary.json must contain a non-empty taskId'),
@@ -94,7 +110,7 @@ export const signAndPackReport = os.crypto.signAndPackReport.use(db).handler(asy
let rawJson: unknown
try {
rawJson = JSON.parse(Buffer.from(summaryFile.bytes).toString('utf-8'))
rawJson = losslessParse(Buffer.from(summaryFile.bytes).toString('utf-8'), undefined, safeNumberParser)
} catch {
throw new ORPCError('BAD_REQUEST', {
message: 'summary.json in the ZIP is not valid JSON',
@@ -138,15 +154,21 @@ export const signAndPackReport = os.crypto.signAndPackReport.use(db).handler(asy
// Build final summary.json with flat structure (matching Kotlin reference)
const finalSummary = {
orgId,
checkId,
orgId: toLosslessNumber(String(orgId)),
checkId: toLosslessNumber(checkId),
taskId: summaryPayload.taskId,
licence: config.licence,
fingerprint: config.fingerprint,
deviceSignature,
summary: summaryPayload.summary ?? '',
}
const summaryBytes = Buffer.from(JSON.stringify(finalSummary), 'utf-8')
const summaryJson = losslessStringify(finalSummary)
if (!summaryJson) {
throw new ORPCError('INTERNAL_SERVER_ERROR', {
message: 'Failed to serialize summary.json',
})
}
const summaryBytes = Buffer.from(summaryJson, 'utf-8')
// Build manifest.json (fixed file list, matching Kotlin reference)
const manifestFiles: Record<string, string> = {