feat: added proper validations for errors with messages
Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
@@ -44,7 +44,7 @@ export default function ProfileInfo() {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="border text-muted-foreground text-xs py-2 flex justify-between items-center px-2">
|
||||
<div className="border-t text-muted-foreground text-xs py-2 flex justify-between items-center px-2">
|
||||
<p>
|
||||
Made with <span className="text-red-500">♥</span> by{" "}
|
||||
<Link
|
||||
|
||||
+11
-1
@@ -4,6 +4,8 @@ import { Client, Pool } from "pg";
|
||||
import { ENV } from "./environment";
|
||||
import * as schema from "@/schema";
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { findMissingKeys } from '@/helpers/data.helper';
|
||||
import { APIError } from '@/lib/api';
|
||||
|
||||
const pool = ENV.DATABASE_URL ? new Pool({
|
||||
connectionString: ENV.DATABASE_URL,
|
||||
@@ -26,7 +28,15 @@ class DatabaseConnectionError extends Error {
|
||||
}
|
||||
export const connectDB = async (db: NodePgDatabase<typeof schema>) => {
|
||||
try {
|
||||
await db.execute(sql`SELECT 1`); // Execute a simple query
|
||||
const missingKeys = findMissingKeys(ENV, ['DB_USERNAME', 'DB_PASSWORD', 'DB_HOST', 'DB_PORT', 'DB_DATABASE_NAME']);
|
||||
if (!ENV.DATABASE_URL && missingKeys.length > 0) {
|
||||
throw new APIError({
|
||||
message: `Some database credentials are missing: ${missingKeys.join(', ')}. Please add them to the .env file`,
|
||||
status: 500,
|
||||
});
|
||||
} else {
|
||||
return await db.execute(sql`SELECT 1`); // Execute a simple query
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new DatabaseConnectionError(error.message, "Database connection failed");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { appConfig } from "@/config/app.config"
|
||||
import { connectDB, db } from "@/config/db"
|
||||
import { ENV } from "@/config/environment"
|
||||
import { APIError } from "@/lib/api"
|
||||
import { getCookie } from "@/lib/cookie"
|
||||
import { NextApiRequest } from "next"
|
||||
|
||||
@@ -22,7 +23,19 @@ export const getCurrentUserFromAPIKey = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
return null
|
||||
throw new APIError({
|
||||
message: "Invalid API key. Please check your API key variable `IMMICH_API_KEY` in the .env file",
|
||||
status: 403,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof APIError) {
|
||||
throw error;
|
||||
}
|
||||
throw new APIError({
|
||||
message: "Failed to connect to Immich API: " + ENV.IMMICH_URL,
|
||||
status: 500,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,6 +50,11 @@ export const getCurrentUserFromAccessToken = (token: string) => {
|
||||
return res.json()
|
||||
}
|
||||
return null
|
||||
}).catch((error) => {
|
||||
throw new APIError({
|
||||
message: "Failed to connect to Immich API: " + ENV.IMMICH_URL,
|
||||
status: 500,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,9 @@ export const removeNullOrUndefinedProperties = (obj: any) => {
|
||||
|
||||
return v !== null && v !== undefined && v !== '' && v !== 'null' && v !== 'undefined' && v !== 'null' && v !== 'undefined'
|
||||
}));
|
||||
}
|
||||
|
||||
export const findMissingKeys = (obj: any, keys: string[]) => {
|
||||
|
||||
return keys.filter((key) => !(key in obj) || obj[key] === '' || obj[key] === null || obj[key] === undefined);
|
||||
}
|
||||
+15
-2
@@ -2,6 +2,21 @@ import type { AxiosError, AxiosRequestConfig, Method } from 'axios'
|
||||
import axios from 'axios'
|
||||
import qs from 'qs'
|
||||
|
||||
export class APIError extends Error {
|
||||
status: number;
|
||||
constructor({
|
||||
message,
|
||||
status,
|
||||
}: {
|
||||
message: string;
|
||||
status: number;
|
||||
}) {
|
||||
super(message);
|
||||
this.name = 'APIError';
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
const handleErrors = (error: AxiosError | any) => {
|
||||
|
||||
@@ -26,8 +41,6 @@ const handleErrors = (error: AxiosError | any) => {
|
||||
}
|
||||
|
||||
export default class API {
|
||||
|
||||
|
||||
static setBaseURL(url: string) {
|
||||
if (url) {
|
||||
axios.defaults.baseURL = url
|
||||
|
||||
@@ -106,40 +106,42 @@ export default function ExifDataAnalytics() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageLayout className="p-4">
|
||||
<PageLayout className="!p-0 !mb-0 relative">
|
||||
<Header leftComponent="Exif Data" />
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{["Total", "Images", "Videos"].map((type, i) => (
|
||||
<Card key={i}>
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{["Total", "Images", "Videos"].map((type, i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center text-lg font-mono">{type}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center text-3xl font-mono">
|
||||
{loading ? "Loading..." : statistics[type.toLowerCase() as keyof typeof statistics].toLocaleString()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center text-lg font-mono">{type}</CardTitle>
|
||||
<CardTitle className="text-center text-lg font-mono">Live Photos</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center text-3xl font-mono">
|
||||
{loading ? "Loading..." : statistics[type.toLowerCase() as keyof typeof statistics].toLocaleString()}
|
||||
{loading ? "Loading..." : livePhotoStatistics.total.toLocaleString()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center text-lg font-mono">Live Photos</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center text-3xl font-mono">
|
||||
{loading ? "Loading..." : livePhotoStatistics.total.toLocaleString()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<AssetHeatMap />
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{exifCharts.map((chart) => (
|
||||
<EXIFDistribution
|
||||
key={chart.column}
|
||||
column={chart.column}
|
||||
title={chart.title}
|
||||
description={chart.description}
|
||||
tooltipValueFormatter={chart.tooltipValueFormatter}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<AssetHeatMap />
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{exifCharts.map((chart) => (
|
||||
<EXIFDistribution
|
||||
key={chart.column}
|
||||
column={chart.column}
|
||||
title={chart.title}
|
||||
description={chart.description}
|
||||
tooltipValueFormatter={chart.tooltipValueFormatter}
|
||||
/>
|
||||
))}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@@ -13,11 +13,15 @@ export default async function GET(req: NextApiRequest, res: NextApiResponse) {
|
||||
return res.status(404).json({ message: "Token not found" });
|
||||
}
|
||||
|
||||
if (!ENV.IMMICH_SHARE_LINK_KEY) {
|
||||
return res.status(401).json({ message: "Please check your link and try again. If you're the admin, check if you've enabled all the configurations in the Immich Power Tools in your environment variables" });
|
||||
}
|
||||
|
||||
const decoded = verify(token as string, ENV.JWT_SECRET);
|
||||
console.log(decoded);
|
||||
|
||||
|
||||
if (!decoded) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
return res.status(401).json({ message: "Link is invalid. Please check your link and try again" });
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,11 +15,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
}
|
||||
|
||||
if (!ENV.IMMICH_SHARE_LINK_KEY) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
if (!ENV.JWT_SECRET) {
|
||||
return res.status(401).json({ message: "The JWT_SECRET is missing in the .env file. Please add it to the .env file for generating share links" });
|
||||
}
|
||||
|
||||
console.log(expiresIn);
|
||||
if (!ENV.IMMICH_SHARE_LINK_KEY) {
|
||||
return res.status(401).json({ message: "The IMMICH_SHARE_LINK_KEY is missing in the .env file. Please add it to the .env file for generating share links" });
|
||||
}
|
||||
|
||||
if (!ENV.POWER_TOOLS_ENDPOINT_URL) {
|
||||
return res.status(401).json({ message: "The POWER_TOOLS_ENDPOINT_URL is missing in the .env file. Please add it to the .env file for generating share links" });
|
||||
}
|
||||
|
||||
const token = sign(filters, ENV.JWT_SECRET, expiresIn !== "never" ? {
|
||||
expiresIn: expiresIn
|
||||
|
||||
Reference in New Issue
Block a user