fix: harden long conversation continuity
Memind CI / Test, build, and release guards (push) Successful in 1m28s

This commit is contained in:
john
2026-07-27 21:39:34 +08:00
parent 5ae166fc44
commit 48c61a3279
16 changed files with 1635 additions and 94 deletions
+51 -24
View File
@@ -302,11 +302,17 @@ export function createWorkspaceAssetSync({
if (!space || space.status !== 'active') {
throw Object.assign(new Error('用户空间不可用'), { code: 'space_unavailable' });
}
const sizeDelta = buffer.length - asNumber(existing.size_bytes);
const available =
asNumber(space.quota_bytes) - asNumber(space.used_bytes) - asNumber(space.reserved_bytes);
if (sizeDelta > 0 && available < sizeDelta) {
throw Object.assign(new Error('剩余空间不足'), { code: 'quota_exceeded' });
const [assetRows] = await conn.query(
`SELECT id, current_version_id, size_bytes, checksum
FROM h5_assets
WHERE id = ? AND user_id = ?
LIMIT 1
FOR UPDATE`,
[existing.id, userId],
);
const currentAsset = assetRows[0];
if (!currentAsset) {
throw Object.assign(new Error('工作区资产不存在'), { code: 'asset_not_found' });
}
const detectedMimeType = assetInternals.detectMimeType(buffer, file.filename);
@@ -321,19 +327,36 @@ export function createWorkspaceAssetSync({
const assetStatus = assetStatusForScan(scan);
const versionScanStatus = versionStatusForScan(scan);
const checksum = crypto.createHash('sha256').update(buffer).digest('hex');
const [versionRows] = await conn.query(
`SELECT COALESCE(MAX(version_no), 0) AS max_version
FROM h5_asset_versions
WHERE asset_id = ?`,
[existing.id],
);
const versionNo = asNumber(versionRows[0]?.max_version) + 1;
const versionId = idFactory();
const finalStorageKey = buildWorkspaceStorageKey(
userId,
category.category_code,
file.filename,
);
if (currentAsset.checksum === checksum) {
await conn.commit();
return {
action: 'skipped',
assetId: currentAsset.id,
filename: file.filename,
checksum,
mimeType: detectedMimeType,
sizeBytes: buffer.length,
storageKey: finalStorageKey,
categoryCode: category.category_code,
};
}
const sizeDelta = buffer.length - asNumber(currentAsset.size_bytes);
const available =
asNumber(space.quota_bytes) - asNumber(space.used_bytes) - asNumber(space.reserved_bytes);
if (sizeDelta > 0 && available < sizeDelta) {
throw Object.assign(new Error('剩余空间不足'), { code: 'quota_exceeded' });
}
const versionId = currentAsset.current_version_id;
if (!versionId) {
throw Object.assign(new Error('工作区资产版本不存在'), {
code: 'asset_version_missing',
});
}
const now = Date.now();
const indexedWorkspacePath = resolveAssetWorkspaceRelativePath({
@@ -342,11 +365,10 @@ export function createWorkspaceAssetSync({
});
await conn.query(
`UPDATE h5_assets
SET current_version_id = ?, size_bytes = ?, checksum = ?, mime_type = ?,
SET size_bytes = ?, checksum = ?, mime_type = ?,
asset_type = ?, risk_level = ?, status = ?, workspace_relative_path = ?, updated_at = ?
WHERE id = ? AND user_id = ?`,
[
versionId,
buffer.length,
checksum,
detectedMimeType,
@@ -360,21 +382,19 @@ export function createWorkspaceAssetSync({
],
);
await conn.query(
`INSERT INTO h5_asset_versions
(id, asset_id, version_no, storage_key, size_bytes, checksum, mime_type,
created_by, change_note, scan_status, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, '工作区文件更新', ?, ?)`,
`UPDATE h5_asset_versions
SET size_bytes = ?, checksum = ?, mime_type = ?, created_by = ?,
change_note = '工作区文件更新', scan_status = ?, created_at = ?
WHERE id = ? AND asset_id = ?`,
[
versionId,
existing.id,
versionNo,
finalStorageKey,
buffer.length,
checksum,
detectedMimeType,
userId,
versionScanStatus,
now,
versionId,
currentAsset.id,
],
);
if (sizeDelta !== 0) {
@@ -387,7 +407,7 @@ export function createWorkspaceAssetSync({
await conn.commit();
return {
action: 'updated',
assetId: existing.id,
assetId: currentAsset.id,
filename: file.filename,
checksum,
mimeType: detectedMimeType,
@@ -484,8 +504,15 @@ export function createWorkspaceAssetSync({
}
if (existing) {
const result = await updateWorkspaceFile(userId, category, existing, file, buffer);
if (result.action === 'skipped') {
existing.checksum = checksum;
existing.size_bytes = buffer.length;
skipped += 1;
continue;
}
await registerWorkspaceArtifactForConversation(userId, source, result);
existing.checksum = checksum;
existing.size_bytes = buffer.length;
updated += 1;
} else {
const result = await importWorkspaceFile(userId, category, file, buffer);