fix(server): 使用 lossless-json 无损处理 summary.json Long 精度
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
"@tanstack/react-start": "catalog:",
|
"@tanstack/react-start": "catalog:",
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"jszip": "catalog:",
|
"jszip": "catalog:",
|
||||||
|
"lossless-json": "catalog:",
|
||||||
"react": "catalog:",
|
"react": "catalog:",
|
||||||
"react-dom": "catalog:",
|
"react-dom": "catalog:",
|
||||||
"systeminformation": "catalog:",
|
"systeminformation": "catalog:",
|
||||||
|
|||||||
@@ -10,12 +10,28 @@ import {
|
|||||||
} from '@furtherverse/crypto'
|
} from '@furtherverse/crypto'
|
||||||
import { ORPCError } from '@orpc/server'
|
import { ORPCError } from '@orpc/server'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
|
import {
|
||||||
|
isInteger,
|
||||||
|
isSafeNumber,
|
||||||
|
LosslessNumber,
|
||||||
|
parse as losslessParse,
|
||||||
|
stringify as losslessStringify,
|
||||||
|
} from 'lossless-json'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { extractSafeZipFiles, ZipValidationError } from '@/server/safe-zip'
|
import { extractSafeZipFiles, ZipValidationError } from '@/server/safe-zip'
|
||||||
import { getUxConfig } from '@/server/ux-config'
|
import { getUxConfig } from '@/server/ux-config'
|
||||||
import { db } from '../middlewares'
|
import { db } from '../middlewares'
|
||||||
import { os } from '../server'
|
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
|
const summaryPayloadSchema = z
|
||||||
.object({
|
.object({
|
||||||
taskId: z.string().min(1, 'summary.json must contain a non-empty taskId'),
|
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
|
let rawJson: unknown
|
||||||
try {
|
try {
|
||||||
rawJson = JSON.parse(Buffer.from(summaryFile.bytes).toString('utf-8'))
|
rawJson = losslessParse(Buffer.from(summaryFile.bytes).toString('utf-8'), undefined, safeNumberParser)
|
||||||
} catch {
|
} catch {
|
||||||
throw new ORPCError('BAD_REQUEST', {
|
throw new ORPCError('BAD_REQUEST', {
|
||||||
message: 'summary.json in the ZIP is not valid JSON',
|
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)
|
// Build final summary.json with flat structure (matching Kotlin reference)
|
||||||
const finalSummary = {
|
const finalSummary = {
|
||||||
orgId,
|
orgId: toLosslessNumber(String(orgId)),
|
||||||
checkId,
|
checkId: toLosslessNumber(checkId),
|
||||||
taskId: summaryPayload.taskId,
|
taskId: summaryPayload.taskId,
|
||||||
licence: config.licence,
|
licence: config.licence,
|
||||||
fingerprint: config.fingerprint,
|
fingerprint: config.fingerprint,
|
||||||
deviceSignature,
|
deviceSignature,
|
||||||
summary: summaryPayload.summary ?? '',
|
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)
|
// Build manifest.json (fixed file list, matching Kotlin reference)
|
||||||
const manifestFiles: Record<string, string> = {
|
const manifestFiles: Record<string, string> = {
|
||||||
|
|||||||
3
bun.lock
3
bun.lock
@@ -49,6 +49,7 @@
|
|||||||
"@tanstack/react-start": "catalog:",
|
"@tanstack/react-start": "catalog:",
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"jszip": "catalog:",
|
"jszip": "catalog:",
|
||||||
|
"lossless-json": "^4.3.0",
|
||||||
"react": "catalog:",
|
"react": "catalog:",
|
||||||
"react-dom": "catalog:",
|
"react-dom": "catalog:",
|
||||||
"systeminformation": "catalog:",
|
"systeminformation": "catalog:",
|
||||||
@@ -1078,6 +1079,8 @@
|
|||||||
|
|
||||||
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
|
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
|
||||||
|
|
||||||
|
"lossless-json": ["lossless-json@4.3.0", "", {}, "sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g=="],
|
||||||
|
|
||||||
"lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
|
"lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
|
||||||
|
|
||||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
"electron-builder": "^26.8.1",
|
"electron-builder": "^26.8.1",
|
||||||
"electron-vite": "^5.0.0",
|
"electron-vite": "^5.0.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
|
"lossless-json": "^4.3.0",
|
||||||
"motion": "^12.35.0",
|
"motion": "^12.35.0",
|
||||||
"nitro": "npm:nitro-nightly@3.0.1-20260227-181935-bfbb207c",
|
"nitro": "npm:nitro-nightly@3.0.1-20260227-181935-bfbb207c",
|
||||||
"openpgp": "^6.0.1",
|
"openpgp": "^6.0.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user