mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-28 16:02:33 +00:00
feat: address the CodeRabbit review on the mailbox allowance work by reserving the mailbox slot inside the account insert transaction under a per-organization lock so concurrent connects cannot both take the last one, resolving the storage limit under the quota lock so a plan change cannot be raced past, deleting refused attachment objects on a context that outlives the request, settling already-connected rows before the bulk batch spends allowance, leaving password columns out of the failed-rows CSV, stopping click propagation from the portalled allowance dialog, and counting bulk progress once
This commit is contained in:
@@ -286,7 +286,9 @@ export default function BulkConnectPanel({
|
||||
const skipped = rows.filter((r) => r.status === "skipped").length;
|
||||
const failed = rows.filter((r) => r.status === "failed").length;
|
||||
const pending = rows.filter((r) => r.status === "pending").length;
|
||||
const total = ready + connected + skipped + failed + pending;
|
||||
// `ready` already includes pending rows, so count the untouched ones alone.
|
||||
const notStarted = rows.filter((r) => r.status === "ready").length;
|
||||
const total = notStarted + connected + skipped + failed + pending;
|
||||
|
||||
if (step === "run") {
|
||||
const done = connected + skipped + failed;
|
||||
@@ -406,7 +408,7 @@ export default function BulkConnectPanel({
|
||||
<button
|
||||
type="button"
|
||||
onClick={downloadFailed}
|
||||
title="The rows as you uploaded them, including passwords, plus an error column"
|
||||
title="The rows as you uploaded them, minus passwords, plus an error column"
|
||||
className="ml-auto h-6 px-2 rounded text-[11px] text-slate-700 hover:text-slate-900 hover:bg-slate-100 inline-flex items-center gap-1 transition-colors"
|
||||
>
|
||||
<DownloadIcon className="w-3 h-3" />
|
||||
@@ -444,7 +446,7 @@ export default function BulkConnectPanel({
|
||||
<div className="px-4 py-2.5 border-t border-slate-200 bg-slate-50/60 flex items-center gap-2 min-w-0 sticky bottom-0">
|
||||
<div className="text-[11px] text-slate-500 min-w-0 flex-1 truncate">
|
||||
{failedRows.length > 0
|
||||
? "Fix the failed rows in the download and upload that file; connected ones are skipped."
|
||||
? "Retry keeps the passwords in memory. The download leaves them out; add them back before re-uploading."
|
||||
: "Warmup and sync start on every new mailbox right away."}
|
||||
</div>
|
||||
{(canRetry || notSent > 0) && (
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
// pointed at a plan instead, because that is its only path to more.
|
||||
//
|
||||
// Layered above the connect modal: it marks itself data-floating so the modal
|
||||
// underneath ignores Escape while it is up, and stops mousedown so the
|
||||
// modal's backdrop handler never sees a click inside it.
|
||||
// underneath ignores Escape while it is up, and stops both mousedown and
|
||||
// click. The portal keeps it in the modal's React tree, so without that a
|
||||
// click anywhere in here would reach the modal's backdrop handler and close
|
||||
// the whole connect flow.
|
||||
|
||||
import React from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
@@ -121,6 +123,7 @@ export default function MailboxAllowanceDialog({
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="fixed inset-0 z-[140] flex items-center justify-center bg-slate-900/40 backdrop-blur-[2px] px-4"
|
||||
>
|
||||
<motion.div
|
||||
|
||||
@@ -206,10 +206,16 @@ export function downloadTemplate() {
|
||||
downloadBlob(new Blob(["" + csv], { type: "text/csv;charset=utf-8" }), "warmbly-mailboxes-template.csv");
|
||||
}
|
||||
|
||||
/** The rows that did not connect, with the user's own columns plus the reason. */
|
||||
/** Columns never written to a download: a CSV in a Downloads folder is
|
||||
* synced, backed up and shared far more than the user intends. */
|
||||
const SECRET_COLUMNS = new Set(["password", "smtp_password", "imap_password"]);
|
||||
|
||||
/** The rows that did not connect, with the user's own columns (minus
|
||||
* passwords) plus the reason. */
|
||||
export function failedRowsCSV(rows: BulkRow[], columns: string[]): string {
|
||||
const header = [...columns, "error"];
|
||||
const body = rows.map((r) => [...columns.map((c) => r.raw[c] ?? ""), r.problem ?? r.message ?? r.code ?? ""]);
|
||||
const keep = columns.filter((c) => !SECRET_COLUMNS.has(c));
|
||||
const header = [...keep, "error"];
|
||||
const body = rows.map((r) => [...keep.map((c) => r.raw[c] ?? ""), r.problem ?? r.message ?? r.code ?? ""]);
|
||||
return Papa.unparse([header, ...body]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user