diff --git a/app/page.tsx b/app/page.tsx
index b05c0ac..1716154 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,7 +1,7 @@
import DashChart from "@/components/DashChart";
import DashRecentTickets from "@/components/DashRecentTickets";
import TicketPieChart from "@/components/TicketPieChart";
-import prisma from "@/prisma/db";
+import prisma, { safeDbOperation } from "@/prisma/db";
const STATUS_COLORS = {
OPEN: "#ef4444",
@@ -16,18 +16,113 @@ const PRIORITY_COLORS = {
};
async function Dashboard() {
- const tickets = await prisma.ticket.findMany({
- where: { NOT: [{ status: "DONE" }] },
- orderBy: { updated_at: "desc" },
- skip: 0,
- take: 5,
- include: { user: true },
- });
+ // Safely fetch tickets with fallback
+ const ticketsResult = await safeDbOperation(
+ () =>
+ prisma.ticket.findMany({
+ where: { NOT: [{ status: "DONE" }] },
+ orderBy: { updated_at: "desc" },
+ skip: 0,
+ take: 5,
+ include: { user: true },
+ }),
+ []
+ );
- const groupTickets = await prisma.ticket.groupBy({
- by: ["status"],
- _count: { id: true },
- });
+ // Safely fetch group tickets for chart data
+ const groupTicketsResult = await safeDbOperation(
+ () =>
+ prisma.ticket.groupBy({
+ by: ["status"],
+ _count: { id: true },
+ }),
+ []
+ );
+
+ // Safely fetch status distribution
+ const statusResult = await safeDbOperation(
+ () =>
+ prisma.ticket.groupBy({
+ by: ["status"],
+ _count: true,
+ }),
+ []
+ );
+
+ // Safely fetch priority distribution
+ const priorityResult = await safeDbOperation(
+ () =>
+ prisma.ticket.groupBy({
+ by: ["priority"],
+ _count: true,
+ }),
+ []
+ );
+
+ // Check if any database operation failed
+ const dbError =
+ ticketsResult.error ||
+ groupTicketsResult.error ||
+ statusResult.error ||
+ priorityResult.error;
+
+ if (dbError) {
+ if (typeof window === "undefined") {
+ // Log detailed error server-side
+ // eslint-disable-next-line no-console
+ console.error("Database error:", dbError);
+ }
+ return (
+
+
+
+
+
+ Database Not Available
+
+
+ Sorry, something went wrong connecting to the database.
+
+
+
To fix this:
+
+ -
+ Set the{" "}
+
+ DATABASE_URL
+ {" "}
+ environment variable
+
+ - Ensure your database server is running
+ - Verify database connection credentials
+ - Run database migrations if needed
+
+
+
+
+
+ );
+ }
+
+ // Process data if database operations were successful
+ const tickets = ticketsResult.data;
+ const groupTickets = groupTicketsResult.data;
+ const statusDistribution = statusResult.data;
+ const priorityDistribution = priorityResult.data;
const data = groupTickets.map((item) => {
return {
@@ -36,16 +131,6 @@ async function Dashboard() {
};
});
- const statusDistribution = await prisma.ticket.groupBy({
- by: ["status"],
- _count: true,
- });
-
- const priorityDistribution = await prisma.ticket.groupBy({
- by: ["priority"],
- _count: true,
- });
-
const statusData = statusDistribution.map((item) => ({
name: item.status,
value: item._count,
diff --git a/prisma/db.ts b/prisma/db.ts
index e5d7482..19ba5fc 100644
--- a/prisma/db.ts
+++ b/prisma/db.ts
@@ -8,6 +8,87 @@ declare const globalThis: {
prismaGlobal: ReturnType;
} & typeof global;
+// Cache database availability check to avoid repeated connection attempts
+let dbAvailabilityCache: { available: boolean; lastChecked: number } | null =
+ null;
+const DB_CHECK_CACHE_DURATION = 30000; // 30 seconds
+
+// Clear the database availability cache (useful for testing or manual refresh)
+export const clearDatabaseAvailabilityCache = (): void => {
+ dbAvailabilityCache = null;
+};
+
+// Check if DATABASE_URL is available and database is reachable
+export const isDatabaseAvailable = async (): Promise => {
+ // First check if DATABASE_URL is set
+ if (!process.env.DATABASE_URL) {
+ return false;
+ }
+
+ // Check cache first
+ const now = Date.now();
+ if (
+ dbAvailabilityCache &&
+ now - dbAvailabilityCache.lastChecked < DB_CHECK_CACHE_DURATION
+ ) {
+ return dbAvailabilityCache.available;
+ }
+
+ try {
+ // Attempt to connect and perform a simple query
+ // Use a temporary PrismaClient instance for the availability check
+ const tempPrisma = new PrismaClient();
+ await tempPrisma.$queryRaw`SELECT 1`;
+ await tempPrisma.$disconnect();
+
+ // Cache successful result
+ dbAvailabilityCache = { available: true, lastChecked: now };
+ return true;
+ } catch (error) {
+ console.error(
+ "Database connectivity check failed:",
+ error instanceof Error ? error.message : JSON.stringify(error)
+ );
+
+ // Cache failed result for a shorter duration to allow retries
+ dbAvailabilityCache = {
+ available: false,
+ lastChecked: now - (DB_CHECK_CACHE_DURATION - 5000),
+ };
+ return false;
+ }
+};
+
+// Safe database operation wrapper
+export const safeDbOperation = async (
+ operation: () => Promise,
+ fallback: T
+): Promise<{ data: T; error: string | null }> => {
+ const dbAvailable = await isDatabaseAvailable();
+
+ if (!dbAvailable) {
+ const errorMessage = !process.env.DATABASE_URL
+ ? "Database connection not configured. Please set DATABASE_URL environment variable."
+ : "Database is not reachable. Please check your database connection and ensure the database server is running.";
+
+ return {
+ data: fallback,
+ error: errorMessage,
+ };
+ }
+
+ try {
+ const data = await operation();
+ return { data, error: null };
+ } catch (error) {
+ console.error("Database operation failed:", error);
+ return {
+ data: fallback,
+ error: error instanceof Error ? error.message : "Unknown database error",
+ };
+ }
+};
+
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();
export default prisma;