fix: address coderabbit review feedback
This commit is contained in:
1 parent
17aef0588f
commit
cf624e2f23
10 files changed
+256
-175
No files matched your search
@@ -40,6 +40,7 @@ const MAX_UNCOMPRESSED_ARCHIVE_BYTES = MAX_UNCOMPRESSED_DATABASE_BYTES + 2 * MEB
|
||||
const MAX_METADATA_BYTES = MEBIBYTE;
|
||||
const MAX_ENCRYPTION_KEY_BYTES = 1024;
|
||||
const MAX_ARCHIVE_ENTRIES = 1024;
|
||||
const UPLOAD_IDLE_TIMEOUT_MS = 60_000;
|
||||
const RESTART_DELAY_MS = 500;
|
||||
|
||||
let restoreInProgress = false;
|
||||
@@ -73,6 +74,26 @@ const createRequestSizeLimiter = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const createUploadIdleTimeout = () => {
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
const stream = new Transform({
|
||||
transform(chunk: Buffer, _encoding, callback) {
|
||||
resetTimeout();
|
||||
callback(null, chunk);
|
||||
},
|
||||
});
|
||||
const resetTimeout = () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(() => stream.destroy(new Error("Backup upload timed out")), UPLOAD_IDLE_TIMEOUT_MS);
|
||||
timeout.unref();
|
||||
};
|
||||
resetTimeout();
|
||||
stream.on("close", () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
});
|
||||
return stream;
|
||||
};
|
||||
|
||||
const streamUploadedBackupAsync = async (request: Request, uploadPath: string): Promise<number | null> => {
|
||||
assertDeclaredRequestSize(request);
|
||||
if (!request.body) return null;
|
||||
@@ -128,7 +149,9 @@ const streamUploadedBackupAsync = async (request: Request, uploadPath: string):
|
||||
|
||||
const requestStream = Readable.fromWeb(request.body as NodeReadableStream<Uint8Array>);
|
||||
try {
|
||||
await pipeline(requestStream, createRequestSizeLimiter(), parser);
|
||||
await pipeline(requestStream, createUploadIdleTimeout(), createRequestSizeLimiter(), parser, {
|
||||
signal: request.signal,
|
||||
});
|
||||
await uploadWrite;
|
||||
} catch (error) {
|
||||
await uploadWrite.catch(() => undefined);
|
||||
@@ -453,6 +476,11 @@ export async function POST(req: Request) {
|
||||
return NextResponse.json({ error: message }, { status: 400 });
|
||||
} finally {
|
||||
zip.close();
|
||||
try {
|
||||
fs.rmSync(uploadPath, { force: true });
|
||||
} catch {
|
||||
// The outer restore-directory cleanup retries this path.
|
||||
}
|
||||
}
|
||||
|
||||
tempDb = new BetterSqlite3(tempPath);
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.viewport[data-board-hydrated="false"] {
|
||||
.horizontalScroller {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
/*
|
||||
* Horizontal scrolling computes the other overflow axis to auto. Keep the
|
||||
* board chrome inside that scrollport without changing the sizer's height.
|
||||
*/
|
||||
padding-block: var(--board-canvas-overflow-top) var(--board-canvas-overflow-bottom);
|
||||
margin-block: calc(0px - var(--board-canvas-overflow-top)) calc(0px - var(--board-canvas-overflow-bottom));
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.viewport[data-board-hydrated="false"] .horizontalScroller {
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import classes from "./scaled-board-canvas.module.css";
|
||||
|
||||
const BoardCanvasScaleContext = createContext(1);
|
||||
const MIN_READABLE_CANVAS_SCALE = 0.5;
|
||||
const CANVAS_OVERFLOW_TOP = 48;
|
||||
const CANVAS_OVERFLOW_BOTTOM = 8;
|
||||
|
||||
export const useBoardCanvasScale = () => useContext(BoardCanvasScaleContext);
|
||||
|
||||
@@ -76,18 +78,26 @@ export const ScaledBoardCanvas = ({
|
||||
data-canvas-initial-width={initialAvailableWidth}
|
||||
aria-label={label}
|
||||
>
|
||||
<Box className={classes.sizer} style={{ width: visualWidth, height: resolvedLogicalHeight * scale }}>
|
||||
<Box
|
||||
ref={canvasRef}
|
||||
className={classes.canvas}
|
||||
style={{
|
||||
"--board-canvas-inverse-scale": inverseScale,
|
||||
"--board-canvas-ui-scale": uiScale,
|
||||
width: logicalWidth,
|
||||
zoom: scale,
|
||||
}}
|
||||
>
|
||||
<BoardCanvasScaleContext.Provider value={scale}>{children}</BoardCanvasScaleContext.Provider>
|
||||
<Box
|
||||
className={classes.horizontalScroller}
|
||||
style={{
|
||||
"--board-canvas-overflow-top": `${CANVAS_OVERFLOW_TOP * Math.max(1, scale)}px`,
|
||||
"--board-canvas-overflow-bottom": `${CANVAS_OVERFLOW_BOTTOM * Math.max(1, scale)}px`,
|
||||
}}
|
||||
>
|
||||
<Box className={classes.sizer} style={{ width: visualWidth, height: resolvedLogicalHeight * scale }}>
|
||||
<Box
|
||||
ref={canvasRef}
|
||||
className={classes.canvas}
|
||||
style={{
|
||||
"--board-canvas-inverse-scale": inverseScale,
|
||||
"--board-canvas-ui-scale": uiScale,
|
||||
width: logicalWidth,
|
||||
zoom: scale,
|
||||
}}
|
||||
>
|
||||
<BoardCanvasScaleContext.Provider value={scale}>{children}</BoardCanvasScaleContext.Provider>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
.board-grid-entry {
|
||||
z-index: 1;
|
||||
overflow: visible;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.board-grid-entry:has([data-board-widget-header]):hover,
|
||||
|
||||
Reference in new issue
Block a user