diff --git a/app/client/src/components/chart/DashboardCharts.svelte b/app/client/src/components/chart/DashboardCharts.svelte
index d7431a7a..7cf4a31a 100644
--- a/app/client/src/components/chart/DashboardCharts.svelte
+++ b/app/client/src/components/chart/DashboardCharts.svelte
@@ -85,7 +85,7 @@
{
label: `Total Fuel Cost (${getCurrencySymbol()})`,
data: costData,
- fill: false,
+ fill: true,
borderColor: 'rgb(75, 192, 192)',
tension: 0.3,
borderWidth: 2,
@@ -102,9 +102,13 @@
{
label: `Mileage (${getMileageUnit()})`,
data: mileageDataPoints,
- fill: true,
borderColor: 'rgb(255, 99, 132)',
- tension: 0.1
+ fill: true,
+ tension: 0.3,
+ borderWidth: 2,
+ borderCapStyle: 'round',
+ pointStyle: 'circle',
+ pointRadius: 2
}
]
};
diff --git a/app/client/src/components/common/Button.svelte b/app/client/src/components/common/Button.svelte
new file mode 100644
index 00000000..0ce4637e
--- /dev/null
+++ b/app/client/src/components/common/Button.svelte
@@ -0,0 +1,46 @@
+
+
+
+ {#if !loading}
+
+ {#if Icon}
+
+ {/if}
+ {text}
+
+ {:else}
+
+ {/if}
+
diff --git a/app/client/src/components/common/FormField.svelte b/app/client/src/components/common/FormField.svelte
index 34b06ae6..47c626ee 100644
--- a/app/client/src/components/common/FormField.svelte
+++ b/app/client/src/components/common/FormField.svelte
@@ -6,6 +6,7 @@
value = $bindable(),
icon = null,
required = false,
+ label = undefined,
ariaLabel = '',
disabled = false,
inputClass = '',
@@ -14,14 +15,19 @@
const Icon = icon;
-
+
+ {#if label}
+
{label}{required ? '*' : ''}
+ {/if}
{#if icon}
{/if}
diff --git a/app/client/src/components/common/FormSubmitButton.svelte b/app/client/src/components/common/FormSubmitButton.svelte
deleted file mode 100644
index 64d7607e..00000000
--- a/app/client/src/components/common/FormSubmitButton.svelte
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- {#if !loading}
-
- {text}
-
- {:else}
-
- {/if}
-
diff --git a/app/client/src/components/common/IconButton.svelte b/app/client/src/components/common/IconButton.svelte
new file mode 100644
index 00000000..a0051c9c
--- /dev/null
+++ b/app/client/src/components/common/IconButton.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/app/client/src/components/common/ModalContainer.svelte b/app/client/src/components/common/ModalContainer.svelte
index 4cb6e68c..5bfad42c 100644
--- a/app/client/src/components/common/ModalContainer.svelte
+++ b/app/client/src/components/common/ModalContainer.svelte
@@ -1,5 +1,7 @@
@@ -7,33 +9,21 @@
-
onclose()}
- aria-label="Close"
- >
-
-
-
-
-
- {title}
-
-
+
+
+ {title}
+
+
+
+
{@render children()}
diff --git a/app/client/src/components/common/StatusBlock.svelte b/app/client/src/components/common/StatusBlock.svelte
new file mode 100644
index 00000000..ce3858d2
--- /dev/null
+++ b/app/client/src/components/common/StatusBlock.svelte
@@ -0,0 +1,26 @@
+
+
+{#if message}
+
+ {#each message.split('\n') as error}
+ {error}
+ {/each}
+
+{/if}
diff --git a/app/client/src/components/common/ThemeToggle.svelte b/app/client/src/components/common/ThemeToggle.svelte
index 0a77f26c..417177e8 100644
--- a/app/client/src/components/common/ThemeToggle.svelte
+++ b/app/client/src/components/common/ThemeToggle.svelte
@@ -31,17 +31,17 @@
{#if darkMode}
-
+
{:else}
-
+
{/if}
diff --git a/app/client/src/components/common/VehicleCard.svelte b/app/client/src/components/common/VehicleCard.svelte
index 92357dfe..c9456e75 100644
--- a/app/client/src/components/common/VehicleCard.svelte
+++ b/app/client/src/components/common/VehicleCard.svelte
@@ -20,6 +20,7 @@
import { puccModelStore } from '$lib/stores/pucc';
import { browser } from '$app/environment';
import { env } from '$env/dynamic/public';
+ import IconButton from './IconButton.svelte';
const { vehicle, updateCallback } = $props();
@@ -28,8 +29,7 @@
return;
}
try {
- const response = await fetch(
- `${env.PUBLIC_API_BASE_URL || ''}/api/vehicles/${vehicleId}`, {
+ const response = await fetch(`${env.PUBLIC_API_BASE_URL || ''}/api/vehicles/${vehicleId}`, {
method: 'DELETE',
headers: {
'X-User-PIN': localStorage.getItem('userPin') || ''
@@ -87,10 +87,16 @@
- Color:
- {vehicle.color ? vehicle.color : '-'}
+
+ Color:
+ {#if vehicle.color}
+
+ {:else}
+ -
+ {/if}
@@ -101,7 +107,7 @@
Insurance:
-
+
{vehicle.insuranceStatus}
@@ -110,71 +116,60 @@
PUCC:
-
+
{vehicle.puccStatus}
{/if}
-
-
+ fuelLogModelStore.show(vehicle.id, null, false, updateCallback)}
- aria-label="Log fuel refill"
- >
-
-
-
-
+ maintenanceModelStore.show(vehicle.id, null, false, updateCallback)}
- aria-label="Log maintenance"
- >
-
-
-
+ insuranceModelStore.show(vehicle.id, null, false, updateCallback)}
- aria-label="Add Insurance"
- >
-
-
-
+ puccModelStore.show(vehicle.id, null, false, updateCallback)}
- aria-label="Add Pollution Certificate"
- >
-
-
+ ariaLabel="Pollution Certificate"
+ />
-
{
vehicleModelStore.show(vehicle, true);
}}
- aria-label="Edit vehicle"
- >
-
-
-
+
deleteVehicle(vehicle.id)}
- aria-label="Delete vehicle"
- >
-
-
+ ariaLabel="Delete"
+ />
diff --git a/app/client/src/components/forms/ConfigForm.svelte b/app/client/src/components/forms/ConfigForm.svelte
index 03533087..565966d2 100644
--- a/app/client/src/components/forms/ConfigForm.svelte
+++ b/app/client/src/components/forms/ConfigForm.svelte
@@ -1,5 +1,5 @@
@@ -105,7 +105,7 @@
{/if}
{/each}
-
+
{#if status.message}
- import FormSubmitButton from '$components/common/FormSubmitButton.svelte';
+ import Button from '$components/common/Button.svelte';
+ import StatusBlock from '$components/common/StatusBlock.svelte';
import { env } from '$env/dynamic/public';
- import { simulateNetworkDelay } from '$lib/utils/dev';
- import { formatDate, getCurrencySymbol } from '$lib/utils/formatting';
+ import { handleApiError } from '$lib/models/Error';
+ import type { Status } from '$lib/models/status';
+ import { getCurrencySymbol } from '$lib/utils/formatting';
import FormField from '../common/FormField.svelte';
import { Calendar1, Gauge, Fuel, FileText, BadgeDollarSign } from '@lucide/svelte';
@@ -16,39 +18,38 @@
} = $props();
let refill = $state({
- date: '',
- odometer: '',
- fuelAmount: '',
- cost: '',
- notes: ''
+ date: null,
+ odometer: null,
+ fuelAmount: null,
+ cost: null,
+ notes: null
});
- let status = $state<{
- message: string | null;
- type: 'ERROR' | 'SUCCESS' | null;
- }>({
- message: null,
- type: null
+ let status = $state({
+ message: undefined,
+ type: 'INFO'
+ });
+
+ $effect(() => {
+ Object.assign(refill, logToEdit);
});
async function persistLog() {
- status.message = '';
if (!vehicleId) {
- status.message = 'No vehicle selected.';
- status.type = 'ERROR';
+ status = {
+ message: 'No vehicle selected.',
+ type: 'ERROR'
+ };
return;
}
if (!refill.date || !refill.odometer || !refill.fuelAmount || !refill.cost) {
- status.message = 'Please fill in all required fields.';
- status.type = 'ERROR';
+ status = {
+ message: 'Date, Odometer, Fuel Amount, and Cost are required.',
+ type: 'ERROR'
+ };
return;
}
try {
- if (loading) return; // Prevent multiple submissions
- loading = true;
- status.message = null;
- status.type = null;
- // await simulateNetworkDelay(2000); // Simulate network delay for development
const response = await fetch(
`${env.PUBLIC_API_BASE_URL || ''}/api/vehicles/${vehicleId}/fuel-logs/${editMode ? logToEdit.id : ''}`,
{
@@ -57,18 +58,14 @@
'Content-Type': 'application/json',
'X-User-PIN': localStorage.getItem('userPin') || ''
},
- body: JSON.stringify({
- date: refill.date,
- odometer: parseFloat(refill.odometer),
- fuelAmount: parseFloat(refill.fuelAmount),
- cost: parseFloat(refill.cost),
- notes: refill.notes
- })
+ body: JSON.stringify(refill)
}
);
if (response.ok) {
- status.message = 'Fuel refill logged successfully.';
- status.type = 'SUCCESS';
+ status = {
+ message: `Fuel refill log ${editMode ? 'updated' : 'added'} successfully!`,
+ type: 'SUCCESS'
+ };
Object.assign(refill, {
date: '',
odometer: '',
@@ -78,83 +75,81 @@
});
} else {
const data = await response.json();
- status.message = data?.message || 'Failed to log fuel refill.';
- status.type = 'ERROR';
+ status = handleApiError(data, editMode);
}
} catch (err) {
- status.message = 'Network error. Please try again.';
- status.type = 'ERROR';
+ status = {
+ message: 'Failed to connect to the server.',
+ type: 'ERROR'
+ };
} finally {
loading = false;
if (status.type === 'SUCCESS') {
- modalVisibility = false; // Close modal on success
+ modalVisibility = false;
callback(true);
}
}
}
- $effect(() => {
- Object.assign(refill, logToEdit);
- });
-{#if status.message}
-
- {#if status.type === 'ERROR'}
- Error: {status.message}
- {:else}
- {status.message}
- {/if}
-
-{/if}
+
diff --git a/app/client/src/components/forms/InsuranceForm.svelte b/app/client/src/components/forms/InsuranceForm.svelte
index a486eb23..50fa6e04 100644
--- a/app/client/src/components/forms/InsuranceForm.svelte
+++ b/app/client/src/components/forms/InsuranceForm.svelte
@@ -1,9 +1,12 @@
-
-{#if status.message}
-
- {#if status.type === 'ERROR'}
- Error: {status.message}
- {:else}
- {status.message}
- {/if}
-
-{/if}
+
diff --git a/app/client/src/components/forms/MaintenanceLogForm.svelte b/app/client/src/components/forms/MaintenanceLogForm.svelte
index b715da76..bfa3a0c3 100644
--- a/app/client/src/components/forms/MaintenanceLogForm.svelte
+++ b/app/client/src/components/forms/MaintenanceLogForm.svelte
@@ -1,7 +1,10 @@
-
+
diff --git a/app/client/src/lib/models/Error.ts b/app/client/src/lib/models/Error.ts
new file mode 100644
index 00000000..6e499ec4
--- /dev/null
+++ b/app/client/src/lib/models/Error.ts
@@ -0,0 +1,18 @@
+import type { Status } from "./status";
+
+export type ApiError = {
+ type: string;
+ errors: {
+ message: string;
+ path?: string;
+ }[]
+}
+
+export const handleApiError = (data: ApiError, editMode: boolean): Status => {
+ console.log(JSON.stringify(data));
+ let message = data.errors.map((e) => e.path + ' : ' + e.message).join('\n');
+ return {
+ message: message || `Failed to ${editMode ? 'update' : 'add'} vehicle.`,
+ type: 'ERROR'
+ };
+ };
\ No newline at end of file
diff --git a/app/client/src/lib/models/status.ts b/app/client/src/lib/models/status.ts
new file mode 100644
index 00000000..42372587
--- /dev/null
+++ b/app/client/src/lib/models/status.ts
@@ -0,0 +1,4 @@
+export type Status = {
+ message?: string;
+ type: 'ERROR' | 'SUCCESS' | 'INFO';
+};
diff --git a/app/client/src/lib/models/vehicle.ts b/app/client/src/lib/models/vehicle.ts
index 083237ef..59aca8bb 100644
--- a/app/client/src/lib/models/vehicle.ts
+++ b/app/client/src/lib/models/vehicle.ts
@@ -1,16 +1,16 @@
export interface NewVehicle {
- make: string;
- model: string;
+ make: string | null;
+ model: string | null;
year: number | null;
- licensePlate: string;
- vin?: string;
- color?: string;
- odometer?: number | null;
+ licensePlate: string | null;
+ vin: string | null;
+ color: string | null;
+ odometer: number | null;
}
export interface Vehicle extends NewVehicle {
vehicleType: string;
id: string;
- insuranceStatus?: string;
- puccStatus?: string;
+ insuranceStatus: string | null;
+ puccStatus?: string | null;
}
diff --git a/app/client/src/routes/+layout.svelte b/app/client/src/routes/+layout.svelte
index bc178086..1b27dc91 100644
--- a/app/client/src/routes/+layout.svelte
+++ b/app/client/src/routes/+layout.svelte
@@ -11,6 +11,7 @@
import { configModelStore } from '$lib/stores/config';
import { vehiclesStore } from '$lib/stores/vehicle';
import { darkModeStore } from '$lib/stores/dark-mode';
+ import IconButton from '$components/common/IconButton.svelte';
let { children } = $props();
@@ -52,11 +53,12 @@
{#if demoMode}
-
🚜 Demo Mode: This is a demo instance. Data will be reset periodically and is not saved
- permanently. Please avoid adding any persoanl info.
+ ⚠️ NOTICE: This is a demo instance. Data will be reset periodically and is not saved
+ permanently.
+ Please avoid adding any persoanl info.
Default PIN : 123456
@@ -78,28 +80,26 @@
Tracktor
-
+
-
- {
- configModelStore.show((status: boolean) => {
- fetchVehicles();
- });
- }}
- />
-
-
-
-
-
-
+
{
+ configModelStore.show((status: boolean) => {
+ fetchVehicles();
+ });
+ }}
+ ariaLabel="Settings"
+ />
+
diff --git a/app/client/src/routes/dashboard/+page.svelte b/app/client/src/routes/dashboard/+page.svelte
index aba20e6d..c0237cdf 100644
--- a/app/client/src/routes/dashboard/+page.svelte
+++ b/app/client/src/routes/dashboard/+page.svelte
@@ -1,5 +1,5 @@
@@ -123,12 +145,6 @@
{/if}
- {#if error}
-
- {error}
-
- {/if}
+
diff --git a/app/server/index.ts b/app/server/index.ts
index 5063802f..9612faf4 100644
--- a/app/server/index.ts
+++ b/app/server/index.ts
@@ -4,6 +4,7 @@ import pinRoutes from "./src/routes/pinRoutes.js";
import vehicleRoutes from "./src/routes/vehicleRoutes.js";
import configRoutes from "./src/routes/configRoutes.js";
import { performDbMigrations, seedData } from "./src/db/index.js";
+import { errorHandler } from "./src/middleware/error-handler.js";
const app = express();
const PORT = Number(process.env.APP_PORT) || 3000;
@@ -16,29 +17,35 @@ app.use("/api", pinRoutes);
app.use("/api/vehicles", vehicleRoutes);
app.use("/api/config", configRoutes);
-if (process.env.NODE_ENV === 'production') {
+if (process.env.NODE_ENV === "production") {
// @ts-ignore
- const { handler } = await import('../client/build/handler.js');
+ const { handler } = await import("../client/build/handler.js");
app.use(handler);
} else {
// In dev, redirect to SvelteKit dev server
- app.use('/', (req, res) => {
+ app.use("/", (req, res) => {
res.redirect(`http://localhost:5173${req.originalUrl}`);
});
}
+app.use(errorHandler);
+
performDbMigrations()
.then(() => {
console.log("DB Migration is Successfull!!!");
- seedData().then(() => {
- console.log("Data Seeded Successfully!!!");
- app.listen(PORT, HOST, () => {
- console.log(`🖥️ Server running @ http://${HOST}:${PORT}`);
+ seedData()
+ .then(() => {
+ console.log("Data Seeded Successfully!!!");
+ app.listen(PORT, HOST, () => {
+ console.log(
+ "---------------------------------------------------------------------------",
+ );
+ console.log(`Server started -> http://${HOST}:${PORT}`);
+ });
+ })
+ .catch((err) => {
+ console.error("Error while seeding : ", err);
});
- }).catch((err) => {
- console.error("Error while seeding : ", err);
- })
-
})
.catch((err) => {
console.error("Error while running db migrations : ", err);
diff --git a/app/server/src/controllers/ConfigController.ts b/app/server/src/controllers/ConfigController.ts
index 13925906..7d5afe72 100644
--- a/app/server/src/controllers/ConfigController.ts
+++ b/app/server/src/controllers/ConfigController.ts
@@ -4,49 +4,33 @@ import {
getAppConfigByKey,
updateAppConfig,
} from "../services/configService.js";
+import { ConfigError } from "../exceptions/ConfigError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const getConfig = async (req: Request, res: Response) => {
- try {
- const config = await getAppConfig();
- res.json(config);
- } catch (error) {
- res.status(500).json({ message: "Error fetching configuration" });
- }
+ const config = await getAppConfig();
+ res.status(200).json(config);
};
export const getConfigByKey = async (req: Request, res: Response) => {
- try {
- const key = req.params.key;
- if (!key) {
- return res.status(400).json({ message: "Key parameter is required" });
- }
- const config = await getAppConfigByKey(key);
- if (!config) {
- return res.status(404).json({ message: "Configuration not found" });
- }
- res.json(config);
- } catch (error) {
- res.status(500).json({ message: "Error fetching configuration" });
+ const key = req.params.key;
+ if (!key) {
+ throw new ConfigError("Key parameter is required", Status.BAD_REQUEST);
}
+ const config = await getAppConfigByKey(key);
+ res.status(200).json(config);
};
export const updateConfig = async (req: Request, res: Response) => {
- try {
- const configs: { key: string; value: string }[] = req.body;
- if (!Array.isArray(configs) || configs.length === 0) {
- return res.status(400).json({ message: "Invalid configuration data" });
- }
- const updatedConfigs = await Promise.all(
- configs.map(async (config) => {
- const { key, value } = config;
- if (!key || value === undefined) {
- throw new Error("Key and value are required for each configuration");
- }
- return await updateAppConfig(key, value);
- }),
- );
- res.json(updatedConfigs);
- } catch (error) {
- res.status(500).json({ message: "Error updating configuration" });
+ const configs: { key: string; value: string }[] = req.body;
+ if (!Array.isArray(configs) || configs.length === 0) {
+ throw new ConfigError("Invalid configuration data", Status.BAD_REQUEST);
}
+ const updatedConfigs = await Promise.all(
+ configs.map(async (config) => {
+ const { key, value } = config;
+ return await updateAppConfig(key, value);
+ }),
+ );
+ res.json(updatedConfigs);
};
diff --git a/app/server/src/controllers/FuelLogController.ts b/app/server/src/controllers/FuelLogController.ts
index 991ee939..7f72dc27 100644
--- a/app/server/src/controllers/FuelLogController.ts
+++ b/app/server/src/controllers/FuelLogController.ts
@@ -1,112 +1,66 @@
-
import { Request, Response } from "express";
import * as fuelLogService from "../services/fuelLogService.js";
-import { VehicleNotFoundError } from "../exceptions/VehicleErrors.js";
-import { FuelLogNotFoundError } from "../exceptions/FuelLogError.js";
+import { FuelLogError } from "../exceptions/FuelLogError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const addFuelLog = async (req: Request, res: Response) => {
+
const { vehicleId } = req.params;
const { date, odometer, fuelAmount, cost } = req.body;
if (!date || !odometer || !fuelAmount || !cost) {
- return res
- .status(400)
- .json({ message: "Date, Odometer, Fuel Amount, and Cost are required." });
+ throw new FuelLogError(
+ "Date, Odometer, Fuel Amount, and Cost are required in request body.",
+ Status.BAD_REQUEST,
+ );
}
if (!vehicleId) {
- console.log("Vehicle ID is missing in request parameters.", JSON.stringify(req.path));
- return res
- .status(400)
- .json({ message: "Vehicle ID is required." });
+ throw new FuelLogError("Vehicle id is required.", Status.BAD_REQUEST);
}
- try {
- const result = await fuelLogService.addFuelLog(vehicleId, req.body);
- res.status(201).json(result);
- } catch (error: any) {
- console.error("Error adding fuel log:", error);
- if (error instanceof VehicleNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
- }
+ const result = await fuelLogService.addFuelLog(vehicleId, req.body);
+ res.status(201).json(result);
};
export const getFuelLogs = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
if (!vehicleId) {
- return res
- .status(400)
- .json({ message: "Vehicle ID is required." });
- }
- try {
- const fuelLogs = await fuelLogService.getFuelLogs(vehicleId);
- res.status(200).json(fuelLogs);
- } catch (error: any) {
- console.error("Error fetching fuel logs:", error);
- res.status(500).json({ message: error.message });
+ throw new FuelLogError("Vehicle id is required.", Status.BAD_REQUEST);
}
+ const fuelLogs = await fuelLogService.getFuelLogs(vehicleId);
+ res.status(200).json(fuelLogs);
};
export const getFuelLogById = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res
- .status(400)
- .json({ message: "Fuel log ID is required." });
- }
- try {
- const fuelLog = await fuelLogService.getFuelLogById(id);
- res.status(200).json(fuelLog);
- } catch (error: any) {
- if (error instanceof FuelLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new FuelLogError("Fuel Log id is required.", Status.BAD_REQUEST);
}
+ const fuelLog = await fuelLogService.getFuelLogById(id);
+ res.status(200).json(fuelLog);
};
export const updateFuelLog = async (req: Request, res: Response) => {
const { id } = req.params;
const { date, odometer, fuelAmount, cost } = req.body;
-
if (!date || !odometer || !fuelAmount || !cost) {
- return res
- .status(400)
- .json({ message: "Date, Odometer, Fuel Amount, and Cost are required." });
+ throw new FuelLogError(
+ "Date, Odometer, Fuel Amount, and Cost are required.",
+ Status.BAD_REQUEST,
+ );
}
if (!id) {
- return res
- .status(400)
- .json({ message: "Fuel log ID is required." });
- }
- try {
- const result = await fuelLogService.updateFuelLog(id, req.body);
- res.status(200).json(result);
- } catch (error: any) {
- console.error("Error updating fuel log:", error);
- if (error instanceof FuelLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new FuelLogError("Fuel log ID is required.", Status.BAD_REQUEST);
}
+ const result = await fuelLogService.updateFuelLog(id, req.body);
+ res.status(200).json(result);
};
export const deleteFuelLog = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res
- .status(400)
- .json({ message: "Fuel log ID is required." });
- }
- try {
- const result = await fuelLogService.deleteFuelLog(id);
- res.status(200).json(result);
- } catch (error: any) {
- console.error("Error deleting fuel log:", error);
- if (error instanceof FuelLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new FuelLogError("Fuel Log id is required.", Status.BAD_REQUEST);
}
+ const result = await fuelLogService.deleteFuelLog(id);
+ res.status(200).json(result);
};
diff --git a/app/server/src/controllers/InsuranceController.ts b/app/server/src/controllers/InsuranceController.ts
index 8cbaae2c..e1e925e2 100644
--- a/app/server/src/controllers/InsuranceController.ts
+++ b/app/server/src/controllers/InsuranceController.ts
@@ -1,59 +1,32 @@
import { Request, Response } from "express";
import * as insuranceService from "../services/insuranceService.js";
-import {
- InsuranceNotFoundError,
- InsuranceExistsError,
- InsuranceServiceError,
-} from "../exceptions/InsuranceError.js";
+import { InsuranceError } from "../exceptions/InsuranceError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const addInsurance = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
const { provider, policyNumber, startDate, endDate, cost } = req.body;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
+ throw new InsuranceError("Vehicle ID is required.", Status.BAD_REQUEST);
}
if (!provider || !policyNumber || !startDate || !endDate || !cost) {
- return res.status(400).json({
- message:
- "Provider, Policy Number, Start Date, End Date, and Cost are required.",
- });
- }
-
- try {
- const result = await insuranceService.addInsurance(vehicleId, req.body);
- res.status(201).json(result);
- } catch (error: any) {
- if (error instanceof InsuranceNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof InsuranceExistsError) {
- return res.status(409).json({ message: error.message });
- }
- if (error instanceof InsuranceServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new InsuranceError(
+ "Provider, Policy Number, Start Date, End Date, and Cost are required.",
+ Status.BAD_REQUEST,
+ );
}
+ const result = await insuranceService.addInsurance(vehicleId, req.body);
+ res.status(201).json(result);
};
export const getInsurances = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const insurances = await insuranceService.getInsurances(vehicleId);
- res.status(200).json(insurances);
- } catch (error: any) {
- if (error instanceof InsuranceNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof InsuranceServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new InsuranceError("Vehicle ID is required.", Status.BAD_REQUEST);
}
+ const insurances = await insuranceService.getInsurances(vehicleId);
+ res.status(200).json(insurances);
};
export const updateInsurance = async (req: Request, res: Response) => {
@@ -61,50 +34,30 @@ export const updateInsurance = async (req: Request, res: Response) => {
const { provider, policyNumber, startDate, endDate, cost } = req.body;
if (!vehicleId || !id) {
- return res
- .status(400)
- .json({ message: "Vehicle ID and Insurance Id is required." });
+ throw new InsuranceError(
+ "Vehicle ID and Insurance Id is required.",
+ Status.BAD_REQUEST,
+ );
}
if (!provider || !policyNumber || !startDate || !endDate || !cost) {
- return res.status(400).json({
- message:
- "Provider, Policy Number, Start Date, End Date, and Cost are required.",
- });
- }
-
- try {
- const result = await insuranceService.updateInsurance(
- vehicleId,
- id,
- req.body,
+ throw new InsuranceError(
+ "Provider, Policy Number, Start Date, End Date, and Cost are required.",
+ Status.BAD_REQUEST,
);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof InsuranceNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof InsuranceServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
}
+ const result = await insuranceService.updateInsurance(
+ vehicleId,
+ id,
+ req.body,
+ );
+ res.status(200).json(result);
};
export const deleteInsurance = async (req: Request, res: Response) => {
- const { vehicleId } = req.params;
- if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const result = await insuranceService.deleteInsurance(vehicleId);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof InsuranceNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof InsuranceServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ const { id } = req.params;
+ if (!id) {
+ throw new InsuranceError("Insurance ID is required.", Status.BAD_REQUEST);
}
+ const result = await insuranceService.deleteInsurance(id);
+ res.status(200).json(result);
};
diff --git a/app/server/src/controllers/MaintenanceLogController.ts b/app/server/src/controllers/MaintenanceLogController.ts
index d5d50ceb..2ada6eb6 100644
--- a/app/server/src/controllers/MaintenanceLogController.ts
+++ b/app/server/src/controllers/MaintenanceLogController.ts
@@ -1,75 +1,56 @@
import { Request, Response } from "express";
import * as maintenanceLogService from "../services/maintenanceLogService.js";
-import {
- MaintenanceLogNotFoundError,
- MaintenanceLogServiceError
-} from "../exceptions/MaintenanceLogErrors.js";
+import { MaintenanceLogError } from "../exceptions/MaintenanceLogError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const addMaintenanceLog = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
- const { date, odometer, service, cost } = req.body;
+ const { date, odometer, serviceCenter, cost } = req.body;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- if (!date || !odometer || !service || !cost) {
- return res
- .status(400)
- .json({ message: "Date, Odometer, Service, and Cost are required." });
+ throw new MaintenanceLogError(
+ "Vehicle ID is required.",
+ Status.BAD_REQUEST,
+ );
}
-
- try {
- const result = await maintenanceLogService.addMaintenanceLog(
- vehicleId,
- req.body
+ if (!date || !odometer || !serviceCenter || !cost) {
+ throw new MaintenanceLogError(
+ "Date, Odometer, ServiceCenter, and Cost are required.",
+ Status.BAD_REQUEST,
);
- res.status(201).json(result);
- } catch (error: any) {
- if (error instanceof MaintenanceLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof MaintenanceLogServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
}
+ const result = await maintenanceLogService.addMaintenanceLog(
+ vehicleId,
+ req.body,
+ );
+ res.status(201).json(result);
};
export const getMaintenanceLogs = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const maintenanceLogs = await maintenanceLogService.getMaintenanceLogs(
- vehicleId
+ throw new MaintenanceLogError(
+ "Vehicle ID is required.",
+ Status.BAD_REQUEST,
);
- res.status(200).json(maintenanceLogs);
- } catch (error: any) {
- if (error instanceof MaintenanceLogServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
}
+ const maintenanceLogs =
+ await maintenanceLogService.getMaintenanceLogs(vehicleId);
+ res.status(200).json(maintenanceLogs);
};
export const getMaintenanceLogById = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res.status(400).json({ message: "Maintenance Log ID is required." });
- }
- try {
- const maintenanceLog = await maintenanceLogService.getMaintenanceLogById(id);
- res.status(200).json(maintenanceLog);
- } catch (error: any) {
- if (error instanceof MaintenanceLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof MaintenanceLogServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new MaintenanceLogError(
+ "Maintenance Log ID is required.",
+ Status.BAD_REQUEST,
+ );
}
+
+ const maintenanceLog =
+ await maintenanceLogService.getMaintenanceLogById(id);
+ res.status(200).json(maintenanceLog);
};
export const updateMaintenanceLog = async (req: Request, res: Response) => {
@@ -77,43 +58,33 @@ export const updateMaintenanceLog = async (req: Request, res: Response) => {
const { date, odometer, service, cost, notes } = req.body;
if (!id) {
- return res.status(400).json({ message: "Maintenance Log ID is required." });
+ throw new MaintenanceLogError(
+ "Maintenance Log ID is required.",
+ Status.BAD_REQUEST,
+ );
}
if (!date || !odometer || !service || !cost) {
- return res
- .status(400)
- .json({ message: "Date, Odometer, Service, and Cost are required." });
- }
-
- try {
- const result = await maintenanceLogService.updateMaintenanceLog(id, req.body);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof MaintenanceLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof MaintenanceLogServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new MaintenanceLogError(
+ "Date, Odometer, Service, and Cost are required.",
+ Status.BAD_REQUEST,
+ );
}
+ const result = await maintenanceLogService.updateMaintenanceLog(
+ id,
+ req.body,
+ );
+ res.status(200).json(result);
};
export const deleteMaintenanceLog = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res.status(400).json({ message: "Maintenance Log ID is required." });
- }
- try {
- const result = await maintenanceLogService.deleteMaintenanceLog(id);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof MaintenanceLogNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof MaintenanceLogServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new MaintenanceLogError(
+ "Maintenance Log ID is required.",
+ Status.BAD_REQUEST,
+ );
}
+
+ const result = await maintenanceLogService.deleteMaintenanceLog(id);
+ res.status(200).json(result);
};
diff --git a/app/server/src/controllers/PUCCController.ts b/app/server/src/controllers/PUCCController.ts
index ab049b6d..47312b59 100644
--- a/app/server/src/controllers/PUCCController.ts
+++ b/app/server/src/controllers/PUCCController.ts
@@ -1,64 +1,44 @@
import { Request, Response } from "express";
import * as pollutionCertificateService from "../services/pollutionCertificateService.js";
-import {
- PollutionCertificateNotFoundError,
- PollutionCertificateExistsError,
- PollutionCertificateServiceError,
-} from "../exceptions/PollutionCertificateErrors.js";
+import { PollutionCertificateError } from "../exceptions/PollutionCertificateError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const addPollutionCertificate = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
- const { certificateNumber, issueDate, expiryDate, testingCenter, notes } =
+ const { certificateNumber, issueDate, expiryDate, testingCenter } =
req.body;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
+ throw new PollutionCertificateError(
+ "Vehicle ID is required.",
+ Status.BAD_REQUEST,
+ );
}
if (!certificateNumber || !issueDate || !expiryDate || !testingCenter) {
- return res.status(400).json({
- message:
- "Certificate Number, Issue Date, Expiry Date, and Testing Center are required.",
- });
- }
-
- try {
- const result = await pollutionCertificateService.addPollutionCertificate(
- vehicleId,
- req.body,
+ throw new PollutionCertificateError(
+ "Certificate Number, Issue Date, Expiry Date, and Testing Center are required.",
+ Status.BAD_REQUEST,
);
- res.status(201).json(result);
- } catch (error: any) {
- if (error instanceof PollutionCertificateNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof PollutionCertificateExistsError) {
- return res.status(409).json({ message: error.message });
- }
- if (error instanceof PollutionCertificateServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
}
+
+ const result = await pollutionCertificateService.addPollutionCertificate(
+ vehicleId,
+ req.body,
+ );
+ res.status(201).json(result);
};
export const getPollutionCertificates = async (req: Request, res: Response) => {
const { vehicleId } = req.params;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const pollutionCertificates =
- await pollutionCertificateService.getPollutionCertificates(vehicleId);
- res.status(200).json(pollutionCertificates);
- } catch (error: any) {
- if (error instanceof PollutionCertificateNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof PollutionCertificateServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new PollutionCertificateError(
+ "Vehicle ID is required.",
+ Status.BAD_REQUEST,
+ );
}
+ const pollutionCertificates =
+ await pollutionCertificateService.getPollutionCertificates(vehicleId);
+ res.status(200).json(pollutionCertificates);
};
export const updatePollutionCertificate = async (
@@ -70,33 +50,23 @@ export const updatePollutionCertificate = async (
req.body;
if (!vehicleId || !id) {
- return res
- .status(400)
- .json({ message: "Vehicle ID and certificate ID are required." });
+ throw new PollutionCertificateError(
+ "Vehicle ID and certificate ID are required.",
+ Status.BAD_REQUEST,
+ );
}
if (!certificateNumber || !issueDate || !expiryDate || !testingCenter) {
- return res.status(400).json({
- message:
- "Certificate Number, Issue Date, Expiry Date, and Testing Center are required.",
- });
- }
-
- try {
- const result = await pollutionCertificateService.updatePollutionCertificate(
- vehicleId,
- id,
- req.body,
+ throw new PollutionCertificateError(
+ "Certificate Number, Issue Date, Expiry Date, and Testing Center are required.",
+ Status.BAD_REQUEST,
);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof PollutionCertificateNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof PollutionCertificateServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
}
+ const result = await pollutionCertificateService.updatePollutionCertificate(
+ vehicleId,
+ id,
+ req.body,
+ );
+ res.status(200).json(result);
};
export const deletePollutionCertificate = async (
@@ -105,19 +75,12 @@ export const deletePollutionCertificate = async (
) => {
const { vehicleId } = req.params;
if (!vehicleId) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const result =
- await pollutionCertificateService.deletePollutionCertificate(vehicleId);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof PollutionCertificateNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- if (error instanceof PollutionCertificateServiceError) {
- return res.status(500).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new PollutionCertificateError(
+ "Vehicle ID is required.",
+ Status.BAD_REQUEST,
+ );
}
+ const result =
+ await pollutionCertificateService.deletePollutionCertificate(vehicleId);
+ res.status(200).json(result);
};
diff --git a/app/server/src/controllers/PinController.ts b/app/server/src/controllers/PinController.ts
index 37f51cad..2e024022 100644
--- a/app/server/src/controllers/PinController.ts
+++ b/app/server/src/controllers/PinController.ts
@@ -1,42 +1,27 @@
-
import { Request, Response } from "express";
import * as pinService from "../services/pinService.js";
+import { AuthError } from "../exceptions/AuthError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const setPin = async (req: Request, res: Response) => {
const { pin } = req.body;
-
if (!pin || pin.length !== 6 || !/^\d+$/.test(pin)) {
- return res.status(400).json({ message: "PIN must be a 6-digit number." });
- }
-
- try {
- const result = await pinService.setPin(pin);
- res.status(result.status).json({ message: result.message });
- } catch (error: any) {
- res.status(500).json({ message: error.message });
+ throw new AuthError("PIN must be a 6-digit number.", Status.BAD_REQUEST);
}
+ const result = await pinService.setPin(pin);
+ res.status(200).json({ message: result.message });
};
export const verifyPin = async (req: Request, res: Response) => {
const { pin } = req.body;
-
if (!pin) {
- return res.status(400).json({ message: "PIN is required." });
- }
-
- try {
- const result = await pinService.verifyPin(pin);
- res.status(result.status).json({ message: result.message });
- } catch (error: any) {
- res.status(500).json({ message: error.message });
+ throw new AuthError("PIN is required.", Status.BAD_REQUEST);
}
+ const result = await pinService.verifyPin(pin);
+ res.status(200).json({ message: result.message });
};
export const getPinStatus = async (req: Request, res: Response) => {
- try {
- const result = await pinService.getPinStatus();
- res.status(200).json(result);
- } catch (error: any) {
- res.status(500).json({ message: error.message });
- }
+ const result = await pinService.getPinStatus();
+ res.status(200).json(result);
};
diff --git a/app/server/src/controllers/VehicleController.ts b/app/server/src/controllers/VehicleController.ts
index 06a4b08b..2559a24c 100644
--- a/app/server/src/controllers/VehicleController.ts
+++ b/app/server/src/controllers/VehicleController.ts
@@ -1,47 +1,32 @@
import { Request, Response } from "express";
import * as vehicleService from "../services/vehicleService.js";
-import { VehicleNotFoundError } from "../exceptions/VehicleErrors.js";
+import { VehicleError } from "../exceptions/VehicleError.js";
+import { Status } from "../exceptions/ServiceError.js";
export const addVehicle = async (req: Request, res: Response) => {
const { make, model, year, licensePlate } = req.body;
-
if (!make || !model || !year || !licensePlate) {
- return res
- .status(400)
- .json({ message: "Make, Model, Year, and License Plate are required." });
- }
-
- try {
- const result = await vehicleService.addVehicle(req.body);
- res.status(201).json(result);
- } catch (error: any) {
- res.status(500).json({ message: error.message });
+ new VehicleError(
+ "Make, Model, Year, and License Plate are required.",
+ Status.BAD_REQUEST,
+ );
}
+ const result = await vehicleService.addVehicle(req.body);
+ res.status(201).json(result);
};
export const getAllVehicles = async (req: Request, res: Response) => {
- try {
- const vehicles = await vehicleService.getAllVehicles();
- res.status(200).json(vehicles);
- } catch (error: any) {
- res.status(500).json({ message: error.message });
- }
+ const vehicles = await vehicleService.getAllVehicles();
+ res.status(200).json(vehicles);
};
export const getVehicleById = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const vehicle = await vehicleService.getVehicleById(id);
- res.status(200).json(vehicle);
- } catch (error: any) {
- if (error instanceof VehicleNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new VehicleError("Vehicle ID is required.", Status.BAD_REQUEST);
}
+ const vehicle = await vehicleService.getVehicleById(id);
+ res.status(200).json(vehicle);
};
export const updateVehicle = async (req: Request, res: Response) => {
@@ -49,36 +34,24 @@ export const updateVehicle = async (req: Request, res: Response) => {
const { make, model, year, licensePlate } = req.body;
if (!make || !model || !year || !licensePlate) {
- return res
- .status(400)
- .json({ message: "Make, Model, Year, and License Plate are required." });
+ throw new VehicleError(
+ "Make, Model, Year, and License Plate are required.",
+ Status.BAD_REQUEST,
+ );
}
if (!id) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const result = await vehicleService.updateVehicle(id, req.body);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof VehicleNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new VehicleError("Vehicle ID is required.", Status.BAD_REQUEST);
}
+ const result = await vehicleService.updateVehicle(id, req.body);
+ res.status(200).json(result);
};
export const deleteVehicle = async (req: Request, res: Response) => {
const { id } = req.params;
if (!id) {
- return res.status(400).json({ message: "Vehicle ID is required." });
- }
- try {
- const result = await vehicleService.deleteVehicle(id);
- res.status(200).json(result);
- } catch (error: any) {
- if (error instanceof VehicleNotFoundError) {
- return res.status(404).json({ message: error.message });
- }
- res.status(500).json({ message: error.message });
+ throw new VehicleError("Vehicle ID is required.", Status.BAD_REQUEST);
}
+
+ const result = await vehicleService.deleteVehicle(id);
+ res.status(200).json(result);
};
diff --git a/app/server/src/db/db.ts b/app/server/src/db/db.ts
index 69d7817a..2da8a6c3 100644
--- a/app/server/src/db/db.ts
+++ b/app/server/src/db/db.ts
@@ -1,15 +1,14 @@
import { config } from "dotenv";
import { Sequelize } from "sequelize";
-config({ quiet: false, debug: true });
+config({ quiet: true, debug: false });
-const showSql = process.env.SHOW_SQL === 'true' || false
-console.log(`--------------------------------- SHOW SQL : ${showSql} --------------------------------------`)
+const showSql = process.env.SHOW_SQL === "true" || false;
const db = new Sequelize({
- dialect: "sqlite",
- storage: `${process.env.DB_PATH || './vehicles.db'}`, // Use environment variable for database path
- logging: showSql,
+ dialect: "sqlite",
+ storage: `${process.env.DB_PATH || "./vehicles.db"}`, // Use environment variable for database path
+ logging: showSql,
});
-export { db };
\ No newline at end of file
+export { db };
diff --git a/app/server/src/db/index.ts b/app/server/src/db/index.ts
index 6b77508a..ad188ff7 100644
--- a/app/server/src/db/index.ts
+++ b/app/server/src/db/index.ts
@@ -1,14 +1,22 @@
import { Umzug, SequelizeStorage } from "umzug";
import bcrypt from "bcrypt";
-import { Auth, Config, FuelLog, Insurance, MaintenanceLog, PollutionCertificate, Vehicle } from "../models/index.js";
+import {
+ Auth,
+ Config,
+ FuelLog,
+ Insurance,
+ MaintenanceLog,
+ PollutionCertificate,
+ Vehicle,
+} from "../models/index.js";
import { db } from "./db.js";
const umzug = new Umzug({
- migrations: { glob: 'migrations/*.ts' },
+ migrations: { glob: "migrations/*.ts" },
context: db.getQueryInterface(),
storage: new SequelizeStorage({
tableName: "migrations",
- sequelize: db
+ sequelize: db,
}),
logger: console,
});
@@ -17,12 +25,12 @@ const umzug = new Umzug({
type Migration = typeof umzug._types.migration;
const performDbMigrations = async () => {
- await db.sync({ alter: true })
+ await db.sync({ alter: false });
return umzug.up({});
-}
+};
const seedData = async () => {
- const demoMode = process.env.DEMO_MODE === 'true'
+ const demoMode = process.env.DEMO_MODE === "true";
if (demoMode) {
await db.dropAllSchemas({});
@@ -30,21 +38,20 @@ const seedData = async () => {
} else {
await setupPinAndConfigs();
}
-
-}
+};
const setupPinAndConfigs = async () => {
- const pin = process.env.AUTH_PIN
+ const pin = process.env.AUTH_PIN;
if (pin) await generatePin(pin);
await generateConfigs();
-}
+};
const insertDummyData = async () => {
try {
await db.sync({ force: true });
console.log("Database synchronized!");
- await generatePin('123456');
+ await generatePin("123456");
await generateConfigs();
const { vehicle1, vehicle2 } = await generateVehicles();
await generateInsurances(vehicle1, vehicle2);
@@ -55,7 +62,7 @@ const insertDummyData = async () => {
} catch (error) {
console.error("Error populating database:", error);
}
-}
+};
async function generateFuelLogData(vehicle1: Vehicle, vehicle2: Vehicle) {
const vehicle1FuelLogs: any = [
@@ -157,15 +164,17 @@ async function generateMaintenenceLogs(vehicle1: Vehicle, vehicle2: Vehicle) {
vehicleId: vehicle1.id,
date: "2023-06-01",
odometer: 18000,
- service: "Oil Change",
+ serviceCenter: "Test Center",
cost: 50,
+ notes: "Oil Change",
},
{
vehicleId: vehicle2.id,
date: "2023-07-01",
odometer: 28000,
- service: "Tire Rotation",
+ serviceCenter: "Test Center",
cost: 30,
+ notes: "Tire Rotation",
},
]);
@@ -200,9 +209,9 @@ async function generateVehicles() {
make: "Honda",
model: "Civic",
year: 2022,
- licensePlate: "TS-07-JA-1997",
+ licensePlate: "TS07JA1997",
vin: "1HGFC1F7XNA000001",
- color: "Black",
+ color: "#000",
odometer: 15000,
});
@@ -210,9 +219,9 @@ async function generateVehicles() {
make: "Toyota",
model: "Corolla",
year: 2021,
- licensePlate: "AP-28-DX-2000",
+ licensePlate: "AP28DX2000",
vin: "1NXBU4EE4AZ000002",
- color: "White",
+ color: "#FFF",
odometer: 25000,
});
@@ -239,7 +248,7 @@ async function generateConfigs() {
},
];
await Config.bulkCreate(configData, {
- ignoreDuplicates: true
+ ignoreDuplicates: true,
});
console.log("Configuration data created.");
}
@@ -251,4 +260,4 @@ async function generatePin(pin: string) {
console.log(`User created with PIN: ${pin}`);
}
-export { Migration, performDbMigrations, seedData, db }
\ No newline at end of file
+export { Migration, performDbMigrations, seedData, db };
diff --git a/app/server/src/exceptions/AuthError.ts b/app/server/src/exceptions/AuthError.ts
new file mode 100644
index 00000000..38317a16
--- /dev/null
+++ b/app/server/src/exceptions/AuthError.ts
@@ -0,0 +1,7 @@
+import { ServiceError, Status } from "./ServiceError.js";
+
+export class AuthError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(AuthError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/ConfigError.ts b/app/server/src/exceptions/ConfigError.ts
new file mode 100644
index 00000000..ab55b4e1
--- /dev/null
+++ b/app/server/src/exceptions/ConfigError.ts
@@ -0,0 +1,7 @@
+import { ServiceError, Status } from "./ServiceError.js";
+
+export class ConfigError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(ConfigError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/FuelLogError.ts b/app/server/src/exceptions/FuelLogError.ts
index ff2d2465..643879ed 100644
--- a/app/server/src/exceptions/FuelLogError.ts
+++ b/app/server/src/exceptions/FuelLogError.ts
@@ -1,13 +1,7 @@
-export class FuelLogError extends Error {
- constructor(message: string) {
- super(message);
- this.name = "FuelLogError";
- }
-}
+import { ServiceError, Status } from "./ServiceError.js";
-export class FuelLogNotFoundError extends Error {
- constructor() {
- super("Fuel log not found.");
- this.name = "FuelLogNotFoundError";
- }
-}
\ No newline at end of file
+export class FuelLogError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(FuelLogError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/InsuranceError.ts b/app/server/src/exceptions/InsuranceError.ts
index 8bd59683..46ff0e91 100644
--- a/app/server/src/exceptions/InsuranceError.ts
+++ b/app/server/src/exceptions/InsuranceError.ts
@@ -1,20 +1,7 @@
-export class InsuranceNotFoundError extends Error {
- constructor(message = "Insurance details not found.") {
- super(message);
- this.name = "InsuranceNotFoundError";
- }
-}
+import { ServiceError, Status } from "./ServiceError.js";
-export class InsuranceExistsError extends Error {
- constructor(message = "Insurance details already exist for this vehicle.") {
- super(message);
- this.name = "InsuranceExistsError";
+export class InsuranceError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(InsuranceError.name, message, status);
}
}
-
-export class InsuranceServiceError extends Error {
- constructor(message = "Insurance service error.") {
- super(message);
- this.name = "InsuranceServiceError";
- }
-}
\ No newline at end of file
diff --git a/app/server/src/exceptions/MaintenanceLogError.ts b/app/server/src/exceptions/MaintenanceLogError.ts
new file mode 100644
index 00000000..f7801442
--- /dev/null
+++ b/app/server/src/exceptions/MaintenanceLogError.ts
@@ -0,0 +1,7 @@
+import { ServiceError, Status } from "./ServiceError.js";
+
+export class MaintenanceLogError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(MaintenanceLogError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/MaintenanceLogErrors.ts b/app/server/src/exceptions/MaintenanceLogErrors.ts
deleted file mode 100644
index f578db23..00000000
--- a/app/server/src/exceptions/MaintenanceLogErrors.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export class MaintenanceLogNotFoundError extends Error {
- constructor(message = "Maintenance log not found.") {
- super(message);
- this.name = "MaintenanceLogNotFoundError";
- }
-}
-
-export class MaintenanceLogServiceError extends Error {
- constructor(message = "Maintenance log service error.") {
- super(message);
- this.name = "MaintenanceLogServiceError";
- }
-}
diff --git a/app/server/src/exceptions/PinErrors.ts b/app/server/src/exceptions/PinErrors.ts
deleted file mode 100644
index b8d37e2c..00000000
--- a/app/server/src/exceptions/PinErrors.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export class PinError extends Error {
- constructor(message: string) {
- super(message);
- this.name = "PinError";
- }
-}
diff --git a/app/server/src/exceptions/PollutionCertificateError.ts b/app/server/src/exceptions/PollutionCertificateError.ts
new file mode 100644
index 00000000..3646ec5d
--- /dev/null
+++ b/app/server/src/exceptions/PollutionCertificateError.ts
@@ -0,0 +1,7 @@
+import { ServiceError, Status } from "./ServiceError.js";
+
+export class PollutionCertificateError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(PollutionCertificateError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/PollutionCertificateErrors.ts b/app/server/src/exceptions/PollutionCertificateErrors.ts
deleted file mode 100644
index 246ba2eb..00000000
--- a/app/server/src/exceptions/PollutionCertificateErrors.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export class PollutionCertificateNotFoundError extends Error {
- constructor(message = "Pollution certificate not found.") {
- super(message);
- this.name = "PollutionCertificateNotFoundError";
- }
-}
-
-export class PollutionCertificateExistsError extends Error {
- constructor(message = "Pollution certificate already exists for this vehicle or certificate number is not unique.") {
- super(message);
- this.name = "PollutionCertificateExistsError";
- }
-}
-
-export class PollutionCertificateServiceError extends Error {
- constructor(message = "Pollution certificate service error.") {
- super(message);
- this.name = "PollutionCertificateServiceError";
- }
-}
diff --git a/app/server/src/exceptions/ServiceError.ts b/app/server/src/exceptions/ServiceError.ts
new file mode 100644
index 00000000..bc6d68fa
--- /dev/null
+++ b/app/server/src/exceptions/ServiceError.ts
@@ -0,0 +1,25 @@
+import { UniqueConstraintError, ValidationError } from "sequelize";
+
+export enum Status {
+ BAD_REQUEST = 400,
+ UNAUTHORIZED = 403,
+ NOT_FOUND = 404,
+ CONFLICT = 409,
+ INTERNAL_SERVER_ERROR = 500,
+}
+
+export class ServiceError extends Error {
+ status: Status;
+ constructor(name: string, message: string, status: Status) {
+ super(message);
+ this.name = name;
+ this.status = status;
+ }
+}
+
+export const statusFromError = (error: any) => {
+ if (error instanceof ServiceError) return error.status;
+ if (error instanceof ValidationError) return Status.BAD_REQUEST;
+ if (error instanceof UniqueConstraintError) return Status.CONFLICT;
+ return Status.INTERNAL_SERVER_ERROR;
+};
diff --git a/app/server/src/exceptions/VehicleError.ts b/app/server/src/exceptions/VehicleError.ts
new file mode 100644
index 00000000..4936de80
--- /dev/null
+++ b/app/server/src/exceptions/VehicleError.ts
@@ -0,0 +1,7 @@
+import { ServiceError, Status } from "./ServiceError.js";
+
+export class VehicleError extends ServiceError {
+ constructor(message: string, status = Status.INTERNAL_SERVER_ERROR) {
+ super(VehicleError.name, message, status);
+ }
+}
diff --git a/app/server/src/exceptions/VehicleErrors.ts b/app/server/src/exceptions/VehicleErrors.ts
deleted file mode 100644
index ffa63548..00000000
--- a/app/server/src/exceptions/VehicleErrors.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export class VehicleNotFoundError extends Error {
- constructor(message = "Vehicle not found.") {
- super(message);
- this.name = "VehicleNotFoundError";
- }
-}
-
-export class VehicleExistsError extends Error {
- constructor(message = "Vehicle with this license plate or VIN already exists.") {
- super(message);
- this.name = "VehicleExistsError";
- }
-}
-
-export class VehicleServiceError extends Error {
- constructor(message = "Vehicle service error.") {
- super(message);
- this.name = "VehicleServiceError";
- }
-}
\ No newline at end of file
diff --git a/app/server/src/middleware/error-handler.ts b/app/server/src/middleware/error-handler.ts
new file mode 100644
index 00000000..2f2e72d3
--- /dev/null
+++ b/app/server/src/middleware/error-handler.ts
@@ -0,0 +1,36 @@
+import { Request, Response } from "express";
+import { ValidationError } from "sequelize";
+
+export const errorHandler = (
+ err: any,
+ req: Request,
+ res: Response,
+ next: any,
+) => {
+ res.setHeader("Content-Type", "application/json");
+ let body = {};
+ switch (err.name) {
+ case "SequelizeValidationError":
+ body = {
+ type: "ValidationError",
+ errors: err.errors.map((e: any) => {
+ return {
+ message: e.message,
+ path: e.path
+ }
+ })
+ };
+ break;
+ default:
+ body = {
+ type: err.name,
+ errors: [
+ { message: err.message }
+ ]
+ }
+ }
+
+ console.error(err.name)
+ res.status(500);
+ res.send(JSON.stringify(body));
+};
diff --git a/app/server/src/models/Config.ts b/app/server/src/models/Config.ts
index 800870e7..8a002853 100644
--- a/app/server/src/models/Config.ts
+++ b/app/server/src/models/Config.ts
@@ -7,11 +7,12 @@ interface ConfigAttributes {
description?: string;
}
-interface ConfigCreationAttributes extends Optional { }
+interface ConfigCreationAttributes extends Optional {}
class Config
extends Model
- implements ConfigAttributes {
+ implements ConfigAttributes
+{
declare public key: string;
declare public value: string;
declare public description: string;
@@ -27,13 +28,11 @@ Config.init(
},
value: {
type: DataTypes.STRING,
- allowNull: true,
- defaultValue: "",
+ allowNull: false,
},
description: {
type: DataTypes.STRING,
allowNull: true,
- defaultValue: "",
},
},
{
diff --git a/app/server/src/models/FuelLog.ts b/app/server/src/models/FuelLog.ts
index d0a87511..592e29fa 100644
--- a/app/server/src/models/FuelLog.ts
+++ b/app/server/src/models/FuelLog.ts
@@ -12,11 +12,12 @@ interface FuelLogAttributes {
notes?: string;
}
-interface FuelLogCreationAttributes extends Optional { }
+interface FuelLogCreationAttributes extends Optional {}
class FuelLog
extends Model
- implements FuelLogAttributes {
+ implements FuelLogAttributes
+{
declare public id: string;
declare public vehicleId: string;
declare public date: string;
@@ -45,36 +46,45 @@ FuelLog.init(
date: {
type: DataTypes.DATEONLY,
allowNull: false,
- validate: {
- isDate: true,
- notEmpty: true,
- },
},
odometer: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
- isInt: true,
- min: 0,
+ min: {
+ args: [0],
+ msg: "Odometer reading must be greater than 0.",
+ },
},
},
fuelAmount: {
type: DataTypes.FLOAT,
allowNull: false,
+ validate: {
+ min: {
+ args: [0],
+ msg: "Fuel Amount must be greater than 0.",
+ },
+ },
},
cost: {
type: DataTypes.FLOAT,
allowNull: false,
validate: {
- isFloat: true,
- min: 0,
+ min: {
+ args: [0],
+ msg: "Fuel Amount must be greater than 0.",
+ },
},
},
notes: {
type: DataTypes.STRING,
allowNull: true,
validate: {
- len: [0, 500],
+ len: {
+ args: [0, 500],
+ msg: "Notes must be lesser than 500 length.",
+ },
},
},
},
diff --git a/app/server/src/models/Insurance.ts b/app/server/src/models/Insurance.ts
index 60709ad2..a414e3e8 100644
--- a/app/server/src/models/Insurance.ts
+++ b/app/server/src/models/Insurance.ts
@@ -1,6 +1,8 @@
import { DataTypes, Model, Optional } from "sequelize";
import { db } from "../db/index.js";
import Vehicle from "./Vehicle.js";
+import { InsuranceError } from "../exceptions/InsuranceError.js";
+import { Status } from "../exceptions/ServiceError.js";
interface InsuranceAttributes {
id: string;
@@ -10,14 +12,16 @@ interface InsuranceAttributes {
startDate: string;
endDate: string;
cost: number;
+ notes?: string;
}
interface InsuranceCreationAttributes
- extends Optional { }
+ extends Optional {}
class Insurance
extends Model
- implements InsuranceAttributes {
+ implements InsuranceAttributes
+{
declare public id: string;
declare public vehicleId: string;
declare public provider: string;
@@ -25,6 +29,7 @@ class Insurance
declare public startDate: string;
declare public endDate: string;
declare public cost: number;
+ declare public notes?: string;
}
Insurance.init(
@@ -47,36 +52,65 @@ Insurance.init(
type: DataTypes.STRING,
allowNull: false,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Provider must be between length 3 to 50.",
+ },
},
},
policyNumber: {
type: DataTypes.STRING,
allowNull: false,
+ unique: true,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Policy Number must be between length 3 to 50.",
+ },
+ is: {
+ args: "^[0-9A-Za-z\s\-]*$",
+ msg: "Only number and characters with space and hyphen are allowed in policy number.",
+ },
},
},
startDate: {
type: DataTypes.DATEONLY,
allowNull: false,
validate: {
- isDate: true,
+ isDate: {
+ args: true,
+ msg: "Start date format is not correct.",
+ },
},
},
endDate: {
type: DataTypes.DATEONLY,
allowNull: false,
validate: {
- isDate: true,
+ isDate: {
+ args: true,
+ msg: "End date format is not correct.",
+ },
},
},
cost: {
type: DataTypes.FLOAT,
allowNull: false,
validate: {
- isFloat: true,
- min: 0,
+ min: {
+ args: [0],
+ msg: "Fuel Amount must be greater than 0.",
+ },
+ },
+ },
+ notes: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ validate: {
+ len: {
+ args: [0, 500],
+ msg: "Notes must be lesser than 500 length.",
+ },
},
},
},
@@ -85,6 +119,33 @@ Insurance.init(
timestamps: true,
underscored: true,
sequelize: db,
+ validate: {
+ async validateDates() {
+ const previousInsEndDate = await Insurance.max("endDate", {
+ where: {
+ vehicleId: this.vehicleId as string,
+ },
+ });
+
+ const sDate = new Date(this.startDate as string);
+ const eDate = new Date(this.endDate as string);
+ const maxEndDate = new Date(previousInsEndDate as string);
+
+ if (sDate >= eDate) {
+ throw new InsuranceError(
+ "Start date must always be before end date.",
+ Status.BAD_REQUEST,
+ );
+ }
+
+ if (sDate < maxEndDate) {
+ throw new InsuranceError(
+ "Start date must always be after previous insurance end date.",
+ Status.BAD_REQUEST,
+ );
+ }
+ },
+ },
},
);
diff --git a/app/server/src/models/MaintenanceLog.ts b/app/server/src/models/MaintenanceLog.ts
index 28464e5b..d314861e 100644
--- a/app/server/src/models/MaintenanceLog.ts
+++ b/app/server/src/models/MaintenanceLog.ts
@@ -7,22 +7,23 @@ interface MaintenanceLogAttributes {
vehicleId: string;
date: string;
odometer: number;
- service: string;
+ serviceCenter: string;
cost: number;
notes?: string;
}
interface MaintenanceLogCreationAttributes
- extends Optional { }
+ extends Optional {}
class MaintenanceLog
extends Model
- implements MaintenanceLogAttributes {
+ implements MaintenanceLogAttributes
+{
declare public id: string;
declare public vehicleId: string;
declare public date: string;
declare public odometer: number;
- declare public service: string;
+ declare public serviceCenter: string;
declare public cost: number;
declare public notes: string;
}
@@ -47,37 +48,50 @@ MaintenanceLog.init(
type: DataTypes.DATEONLY,
allowNull: false,
validate: {
- isDate: true,
+ isDate: {
+ args: true,
+ msg: "Date format is not correct",
+ },
},
},
odometer: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
- isInt: true,
- min: 0,
+ min: {
+ args: [0],
+ msg: "Odometer reading must be greater than 0.",
+ },
},
},
- service: {
+ serviceCenter: {
type: DataTypes.STRING,
allowNull: false,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Service Center must be between length 3 to 50.",
+ },
},
},
cost: {
type: DataTypes.FLOAT,
allowNull: false,
validate: {
- isFloat: true,
- min: 0,
+ min: {
+ args: [0],
+ msg: "Cost must be greater than 0.",
+ },
},
},
notes: {
type: DataTypes.STRING,
allowNull: true,
validate: {
- notEmpty: true,
+ len: {
+ args: [0, 500],
+ msg: "Notes must be lesser than 500 length.",
+ },
},
},
},
diff --git a/app/server/src/models/PUCC.ts b/app/server/src/models/PUCC.ts
index 068c5120..9a684dff 100644
--- a/app/server/src/models/PUCC.ts
+++ b/app/server/src/models/PUCC.ts
@@ -1,6 +1,8 @@
import { DataTypes, Model, Optional } from "sequelize";
import { db } from "../db/index.js";
import Vehicle from "./Vehicle.js";
+import { PollutionCertificateError } from "../exceptions/PollutionCertificateError.js";
+import { Status } from "../exceptions/ServiceError.js";
interface PollutionCertificateAttributes {
id: string;
@@ -13,14 +15,15 @@ interface PollutionCertificateAttributes {
}
interface PollutionCertificateCreationAttributes
- extends Optional { }
+ extends Optional {}
class PollutionCertificate
extends Model<
PollutionCertificateAttributes,
PollutionCertificateCreationAttributes
>
- implements PollutionCertificateAttributes {
+ implements PollutionCertificateAttributes
+{
declare public id: string;
declare public vehicleId: string;
declare public certificateNumber: string;
@@ -41,7 +44,7 @@ PollutionCertificate.init(
vehicleId: {
type: DataTypes.UUIDV4,
references: {
- model: Vehicle, // Use string reference
+ model: Vehicle,
key: "id",
},
allowNull: false,
@@ -51,40 +54,92 @@ PollutionCertificate.init(
allowNull: false,
unique: true,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Certificate Number must be between length 3 to 50.",
+ },
+ is: {
+ args: "^[0-9A-Za-z\s\-]*$",
+ msg: "Only number and characters with space and hyphen are allowed in certificate number.",
+ },
},
},
issueDate: {
type: DataTypes.DATEONLY,
allowNull: false,
validate: {
- isDate: true,
+ isDate: {
+ args: true,
+ msg: "Issue date format is not correct.",
+ },
},
},
expiryDate: {
type: DataTypes.DATEONLY,
allowNull: false,
validate: {
- isDate: true,
+ isDate: {
+ args: true,
+ msg: "Expiry date format is not correct.",
+ },
},
},
testingCenter: {
type: DataTypes.STRING,
allowNull: false,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Testing Center must be between length 3 to 50.",
+ },
},
},
notes: {
type: DataTypes.STRING,
allowNull: true,
+ validate: {
+ len: {
+ args: [0, 500],
+ msg: "Notes must be lesser than 500 length.",
+ },
+ },
},
},
{
tableName: "pollution_certificates",
timestamps: true,
underscored: true,
- sequelize: db
+ sequelize: db,
+ validate: {
+ async validateDates() {
+ const previousInsEndDate = await PollutionCertificate.max(
+ "expiryDate",
+ {
+ where: {
+ vehicleId: this.vehicleId as string,
+ },
+ },
+ );
+
+ const sDate = new Date(this.issueDate as string);
+ const eDate = new Date(this.expiryDate as string);
+ const maxEndDate = new Date(previousInsEndDate as string);
+
+ if (sDate >= eDate) {
+ throw new PollutionCertificateError(
+ "Issue date must always be before Expiry date.",
+ Status.BAD_REQUEST,
+ );
+ }
+
+ if (sDate < maxEndDate) {
+ throw new PollutionCertificateError(
+ "Issue date must always be after previous PUCC Expiry date.",
+ Status.BAD_REQUEST,
+ );
+ }
+ },
+ },
},
);
diff --git a/app/server/src/models/Vehicle.ts b/app/server/src/models/Vehicle.ts
index 424235f2..aa2486ca 100644
--- a/app/server/src/models/Vehicle.ts
+++ b/app/server/src/models/Vehicle.ts
@@ -1,5 +1,8 @@
import { DataTypes, Model, Optional } from "sequelize";
import { db } from "../db/index.js";
+import FuelLog from "./FuelLog.js";
+import { VehicleError } from "../exceptions/VehicleError.js";
+import { Status } from "../exceptions/ServiceError.js";
interface VehicleAttributes {
id: string;
@@ -12,11 +15,12 @@ interface VehicleAttributes {
odometer?: number;
}
-interface VehicleCreationAttributes extends Optional { }
+interface VehicleCreationAttributes extends Optional {}
class Vehicle
extends Model
- implements VehicleAttributes {
+ implements VehicleAttributes
+{
declare public id: string;
declare public make: string;
declare public model: string;
@@ -39,14 +43,20 @@ Vehicle.init(
type: DataTypes.STRING,
allowNull: false,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Manufacturer must be between length 3 to 50.",
+ },
},
},
model: {
type: DataTypes.STRING,
allowNull: false,
validate: {
- notEmpty: true,
+ len: {
+ args: [3, 50],
+ msg: "Model must be between length 3 to 50.",
+ },
},
},
year: {
@@ -54,8 +64,14 @@ Vehicle.init(
allowNull: false,
validate: {
isInt: true,
- min: 1886, // The year the first car was invented
- max: new Date().getFullYear() + 1,
+ min: {
+ args: [1886],
+ msg: "Year should be grater than 1886(when first car was invented).",
+ },
+ max: {
+ args: [new Date().getFullYear()],
+ msg: "Year should be less than current year.",
+ },
},
},
licensePlate: {
@@ -63,25 +79,70 @@ Vehicle.init(
allowNull: false,
unique: true,
validate: {
- notEmpty: true,
- len: [1, 15], // Assuming a maximum length for license plates
+ is: {
+ args: "^[A-Z0-9\- ]{2,10}$",
+ msg: "Licence Plate format is incorrect.",
+ },
},
},
vin: {
type: DataTypes.STRING,
- unique: true,
allowNull: true,
+ validate: {
+ notEmpty: {
+ msg: "VIN number can't be an empty string.",
+ },
+ is: {
+ args: "^[A-HJ-NPR-Z0-9]{17}$",
+ msg: "VIN number format is incorrect.",
+ },
+ async isUnique(value: string) {
+ if (!value) return;
+ const existingVehicles = await db
+ .getQueryInterface()
+ .select(null, "vehicles", {
+ where: {
+ vin: value,
+ },
+ });
+ if (existingVehicles.length > 0) {
+ throw new VehicleError(
+ "VIN NUmber already exists.",
+ Status.CONFLICT,
+ );
+ }
+ },
+ },
},
color: {
type: DataTypes.STRING,
allowNull: true,
+ validate: {
+ is: {
+ args: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
+ msg: "Only hex color codes are allowed.",
+ },
+ },
},
odometer: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {
isInt: true,
- min: 0, // Odometer cannot be negative
+ async validateOdometer(value: number) {
+ const minOdometer: number = await FuelLog.min("odometer", {
+ where: {
+ vehicleId: this.id as string,
+ },
+ });
+
+ if (minOdometer && value > minOdometer) {
+ throw new VehicleError(
+ "Initial Odometer Reading must be lesser than first fuel log odometer.",
+ Status.BAD_REQUEST,
+ );
+ }
+ },
},
},
},
diff --git a/app/server/src/models/index.ts b/app/server/src/models/index.ts
index a6b53708..6e7a1e21 100644
--- a/app/server/src/models/index.ts
+++ b/app/server/src/models/index.ts
@@ -7,10 +7,10 @@ import Auth from "./Auth.js";
import Config from "./Config.js";
// Associations
-Vehicle.hasOne(Insurance, { foreignKey: "vehicleId", as: "insurance" });
+Vehicle.hasMany(Insurance, { foreignKey: "vehicleId", as: "insurance" });
Insurance.belongsTo(Vehicle, { foreignKey: "vehicleId", as: "vehicle" });
-Vehicle.hasOne(PollutionCertificate, {
+Vehicle.hasMany(PollutionCertificate, {
foreignKey: "vehicleId",
as: "pollutionCertificate",
});
diff --git a/app/server/src/services/configService.ts b/app/server/src/services/configService.ts
index b86774d1..3f7df42d 100644
--- a/app/server/src/services/configService.ts
+++ b/app/server/src/services/configService.ts
@@ -1,3 +1,5 @@
+import { ConfigError } from "../exceptions/ConfigError.js";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
import Config from "../models/Config.js";
export const getAppConfig = async () => {
@@ -12,10 +14,22 @@ export const getAppConfig = async () => {
export const getAppConfigByKey = async (key: string) => {
const config = await Config.findOne({ where: { key } });
+ if (!config) {
+ throw new ConfigError(
+ `No config found for key : ${key}`,
+ Status.NOT_FOUND,
+ );
+ }
return config;
};
export const updateAppConfig = async (key: string, value: string) => {
+ if (!key || value === undefined) {
+ throw new ConfigError(
+ "Key and value are required for each configuration",
+ Status.BAD_REQUEST,
+ );
+ }
const config = await getAppConfigByKey(key);
if (!config) {
return await Config.create({ key, value });
diff --git a/app/server/src/services/fuelLogService.ts b/app/server/src/services/fuelLogService.ts
index 77a08b93..8f1c7e08 100644
--- a/app/server/src/services/fuelLogService.ts
+++ b/app/server/src/services/fuelLogService.ts
@@ -1,105 +1,87 @@
-import { FuelLogError, FuelLogNotFoundError } from "../exceptions/FuelLogError.js";
-import { VehicleNotFoundError } from "../exceptions/VehicleErrors.js";
-import {Vehicle, FuelLog} from "../models/index.js";
+import { FuelLogError } from "../exceptions/FuelLogError.js";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
+import { VehicleError } from "../exceptions/VehicleError.js";
+import { Vehicle, FuelLog } from "../models/index.js";
export const addFuelLog = async (vehicleId: string, fuelLogData: any) => {
- try {
- const vehicle = await Vehicle.findByPk(vehicleId);
- if (!vehicle) {
- throw new VehicleNotFoundError("Vehicle not found.");
- }
-
- const fuelLog = await FuelLog.create({
- ...fuelLogData,
- vehicleId: vehicleId,
- });
- return { id: fuelLog.id, message: "Fuel log added successfully." };
- } catch (error: any) {
- console.error("Error adding fuel log: ", error);
- if (error instanceof VehicleNotFoundError) {
- throw error;
- }
- throw new FuelLogError("Error adding fuel log.");
+ const vehicle = await Vehicle.findByPk(vehicleId);
+ if (!vehicle) {
+ throw new VehicleError(
+ `No vehicle found for id : ${vehicleId}`,
+ Status.NOT_FOUND,
+ );
}
+ const fuelLog = await FuelLog.create({
+ ...fuelLogData,
+ vehicleId: vehicleId,
+ });
+ return { id: fuelLog.id, message: "Fuel log added successfully." };
};
export const getFuelLogs = async (vehicleId: string) => {
- try {
- const fuelLogs = await FuelLog.findAll({
- where: { vehicleId: vehicleId },
- order: [
- ["date", "ASC"],
- ["odometer", "ASC"],
- ],
- });
- // Calculate mileage
- return fuelLogs.map((log, index, arr) => {
- if (index > 0) {
- const prevLog = arr[index - 1];
- if (!prevLog) {
- return { ...log.toJSON(), mileage: null };
- }
- const distance = log.odometer - prevLog.odometer;
- const mileage = distance / log.fuelAmount; // km/L or miles/gallon
- return { ...log.toJSON(), mileage: parseFloat(mileage.toFixed(2)) };
+ const fuelLogs = await FuelLog.findAll({
+ where: { vehicleId: vehicleId },
+ order: [
+ ["date", "ASC"],
+ ["odometer", "ASC"],
+ ],
+ });
+
+ // Calculate mileage
+ return fuelLogs.map((log, index, arr) => {
+ if (index > 0) {
+ const prevLog = arr[index - 1];
+ if (!prevLog) {
+ return { ...log.toJSON(), mileage: null };
}
- return { ...log.toJSON(), mileage: null };
- });
- } catch (error: any) {
- console.error("Error fetching fuel logs: ", error);
- throw new FuelLogError("Error fetching fuel logs.");
- }
+ const distance = log.odometer - prevLog.odometer;
+ const mileage = distance / log.fuelAmount;
+ return { ...log.toJSON(), mileage: parseFloat(mileage.toFixed(2)) };
+ }
+ return { ...log.toJSON(), mileage: null };
+ });
+
};
export const getFuelLogById = async (id: string) => {
- try {
- const fuelLog = await FuelLog.findByPk(id);
- if (!fuelLog) {
- throw new FuelLogNotFoundError();
- }
- return fuelLog;
- } catch (error: any) {
- console.error("Error fetching fuel log: ", error);
- if (error instanceof FuelLogNotFoundError) {
- throw error;
- }
- throw new FuelLogError("Error fetching fuel log.");
+
+ const fuelLog = await FuelLog.findByPk(id);
+ if (!fuelLog) {
+ throw new FuelLogError(
+ `No Fuel Logs found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return fuelLog;
+
};
export const updateFuelLog = async (id: string, fuelLogData: any) => {
- try {
- const fuelLog = await FuelLog.findByPk(id);
- if (!fuelLog) {
- throw new FuelLogNotFoundError();
- }
- await fuelLog.update(fuelLogData);
- return { message: "Fuel log updated successfully." };
- } catch (error: any) {
- console.error("Error updating fuel log: ", error);
- if (error instanceof FuelLogNotFoundError) {
- throw error;
- }
- throw new Error("Error updating fuel log.");
+ const fuelLog = await FuelLog.findByPk(id);
+ if (!fuelLog) {
+ throw new FuelLogError(
+ `No Fuel Logs found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+
+ await fuelLog.update(fuelLogData);
+ return { message: "Fuel log updated successfully." };
+
};
export const deleteFuelLog = async (id: string) => {
- try {
- const result = await FuelLog.destroy({
- where: { id: id },
- });
- if (result === 0) {
- throw new FuelLogNotFoundError();
- }
- return { message: "Fuel log deleted successfully." };
- } catch (error: any) {
- console.error("Error deleting fuel log: ", error);
- if (error instanceof FuelLogNotFoundError) {
- throw error;
- }
- throw new Error("Error deleting fuel log.");
+
+ const result = await FuelLog.destroy({
+ where: { id: id },
+ });
+ if (result === 0) {
+ throw new FuelLogError(
+ `No Fuel Logs found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return { message: "Fuel log deleted successfully." };
};
diff --git a/app/server/src/services/insuranceService.ts b/app/server/src/services/insuranceService.ts
index 3b5980ad..8d79747f 100644
--- a/app/server/src/services/insuranceService.ts
+++ b/app/server/src/services/insuranceService.ts
@@ -1,52 +1,41 @@
-import {
- InsuranceNotFoundError,
- InsuranceExistsError,
- InsuranceServiceError,
-} from "../exceptions/InsuranceError.js";
+import { InsuranceError } from "../exceptions/InsuranceError.js";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
import { Insurance, Vehicle } from "../models/index.js";
import { UniqueConstraintError } from "sequelize";
export const addInsurance = async (vehicleId: string, insuranceData: any) => {
- try {
- const vehicle = await Vehicle.findByPk(vehicleId);
- if (!vehicle) {
- throw new InsuranceNotFoundError("Vehicle not found.");
- }
- const insurance = await Insurance.create({
- ...insuranceData,
- vehicleId: vehicleId,
- });
- return {
- id: insurance.id,
- message: "Insurance details added successfully.",
- };
- } catch (error: unknown) {
- if (error instanceof UniqueConstraintError) {
- throw new InsuranceExistsError();
- }
- if (error instanceof InsuranceNotFoundError) {
- throw error;
- }
- throw new InsuranceServiceError("Error adding insurance details.");
+ const vehicle = await Vehicle.findByPk(vehicleId);
+ if (!vehicle) {
+ throw new InsuranceError(
+ `No Vehicle found for id : ${vehicleId}`,
+ Status.NOT_FOUND,
+ );
}
+ const insurance = await Insurance.create({
+ ...insuranceData,
+ vehicleId: vehicleId,
+ });
+ return {
+ id: insurance.id,
+ message: "Insurance details added successfully.",
+ };
+
};
export const getInsurances = async (vehicleId: string) => {
- try {
- const insurance = await Insurance.findAll({
- where: { vehicleId: vehicleId },
- });
- if (!insurance) {
- throw new InsuranceNotFoundError();
- }
- return insurance;
- } catch (error: unknown) {
- if (error instanceof InsuranceNotFoundError) {
- throw error;
- }
- throw new InsuranceServiceError("Error fetching insurance details.");
+
+ const insurance = await Insurance.findAll({
+ where: { vehicleId: vehicleId },
+ });
+ if (!insurance) {
+ throw new InsuranceError(
+ `No Insurances found for vehicle id : ${vehicleId}`,
+ Status.NOT_FOUND,
+ );
}
+ return insurance;
+
};
export const updateInsurance = async (
@@ -54,37 +43,31 @@ export const updateInsurance = async (
id: string,
insuranceData: any,
) => {
- try {
- const insurance = await Insurance.findOne({
- where: { vehicleId: vehicleId, id },
- });
- if (!insurance) {
- throw new InsuranceNotFoundError();
- }
- await insurance.update(insuranceData);
- return { message: "Insurance details updated successfully." };
- } catch (error: unknown) {
- if (error instanceof InsuranceNotFoundError) {
- throw error;
- }
- throw new InsuranceServiceError("Error updating insurance details.");
+ const insurance = await Insurance.findOne({
+ where: { vehicleId: vehicleId, id },
+ });
+ if (!insurance) {
+ throw new InsuranceError(
+ `No Insurances found for id: ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ await insurance.update(insuranceData);
+ return { message: "Insurance details updated successfully." };
+
};
-export const deleteInsurance = async (vehicleId: string) => {
- try {
- const result = await Insurance.destroy({
- where: { vehicleId: vehicleId },
- });
- if (result === 0) {
- throw new InsuranceNotFoundError();
- }
- return { message: "Insurance details deleted successfully." };
- } catch (error: unknown) {
- if (error instanceof InsuranceNotFoundError) {
- throw error;
- }
- throw new InsuranceServiceError("Error deleting insurance details.");
+export const deleteInsurance = async (id: string) => {
+
+ const result = await Insurance.destroy({
+ where: { id },
+ });
+ if (result === 0) {
+ throw new InsuranceError(
+ `No Insurances found for id: ${id}`,
+ Status.NOT_FOUND,
+ );
}
-};
+ return { message: "Insurance details deleted successfully." };
+}
diff --git a/app/server/src/services/maintenanceLogService.ts b/app/server/src/services/maintenanceLogService.ts
index 8c5a00f1..f661b2ab 100644
--- a/app/server/src/services/maintenanceLogService.ts
+++ b/app/server/src/services/maintenanceLogService.ts
@@ -1,103 +1,86 @@
import { MaintenanceLog, Vehicle } from "../models/index.js";
-import {
- MaintenanceLogNotFoundError,
- MaintenanceLogServiceError
-} from "../exceptions/MaintenanceLogErrors.js";
+import { MaintenanceLogError } from "../exceptions/MaintenanceLogError.js";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
export const addMaintenanceLog = async (
vehicleId: string,
- maintenanceLogData: any
+ maintenanceLogData: any,
) => {
- try {
- const vehicle = await Vehicle.findByPk(vehicleId);
- if (!vehicle) {
- throw new MaintenanceLogNotFoundError("Vehicle not found.");
- }
-
- const maintenanceLog = await MaintenanceLog.create({
- ...maintenanceLogData,
- vehicleId: vehicleId,
- });
- return {
- id: maintenanceLog.id,
- message: "Maintenance log added successfully.",
- };
- } catch (error: unknown) {
- console.error("Error adding maintenance log: ", error);
- if (error instanceof MaintenanceLogNotFoundError) {
- throw error;
- }
- throw new MaintenanceLogServiceError("Error adding maintenance log.");
+
+ const vehicle = await Vehicle.findByPk(vehicleId);
+ if (!vehicle) {
+ throw new MaintenanceLogError(
+ `No vehicle found for id ${vehicleId}`,
+ Status.NOT_FOUND,
+ );
}
+
+ const maintenanceLog = await MaintenanceLog.create({
+ ...maintenanceLogData,
+ vehicleId: vehicleId,
+ });
+ return {
+ id: maintenanceLog.id,
+ message: "Maintenance log added successfully.",
+ };
+
};
export const getMaintenanceLogs = async (vehicleId: string) => {
- try {
- const maintenanceLogs = await MaintenanceLog.findAll({
- where: { vehicleId: vehicleId },
- order: [
- ["date", "ASC"],
- ["odometer", "ASC"],
- ],
- });
- return maintenanceLogs;
- } catch (error: unknown) {
- console.error("Error fetching maintenance logs: ", error);
- throw new MaintenanceLogServiceError("Error fetching maintenance logs.");
- }
+
+ const maintenanceLogs = await MaintenanceLog.findAll({
+ where: { vehicleId: vehicleId },
+ order: [
+ ["date", "ASC"],
+ ["odometer", "ASC"],
+ ],
+ });
+ return maintenanceLogs;
+
};
export const getMaintenanceLogById = async (id: string) => {
- try {
- const maintenanceLog = await MaintenanceLog.findByPk(id);
- if (!maintenanceLog) {
- throw new MaintenanceLogNotFoundError();
- }
- return maintenanceLog;
- } catch (error: unknown) {
- console.error("Error fetching maintenance log: ", error);
- if (error instanceof MaintenanceLogNotFoundError) {
- throw error;
- }
- throw new MaintenanceLogServiceError("Error fetching maintenance log.");
+
+ const maintenanceLog = await MaintenanceLog.findByPk(id);
+ if (!maintenanceLog) {
+ throw new MaintenanceLogError(
+ `No Maintenence log found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return maintenanceLog;
+
};
export const updateMaintenanceLog = async (
id: string,
- maintenanceLogData: any
+ maintenanceLogData: any,
) => {
- try {
- const maintenanceLog = await MaintenanceLog.findByPk(id);
- if (!maintenanceLog) {
- throw new MaintenanceLogNotFoundError();
- }
-
- await maintenanceLog.update(maintenanceLogData);
- return { message: "Maintenance log updated successfully." };
- } catch (error: unknown) {
- console.error("Error updating maintenance log: ", error);
- if (error instanceof MaintenanceLogNotFoundError) {
- throw error;
- }
- throw new MaintenanceLogServiceError("Error updating maintenance log.");
+
+ const maintenanceLog = await MaintenanceLog.findByPk(id);
+ if (!maintenanceLog) {
+ throw new MaintenanceLogError(
+ `No Maintenence log found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+
+ await maintenanceLog.update(maintenanceLogData);
+ return { message: "Maintenance log updated successfully." };
+
};
export const deleteMaintenanceLog = async (id: string) => {
- try {
- const result = await MaintenanceLog.destroy({
- where: { id: id },
- });
- if (result === 0) {
- throw new MaintenanceLogNotFoundError();
- }
- return { message: "Maintenance log deleted successfully." };
- } catch (error: unknown) {
- console.error("Error deleting maintenance log: ", error);
- if (error instanceof MaintenanceLogNotFoundError) {
- throw error;
- }
- throw new MaintenanceLogServiceError("Error deleting maintenance log.");
+
+ const result = await MaintenanceLog.destroy({
+ where: { id: id },
+ });
+ if (result === 0) {
+ throw new MaintenanceLogError(
+ `No Maintenence log found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return { message: "Maintenance log deleted successfully." };
+
};
diff --git a/app/server/src/services/pinService.ts b/app/server/src/services/pinService.ts
index 1a3734d1..466b99ef 100644
--- a/app/server/src/services/pinService.ts
+++ b/app/server/src/services/pinService.ts
@@ -1,58 +1,48 @@
-
import bcrypt from "bcrypt";
-import {Auth} from "../models/index.js";
-import { PinError } from "../exceptions/PinErrors.js";
+import { Auth } from "../models/index.js";
+import { AuthError } from "../exceptions/AuthError.js";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
export const setPin = async (pin: string) => {
- try {
- const hash = await bcrypt.hash(pin, 10);
-
- const [auth, created] = await Auth.findOrCreate({
- where: { id: 1 },
- defaults: { hash: hash },
- });
-
- if (!created) {
- auth.hash = hash;
- await auth.save();
- return { status: 200, message: "PIN updated successfully." };
- } else {
- return { status: 201, message: "PIN set successfully." };
- }
- } catch (error: any) {
- console.error("Error hashing or setting PIN:", error);
- throw new PinError("Error hashing or setting PIN.");
+
+ const hash = await bcrypt.hash(pin, 10);
+
+ const [auth, created] = await Auth.findOrCreate({
+ where: { id: 1 },
+ defaults: { hash: hash },
+ });
+
+ if (!created) {
+ auth.hash = hash;
+ await auth.save();
+ return { message: "PIN updated successfully." };
+ } else {
+ return { message: "PIN set successfully." };
}
+
};
export const verifyPin = async (pin: string) => {
- try {
- const auth = await Auth.findByPk(1);
- if (!auth) {
- return {
- status: 404,
- message: "PIN not set. Please set a PIN first.",
- };
- }
-
- const match = await bcrypt.compare(pin, auth.get("hash"));
- if (match) {
- return { status: 200, message: "PIN verified successfully." };
- } else {
- return { status: 401, message: "Invalid PIN." };
- }
- } catch (error: any) {
- console.error("Error verifying PIN:", error);
- throw new PinError("Error while verifying PIN.");
+
+ const auth = await Auth.findByPk(1);
+ if (!auth) {
+ throw new AuthError("PIN is not set yet. Please set PIN first.");
}
+ const match = await bcrypt.compare(pin, auth.get("hash"));
+ if (match) {
+ return { message: "PIN verified successfully." };
+ } else {
+ throw new AuthError(
+ "Incorrect PIN provided. Please try again with correct PIN",
+ Status.UNAUTHORIZED,
+ );
+ }
+
};
export const getPinStatus = async () => {
- try {
- const auth = await Auth.findByPk(1);
- return { exists: !!auth };
- } catch (error: any) {
- console.error("Error checking PIN status:", error);
- throw new PinError("Error checking PIN status.");
- }
+
+ const auth = await Auth.findByPk(1);
+ return { exists: !!auth };
+
};
diff --git a/app/server/src/services/pollutionCertificateService.ts b/app/server/src/services/pollutionCertificateService.ts
index 75000f36..1c1ac1bf 100644
--- a/app/server/src/services/pollutionCertificateService.ts
+++ b/app/server/src/services/pollutionCertificateService.ts
@@ -1,59 +1,44 @@
import { Vehicle, PollutionCertificate } from "../models/index.js";
-import {
- PollutionCertificateNotFoundError,
- PollutionCertificateExistsError,
- PollutionCertificateServiceError,
-} from "../exceptions/PollutionCertificateErrors.js";
+import { PollutionCertificateError } from "../exceptions/PollutionCertificateError.js";
import { UniqueConstraintError } from "sequelize";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
export const addPollutionCertificate = async (
vehicleId: string,
pollutionCertificateData: any,
) => {
- try {
- const vehicle = await Vehicle.findByPk(vehicleId);
- if (!vehicle) {
- throw new PollutionCertificateNotFoundError("Vehicle not found.");
- }
- const pollutionCertificate = await PollutionCertificate.create({
- ...pollutionCertificateData,
- vehicleId: vehicleId,
- });
- return {
- id: pollutionCertificate.id,
- message: "Pollution certificate added successfully.",
- };
- } catch (error: unknown) {
- if (error instanceof UniqueConstraintError) {
- throw new PollutionCertificateExistsError();
- }
- if (error instanceof PollutionCertificateNotFoundError) {
- throw error;
- }
- throw new PollutionCertificateServiceError(
- "Error adding pollution certificate.",
+ const vehicle = await Vehicle.findByPk(vehicleId);
+ if (!vehicle) {
+ throw new PollutionCertificateError(
+ `No vehicle found for id : ${vehicleId}`,
+ Status.NOT_FOUND,
);
}
+ const pollutionCertificate = await PollutionCertificate.create({
+ ...pollutionCertificateData,
+ vehicleId: vehicleId,
+ });
+ return {
+ id: pollutionCertificate.id,
+ message: "Pollution certificate added successfully.",
+ };
+
};
export const getPollutionCertificates = async (vehicleId: string) => {
- try {
- const pollutionCertificate = await PollutionCertificate.findAll({
- where: { vehicleId: vehicleId },
- });
- if (!pollutionCertificate) {
- throw new PollutionCertificateNotFoundError();
- }
- return pollutionCertificate;
- } catch (error: unknown) {
- if (error instanceof PollutionCertificateNotFoundError) {
- throw error;
- }
- throw new PollutionCertificateServiceError(
- "Error fetching pollution certificate.",
+
+ const pollutionCertificates = await PollutionCertificate.findAll({
+ where: { vehicleId: vehicleId },
+ });
+ if (!pollutionCertificates) {
+ throw new PollutionCertificateError(
+ `No PUCC found for vehicle id : ${vehicleId}`,
+ Status.NOT_FOUND,
);
}
+ return pollutionCertificates;
+
};
export const updatePollutionCertificate = async (
@@ -61,42 +46,32 @@ export const updatePollutionCertificate = async (
id: string,
pollutionCertificateData: any,
) => {
- try {
- const pollutionCertificate = await PollutionCertificate.findOne({
- where: { vehicleId: vehicleId, id: id },
- });
- if (!pollutionCertificate) {
- throw new PollutionCertificateNotFoundError();
- }
- await pollutionCertificate.update(pollutionCertificateData);
- return { message: "Pollution certificate updated successfully." };
- } catch (error: unknown) {
- console.error(error);
- if (error instanceof PollutionCertificateNotFoundError) {
- throw error;
- }
- throw new PollutionCertificateServiceError(
- "Error updating pollution certificate.",
+ const pollutionCertificate = await PollutionCertificate.findOne({
+ where: { vehicleId: vehicleId, id: id },
+ });
+ if (!pollutionCertificate) {
+ throw new PollutionCertificateError(
+ `No PUCC found for id : ${id}`,
+ Status.NOT_FOUND,
);
}
+
+ await pollutionCertificate.update(pollutionCertificateData);
+ return { message: "Pollution certificate updated successfully." };
+
};
-export const deletePollutionCertificate = async (vehicleId: string) => {
- try {
- const result = await PollutionCertificate.destroy({
- where: { vehicleId: vehicleId },
- });
- if (result === 0) {
- throw new PollutionCertificateNotFoundError();
- }
- return { message: "Pollution certificate deleted successfully." };
- } catch (error: unknown) {
- if (error instanceof PollutionCertificateNotFoundError) {
- throw error;
- }
- throw new PollutionCertificateServiceError(
- "Error deleting pollution certificate.",
+export const deletePollutionCertificate = async (id: string) => {
+
+ const result = await PollutionCertificate.destroy({
+ where: { id },
+ });
+ if (result === 0) {
+ throw new PollutionCertificateError(
+ `No PUCC found for id : ${id}`,
+ Status.NOT_FOUND,
);
}
-};
+ return { message: "Pollution certificate deleted successfully." };
+}
diff --git a/app/server/src/services/vehicleService.ts b/app/server/src/services/vehicleService.ts
index 4436f9da..d7234bea 100644
--- a/app/server/src/services/vehicleService.ts
+++ b/app/server/src/services/vehicleService.ts
@@ -1,109 +1,95 @@
-import { VehicleExistsError, VehicleServiceError, VehicleNotFoundError } from "../exceptions/VehicleErrors.js";
-import { Vehicle } from "../models/index.js";
-import { UniqueConstraintError } from "sequelize";
+import { Status, statusFromError } from "../exceptions/ServiceError.js";
+import { VehicleError } from "../exceptions/VehicleError.js";
+import { Insurance, PollutionCertificate, Vehicle } from "../models/index.js";
export const addVehicle = async (vehicleData: any) => {
- try {
- const vehicle = await Vehicle.create(vehicleData);
- return { id: vehicle.id, message: "Vehicle added successfully." };
- } catch (error: unknown) {
- console.error("Error adding vehicle: ", error);
- if (error instanceof UniqueConstraintError) {
- throw new VehicleExistsError();
- }
- throw new VehicleServiceError("Error adding vehicle.");
- }
+ const vehicle = await Vehicle.create(vehicleData);
+ return { id: vehicle.id, message: "Vehicle added successfully." };
};
export const getAllVehicles = async () => {
- try {
- const vehicles = await Vehicle.findAll({
- include: [
- { association: 'insurance' },
- { association: 'pollutionCertificate' }
- ]
- });
-
- return vehicles.map(vehicle => {
- const insurance = (vehicle as any).insurance;
- const pollutionCertificate = (vehicle as any).pollutionCertificate;
-
- let insuranceStatus = "N/A";
- if (insurance) {
+
+ const vehicles = await Vehicle.findAll({
+ include: [
+ { association: "insurance" },
+ { association: "pollutionCertificate" },
+ ],
+ });
+
+ return vehicles.map((vehicle) => {
+ const insurances: Insurance[] = (vehicle as any).insurance;
+ const pollutionCertificates: PollutionCertificate[] = (vehicle as any)
+ .pollutionCertificate;
+
+ let insuranceStatus = "Not Available";
+ if (insurances && insurances.length > 0) {
+ insuranceStatus = "Expired";
+ insurances.forEach((insurance) => {
const endDate = new Date(insurance.endDate);
- insuranceStatus = endDate > new Date() ? "Active" : "Expired";
- }
-
- let puccStatus = "N/A";
- if (pollutionCertificate) {
- const expiryDate = new Date(pollutionCertificate.expiryDate);
- puccStatus = expiryDate > new Date() ? "Active" : "Expired";
- }
-
- return {
- ...vehicle.toJSON(),
- insuranceStatus,
- puccStatus
- };
- });
- } catch (error: unknown) {
- console.error("Error fetching vehicles: ", error);
- throw new VehicleServiceError("Error fetching vehicles.");
- }
+ const today = new Date();
+ if (endDate > today) {
+ insuranceStatus = "Active";
+ }
+ });
+ }
+
+ let puccStatus = "Not Available";
+ if (pollutionCertificates && pollutionCertificates.length > 0) {
+ puccStatus = "Expired";
+ pollutionCertificates.forEach((pucc) => {
+ const expiryDate = new Date(pucc.expiryDate);
+ const today = new Date();
+ if (expiryDate > today) {
+ puccStatus = "Active";
+ }
+ });
+ }
+
+ return {
+ ...vehicle.toJSON(),
+ insuranceStatus,
+ puccStatus,
+ };
+ });
+
};
export const getVehicleById = async (id: string) => {
- try {
- const vehicle = await Vehicle.findByPk(id);
- if (!vehicle) {
- throw new VehicleNotFoundError();
- }
- return vehicle;
- } catch (error: unknown) {
- console.error(`Error fetching vehicle(${id}):`, error);
- if (error instanceof VehicleNotFoundError) {
- throw error;
- }
- throw new VehicleServiceError("Error fetching vehicle.");
+
+ const vehicle = await Vehicle.findByPk(id);
+ if (!vehicle) {
+ throw new VehicleError(
+ `No vehicle found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return vehicle;
+
};
export const updateVehicle = async (id: string, vehicleData: any) => {
- try {
- const vehicle = await Vehicle.findByPk(id);
- if (!vehicle) {
- throw new VehicleNotFoundError();
- }
- await vehicle.update(vehicleData);
- return { message: "Vehicle updated successfully." };
- } catch (error: unknown) {
- console.error(`Error updating vehicle(${id}):`, error);
- if (error instanceof UniqueConstraintError) {
- throw new VehicleExistsError();
- }
- if (error instanceof VehicleNotFoundError) {
- throw error;
- }
- throw new VehicleServiceError("Error updating vehicle.");
+ const vehicle = await Vehicle.findByPk(id);
+ if (!vehicle) {
+ throw new VehicleError(`No vehicle found for id : ${id}`, Status.NOT_FOUND);
}
+
+ await vehicle.update(vehicleData);
+ return { message: "Vehicle updated successfully." };
};
export const deleteVehicle = async (id: string) => {
- try {
- const result = await Vehicle.destroy({
- where: { id: id },
- cascade: true
- });
- if (result === 0) {
- throw new VehicleNotFoundError();
- }
- return { message: "Vehicle deleted successfully." };
- } catch (error: unknown) {
- console.error(`Error deleting vehicle(${id}):`, error);
- if (error instanceof VehicleNotFoundError) {
- throw error;
- }
- throw new VehicleServiceError("Error deleting vehicle.");
+
+ const result = await Vehicle.destroy({
+ where: { id: id },
+ cascade: true,
+ });
+ if (result === 0) {
+ throw new VehicleError(
+ `No vehicle found for id : ${id}`,
+ Status.NOT_FOUND,
+ );
}
+ return { message: "Vehicle deleted successfully." };
+
};
diff --git a/app/server/tsconfig.json b/app/server/tsconfig.json
index 28b84bb8..80527aab 100644
--- a/app/server/tsconfig.json
+++ b/app/server/tsconfig.json
@@ -16,8 +16,7 @@
"moduleResolution": "nodenext",
"noUncheckedIndexedAccess": true,
"declaration": true
-
},
"include": ["**/*.ts"],
- "exclude": ["node_modules", "dist"]
+ "exclude": ["node_modules", "./dist"]
}