diff --git a/app/api/organization/[organizationId]/export/route.ts b/app/api/organization/[organizationId]/export/route.ts index 86dee6ae..e9e49c4a 100644 --- a/app/api/organization/[organizationId]/export/route.ts +++ b/app/api/organization/[organizationId]/export/route.ts @@ -11,7 +11,7 @@ import { import { MAX_ORGANIZATION_ARCHIVE_MIB, ORGANIZATION_ARCHIVE_MEDIA_TYPE, - ORGANIZATION_EXPORT_COOLDOWN_DAYS, + ORGANIZATION_EXPORT_COOLDOWN_HOURS, ORGANIZATION_EXPORT_INTENT_HEADER, ORGANIZATION_EXPORT_INTENT_VALUE, OrganizationArchiveError, @@ -54,7 +54,7 @@ function jsonError( } /** - * Verify browser intent before a request can consume the monthly export slot. + * Verify browser intent before a request can consume the export slot. * * @param request - Candidate workspace export request. * @returns True for the application's non-simple same-origin POST. @@ -128,9 +128,10 @@ export async function POST( }); } catch (error) { if (error instanceof OrganizationExportLimitError) { + const hoursLeft = Math.ceil(error.retryAfterSeconds / 3600); return jsonError( "export_limit_reached", - `You can generate one workspace archive every ${ORGANIZATION_EXPORT_COOLDOWN_DAYS} days.`, + `You can generate one workspace archive every ${ORGANIZATION_EXPORT_COOLDOWN_HOURS} hours. Try again in ${hoursLeft === 1 ? "1 hour" : `${hoursLeft} hours`}.`, 429, { "retry-after": String(error.retryAfterSeconds) }, ); diff --git a/app/settings/_components/team-manage/WorkspaceExportSection.tsx b/app/settings/_components/team-manage/WorkspaceExportSection.tsx index 665d13e9..bcaec8a0 100644 --- a/app/settings/_components/team-manage/WorkspaceExportSection.tsx +++ b/app/settings/_components/team-manage/WorkspaceExportSection.tsx @@ -4,7 +4,7 @@ import { useRef, useState, useTransition } from "react"; import { Button } from "@/components/shared/Button"; import { IconCheck, IconDoc } from "@/components/shared/icons"; import { - ORGANIZATION_EXPORT_COOLDOWN_DAYS, + ORGANIZATION_EXPORT_COOLDOWN_HOURS, ORGANIZATION_EXPORT_INTENT_HEADER, ORGANIZATION_EXPORT_INTENT_VALUE, organizationArchiveFilename, @@ -108,7 +108,7 @@ export function WorkspaceExportSection({ activity history as one JSON archive. Member accounts and other members' private notes are not included. Your own private notes are included. You can generate one archive every{" "} - {ORGANIZATION_EXPORT_COOLDOWN_DAYS} days. + {ORGANIZATION_EXPORT_COOLDOWN_HOURS} hours.
The archive can include confidential content, deleted notes, and
diff --git a/lib/data/organization-portability.ts b/lib/data/organization-portability.ts
index 4662b53a..17aef09d 100644
--- a/lib/data/organization-portability.ts
+++ b/lib/data/organization-portability.ts
@@ -36,7 +36,7 @@ import { deriveIdentifier } from "@/lib/graph/identifier";
import {
MAX_ORGANIZATION_ARCHIVE_BYTES,
MAX_ORGANIZATION_ARCHIVE_ROWS,
- ORGANIZATION_EXPORT_COOLDOWN_DAYS,
+ ORGANIZATION_EXPORT_COOLDOWN_HOURS,
OrganizationArchiveError,
decodeOrganizationArchive,
serializeOrganizationArchiveRow,
@@ -155,16 +155,16 @@ async function reserveOrganizationExport(
target: organizationExportLimits.userId,
set: { lastStartedAt: sql`clock_timestamp()` },
setWhere: sql`${organizationExportLimits.lastStartedAt} <= clock_timestamp() - interval '${sql.raw(
- String(ORGANIZATION_EXPORT_COOLDOWN_DAYS),
- )} days'`,
+ String(ORGANIZATION_EXPORT_COOLDOWN_HOURS),
+ )} hours'`,
})
.returning({ startedAt: organizationExportLimits.lastStartedAt });
if (reservations.length === 0) {
const [limit] = await tx
.select({
retryAfterSeconds: sql