layouting fixes
This commit is contained in:
@@ -255,12 +255,13 @@ export default function PostForm(props: PostFormProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="bg-base text-text min-h-screen px-8 py-32">
|
<div class="bg-base text-text min-h-screen py-32">
|
||||||
<div class="text-center text-2xl tracking-wide">
|
<div class="text-center text-2xl tracking-wide">
|
||||||
{props.mode === "edit" ? "Edit a Blog" : "Create a Blog"}
|
{props.mode === "edit" ? "Edit a Blog" : "Create a Blog"}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex h-full w-full justify-center">
|
<div class="flex h-full w-full justify-center">
|
||||||
<form onSubmit={handleSubmit} class="w-full md:w-3/4 lg:w-1/3 xl:w-1/2">
|
<form onSubmit={handleSubmit} class="px-4">
|
||||||
|
<div class="mx-auto w-full md:w-3/4 xl:w-1/2">
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div class="input-group mx-4">
|
<div class="input-group mx-4">
|
||||||
<input
|
<input
|
||||||
@@ -327,9 +328,10 @@ export default function PostForm(props: PostFormProps) {
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Text Editor */}
|
{/* Text Editor */}
|
||||||
<div class="-mx-6 md:-mx-36">
|
<div class="">
|
||||||
<TextEditor updateContent={setBody} preSet={initialBody()} />
|
<TextEditor updateContent={setBody} preSet={initialBody()} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ import { TRPCError } from "@trpc/server";
|
|||||||
import { OAuth2Client } from "google-auth-library";
|
import { OAuth2Client } from "google-auth-library";
|
||||||
import { jwtVerify } from "jose";
|
import { jwtVerify } from "jose";
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/utils";
|
import { createTRPCRouter, publicProcedure } from "~/server/api/utils";
|
||||||
|
import {
|
||||||
|
fetchWithTimeout,
|
||||||
|
checkResponse,
|
||||||
|
NetworkError,
|
||||||
|
TimeoutError,
|
||||||
|
APIError
|
||||||
|
} from "~/server/fetch-utils";
|
||||||
|
|
||||||
export const lineageDatabaseRouter = createTRPCRouter({
|
export const lineageDatabaseRouter = createTRPCRouter({
|
||||||
credentials: publicProcedure
|
credentials: publicProcedure
|
||||||
@@ -155,17 +162,20 @@ export const lineageDatabaseRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (dumpRes.success) {
|
if (dumpRes.success) {
|
||||||
const deleteRes = await fetch(
|
try {
|
||||||
|
const deleteRes = await fetchWithTimeout(
|
||||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
||||||
}
|
},
|
||||||
|
timeout: 20000 // 20s for database deletion
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteRes.ok) {
|
await checkResponse(deleteRes);
|
||||||
|
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `DELETE FROM User WHERE email = ?`,
|
sql: `DELETE FROM User WHERE email = ?`,
|
||||||
args: [email]
|
args: [email]
|
||||||
@@ -175,12 +185,36 @@ export const lineageDatabaseRouter = createTRPCRouter({
|
|||||||
status: 200,
|
status: 200,
|
||||||
message: `Account and Database deleted, db dump sent to email: ${send_dump_target}`
|
message: `Account and Database deleted, db dump sent to email: ${send_dump_target}`
|
||||||
};
|
};
|
||||||
} else {
|
} catch (error) {
|
||||||
|
if (error instanceof TimeoutError) {
|
||||||
|
console.error("Database deletion timeout:", error.message);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "TIMEOUT",
|
||||||
|
message:
|
||||||
|
"Database deletion timed out. Please contact support."
|
||||||
|
});
|
||||||
|
} else if (error instanceof NetworkError) {
|
||||||
|
console.error(
|
||||||
|
"Network error during database deletion:",
|
||||||
|
error.message
|
||||||
|
);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Network error deleting database. Please try again."
|
||||||
|
});
|
||||||
|
} else if (error instanceof APIError) {
|
||||||
|
console.error(
|
||||||
|
"API error deleting database:",
|
||||||
|
error.status,
|
||||||
|
error.statusText
|
||||||
|
);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
message: "Failed to delete database"
|
message: "Failed to delete database"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
@@ -188,17 +222,20 @@ export const lineageDatabaseRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const deleteRes = await fetch(
|
try {
|
||||||
|
const deleteRes = await fetchWithTimeout(
|
||||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
||||||
}
|
},
|
||||||
|
timeout: 20000
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteRes.ok) {
|
await checkResponse(deleteRes);
|
||||||
|
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `DELETE FROM User WHERE email = ?`,
|
sql: `DELETE FROM User WHERE email = ?`,
|
||||||
args: [email]
|
args: [email]
|
||||||
@@ -208,12 +245,35 @@ export const lineageDatabaseRouter = createTRPCRouter({
|
|||||||
status: 200,
|
status: 200,
|
||||||
message: `Account and Database deleted`
|
message: `Account and Database deleted`
|
||||||
};
|
};
|
||||||
} else {
|
} catch (error) {
|
||||||
|
if (error instanceof TimeoutError) {
|
||||||
|
console.error("Database deletion timeout:", error.message);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "TIMEOUT",
|
||||||
|
message: "Database deletion timed out. Please contact support."
|
||||||
|
});
|
||||||
|
} else if (error instanceof NetworkError) {
|
||||||
|
console.error(
|
||||||
|
"Network error during database deletion:",
|
||||||
|
error.message
|
||||||
|
);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Network error deleting database. Please try again."
|
||||||
|
});
|
||||||
|
} else if (error instanceof APIError) {
|
||||||
|
console.error(
|
||||||
|
"API error deleting database:",
|
||||||
|
error.status,
|
||||||
|
error.statusText
|
||||||
|
);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
message: "Failed to delete database"
|
message: "Failed to delete database"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const insertRes = await conn.execute({
|
const insertRes = await conn.execute({
|
||||||
@@ -343,41 +403,59 @@ export const lineageDatabaseRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (dumpRes.success) {
|
if (dumpRes.success) {
|
||||||
const deleteRes = await fetch(
|
try {
|
||||||
|
const deleteRes = await fetchWithTimeout(
|
||||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
||||||
}
|
},
|
||||||
|
timeout: 20000
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteRes.ok) {
|
await checkResponse(deleteRes);
|
||||||
|
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `DELETE FROM User WHERE email = ?`,
|
sql: `DELETE FROM User WHERE email = ?`,
|
||||||
args: [email]
|
args: [email]
|
||||||
});
|
});
|
||||||
executed_ids.push(id as number);
|
executed_ids.push(id as number);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to delete database ${db_name} in cron job:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
// Continue with other deletions even if one fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const deleteRes = await fetch(
|
try {
|
||||||
|
const deleteRes = await fetchWithTimeout(
|
||||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`
|
||||||
}
|
},
|
||||||
|
timeout: 20000
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteRes.ok) {
|
await checkResponse(deleteRes);
|
||||||
|
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `DELETE FROM User WHERE email = ?`,
|
sql: `DELETE FROM User WHERE email = ?`,
|
||||||
args: [email]
|
args: [email]
|
||||||
});
|
});
|
||||||
executed_ids.push(id as number);
|
executed_ids.push(id as number);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to delete database ${db_name} in cron job:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
// Continue with other deletions even if one fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,14 @@ import { TRPCError } from "@trpc/server";
|
|||||||
import { ConnectionFactory } from "~/server/utils";
|
import { ConnectionFactory } from "~/server/utils";
|
||||||
import * as bcrypt from "bcrypt";
|
import * as bcrypt from "bcrypt";
|
||||||
import { getCookie, setCookie } from "vinxi/http";
|
import { getCookie, setCookie } from "vinxi/http";
|
||||||
|
import {
|
||||||
|
fetchWithTimeout,
|
||||||
|
checkResponse,
|
||||||
|
fetchWithRetry,
|
||||||
|
NetworkError,
|
||||||
|
TimeoutError,
|
||||||
|
APIError
|
||||||
|
} from "~/server/fetch-utils";
|
||||||
const assets: Record<string, string> = {
|
const assets: Record<string, string> = {
|
||||||
"shapes-with-abigail": "shapes-with-abigail.apk",
|
"shapes-with-abigail": "shapes-with-abigail.apk",
|
||||||
"magic-delve": "magic-delve.apk",
|
"magic-delve": "magic-delve.apk",
|
||||||
@@ -285,16 +292,28 @@ export const miscRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetch(apiUrl, {
|
await fetchWithRetry(
|
||||||
|
async () => {
|
||||||
|
const response = await fetchWithTimeout(apiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
accept: "application/json",
|
accept: "application/json",
|
||||||
"api-key": apiKey,
|
"api-key": apiKey,
|
||||||
"content-type": "application/json"
|
"content-type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(sendinblueData)
|
body: JSON.stringify(sendinblueData),
|
||||||
|
timeout: 15000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await checkResponse(response);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maxRetries: 2,
|
||||||
|
retryDelay: 1000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Set cookie to prevent spam (60 second cooldown)
|
// Set cookie to prevent spam (60 second cooldown)
|
||||||
const exp = new Date(Date.now() + 1 * 60 * 1000);
|
const exp = new Date(Date.now() + 1 * 60 * 1000);
|
||||||
setCookie("contactRequestSent", exp.toUTCString(), {
|
setCookie("contactRequestSent", exp.toUTCString(), {
|
||||||
@@ -304,11 +323,37 @@ export const miscRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return { message: "email sent" };
|
return { message: "email sent" };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
// Provide specific error messages for different failure types
|
||||||
|
if (error instanceof TimeoutError) {
|
||||||
|
console.error("Contact form email timeout:", error.message);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "TIMEOUT",
|
||||||
|
message:
|
||||||
|
"Email service timed out. Please try again or contact michael@freno.me"
|
||||||
|
});
|
||||||
|
} else if (error instanceof NetworkError) {
|
||||||
|
console.error("Contact form network error:", error.message);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
message:
|
message:
|
||||||
"SMTP server error: Sorry! You can reach me at michael@freno.me"
|
"Network error. Please try again or contact michael@freno.me"
|
||||||
|
});
|
||||||
|
} else if (error instanceof APIError) {
|
||||||
|
console.error(
|
||||||
|
"Contact form API error:",
|
||||||
|
error.status,
|
||||||
|
error.statusText
|
||||||
|
);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Email service error. You can reach me at michael@freno.me"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Contact form error:", error);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Sorry! You can reach me at michael@freno.me"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -362,26 +407,43 @@ export const miscRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send both emails
|
// Send both emails with retry logic
|
||||||
await fetch(apiUrl, {
|
await Promise.all([
|
||||||
|
fetchWithRetry(
|
||||||
|
async () => {
|
||||||
|
const response = await fetchWithTimeout(apiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
accept: "application/json",
|
accept: "application/json",
|
||||||
"api-key": apiKey,
|
"api-key": apiKey,
|
||||||
"content-type": "application/json"
|
"content-type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(sendinblueMyData)
|
body: JSON.stringify(sendinblueMyData),
|
||||||
|
timeout: 15000
|
||||||
});
|
});
|
||||||
|
await checkResponse(response);
|
||||||
await fetch(apiUrl, {
|
return response;
|
||||||
|
},
|
||||||
|
{ maxRetries: 2, retryDelay: 1000 }
|
||||||
|
),
|
||||||
|
fetchWithRetry(
|
||||||
|
async () => {
|
||||||
|
const response = await fetchWithTimeout(apiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
accept: "application/json",
|
accept: "application/json",
|
||||||
"api-key": apiKey,
|
"api-key": apiKey,
|
||||||
"content-type": "application/json"
|
"content-type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(sendinblueUserData)
|
body: JSON.stringify(sendinblueUserData),
|
||||||
|
timeout: 15000
|
||||||
});
|
});
|
||||||
|
await checkResponse(response);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{ maxRetries: 2, retryDelay: 1000 }
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
// Set cookie to prevent spam (60 second cooldown)
|
// Set cookie to prevent spam (60 second cooldown)
|
||||||
const exp = new Date(Date.now() + 1 * 60 * 1000);
|
const exp = new Date(Date.now() + 1 * 60 * 1000);
|
||||||
@@ -392,11 +454,35 @@ export const miscRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return { message: "request sent" };
|
return { message: "request sent" };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
// Provide specific error messages
|
||||||
|
if (error instanceof TimeoutError) {
|
||||||
|
console.error("Deletion request email timeout:", error.message);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "TIMEOUT",
|
||||||
|
message: "Email service timed out. Please try again."
|
||||||
|
});
|
||||||
|
} else if (error instanceof NetworkError) {
|
||||||
|
console.error("Deletion request network error:", error.message);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
message:
|
message: "Network error. Please try again later."
|
||||||
"SMTP server error: Sorry! You can reach me at michael@freno.me"
|
});
|
||||||
|
} else if (error instanceof APIError) {
|
||||||
|
console.error(
|
||||||
|
"Deletion request API error:",
|
||||||
|
error.status,
|
||||||
|
error.statusText
|
||||||
|
);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Email service error. Please try again later."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Deletion request error:", error);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Failed to send deletion request. Please try again."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ import { v4 as uuid } from "uuid";
|
|||||||
import { env } from "~/env/server";
|
import { env } from "~/env/server";
|
||||||
import type { H3Event } from "vinxi/http";
|
import type { H3Event } from "vinxi/http";
|
||||||
import { getUserID } from "./auth";
|
import { getUserID } from "./auth";
|
||||||
|
import {
|
||||||
|
fetchWithTimeout,
|
||||||
|
checkResponse,
|
||||||
|
fetchWithRetry,
|
||||||
|
NetworkError,
|
||||||
|
TimeoutError,
|
||||||
|
APIError
|
||||||
|
} from "~/server/fetch-utils";
|
||||||
|
|
||||||
let mainDBConnection: ReturnType<typeof createClient> | null = null;
|
let mainDBConnection: ReturnType<typeof createClient> | null = null;
|
||||||
let lineageDBConnection: ReturnType<typeof createClient> | null = null;
|
let lineageDBConnection: ReturnType<typeof createClient> | null = null;
|
||||||
@@ -83,16 +91,20 @@ export async function dumpAndSendDB({
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
}> {
|
}> {
|
||||||
const res = await fetch(`https://${dbName}-mikefreno.turso.io/dump`, {
|
try {
|
||||||
|
// Fetch database dump with timeout
|
||||||
|
const res = await fetchWithTimeout(
|
||||||
|
`https://${dbName}-mikefreno.turso.io/dump`,
|
||||||
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${dbToken}`
|
Authorization: `Bearer ${dbToken}`
|
||||||
|
},
|
||||||
|
timeout: 30000 // 30s for database dump
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
if (!res.ok) {
|
|
||||||
console.error(res);
|
await checkResponse(res);
|
||||||
return { success: false, reason: "bad dump request response" };
|
|
||||||
}
|
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
const base64Content = Buffer.from(text, "utf-8").toString("base64");
|
const base64Content = Buffer.from(text, "utf-8").toString("base64");
|
||||||
|
|
||||||
@@ -119,20 +131,50 @@ export async function dumpAndSendDB({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const sendRes = await fetch(apiUrl, {
|
|
||||||
|
// Send email with retry logic
|
||||||
|
await fetchWithRetry(
|
||||||
|
async () => {
|
||||||
|
const sendRes = await fetchWithTimeout(apiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
accept: "application/json",
|
accept: "application/json",
|
||||||
"api-key": apiKey,
|
"api-key": apiKey,
|
||||||
"content-type": "application/json"
|
"content-type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(emailPayload)
|
body: JSON.stringify(emailPayload),
|
||||||
|
timeout: 20000 // 20s for email with attachment
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sendRes.ok) {
|
await checkResponse(sendRes);
|
||||||
return { success: false, reason: "email send failure" };
|
return sendRes;
|
||||||
} else {
|
},
|
||||||
|
{
|
||||||
|
maxRetries: 2,
|
||||||
|
retryDelay: 2000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
// Log specific error types for debugging
|
||||||
|
if (error instanceof TimeoutError) {
|
||||||
|
console.error("Database dump timeout:", error.message);
|
||||||
|
return { success: false, reason: "Database dump timed out" };
|
||||||
|
} else if (error instanceof NetworkError) {
|
||||||
|
console.error("Network error during database dump:", error.message);
|
||||||
|
return { success: false, reason: "Network error" };
|
||||||
|
} else if (error instanceof APIError) {
|
||||||
|
console.error(
|
||||||
|
"API error during database dump:",
|
||||||
|
error.status,
|
||||||
|
error.statusText
|
||||||
|
);
|
||||||
|
return { success: false, reason: `API error: ${error.statusText}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Unexpected error during database dump:", error);
|
||||||
|
return { success: false, reason: "Unknown error occurred" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user