archetecture security fix

This commit is contained in:
root
2026-06-11 03:22:12 -04:00
parent 6def9993da
commit 9483750161
3126 changed files with 177194 additions and 37211 deletions
+68
View File
@@ -0,0 +1,68 @@
export declare function presentBrand(company: {
id: string;
slug: string;
name: string;
phone: string | null;
brand: any;
}): {
company: {
id: string;
slug: string;
name: string;
phone: string | null;
};
brand: {
displayName: any;
tagline: any;
logoUrl: any;
faviconUrl: any;
heroImageUrl: any;
primaryColor: any;
accentColor: any;
subdomain: any;
customDomain: any;
customDomainVerified: any;
customDomainAddedAt: any;
publicEmail: any;
publicPhone: any;
publicAddress: any;
publicCity: any;
publicCountry: any;
websiteUrl: any;
whatsappNumber: any;
facebookUrl: any;
instagramUrl: any;
defaultLocale: any;
defaultCurrency: any;
paypalEmail: any;
paypalMerchantId: any;
paymentMethodsEnabled: any;
isListedOnMarketplace: any;
marketplaceRating: any;
homePageConfig: any;
menuConfig: any;
} | null;
};
export declare function presentPublicBooking(reservation: any): {
id: any;
status: any;
pickupAt: any;
returnAt: any;
vehicle: {
make: any;
model: any;
year: any;
imageUrl: any;
};
customer: {
displayName: string;
maskedEmail: string | undefined;
maskedPhone: string | undefined;
};
totals: {
amountDue: number;
currency: string;
};
paymentStatus: any;
};
//# sourceMappingURL=site.presenter.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.presenter.d.ts","sourceRoot":"","sources":["../../../src/modules/site/site.presenter.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACpC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CACzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCA;AAcD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;EAuBpD"}
+78
View File
@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.presentBrand = presentBrand;
exports.presentPublicBooking = presentPublicBooking;
function presentBrand(company) {
const brand = company.brand;
return {
company: { id: company.id, slug: company.slug, name: company.name, phone: company.phone },
brand: brand ? {
displayName: brand.displayName,
tagline: brand.tagline,
logoUrl: brand.logoUrl,
faviconUrl: brand.faviconUrl,
heroImageUrl: brand.heroImageUrl,
primaryColor: brand.primaryColor,
accentColor: brand.accentColor,
subdomain: brand.subdomain,
customDomain: brand.customDomain,
customDomainVerified: brand.customDomainVerified,
customDomainAddedAt: brand.customDomainAddedAt,
publicEmail: brand.publicEmail,
publicPhone: brand.publicPhone,
publicAddress: brand.publicAddress,
publicCity: brand.publicCity,
publicCountry: brand.publicCountry,
websiteUrl: brand.websiteUrl,
whatsappNumber: brand.whatsappNumber,
facebookUrl: brand.facebookUrl,
instagramUrl: brand.instagramUrl,
defaultLocale: brand.defaultLocale,
defaultCurrency: brand.defaultCurrency,
paypalEmail: brand.paypalEmail,
paypalMerchantId: brand.paypalMerchantId,
paymentMethodsEnabled: brand.paymentMethodsEnabled,
isListedOnMarketplace: brand.isListedOnMarketplace,
marketplaceRating: brand.marketplaceRating,
homePageConfig: brand.homePageConfig,
menuConfig: brand.menuConfig,
} : null,
};
}
function maskEmail(email) {
if (!email || !email.includes('@'))
return undefined;
const [name = '', domain = ''] = email.split('@');
return `${name.slice(0, 1)}***@${domain}`;
}
function maskPhone(phone) {
if (!phone)
return undefined;
const visible = phone.replace(/\D/g, '').slice(-4);
return visible ? `***${visible}` : undefined;
}
function presentPublicBooking(reservation) {
return {
id: reservation.id,
status: reservation.status,
pickupAt: reservation.startDate instanceof Date ? reservation.startDate.toISOString() : reservation.startDate,
returnAt: reservation.endDate instanceof Date ? reservation.endDate.toISOString() : reservation.endDate,
vehicle: {
make: reservation.vehicle?.make,
model: reservation.vehicle?.model,
year: reservation.vehicle?.year,
imageUrl: Array.isArray(reservation.vehicle?.images) ? reservation.vehicle.images[0] : reservation.vehicle?.imageUrl,
},
customer: {
displayName: [reservation.customer?.firstName, reservation.customer?.lastName?.slice(0, 1)].filter(Boolean).join(' '),
maskedEmail: maskEmail(reservation.customer?.email),
maskedPhone: maskPhone(reservation.customer?.phone),
},
totals: {
amountDue: reservation.totalAmount - (reservation.paidAmount ?? 0),
currency: 'MAD',
},
paymentStatus: reservation.paymentStatus,
};
}
//# sourceMappingURL=site.presenter.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.presenter.js","sourceRoot":"","sources":["../../../src/modules/site/site.presenter.ts"],"names":[],"mappings":"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"}
+42
View File
@@ -0,0 +1,42 @@
export declare function findCompanyBySlug(slug: string): Promise<any>;
export declare function findPublishedVehicles(companyId: string): Promise<any>;
export declare function findVehicleById(vehicleId: string, companyId: string): Promise<any>;
export declare function findActiveOffers(companyId: string): Promise<any>;
export declare function findInsurancePolicies(companyId: string): Promise<any>;
export declare function findOfferByPromoCode(companyId: string, code: string): Promise<any>;
export declare function upsertCustomer(companyId: string, data: {
email: string;
firstName: string;
lastName: string;
phone: string;
driverLicense: string;
dateOfBirth: string;
licenseExpiry: string;
licenseIssuedAt: string;
nationality: string;
identityDocumentNumber: string;
fullAddress: string;
licenseCountry: string;
licenseNumber?: string | null;
licenseCategory: string;
internationalLicenseNumber?: string | null;
}): Promise<any>;
export declare function createReservation(data: object): Promise<any>;
export declare function findReservationWithDetails(reservationId: string): Promise<any>;
export declare function findBooking(reservationId: string, companyId: string): Promise<any>;
export declare function findReservationForPayment(reservationId: string, companyId: string): Promise<any>;
export declare function createRentalPayment(data: {
companyId: string;
reservationId: string;
amount: number;
currency: string;
paymentProvider: string;
amanpayTransactionId?: string | null;
paypalCaptureId?: string | null;
}): Promise<any>;
export declare function findPaymentByPaypalOrderId(paypalOrderId: string, companyId: string): Promise<any>;
export declare function capturePaypalPayment(paymentId: string, captureId: string, reservationId: string, amount: number): Promise<void>;
export declare function createReservationPublicAccess(reservationId: string, tokenHash: string, expiresAt: Date): Promise<any>;
export declare function findReservationPublicAccess(reservationId: string, tokenHash: string): Promise<any>;
export declare function markReservationPublicAccessUsed(id: string): Promise<any>;
//# sourceMappingURL=site.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/site/site.repo.ts"],"names":[],"mappings":"AAEA,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,gBAKnD;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,gBAK5D;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAIzE;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,gBAKvD;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,gBAK5D;AAED,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAIzE;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3C,gBAyCA;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,gBAEnD;AAED,wBAAsB,0BAA0B,CAAC,aAAa,EAAE,MAAM,gBAKrE;AAED,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAKzE;AAED,wBAAsB,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAKvF;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;IAC1E,eAAe,EAAE,MAAM,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/F,gBAIA;AAED,wBAAsB,0BAA0B,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAExF;AAED,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAGrH;AAGD,wBAAsB,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,gBAI5G;AAED,wBAAsB,2BAA2B,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAIzF;AAED,wBAAsB,+BAA+B,CAAC,EAAE,EAAE,MAAM,gBAK/D"}
+142
View File
@@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findCompanyBySlug = findCompanyBySlug;
exports.findPublishedVehicles = findPublishedVehicles;
exports.findVehicleById = findVehicleById;
exports.findActiveOffers = findActiveOffers;
exports.findInsurancePolicies = findInsurancePolicies;
exports.findOfferByPromoCode = findOfferByPromoCode;
exports.upsertCustomer = upsertCustomer;
exports.createReservation = createReservation;
exports.findReservationWithDetails = findReservationWithDetails;
exports.findBooking = findBooking;
exports.findReservationForPayment = findReservationForPayment;
exports.createRentalPayment = createRentalPayment;
exports.findPaymentByPaypalOrderId = findPaymentByPaypalOrderId;
exports.capturePaypalPayment = capturePaypalPayment;
exports.createReservationPublicAccess = createReservationPublicAccess;
exports.findReservationPublicAccess = findReservationPublicAccess;
exports.markReservationPublicAccessUsed = markReservationPublicAccessUsed;
const prisma_1 = require("../../lib/prisma");
async function findCompanyBySlug(slug) {
return prisma_1.prisma.company.findFirstOrThrow({
where: { slug, status: { in: ['ACTIVE', 'TRIALING', 'PAST_DUE', 'SUSPENDED'] } },
include: { brand: true, contractSettings: true },
});
}
async function findPublishedVehicles(companyId) {
return prisma_1.prisma.vehicle.findMany({
where: { companyId, isPublished: true },
orderBy: { createdAt: 'desc' },
});
}
async function findVehicleById(vehicleId, companyId) {
return prisma_1.prisma.vehicle.findFirstOrThrow({
where: { id: vehicleId, companyId, isPublished: true },
});
}
async function findActiveOffers(companyId) {
return prisma_1.prisma.offer.findMany({
where: { companyId, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } },
orderBy: [{ isFeatured: 'desc' }, { createdAt: 'desc' }],
});
}
async function findInsurancePolicies(companyId) {
return prisma_1.prisma.insurancePolicy.findMany({
where: { companyId, isActive: true },
orderBy: [{ isRequired: 'desc' }, { sortOrder: 'asc' }, { name: 'asc' }],
});
}
async function findOfferByPromoCode(companyId, code) {
return prisma_1.prisma.offer.findFirst({
where: { companyId, promoCode: code, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } },
});
}
async function upsertCustomer(companyId, data) {
const existing = await prisma_1.prisma.customer.findUnique({
where: { companyId_email: { companyId, email: data.email } },
select: { id: true, address: true },
});
const address = {
...(existing?.address && typeof existing.address === 'object' && !Array.isArray(existing.address)
? existing.address
: {}),
fullAddress: data.fullAddress,
identityDocumentNumber: data.identityDocumentNumber,
internationalLicenseNumber: data.internationalLicenseNumber ?? null,
};
const payload = {
firstName: data.firstName,
lastName: data.lastName,
email: data.email,
phone: data.phone,
driverLicense: data.driverLicense,
dateOfBirth: new Date(data.dateOfBirth),
nationality: data.nationality,
address,
licenseExpiry: new Date(data.licenseExpiry),
licenseIssuedAt: new Date(data.licenseIssuedAt),
licenseCountry: data.licenseCountry,
licenseNumber: data.licenseNumber ?? data.driverLicense,
licenseCategory: data.licenseCategory,
};
if (!existing) {
return prisma_1.prisma.customer.create({
data: { companyId, ...payload },
});
}
return prisma_1.prisma.customer.update({
where: { id: existing.id },
data: payload,
});
}
async function createReservation(data) {
return prisma_1.prisma.reservation.create({ data: data });
}
async function findReservationWithDetails(reservationId) {
return prisma_1.prisma.reservation.findUniqueOrThrow({
where: { id: reservationId },
include: { insurances: true, additionalDrivers: true },
});
}
async function findBooking(reservationId, companyId) {
return prisma_1.prisma.reservation.findFirstOrThrow({
where: { id: reservationId, companyId },
include: { vehicle: true, customer: true },
});
}
async function findReservationForPayment(reservationId, companyId) {
return prisma_1.prisma.reservation.findFirstOrThrow({
where: { id: reservationId, companyId },
include: { vehicle: true, customer: true, additionalDrivers: true },
});
}
async function createRentalPayment(data) {
return prisma_1.prisma.rentalPayment.create({
data: { ...data, paymentProvider: data.paymentProvider, status: 'PENDING', type: 'CHARGE' },
});
}
async function findPaymentByPaypalOrderId(paypalOrderId, companyId) {
return prisma_1.prisma.rentalPayment.findFirstOrThrow({ where: { paypalCaptureId: paypalOrderId, companyId } });
}
async function capturePaypalPayment(paymentId, captureId, reservationId, amount) {
await prisma_1.prisma.rentalPayment.update({ where: { id: paymentId }, data: { status: 'SUCCEEDED', paidAt: new Date(), paypalCaptureId: captureId } });
await prisma_1.prisma.reservation.update({ where: { id: reservationId }, data: { paymentStatus: 'PAID', paidAmount: { increment: amount } } });
}
async function createReservationPublicAccess(reservationId, tokenHash, expiresAt) {
return prisma_1.prisma.reservationPublicAccess.create({
data: { reservationId, tokenHash, expiresAt },
});
}
async function findReservationPublicAccess(reservationId, tokenHash) {
return prisma_1.prisma.reservationPublicAccess.findFirst({
where: { reservationId, tokenHash, revokedAt: null, expiresAt: { gt: new Date() } },
});
}
async function markReservationPublicAccessUsed(id) {
return prisma_1.prisma.reservationPublicAccess.update({
where: { id },
data: { usedAt: new Date() },
});
}
//# sourceMappingURL=site.repo.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=site.routes.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/site/site.routes.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,MAAM,4CAAW,CAAA;AA2JvB,eAAe,MAAM,CAAA"}
+207
View File
@@ -0,0 +1,207 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const types_1 = require("@rentaldrivego/types");
const express_1 = require("express");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const isDatabaseUnavailable_1 = require("../../lib/isDatabaseUnavailable");
const service = __importStar(require("./site.service"));
const site_schemas_1 = require("./site.schemas");
const router = (0, express_1.Router)();
router.get('/platform/homepage', async (_req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getPlatformHomepage());
}
catch (err) {
next(err);
}
});
router.get('/platform/pricing', async (_req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getPlatformPricing());
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.json({ data: { prices: types_1.PLAN_PRICES, planFeatures: types_1.PLAN_FEATURES } });
}
next(err);
}
});
router.get('/:slug/brand', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
(0, respond_1.ok)(res, await service.getBrand(slug));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.json({ data: { company: { id: 'demo', slug: req.params.slug, name: 'Demo Company', phone: null }, brand: null } });
}
next(err);
}
});
router.get('/:slug/vehicles', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
(0, respond_1.ok)(res, await service.getPublicVehicles(slug));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err))
return res.json({ data: [] });
next(err);
}
});
router.get('/:slug/vehicles/:id', async (req, res, next) => {
try {
const { slug, id } = (0, validate_1.parseParams)(site_schemas_1.bookingParamSchema, req);
(0, respond_1.ok)(res, await service.getVehicleDetail(slug, id));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Vehicle details are temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.get('/:slug/offers', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
(0, respond_1.ok)(res, await service.getOffers(slug));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err))
return res.json({ data: [] });
next(err);
}
});
router.get('/:slug/booking-options', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
(0, respond_1.ok)(res, await service.getBookingOptions(slug));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err))
return res.json({ data: { insurancePolicies: [], contractSettings: null } });
next(err);
}
});
router.post('/:slug/availability', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
const { vehicleId, startDate, endDate } = (0, validate_1.parseBody)(site_schemas_1.availabilitySchema, req);
(0, respond_1.ok)(res, await service.checkAvailability(slug, vehicleId, startDate, endDate));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Availability checks are temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.post('/:slug/book/validate-code', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
const { code } = (0, validate_1.parseBody)(site_schemas_1.validateCodeSchema, req);
(0, respond_1.ok)(res, await service.validatePromoCode(slug, code));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Promo code validation is temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.post('/:slug/book', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
const body = (0, validate_1.parseBody)(site_schemas_1.bookSchema, req);
const result = await service.createBooking(slug, body);
(0, respond_1.created)(res, result);
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Booking is temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.get('/:slug/booking/:id', async (req, res, next) => {
try {
const { slug, id } = (0, validate_1.parseParams)(site_schemas_1.bookingParamSchema, req);
const accessToken = typeof req.query.token === 'string' ? req.query.token : undefined;
(0, respond_1.ok)(res, await service.getBooking(slug, id, accessToken));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Booking details are temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.post('/:slug/booking/:id/pay', async (req, res, next) => {
try {
const { slug, id } = (0, validate_1.parseParams)(site_schemas_1.bookingParamSchema, req);
const body = (0, validate_1.parseBody)(site_schemas_1.paySchema, req);
(0, respond_1.ok)(res, await service.initPayment(slug, id, body));
}
catch (err) {
if ((0, isDatabaseUnavailable_1.isDatabaseUnavailableError)(err)) {
return res.status(503).json({ error: 'database_unavailable', message: 'Payment initiation is temporarily unavailable', statusCode: 503 });
}
next(err);
}
});
router.post('/:slug/booking/:id/capture-paypal', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
const { paypalOrderId } = (0, validate_1.parseBody)(site_schemas_1.capturePaypalSchema, req);
(0, respond_1.ok)(res, await service.capturePaypal(slug, paypalOrderId));
}
catch (err) {
next(err);
}
});
router.post('/:slug/contact', async (req, res, next) => {
try {
const { slug } = (0, validate_1.parseParams)(site_schemas_1.slugParamSchema, req);
const body = (0, validate_1.parseBody)(site_schemas_1.contactSchema, req);
(0, respond_1.ok)(res, await service.handleContact(slug, body));
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=site.routes.js.map
File diff suppressed because one or more lines are too long
+204
View File
@@ -0,0 +1,204 @@
import { z } from 'zod';
export declare const slugParamSchema: z.ZodObject<{
slug: z.ZodString;
}, "strip", z.ZodTypeAny, {
slug: string;
}, {
slug: string;
}>;
export declare const bookingParamSchema: z.ZodObject<{
slug: z.ZodString;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
id: string;
slug: string;
}, {
id: string;
slug: string;
}>;
export declare const availabilitySchema: z.ZodObject<{
vehicleId: z.ZodString;
startDate: z.ZodString;
endDate: z.ZodString;
}, "strip", z.ZodTypeAny, {
startDate: string;
endDate: string;
vehicleId: string;
}, {
startDate: string;
endDate: string;
vehicleId: string;
}>;
export declare const validateCodeSchema: z.ZodObject<{
code: z.ZodString;
}, "strip", z.ZodTypeAny, {
code: string;
}, {
code: string;
}>;
export declare const bookSchema: z.ZodObject<{
vehicleId: z.ZodString;
startDate: z.ZodString;
endDate: z.ZodString;
firstName: z.ZodString;
lastName: z.ZodString;
email: z.ZodString;
phone: z.ZodString;
driverLicense: z.ZodString;
dateOfBirth: z.ZodString;
licenseExpiry: z.ZodString;
licenseIssuedAt: z.ZodString;
nationality: z.ZodString;
identityDocumentNumber: z.ZodString;
fullAddress: z.ZodString;
licenseCountry: z.ZodString;
licenseNumber: z.ZodOptional<z.ZodString>;
licenseCategory: z.ZodString;
internationalLicenseNumber: z.ZodOptional<z.ZodString>;
offerId: z.ZodOptional<z.ZodString>;
promoCodeUsed: z.ZodOptional<z.ZodString>;
notes: z.ZodOptional<z.ZodString>;
source: z.ZodDefault<z.ZodEnum<["PUBLIC_SITE", "MARKETPLACE"]>>;
selectedInsurancePolicyIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
additionalDrivers: z.ZodDefault<z.ZodArray<z.ZodObject<{
firstName: z.ZodString;
lastName: z.ZodString;
email: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
driverLicense: z.ZodString;
licenseExpiry: z.ZodOptional<z.ZodString>;
licenseIssuedAt: z.ZodOptional<z.ZodString>;
dateOfBirth: z.ZodOptional<z.ZodString>;
nationality: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
firstName: string;
lastName: string;
driverLicense: string;
email?: string | undefined;
phone?: string | undefined;
licenseExpiry?: string | undefined;
licenseIssuedAt?: string | undefined;
dateOfBirth?: string | undefined;
nationality?: string | undefined;
}, {
firstName: string;
lastName: string;
driverLicense: string;
email?: string | undefined;
phone?: string | undefined;
licenseExpiry?: string | undefined;
licenseIssuedAt?: string | undefined;
dateOfBirth?: string | undefined;
nationality?: string | undefined;
}>, "many">>;
}, "strip", z.ZodTypeAny, {
startDate: string;
endDate: string;
firstName: string;
lastName: string;
email: string;
phone: string;
driverLicense: string;
licenseExpiry: string;
licenseIssuedAt: string;
dateOfBirth: string;
nationality: string;
vehicleId: string;
selectedInsurancePolicyIds: string[];
additionalDrivers: {
firstName: string;
lastName: string;
driverLicense: string;
email?: string | undefined;
phone?: string | undefined;
licenseExpiry?: string | undefined;
licenseIssuedAt?: string | undefined;
dateOfBirth?: string | undefined;
nationality?: string | undefined;
}[];
source: "MARKETPLACE" | "PUBLIC_SITE";
licenseCountry: string;
licenseCategory: string;
fullAddress: string;
identityDocumentNumber: string;
notes?: string | undefined;
offerId?: string | undefined;
promoCodeUsed?: string | undefined;
licenseNumber?: string | undefined;
internationalLicenseNumber?: string | undefined;
}, {
startDate: string;
endDate: string;
firstName: string;
lastName: string;
email: string;
phone: string;
driverLicense: string;
licenseExpiry: string;
licenseIssuedAt: string;
dateOfBirth: string;
nationality: string;
vehicleId: string;
licenseCountry: string;
licenseCategory: string;
fullAddress: string;
identityDocumentNumber: string;
notes?: string | undefined;
offerId?: string | undefined;
promoCodeUsed?: string | undefined;
selectedInsurancePolicyIds?: string[] | undefined;
additionalDrivers?: {
firstName: string;
lastName: string;
driverLicense: string;
email?: string | undefined;
phone?: string | undefined;
licenseExpiry?: string | undefined;
licenseIssuedAt?: string | undefined;
dateOfBirth?: string | undefined;
nationality?: string | undefined;
}[] | undefined;
source?: "MARKETPLACE" | "PUBLIC_SITE" | undefined;
licenseNumber?: string | undefined;
internationalLicenseNumber?: string | undefined;
}>;
export declare const paySchema: z.ZodObject<{
provider: z.ZodEnum<["AMANPAY", "PAYPAL"]>;
currency: z.ZodDefault<z.ZodLiteral<"MAD">>;
successUrl: z.ZodString;
failureUrl: z.ZodString;
accessToken: z.ZodString;
}, "strip", z.ZodTypeAny, {
provider: "AMANPAY" | "PAYPAL";
currency: "MAD";
successUrl: string;
failureUrl: string;
accessToken: string;
}, {
provider: "AMANPAY" | "PAYPAL";
successUrl: string;
failureUrl: string;
accessToken: string;
currency?: "MAD" | undefined;
}>;
export declare const capturePaypalSchema: z.ZodObject<{
paypalOrderId: z.ZodString;
}, "strip", z.ZodTypeAny, {
paypalOrderId: string;
}, {
paypalOrderId: string;
}>;
export declare const contactSchema: z.ZodObject<{
name: z.ZodString;
email: z.ZodString;
message: z.ZodString;
}, "strip", z.ZodTypeAny, {
message: string;
name: string;
email: string;
}, {
message: string;
name: string;
email: string;
}>;
//# sourceMappingURL=site.schemas.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/site/site.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,eAAe;;;;;;EAAmC,CAAA;AAC/D,eAAO,MAAM,kBAAkB;;;;;;;;;EAAiD,CAAA;AAEhF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAI7B,CAAA;AAEF,eAAO,MAAM,kBAAkB;;;;;;EAAwC,CAAA;AAEvE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCrB,CAAA;AAEF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;EAMpB,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;EAA0C,CAAA;AAE1E,eAAO,MAAM,aAAa;;;;;;;;;;;;EAIxB,CAAA"}
+62
View File
@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.contactSchema = exports.capturePaypalSchema = exports.paySchema = exports.bookSchema = exports.validateCodeSchema = exports.availabilitySchema = exports.bookingParamSchema = exports.slugParamSchema = void 0;
const zod_1 = require("zod");
exports.slugParamSchema = zod_1.z.object({ slug: zod_1.z.string() });
exports.bookingParamSchema = zod_1.z.object({ slug: zod_1.z.string(), id: zod_1.z.string() });
exports.availabilitySchema = zod_1.z.object({
vehicleId: zod_1.z.string().cuid(),
startDate: zod_1.z.string().datetime(),
endDate: zod_1.z.string().datetime(),
});
exports.validateCodeSchema = zod_1.z.object({ code: zod_1.z.string().min(1) });
exports.bookSchema = zod_1.z.object({
vehicleId: zod_1.z.string().cuid(),
startDate: zod_1.z.string().datetime(),
endDate: zod_1.z.string().datetime(),
firstName: zod_1.z.string().min(1),
lastName: zod_1.z.string().min(1),
email: zod_1.z.string().email(),
phone: zod_1.z.string().min(1).max(30),
driverLicense: zod_1.z.string().min(1).max(50),
dateOfBirth: zod_1.z.string().datetime(),
licenseExpiry: zod_1.z.string().datetime(),
licenseIssuedAt: zod_1.z.string().datetime(),
nationality: zod_1.z.string().min(1).max(100),
identityDocumentNumber: zod_1.z.string().min(1).max(100),
fullAddress: zod_1.z.string().min(1).max(500),
licenseCountry: zod_1.z.string().min(1).max(100),
licenseNumber: zod_1.z.string().min(1).max(50).optional(),
licenseCategory: zod_1.z.string().min(1).max(20),
internationalLicenseNumber: zod_1.z.string().trim().max(100).optional(),
offerId: zod_1.z.string().cuid().optional(),
promoCodeUsed: zod_1.z.string().optional(),
notes: zod_1.z.string().optional(),
source: zod_1.z.enum(['PUBLIC_SITE', 'MARKETPLACE']).default('PUBLIC_SITE'),
selectedInsurancePolicyIds: zod_1.z.array(zod_1.z.string()).default([]),
additionalDrivers: zod_1.z.array(zod_1.z.object({
firstName: zod_1.z.string().min(1),
lastName: zod_1.z.string().min(1),
email: zod_1.z.string().email().optional(),
phone: zod_1.z.string().optional(),
driverLicense: zod_1.z.string().min(1),
licenseExpiry: zod_1.z.string().datetime().optional(),
licenseIssuedAt: zod_1.z.string().datetime().optional(),
dateOfBirth: zod_1.z.string().datetime().optional(),
nationality: zod_1.z.string().optional(),
})).default([]),
});
exports.paySchema = zod_1.z.object({
provider: zod_1.z.enum(['AMANPAY', 'PAYPAL']),
currency: zod_1.z.literal('MAD').default('MAD'),
successUrl: zod_1.z.string().url(),
failureUrl: zod_1.z.string().url(),
accessToken: zod_1.z.string().min(20),
});
exports.capturePaypalSchema = zod_1.z.object({ paypalOrderId: zod_1.z.string() });
exports.contactSchema = zod_1.z.object({
name: zod_1.z.string().min(1),
email: zod_1.z.string().email(),
message: zod_1.z.string().min(1),
});
//# sourceMappingURL=site.schemas.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.schemas.js","sourceRoot":"","sources":["../../../src/modules/site/site.schemas.ts"],"names":[],"mappings":"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"}
+132
View File
@@ -0,0 +1,132 @@
export declare function getPlatformHomepage(): Promise<import("@rentaldrivego/types").MarketplaceHomepageConfig>;
export declare function getPlatformPricing(): Promise<{
prices: any;
planFeatures: {
[k: string]: any;
};
}>;
export declare function getBrand(slug: string): Promise<{
company: {
id: string;
slug: string;
name: string;
phone: string | null;
};
brand: {
displayName: any;
tagline: any;
logoUrl: any;
faviconUrl: any;
heroImageUrl: any;
primaryColor: any;
accentColor: any;
subdomain: any;
customDomain: any;
customDomainVerified: any;
customDomainAddedAt: any;
publicEmail: any;
publicPhone: any;
publicAddress: any;
publicCity: any;
publicCountry: any;
websiteUrl: any;
whatsappNumber: any;
facebookUrl: any;
instagramUrl: any;
defaultLocale: any;
defaultCurrency: any;
paypalEmail: any;
paypalMerchantId: any;
paymentMethodsEnabled: any;
isListedOnMarketplace: any;
marketplaceRating: any;
homePageConfig: any;
menuConfig: any;
} | null;
}>;
export declare function getPublicVehicles(slug: string): Promise<any>;
export declare function getVehicleDetail(slug: string, vehicleId: string): Promise<any>;
export declare function getOffers(slug: string): Promise<any>;
export declare function getBookingOptions(slug: string): Promise<{
insurancePolicies: any;
contractSettings: any;
}>;
export declare function checkAvailability(slug: string, vehicleId: string, startDate: string, endDate: string): Promise<{
available: boolean;
nextAvailableAt: Date | null;
}>;
export declare function validatePromoCode(slug: string, code: string): Promise<any>;
export declare function createBooking(slug: string, body: {
vehicleId: string;
startDate: string;
endDate: string;
firstName: string;
lastName: string;
email: string;
phone: string;
driverLicense: string;
dateOfBirth: string;
licenseExpiry: string;
licenseIssuedAt: string;
nationality: string;
identityDocumentNumber: string;
fullAddress: string;
licenseCountry: string;
licenseNumber?: string;
licenseCategory: string;
internationalLicenseNumber?: string;
offerId?: string;
promoCodeUsed?: string;
notes?: string;
source?: string;
selectedInsurancePolicyIds?: string[];
additionalDrivers?: any[];
}): Promise<any>;
export declare function getBooking(slug: string, reservationId: string, accessToken?: string): Promise<{
id: any;
status: any;
pickupAt: any;
returnAt: any;
vehicle: {
make: any;
model: any;
year: any;
imageUrl: any;
};
customer: {
displayName: string;
maskedEmail: string | undefined;
maskedPhone: string | undefined;
};
totals: {
amountDue: number;
currency: string;
};
paymentStatus: any;
}>;
export declare function initPayment(slug: string, reservationId: string, body: {
provider: 'AMANPAY' | 'PAYPAL';
currency?: 'MAD';
successUrl: string;
failureUrl: string;
accessToken?: string;
}): Promise<{
checkoutUrl: string;
}>;
export declare function capturePaypal(slug: string, paypalOrderId: string): Promise<{
success: boolean;
}>;
export declare function handleContact(slug: string, body: {
name: string;
email: string;
message: string;
}): Promise<{
success: boolean;
deliveredTo: any;
preview: {
name: string;
email: string;
message: string;
};
}>;
//# sourceMappingURL=site.service.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"site.service.d.ts","sourceRoot":"","sources":["../../../src/modules/site/site.service.ts"],"names":[],"mappings":"AAkDA,wBAAsB,mBAAmB,sEAExC;AAED,wBAAsB,kBAAkB;;;;;GAwBvC;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAG1C;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,gBASnD;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAKrE;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,gBAG3C;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM;;;GAInD;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;GAQ1G;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAKjE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IACtD,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;IACrD,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IACjE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;IAC/G,sBAAsB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACjK,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACzE,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,iBAAiB,CAAC,EAAE,GAAG,EAAE,CAAA;CACjE,gBAsGA;AASD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;GAIzF;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE;IAC3E,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC/G;;GA6EA;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;;GAOtE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;cAAxC,MAAM;eAAS,MAAM;iBAAW,MAAM;;GAOrG"}
+354
View File
@@ -0,0 +1,354 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlatformHomepage = getPlatformHomepage;
exports.getPlatformPricing = getPlatformPricing;
exports.getBrand = getBrand;
exports.getPublicVehicles = getPublicVehicles;
exports.getVehicleDetail = getVehicleDetail;
exports.getOffers = getOffers;
exports.getBookingOptions = getBookingOptions;
exports.checkAvailability = checkAvailability;
exports.validatePromoCode = validatePromoCode;
exports.createBooking = createBooking;
exports.getBooking = getBooking;
exports.initPayment = initPayment;
exports.capturePaypal = capturePaypal;
exports.handleContact = handleContact;
const types_1 = require("@rentaldrivego/types");
const errors_1 = require("../../http/errors");
const vehicleAvailabilityService_1 = require("../../services/vehicleAvailabilityService");
const insuranceService_1 = require("../../services/insuranceService");
const additionalDriverService_1 = require("../../services/additionalDriverService");
const pricingRuleService_1 = require("../../services/pricingRuleService");
const licenseValidationService_1 = require("../../services/licenseValidationService");
const platformContentService_1 = require("../../services/platformContentService");
const prisma_1 = require("../../lib/prisma");
const amanpay = __importStar(require("../../services/amanpayService"));
const paypal = __importStar(require("../../services/paypalService"));
const repo = __importStar(require("./site.repo"));
const site_presenter_1 = require("./site.presenter");
const publicAccessTokens_1 = require("../../security/publicAccessTokens");
function assertAllowedPaymentRedirect(urlValue, company) {
let parsed;
try {
parsed = new URL(urlValue);
}
catch {
throw new errors_1.AppError('Invalid payment redirect URL', 400, 'invalid_redirect_url');
}
if (process.env.NODE_ENV === 'production' && parsed.protocol !== 'https:') {
throw new errors_1.AppError('Payment redirect URLs must use HTTPS', 400, 'invalid_redirect_url');
}
const allowedHosts = new Set();
if (process.env.NODE_ENV !== 'production') {
allowedHosts.add('localhost:3000');
allowedHosts.add('localhost:4000');
allowedHosts.add('127.0.0.1:3000');
allowedHosts.add('127.0.0.1:4000');
}
for (const value of [process.env.MARKETPLACE_URL, process.env.DASHBOARD_URL, process.env.NEXT_PUBLIC_MARKETPLACE_URL, process.env.NEXT_PUBLIC_DASHBOARD_URL]) {
if (!value)
continue;
try {
allowedHosts.add(new URL(value).host);
}
catch { }
}
const brand = company.brand;
if (brand?.customDomain && brand?.customDomainVerified)
allowedHosts.add(brand.customDomain);
if (brand?.subdomain && process.env.PUBLIC_SITE_BASE_DOMAIN) {
allowedHosts.add(`${brand.subdomain}.${process.env.PUBLIC_SITE_BASE_DOMAIN}`);
}
if (!allowedHosts.has(parsed.host)) {
throw new errors_1.AppError('Payment redirect host is not allowed', 400, 'invalid_redirect_url');
}
}
async function getPlatformHomepage() {
return (0, platformContentService_1.getMarketplaceHomepageContent)();
}
async function getPlatformPricing() {
const [configs, features] = await Promise.all([
prisma_1.prisma.pricingConfig.findMany(),
prisma_1.prisma.planFeature.findMany({
orderBy: [{ plan: 'asc' }, { sortOrder: 'asc' }, { createdAt: 'asc' }],
}),
]);
const prices = configs.length === 0
? types_1.PLAN_PRICES
: configs.reduce((acc, config) => {
if (!acc[config.plan])
acc[config.plan] = {};
acc[config.plan][config.billingPeriod] = { MAD: config.amount };
return acc;
}, {});
const planFeatures = Object.fromEntries(Object.entries(types_1.PLAN_FEATURES).map(([plan, fallback]) => {
const labels = features.filter((feature) => feature.plan === plan).map((feature) => feature.label);
return [plan, labels.length > 0 ? labels : fallback];
}));
return { prices, planFeatures };
}
async function getBrand(slug) {
const company = await repo.findCompanyBySlug(slug);
return (0, site_presenter_1.presentBrand)(company);
}
async function getPublicVehicles(slug) {
const company = await repo.findCompanyBySlug(slug);
const vehicles = await repo.findPublishedVehicles(company.id);
return Promise.all(vehicles.map(async (v) => {
const a = await (0, vehicleAvailabilityService_1.getVehicleAvailabilitySummary)(v.id, { companyId: v.companyId });
return { ...v, availability: a.available, availabilityStatus: a.status, nextAvailableAt: a.nextAvailableAt };
}));
}
async function getVehicleDetail(slug, vehicleId) {
const company = await repo.findCompanyBySlug(slug);
const vehicle = await repo.findVehicleById(vehicleId, company.id);
const a = await (0, vehicleAvailabilityService_1.getVehicleAvailabilitySummary)(vehicle.id, { companyId: vehicle.companyId });
return { ...vehicle, availability: a.available, availabilityStatus: a.status, nextAvailableAt: a.nextAvailableAt };
}
async function getOffers(slug) {
const company = await repo.findCompanyBySlug(slug);
return repo.findActiveOffers(company.id);
}
async function getBookingOptions(slug) {
const company = await repo.findCompanyBySlug(slug);
const insurancePolicies = await repo.findInsurancePolicies(company.id);
return { insurancePolicies, contractSettings: company.contractSettings };
}
async function checkAvailability(slug, vehicleId, startDate, endDate) {
const company = await repo.findCompanyBySlug(slug);
const vehicle = await repo.findVehicleById(vehicleId, company.id);
const availability = await (0, vehicleAvailabilityService_1.getVehicleAvailabilitySummary)(vehicle.id, {
companyId: company.id,
range: { startDate: new Date(startDate), endDate: new Date(endDate) },
});
return { available: availability.available, nextAvailableAt: availability.nextAvailableAt };
}
async function validatePromoCode(slug, code) {
const company = await repo.findCompanyBySlug(slug);
const offer = await repo.findOfferByPromoCode(company.id, code);
if (!offer)
throw new errors_1.NotFoundError('Promo code not found or expired');
return offer;
}
async function createBooking(slug, body) {
const company = await repo.findCompanyBySlug(slug);
const vehicle = await repo.findVehicleById(body.vehicleId, company.id);
const start = new Date(body.startDate);
const end = new Date(body.endDate);
if (end <= start)
throw new errors_1.AppError('End date must be after start date', 400, 'invalid_dates');
const availability = await (0, vehicleAvailabilityService_1.getVehicleAvailabilitySummary)(vehicle.id, { companyId: company.id, range: { startDate: start, endDate: end } });
if (!availability.available) {
throw new errors_1.AppError('Vehicle is not available for the selected dates', 409, 'unavailable', {
nextAvailableAt: availability.nextAvailableAt?.toISOString() ?? null,
});
}
const customer = await repo.upsertCustomer(company.id, {
email: body.email,
firstName: body.firstName,
lastName: body.lastName,
phone: body.phone,
driverLicense: body.driverLicense,
dateOfBirth: body.dateOfBirth,
licenseExpiry: body.licenseExpiry,
licenseIssuedAt: body.licenseIssuedAt,
nationality: body.nationality,
identityDocumentNumber: body.identityDocumentNumber,
fullAddress: body.fullAddress,
licenseCountry: body.licenseCountry,
licenseNumber: body.licenseNumber,
licenseCategory: body.licenseCategory,
internationalLicenseNumber: body.internationalLicenseNumber,
});
const totalDays = Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 86_400_000));
const baseAmount = vehicle.dailyRate * totalDays;
let discountAmount = 0;
if (body.promoCodeUsed) {
const offer = await repo.findOfferByPromoCode(company.id, body.promoCodeUsed);
if (offer) {
if (offer.type === 'PERCENTAGE')
discountAmount = Math.round(baseAmount * offer.discountValue / 100);
else if (offer.type === 'FIXED_AMOUNT')
discountAmount = offer.discountValue;
else if (offer.type === 'FREE_DAY')
discountAmount = vehicle.dailyRate * offer.discountValue;
}
}
const additionalDriversList = body.additionalDrivers ?? [];
const { applied, total: pricingRulesTotal } = await (0, pricingRuleService_1.applyPricingRules)(company.id, customer.id, additionalDriversList, vehicle.dailyRate, totalDays);
const primaryLicenseResult = (0, licenseValidationService_1.validateLicense)(body.licenseExpiry ? new Date(body.licenseExpiry) : null);
if (primaryLicenseResult.status === 'EXPIRED') {
throw new errors_1.AppError('The primary driver license is expired', 400, 'license_expired');
}
const reservation = await repo.createReservation({
companyId: company.id,
vehicleId: vehicle.id,
customerId: customer.id,
offerId: body.offerId ?? null,
promoCodeUsed: body.promoCodeUsed ?? null,
vehicleCategory: vehicle.category,
source: body.source ?? 'PUBLIC_SITE',
startDate: start,
endDate: end,
dailyRate: vehicle.dailyRate,
totalDays,
totalAmount: baseAmount - discountAmount + pricingRulesTotal,
discountAmount,
pricingRulesApplied: applied,
pricingRulesTotal,
notes: body.notes ?? null,
status: 'DRAFT',
});
const insuranceIds = body.selectedInsurancePolicyIds ?? [];
if (insuranceIds.length > 0) {
await (0, insuranceService_1.applyInsurancesToReservation)(reservation.id, company.id, insuranceIds, totalDays, baseAmount);
}
if (additionalDriversList.length > 0) {
await (0, additionalDriverService_1.applyAdditionalDriversToReservation)(reservation.id, company.id, additionalDriversList, totalDays);
}
if (body.licenseExpiry) {
await (0, licenseValidationService_1.validateAndFlagLicense)(customer.id).catch(() => null);
}
const publicAccess = (0, publicAccessTokens_1.generatePublicAccessToken)();
await repo.createReservationPublicAccess(reservation.id, publicAccess.tokenHash, new Date(Date.now() + 14 * 24 * 60 * 60 * 1000));
const refreshed = await repo.findReservationWithDetails(reservation.id);
return {
...refreshed,
publicAccessToken: publicAccess.token,
requiresManualApproval: primaryLicenseResult.requiresApproval ||
refreshed.additionalDrivers.some((d) => d.requiresApproval),
};
}
async function assertPublicBookingAccess(reservationId, token) {
if (!token)
throw new errors_1.AppError('Booking access token is required', 403, 'booking_token_required');
const access = await repo.findReservationPublicAccess(reservationId, (0, publicAccessTokens_1.hashPublicAccessToken)(token));
if (!access)
throw new errors_1.AppError('Booking not found', 404, 'not_found');
await repo.markReservationPublicAccessUsed(access.id);
}
async function getBooking(slug, reservationId, accessToken) {
const company = await repo.findCompanyBySlug(slug);
await assertPublicBookingAccess(reservationId, accessToken);
return (0, site_presenter_1.presentPublicBooking)(await repo.findBooking(reservationId, company.id));
}
async function initPayment(slug, reservationId, body) {
const company = await repo.findCompanyBySlug(slug);
await assertPublicBookingAccess(reservationId, body.accessToken);
const reservation = await repo.findReservationForPayment(reservationId, company.id);
if (reservation.paymentStatus === 'PAID') {
throw new errors_1.AppError('This reservation is already paid', 409, 'already_paid');
}
const customerLicenseResult = (0, licenseValidationService_1.validateLicense)(reservation.customer.licenseExpiry);
const licenseBlocked = reservation.customer.licenseValidationStatus === 'DENIED' ||
customerLicenseResult.status === 'EXPIRED' ||
(customerLicenseResult.requiresApproval && reservation.customer.licenseValidationStatus !== 'APPROVED') ||
reservation.additionalDrivers.some((d) => d.licenseExpired || (d.requiresApproval && !d.approvedAt));
if (licenseBlocked) {
throw new errors_1.AppError('This reservation requires license review before payment can be processed', 409, 'license_review_required');
}
assertAllowedPaymentRedirect(body.successUrl, company);
assertAllowedPaymentRedirect(body.failureUrl, company);
const currency = body.currency ?? 'MAD';
const amount = reservation.totalAmount;
const description = `Rental: ${reservation.vehicle.make} ${reservation.vehicle.model}`;
const orderId = `res-${reservation.id}-${Date.now()}`;
const webhookBase = process.env.API_URL ?? 'http://localhost:4000';
let checkoutUrl;
let amanpayTransactionId = null;
let paypalCaptureId = null;
if (body.provider === 'AMANPAY') {
if (!amanpay.isConfigured()) {
throw new errors_1.AppError('Online payment is not available for this company', 503, 'provider_not_configured');
}
const brand = company.brand;
const merchantId = brand?.amanpayMerchantId ?? process.env.AMANPAY_MERCHANT_ID ?? '';
const secretKey = brand?.amanpaySecretKey ?? process.env.AMANPAY_SECRET_KEY ?? '';
if (!merchantId || !secretKey) {
throw new errors_1.AppError('AmanPay is not configured for this company', 503, 'provider_not_configured');
}
const result = await amanpay.createCheckout({
amount, currency, orderId, description,
customerEmail: reservation.customer.email,
customerName: `${reservation.customer.firstName} ${reservation.customer.lastName}`,
successUrl: body.successUrl,
failureUrl: body.failureUrl,
webhookUrl: `${webhookBase}/api/v1/payments/webhooks/amanpay`,
});
checkoutUrl = result.checkoutUrl;
amanpayTransactionId = result.transactionId;
}
else {
if (!paypal.isConfigured()) {
throw new errors_1.AppError('PayPal is not available for this company', 503, 'provider_not_configured');
}
const result = await paypal.createOrder({
amount, currency, orderId, description,
returnUrl: body.successUrl,
cancelUrl: body.failureUrl,
});
checkoutUrl = result.approveUrl;
paypalCaptureId = result.orderId;
}
await repo.createRentalPayment({
companyId: company.id,
reservationId: reservation.id,
amount,
currency,
paymentProvider: body.provider,
amanpayTransactionId,
paypalCaptureId,
});
return { checkoutUrl };
}
async function capturePaypal(slug, paypalOrderId) {
const company = await repo.findCompanyBySlug(slug);
const payment = await repo.findPaymentByPaypalOrderId(paypalOrderId, company.id);
const capture = await paypal.captureOrder(paypalOrderId);
const captureId = capture.purchase_units?.[0]?.payments?.captures?.[0]?.id ?? paypalOrderId;
await repo.capturePaypalPayment(payment.id, captureId, payment.reservationId, payment.amount);
return { success: true };
}
async function handleContact(slug, body) {
const company = await repo.findCompanyBySlug(slug);
return {
success: true,
deliveredTo: company.brand?.publicEmail ?? company.email,
preview: body,
};
}
//# sourceMappingURL=site.service.js.map
File diff suppressed because one or more lines are too long