archetecture security fix
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
export declare function findMany(companyId: string, where: Record<string, unknown>, skip: number, take: number): Promise<any>;
|
||||
export declare function findById(id: string, companyId: string): Promise<any>;
|
||||
export declare function updateById(id: string, data: Record<string, unknown>): Promise<any>;
|
||||
export declare function getStats(companyId: string): Promise<{
|
||||
total: any;
|
||||
averageOverall: number;
|
||||
averageVehicle: number;
|
||||
averageService: number;
|
||||
byRating: Record<number, number>;
|
||||
}>;
|
||||
export declare function sendReminder(reservationId: string, sentAt: Date, field: 'reviewReminderSentAt' | 'reviewFinalReminderSentAt'): Promise<any>;
|
||||
//# sourceMappingURL=review.repo.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/reviews/review.repo.ts"],"names":[],"mappings":"AAkCA,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,gBAab;AAED,wBAAsB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAK3D;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBAMzE;AAED,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM;;;;;;GA6B/C;AAED,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,sBAAsB,GAAG,2BAA2B,gBAM5D"}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.findMany = findMany;
|
||||
exports.findById = findById;
|
||||
exports.updateById = updateById;
|
||||
exports.getStats = getStats;
|
||||
exports.sendReminder = sendReminder;
|
||||
const prisma_1 = require("../../lib/prisma");
|
||||
const REVIEW_INCLUDE = {
|
||||
reservation: {
|
||||
select: {
|
||||
id: true,
|
||||
startDate: true,
|
||||
endDate: true,
|
||||
reviewToken: true,
|
||||
reviewRequestSentAt: true,
|
||||
reviewReminderSentAt: true,
|
||||
reviewFinalReminderSentAt: true,
|
||||
reviewPaused: true,
|
||||
customer: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
reviewOptOut: true,
|
||||
},
|
||||
},
|
||||
vehicle: {
|
||||
select: {
|
||||
id: true,
|
||||
make: true,
|
||||
model: true,
|
||||
year: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
async function findMany(companyId, where, skip, take) {
|
||||
const baseWhere = { companyId, ...where };
|
||||
return Promise.all([
|
||||
prisma_1.prisma.review.findMany({
|
||||
where: baseWhere,
|
||||
include: REVIEW_INCLUDE,
|
||||
skip,
|
||||
take,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma_1.prisma.review.count({ where: baseWhere }),
|
||||
]);
|
||||
}
|
||||
async function findById(id, companyId) {
|
||||
return prisma_1.prisma.review.findFirst({
|
||||
where: { id, companyId },
|
||||
include: REVIEW_INCLUDE,
|
||||
});
|
||||
}
|
||||
async function updateById(id, data) {
|
||||
return prisma_1.prisma.review.update({
|
||||
where: { id },
|
||||
data,
|
||||
include: REVIEW_INCLUDE,
|
||||
});
|
||||
}
|
||||
async function getStats(companyId) {
|
||||
const reviews = await prisma_1.prisma.review.findMany({
|
||||
where: { companyId },
|
||||
select: { overallRating: true, vehicleRating: true, serviceRating: true },
|
||||
});
|
||||
const total = reviews.length;
|
||||
const byRating = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
|
||||
let sumOverall = 0;
|
||||
let sumVehicle = 0;
|
||||
let countVehicle = 0;
|
||||
let sumService = 0;
|
||||
let countService = 0;
|
||||
for (const r of reviews) {
|
||||
sumOverall += r.overallRating;
|
||||
byRating[r.overallRating] = (byRating[r.overallRating] ?? 0) + 1;
|
||||
if (r.vehicleRating != null) {
|
||||
sumVehicle += r.vehicleRating;
|
||||
countVehicle++;
|
||||
}
|
||||
if (r.serviceRating != null) {
|
||||
sumService += r.serviceRating;
|
||||
countService++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
total,
|
||||
averageOverall: total > 0 ? Math.round((sumOverall / total) * 10) / 10 : 0,
|
||||
averageVehicle: countVehicle > 0 ? Math.round((sumVehicle / countVehicle) * 10) / 10 : 0,
|
||||
averageService: countService > 0 ? Math.round((sumService / countService) * 10) / 10 : 0,
|
||||
byRating,
|
||||
};
|
||||
}
|
||||
async function sendReminder(reservationId, sentAt, field) {
|
||||
return prisma_1.prisma.reservation.update({
|
||||
where: { id: reservationId },
|
||||
data: { [field]: sentAt },
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=review.repo.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.repo.js","sourceRoot":"","sources":["../../../src/modules/reviews/review.repo.ts"],"names":[],"mappings":"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"}
|
||||
@@ -0,0 +1,3 @@
|
||||
declare const router: import("express-serve-static-core").Router;
|
||||
export default router;
|
||||
//# sourceMappingURL=review.routes.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/reviews/review.routes.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,MAAM,4CAAW,CAAA;AA4CvB,eAAe,MAAM,CAAA"}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
"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 express_1 = require("express");
|
||||
const requireCompanyAuth_1 = require("../../middleware/requireCompanyAuth");
|
||||
const requireTenant_1 = require("../../middleware/requireTenant");
|
||||
const requireSubscription_1 = require("../../middleware/requireSubscription");
|
||||
const validate_1 = require("../../http/validate");
|
||||
const respond_1 = require("../../http/respond");
|
||||
const service = __importStar(require("./review.service"));
|
||||
const review_schemas_1 = require("./review.schemas");
|
||||
const router = (0, express_1.Router)();
|
||||
router.use(requireCompanyAuth_1.requireCompanyAuth, requireTenant_1.requireTenant, requireSubscription_1.requireSubscription);
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const query = (0, validate_1.parseQuery)(review_schemas_1.listQuerySchema, req);
|
||||
const result = await service.listReviews(req.companyId, query);
|
||||
(0, respond_1.ok)(res, result);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/stats', async (req, res, next) => {
|
||||
try {
|
||||
const stats = await service.getStats(req.companyId);
|
||||
(0, respond_1.ok)(res, stats);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(review_schemas_1.idParamSchema, req);
|
||||
const review = await service.getReview(id, req.companyId);
|
||||
(0, respond_1.ok)(res, review);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.patch('/:id/reply', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(review_schemas_1.idParamSchema, req);
|
||||
const { companyReply } = (0, validate_1.parseBody)(review_schemas_1.replySchema, req);
|
||||
const updated = await service.replyToReview(id, req.companyId, companyReply);
|
||||
(0, respond_1.ok)(res, updated);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/:id/remind', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(review_schemas_1.idParamSchema, req);
|
||||
const result = await service.sendReviewReminder(id, req.companyId);
|
||||
(0, respond_1.ok)(res, result);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=review.routes.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.routes.js","sourceRoot":"","sources":["../../../src/modules/reviews/review.routes.ts"],"names":[],"mappings":"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"}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { z } from 'zod';
|
||||
export declare const replySchema: z.ZodObject<{
|
||||
companyReply: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
companyReply: string;
|
||||
}, {
|
||||
companyReply: string;
|
||||
}>;
|
||||
export declare const listQuerySchema: z.ZodObject<{
|
||||
rating: z.ZodOptional<z.ZodNumber>;
|
||||
page: z.ZodDefault<z.ZodNumber>;
|
||||
pageSize: z.ZodDefault<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
rating?: number | undefined;
|
||||
}, {
|
||||
page?: number | undefined;
|
||||
pageSize?: number | undefined;
|
||||
rating?: number | undefined;
|
||||
}>;
|
||||
export declare const idParamSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
id: string;
|
||||
}, {
|
||||
id: string;
|
||||
}>;
|
||||
//# sourceMappingURL=review.schemas.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/reviews/review.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,WAAW;;;;;;EAEtB,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;EAI1B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;EAExB,CAAA"}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.idParamSchema = exports.listQuerySchema = exports.replySchema = void 0;
|
||||
const zod_1 = require("zod");
|
||||
exports.replySchema = zod_1.z.object({
|
||||
companyReply: zod_1.z.string().min(1),
|
||||
});
|
||||
exports.listQuerySchema = zod_1.z.object({
|
||||
rating: zod_1.z.coerce.number().int().min(1).max(5).optional(),
|
||||
page: zod_1.z.coerce.number().int().min(1).default(1),
|
||||
pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
|
||||
});
|
||||
exports.idParamSchema = zod_1.z.object({
|
||||
id: zod_1.z.string(),
|
||||
});
|
||||
//# sourceMappingURL=review.schemas.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.schemas.js","sourceRoot":"","sources":["../../../src/modules/reviews/review.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,WAAW,GAAG,OAAC,CAAC,MAAM,CAAC;IAClC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAChC,CAAC,CAAA;AAEW,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,MAAM,EAAI,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1D,IAAI,EAAM,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC9D,CAAC,CAAA;AAEW,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE;CACf,CAAC,CAAA"}
|
||||
@@ -0,0 +1,27 @@
|
||||
export declare function listReviews(companyId: string, query: {
|
||||
rating?: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}): Promise<{
|
||||
data: any;
|
||||
meta: {
|
||||
total: any;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
};
|
||||
}>;
|
||||
export declare function getReview(id: string, companyId: string): Promise<any>;
|
||||
export declare function replyToReview(id: string, companyId: string, companyReply: string): Promise<any>;
|
||||
export declare function getStats(companyId: string): Promise<{
|
||||
total: any;
|
||||
averageOverall: number;
|
||||
averageVehicle: number;
|
||||
averageService: number;
|
||||
byRating: Record<number, number>;
|
||||
}>;
|
||||
export declare function sendReviewReminder(id: string, companyId: string): Promise<{
|
||||
success: boolean;
|
||||
field: "reviewReminderSentAt" | "reviewFinalReminderSentAt";
|
||||
}>;
|
||||
//# sourceMappingURL=review.service.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.service.d.ts","sourceRoot":"","sources":["../../../src/modules/reviews/review.service.ts"],"names":[],"mappings":"AAMA,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;GAW3D;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAI5D;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,gBAQtF;AAED,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM;;;;;;GAE/C;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;GAkCrE"}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
"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.listReviews = listReviews;
|
||||
exports.getReview = getReview;
|
||||
exports.replyToReview = replyToReview;
|
||||
exports.getStats = getStats;
|
||||
exports.sendReviewReminder = sendReviewReminder;
|
||||
const errors_1 = require("../../http/errors");
|
||||
const notificationService_1 = require("../../services/notificationService");
|
||||
const emailTranslations_1 = require("../../lib/emailTranslations");
|
||||
const repo = __importStar(require("./review.repo"));
|
||||
const reservationRepo = __importStar(require("../reservations/reservation.repo"));
|
||||
async function listReviews(companyId, query) {
|
||||
const { rating, page, pageSize } = query;
|
||||
const where = {};
|
||||
if (rating != null)
|
||||
where.overallRating = rating;
|
||||
const [reviews, total] = await repo.findMany(companyId, where, (page - 1) * pageSize, pageSize);
|
||||
return {
|
||||
data: reviews,
|
||||
meta: { total, page, pageSize, totalPages: Math.ceil(total / pageSize) },
|
||||
};
|
||||
}
|
||||
async function getReview(id, companyId) {
|
||||
const review = await repo.findById(id, companyId);
|
||||
if (!review)
|
||||
throw new errors_1.NotFoundError('Review not found');
|
||||
return review;
|
||||
}
|
||||
async function replyToReview(id, companyId, companyReply) {
|
||||
const review = await repo.findById(id, companyId);
|
||||
if (!review)
|
||||
throw new errors_1.NotFoundError('Review not found');
|
||||
return repo.updateById(id, {
|
||||
companyReply,
|
||||
companyRepliedAt: new Date(),
|
||||
});
|
||||
}
|
||||
async function getStats(companyId) {
|
||||
return repo.getStats(companyId);
|
||||
}
|
||||
async function sendReviewReminder(id, companyId) {
|
||||
const review = await repo.findById(id, companyId);
|
||||
if (!review)
|
||||
throw new errors_1.NotFoundError('Review not found');
|
||||
const reservation = review.reservation;
|
||||
if (!reservation)
|
||||
throw new errors_1.AppError('Review has no linked reservation', 400, 'no_reservation');
|
||||
if (reservation.reviewPaused)
|
||||
throw new errors_1.AppError('Review requests are paused for this reservation', 400, 'review_paused');
|
||||
const customer = reservation.customer;
|
||||
if (!customer || customer.reviewOptOut)
|
||||
throw new errors_1.AppError('Customer has opted out of review requests', 400, 'opted_out');
|
||||
if (!customer.email)
|
||||
throw new errors_1.AppError('Customer has no email address', 400, 'no_email');
|
||||
if (!reservation.reviewToken)
|
||||
throw new errors_1.AppError('No review token available for this reservation', 400, 'no_token');
|
||||
// Determine which reminder field to update
|
||||
const field = reservation.reviewReminderSentAt ? 'reviewFinalReminderSentAt' : 'reviewReminderSentAt';
|
||||
const company = await reservationRepo.findCompanyWithBrand(companyId);
|
||||
const lang = (company?.brand?.defaultLocale ?? 'fr');
|
||||
const companyName = company?.brand?.displayName ?? company?.name ?? 'the rental company';
|
||||
const vehicle = reservation.vehicle;
|
||||
const vehicleLabel = vehicle ? `${vehicle.year} ${vehicle.make} ${vehicle.model}` : 'your rental vehicle';
|
||||
const reviewUrl = `${process.env.MARKETPLACE_URL ?? 'http://localhost:3000'}/review?token=${reservation.reviewToken}`;
|
||||
await (0, notificationService_1.sendTransactionalEmail)({
|
||||
to: customer.email,
|
||||
subject: emailTranslations_1.reviewRequestEmail.subject(vehicleLabel, lang),
|
||||
html: emailTranslations_1.reviewRequestEmail.html({ firstName: customer.firstName, companyName, vehicleLabel, reviewUrl }, lang),
|
||||
text: emailTranslations_1.reviewRequestEmail.text({ firstName: customer.firstName, companyName, vehicleLabel, reviewUrl }, lang),
|
||||
});
|
||||
await repo.sendReminder(reservation.id, new Date(), field);
|
||||
return { success: true, field };
|
||||
}
|
||||
//# sourceMappingURL=review.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"review.service.js","sourceRoot":"","sources":["../../../src/modules/reviews/review.service.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user