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
+69
View File
@@ -0,0 +1,69 @@
export declare function listBillingAccounts(query: {
q?: string;
status?: string;
plan?: string;
page: number;
pageSize: number;
}): Promise<{
data: any[];
total: any;
stats: {
billingAccountCount: any;
openInvoiceCount: any;
pastDueInvoiceCount: any;
paidInvoiceCount: any;
accountsReceivableTotal: any;
recognizedRevenueTotal: any;
};
}>;
export declare function getBillingAccountDetail(companyId: string): Promise<any>;
export declare function updateBillingAccount(billingAccountId: string, data: {
legalName?: string;
billingEmail?: string;
billingAddress?: any;
taxId?: string | null;
taxExempt?: boolean;
invoiceTerms?: string;
netTermsDays?: number;
}, adminId: string, ip?: string): Promise<any>;
export declare function setDunningPaused(billingAccountId: string, paused: boolean, adminId: string, ip?: string): Promise<any>;
export declare function createDraftInvoice(billingAccountId: string, data: {
subscriptionId?: string | null;
invoiceType: string;
currency?: string;
dueAt?: string | null;
isSubscriptionBlocking?: boolean;
adminReason?: string | null;
lineItems: Array<{
type: string;
description: string;
quantity: number;
unitAmount: number;
periodStart?: string | null;
periodEnd?: string | null;
}>;
}, adminId: string, ip?: string): Promise<any>;
export declare function finalizeInvoice(invoiceId: string, adminId: string, ip?: string): Promise<any>;
export declare function payInvoice(invoiceId: string, data: {
amount?: number;
paymentMethodId?: string | null;
providerPaymentId?: string | null;
}, adminId: string, ip?: string): Promise<any>;
export declare function retryInvoicePayment(invoiceId: string, data: {
paymentMethodId?: string | null;
}, adminId: string, ip?: string): Promise<any>;
export declare function voidInvoice(invoiceId: string, reason: string, adminId: string, ip?: string): Promise<any>;
export declare function markInvoiceUncollectible(invoiceId: string, reason: string, adminId: string, ip?: string): Promise<any>;
export declare function issueCreditNote(invoiceId: string, data: {
amount: number;
reason: string;
}, adminId: string, ip?: string): Promise<any>;
export declare function issueRefund(invoiceId: string, data: {
amount: number;
reason: string;
}, adminId: string, ip?: string): Promise<any>;
export declare function getInvoicePdf(invoiceId: string): Promise<{
pdfBuffer: Buffer<ArrayBufferLike>;
invoiceNumber: any;
}>;
//# sourceMappingURL=admin.billing.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"admin.billing.service.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin.billing.service.ts"],"names":[],"mappings":"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"}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+13
View File
@@ -0,0 +1,13 @@
export declare function presentAdminUser<T extends Record<string, any>>(admin: T): Omit<T, "passwordHash" | "totpSecret">;
export declare function presentAdminSession<T extends Record<string, any>>(admin: T, token: string): {
token: string;
admin: Omit<T, "passwordHash" | "totpSecret">;
};
export declare function presentPaginated<T>(data: T[], total: number, page: number, pageSize: number, extra?: Record<string, unknown>): {
data: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
//# sourceMappingURL=admin.presenter.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.presenter.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin.presenter.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,0CAGvE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM;;;EAKzF;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,IAAI,EAAE,CAAC,EAAE,EACT,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;EAUpC"}
+26
View File
@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.presentAdminUser = presentAdminUser;
exports.presentAdminSession = presentAdminSession;
exports.presentPaginated = presentPaginated;
function presentAdminUser(admin) {
const { passwordHash, totpSecret, ...safe } = admin;
return safe;
}
function presentAdminSession(admin, token) {
return {
token,
admin: presentAdminUser(admin),
};
}
function presentPaginated(data, total, page, pageSize, extra = {}) {
return {
data,
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
...extra,
};
}
//# sourceMappingURL=admin.presenter.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.presenter.js","sourceRoot":"","sources":["../../../src/modules/admin/admin.presenter.ts"],"names":[],"mappings":";;AAAA,4CAGC;AAED,kDAKC;AAED,4CAeC;AA3BD,SAAgB,gBAAgB,CAAgC,KAAQ;IACtE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IACnD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAgB,mBAAmB,CAAgC,KAAQ,EAAE,KAAa;IACxF,OAAO;QACL,KAAK;QACL,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC;KAC/B,CAAA;AACH,CAAC;AAED,SAAgB,gBAAgB,CAC9B,IAAS,EACT,KAAa,EACb,IAAY,EACZ,QAAgB,EAChB,QAAiC,EAAE;IAEnC,OAAO;QACL,IAAI;QACJ,KAAK;QACL,IAAI;QACJ,QAAQ;QACR,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACvC,GAAG,KAAK;KACT,CAAA;AACH,CAAC"}
+161
View File
@@ -0,0 +1,161 @@
export declare function findAdminByEmail(email: string): any;
export declare function findAdminByIdOrThrow(id: string): any;
export declare function updateAdminLastLogin(id: string): any;
export declare function updateAdminTotpSecret(id: string, secret: string): any;
export declare function enableAdminTotp(id: string): any;
export declare function replaceAdminRecoveryCodes(adminUserId: string, codeHashes: string[]): Promise<any>;
export declare function listUnusedAdminRecoveryCodes(adminUserId: string): any;
export declare function markAdminRecoveryCodeUsed(id: string): any;
export declare function setAdminPasswordReset(id: string, token: string, expiresAt: Date): any;
export declare function findAdminByResetToken(token: string): any;
export declare function updateAdminPassword(id: string, passwordHash: string): any;
export declare function createAuditLog(data: Record<string, unknown>): any;
export declare function listCompaniesPage(query: {
q?: string;
status?: string;
plan?: string;
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
export declare function getCompanyDetail(id: string): any;
export declare function getCompanyUpdateSnapshot(id: string): any;
export declare function applyCompanyUpdate(id: string, body: any, current: {
name: string;
slug: string;
address?: unknown;
brand?: {
paymentMethodsEnabled?: any[] | null;
} | null;
}): Promise<any>;
export declare function updateCompanyStatus(id: string, status: string): any;
export declare function deleteCompany(id: string): any;
export declare function getCompanyForImpersonation(id: string): any;
export declare function listRentersPage(query: {
q?: string;
blocked?: string;
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
export declare function updateRenterActive(id: string, isActive: boolean): any;
export declare function getPlatformMetricCounts(): Promise<{
totalCompanies: any;
activeCompanies: any;
trialingCompanies: any;
suspendedCompanies: any;
totalRenters: any;
totalReservations: any;
}>;
export declare function listAuditLogsPage(query: {
adminId?: string;
action?: string;
companyId?: string;
entityId?: string;
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
export declare function listAdmins(): any;
export declare function createAdmin(data: {
email: string;
firstName: string;
lastName: string;
role: string;
passwordHash: string;
permissions?: any[];
}): any;
export declare function updateAdminRole(id: string, role: string): any;
export declare function updateAdmin(id: string, data: {
email?: string;
firstName?: string;
lastName?: string;
role?: string;
passwordHash?: string;
isActive?: boolean;
}): any;
export declare function replaceAdminPermissions(id: string, permissions: any[]): Promise<any>;
export declare function listBillingPage(query: {
status?: string;
plan?: string;
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
export declare function listActiveSubscriptionsForMrr(): any;
export declare function getBillingStatusCounts(): Promise<{
activeCount: any;
trialingCount: any;
pastDueCount: any;
cancelledCount: any;
}>;
export declare function listCompanyInvoicesPage(companyId: string, query: {
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
export declare function getInvoicePdfRecord(invoiceId: string): any;
export declare function listPricingConfigs(): any;
export declare function upsertPricingConfig(plan: string, billingPeriod: string, amount: number, updatedBy?: string): any;
export declare function listPlanFeatures(): any;
export declare function createPlanFeature(data: {
plan: 'STARTER' | 'GROWTH' | 'PRO';
label: string;
sortOrder?: number;
}): any;
export declare function updatePlanFeature(id: string, data: Partial<{
plan: 'STARTER' | 'GROWTH' | 'PRO';
label: string;
sortOrder: number;
}>): any;
export declare function deletePlanFeature(id: string): any;
export declare function listPromotions(): any;
export declare function createPromotion(data: {
code: string;
name: string;
description?: string | null;
discountType: string;
discountValue: number;
plans: string[];
periods: string[];
maxUses?: number | null;
validFrom: string;
validUntil?: string | null;
isActive: boolean;
createdBy?: string | null;
}): any;
export declare function updatePromotion(id: string, data: Partial<{
code: string;
name: string;
description: string | null;
discountType: string;
discountValue: number;
plans: string[];
periods: string[];
maxUses: number | null;
validFrom: string;
validUntil: string | null;
isActive: boolean;
}>): any;
export declare function deletePromotion(id: string): any;
export declare function listNotificationsPage(query: {
channel?: string;
status?: string;
companyId?: string;
page: number;
pageSize: number;
}): Promise<{
data: any;
total: any;
}>;
//# sourceMappingURL=admin.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin.repo.ts"],"names":[],"mappings":"AAmCA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,OAS7C;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,OAE9C;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,OAE9C;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAE/D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,OAEzC;AAGD,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,gBAOxF;AAED,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,OAM/D;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,OAEnD;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,OAK/E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,OAOlD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,OASnE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAE3D;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;GAwB5H;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,OAE1C;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,OAKlD;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,GAAG,EACT,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACxD,gBA0EF;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAE7D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,OAEvC;AAED,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,MAAM,OAKpD;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;GA+B5G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,OAE/D;AAED,wBAAsB,uBAAuB;;;;;;;GAyB5C;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;GAmB1J;AAED,wBAAgB,UAAU,QAIzB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;CACpB,OAYA;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAEvD;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,OAcF;AAED,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,gBAa3E;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;GAiB9G;AAED,wBAAgB,6BAA6B,QAK5C;AAED,wBAAsB,sBAAsB;;;;;GAS3C;AAED,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;GAYzG;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,OAgBpD;AAED,wBAAgB,kBAAkB,QAEjC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,OAM1G;AAED,wBAAgB,gBAAgB,QAI/B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,OAQhH;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,OAKpI;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,OAE3C;AAID,wBAAgB,cAAc,QAE7B;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvD,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAClC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7C,OAQA;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC;IACxD,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACtD,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAClC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACpE,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAC,OAUD;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,OAEzC;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB;;;GAiBA"}
+523
View File
@@ -0,0 +1,523 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findAdminByEmail = findAdminByEmail;
exports.findAdminByIdOrThrow = findAdminByIdOrThrow;
exports.updateAdminLastLogin = updateAdminLastLogin;
exports.updateAdminTotpSecret = updateAdminTotpSecret;
exports.enableAdminTotp = enableAdminTotp;
exports.replaceAdminRecoveryCodes = replaceAdminRecoveryCodes;
exports.listUnusedAdminRecoveryCodes = listUnusedAdminRecoveryCodes;
exports.markAdminRecoveryCodeUsed = markAdminRecoveryCodeUsed;
exports.setAdminPasswordReset = setAdminPasswordReset;
exports.findAdminByResetToken = findAdminByResetToken;
exports.updateAdminPassword = updateAdminPassword;
exports.createAuditLog = createAuditLog;
exports.listCompaniesPage = listCompaniesPage;
exports.getCompanyDetail = getCompanyDetail;
exports.getCompanyUpdateSnapshot = getCompanyUpdateSnapshot;
exports.applyCompanyUpdate = applyCompanyUpdate;
exports.updateCompanyStatus = updateCompanyStatus;
exports.deleteCompany = deleteCompany;
exports.getCompanyForImpersonation = getCompanyForImpersonation;
exports.listRentersPage = listRentersPage;
exports.updateRenterActive = updateRenterActive;
exports.getPlatformMetricCounts = getPlatformMetricCounts;
exports.listAuditLogsPage = listAuditLogsPage;
exports.listAdmins = listAdmins;
exports.createAdmin = createAdmin;
exports.updateAdminRole = updateAdminRole;
exports.updateAdmin = updateAdmin;
exports.replaceAdminPermissions = replaceAdminPermissions;
exports.listBillingPage = listBillingPage;
exports.listActiveSubscriptionsForMrr = listActiveSubscriptionsForMrr;
exports.getBillingStatusCounts = getBillingStatusCounts;
exports.listCompanyInvoicesPage = listCompanyInvoicesPage;
exports.getInvoicePdfRecord = getInvoicePdfRecord;
exports.listPricingConfigs = listPricingConfigs;
exports.upsertPricingConfig = upsertPricingConfig;
exports.listPlanFeatures = listPlanFeatures;
exports.createPlanFeature = createPlanFeature;
exports.updatePlanFeature = updatePlanFeature;
exports.deletePlanFeature = deletePlanFeature;
exports.listPromotions = listPromotions;
exports.createPromotion = createPromotion;
exports.updatePromotion = updatePromotion;
exports.deletePromotion = deletePromotion;
exports.listNotificationsPage = listNotificationsPage;
const prisma_1 = require("../../lib/prisma");
const companyListInclude = {
brand: { select: { displayName: true, logoUrl: true, subdomain: true } },
contractSettings: { select: { legalName: true } },
subscription: { select: { plan: true, status: true } },
_count: { select: { employees: true, vehicles: true } },
};
const companyDetailInclude = {
brand: true,
contractSettings: true,
accountingSettings: true,
subscription: { include: { invoices: { orderBy: { createdAt: 'desc' }, take: 10 } } },
employees: true,
_count: { select: { employees: true, vehicles: true, customers: true, reservations: true } },
};
const billingInclude = {
company: { select: { id: true, name: true, email: true, slug: true, status: true } },
invoices: {
select: { id: true, amount: true, currency: true, status: true, paidAt: true, createdAt: true },
orderBy: { createdAt: 'desc' },
take: 5,
},
_count: { select: { invoices: true } },
};
function parseOptionalDate(value) {
if (!value)
return null;
return new Date(value);
}
function findAdminByEmail(email) {
return prisma_1.prisma.adminUser.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
}
function findAdminByIdOrThrow(id) {
return prisma_1.prisma.adminUser.findUniqueOrThrow({ where: { id } });
}
function updateAdminLastLogin(id) {
return prisma_1.prisma.adminUser.update({ where: { id }, data: { lastLoginAt: new Date() } });
}
function updateAdminTotpSecret(id, secret) {
return prisma_1.prisma.adminUser.update({ where: { id }, data: { totpSecret: secret } });
}
function enableAdminTotp(id) {
return prisma_1.prisma.adminUser.update({ where: { id }, data: { totpEnabled: true } });
}
async function replaceAdminRecoveryCodes(adminUserId, codeHashes) {
return prisma_1.prisma.$transaction(async (tx) => {
await tx.adminRecoveryCode.deleteMany({ where: { adminUserId } });
await tx.adminRecoveryCode.createMany({
data: codeHashes.map((codeHash) => ({ adminUserId, codeHash })),
});
});
}
function listUnusedAdminRecoveryCodes(adminUserId) {
return prisma_1.prisma.adminRecoveryCode.findMany({
where: { adminUserId, usedAt: null },
select: { id: true, codeHash: true },
orderBy: { createdAt: 'asc' },
});
}
function markAdminRecoveryCodeUsed(id) {
return prisma_1.prisma.adminRecoveryCode.update({ where: { id }, data: { usedAt: new Date() } });
}
function setAdminPasswordReset(id, token, expiresAt) {
return prisma_1.prisma.adminUser.update({
where: { id },
data: { passwordResetToken: token, passwordResetExpiresAt: expiresAt },
});
}
function findAdminByResetToken(token) {
return prisma_1.prisma.adminUser.findFirst({
where: {
passwordResetToken: token,
passwordResetExpiresAt: { gt: new Date() },
},
});
}
function updateAdminPassword(id, passwordHash) {
return prisma_1.prisma.adminUser.update({
where: { id },
data: {
passwordHash,
passwordResetToken: null,
passwordResetExpiresAt: null,
},
});
}
function createAuditLog(data) {
return prisma_1.prisma.auditLog.create({ data: data });
}
async function listCompaniesPage(query) {
const where = {};
if (query.status)
where.status = query.status;
if (query.q) {
where.OR = [
{ name: { contains: query.q, mode: 'insensitive' } },
{ email: { contains: query.q, mode: 'insensitive' } },
{ slug: { contains: query.q, mode: 'insensitive' } },
];
}
if (query.plan)
where.subscription = { plan: query.plan };
const [data, total] = await Promise.all([
prisma_1.prisma.company.findMany({
where,
include: companyListInclude,
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
orderBy: { createdAt: 'desc' },
}),
prisma_1.prisma.company.count({ where }),
]);
return { data, total };
}
function getCompanyDetail(id) {
return prisma_1.prisma.company.findUniqueOrThrow({ where: { id }, include: companyDetailInclude });
}
function getCompanyUpdateSnapshot(id) {
return prisma_1.prisma.company.findUniqueOrThrow({
where: { id },
include: { brand: true, contractSettings: true, accountingSettings: true, subscription: true },
});
}
async function applyCompanyUpdate(id, body, current) {
return prisma_1.prisma.$transaction(async (tx) => {
if (body.company) {
const companyData = { ...body.company };
if (companyData.address && typeof companyData.address === 'object' && !Array.isArray(companyData.address)) {
const baseAddress = current.address && typeof current.address === 'object' && !Array.isArray(current.address)
? current.address
: {};
companyData.address = { ...baseAddress, ...companyData.address };
}
await tx.company.update({ where: { id }, data: companyData });
}
if (body.subscription) {
const sub = body.subscription;
await tx.subscription.upsert({
where: { companyId: id },
update: {
...sub,
trialStartAt: parseOptionalDate(sub.trialStartAt),
trialEndAt: parseOptionalDate(sub.trialEndAt),
currentPeriodStart: parseOptionalDate(sub.currentPeriodStart),
currentPeriodEnd: parseOptionalDate(sub.currentPeriodEnd),
cancelledAt: parseOptionalDate(sub.cancelledAt),
},
create: {
companyId: id,
plan: sub.plan ?? 'STARTER',
billingPeriod: sub.billingPeriod ?? 'MONTHLY',
status: sub.status ?? 'TRIALING',
currency: sub.currency ?? 'MAD',
trialStartAt: parseOptionalDate(sub.trialStartAt),
trialEndAt: parseOptionalDate(sub.trialEndAt),
currentPeriodStart: parseOptionalDate(sub.currentPeriodStart),
currentPeriodEnd: parseOptionalDate(sub.currentPeriodEnd),
cancelledAt: parseOptionalDate(sub.cancelledAt),
cancelAtPeriodEnd: sub.cancelAtPeriodEnd ?? false,
},
});
}
if (body.brand) {
await tx.brandSettings.upsert({
where: { companyId: id },
update: body.brand,
create: {
companyId: id,
displayName: body.brand.displayName ?? current.name,
subdomain: body.brand.subdomain ?? current.slug,
paymentMethodsEnabled: current.brand?.paymentMethodsEnabled ?? [],
...body.brand,
},
});
}
if (body.contractSettings) {
await tx.contractSettings.upsert({
where: { companyId: id },
update: body.contractSettings,
create: { companyId: id, ...body.contractSettings },
});
}
if (body.accountingSettings) {
await tx.accountingSettings.upsert({
where: { companyId: id },
update: body.accountingSettings,
create: { companyId: id, ...body.accountingSettings },
});
}
return tx.company.findUniqueOrThrow({ where: { id }, include: companyDetailInclude });
});
}
function updateCompanyStatus(id, status) {
return prisma_1.prisma.company.update({ where: { id }, data: { status: status } });
}
function deleteCompany(id) {
return prisma_1.prisma.company.delete({ where: { id } });
}
function getCompanyForImpersonation(id) {
return prisma_1.prisma.company.findUniqueOrThrow({
where: { id },
include: { employees: { where: { role: 'OWNER' } } },
});
}
async function listRentersPage(query) {
const where = {};
if (query.blocked !== undefined)
where.isActive = query.blocked === 'false';
if (query.q) {
where.OR = [
{ firstName: { contains: query.q, mode: 'insensitive' } },
{ email: { contains: query.q, mode: 'insensitive' } },
];
}
const [data, total] = await Promise.all([
prisma_1.prisma.renter.findMany({
where,
select: {
id: true,
firstName: true,
lastName: true,
email: true,
phone: true,
isActive: true,
createdAt: true,
_count: { select: { reservations: true } },
},
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
orderBy: { createdAt: 'desc' },
}),
prisma_1.prisma.renter.count({ where }),
]);
return { data, total };
}
function updateRenterActive(id, isActive) {
return prisma_1.prisma.renter.update({ where: { id }, data: { isActive } });
}
async function getPlatformMetricCounts() {
const [totalCompanies, activeCompanies, trialingCompanies, suspendedCompanies, totalRenters, totalReservations,] = await Promise.all([
prisma_1.prisma.company.count(),
prisma_1.prisma.company.count({ where: { status: 'ACTIVE' } }),
prisma_1.prisma.company.count({ where: { status: 'TRIALING' } }),
prisma_1.prisma.company.count({ where: { status: 'SUSPENDED' } }),
prisma_1.prisma.renter.count(),
prisma_1.prisma.reservation.count(),
]);
return {
totalCompanies,
activeCompanies,
trialingCompanies,
suspendedCompanies,
totalRenters,
totalReservations,
};
}
async function listAuditLogsPage(query) {
const where = {};
if (query.adminId)
where.adminUserId = query.adminId;
if (query.action)
where.action = { contains: query.action };
if (query.companyId)
where.companyId = query.companyId;
if (query.entityId)
where.resourceId = query.entityId;
const [data, total] = await Promise.all([
prisma_1.prisma.auditLog.findMany({
where,
include: { adminUser: { select: { firstName: true, lastName: true, email: true } } },
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
orderBy: { createdAt: 'desc' },
}),
prisma_1.prisma.auditLog.count({ where }),
]);
return { data, total };
}
function listAdmins() {
return prisma_1.prisma.adminUser.findMany({
include: { permissions: true },
});
}
function createAdmin(data) {
return prisma_1.prisma.adminUser.create({
data: {
email: data.email,
firstName: data.firstName,
lastName: data.lastName,
role: data.role,
passwordHash: data.passwordHash,
permissions: data.permissions ? { create: data.permissions } : undefined,
},
include: { permissions: true },
});
}
function updateAdminRole(id, role) {
return prisma_1.prisma.adminUser.update({ where: { id }, data: { role: role } });
}
function updateAdmin(id, data) {
return prisma_1.prisma.adminUser.update({
where: { id },
data: {
...(data.email !== undefined ? { email: data.email } : {}),
...(data.firstName !== undefined ? { firstName: data.firstName } : {}),
...(data.lastName !== undefined ? { lastName: data.lastName } : {}),
...(data.role !== undefined ? { role: data.role } : {}),
...(data.passwordHash !== undefined ? { passwordHash: data.passwordHash } : {}),
...(data.isActive !== undefined ? { isActive: data.isActive } : {}),
},
include: { permissions: true },
});
}
async function replaceAdminPermissions(id, permissions) {
await prisma_1.prisma.adminUser.findUniqueOrThrow({ where: { id } });
await prisma_1.prisma.$transaction([
prisma_1.prisma.adminPermission.deleteMany({ where: { adminUserId: id } }),
prisma_1.prisma.adminPermission.createMany({
data: permissions.map((permission) => ({
adminUserId: id,
resource: permission.resource,
actions: permission.actions,
})),
}),
]);
return prisma_1.prisma.adminUser.findUniqueOrThrow({ where: { id }, include: { permissions: true } });
}
async function listBillingPage(query) {
const where = {};
if (query.status)
where.status = query.status;
if (query.plan)
where.plan = query.plan;
const [data, total] = await Promise.all([
prisma_1.prisma.subscription.findMany({
where,
include: billingInclude,
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
orderBy: { createdAt: 'desc' },
}),
prisma_1.prisma.subscription.count({ where }),
]);
return { data, total };
}
function listActiveSubscriptionsForMrr() {
return prisma_1.prisma.subscription.findMany({
where: { status: { in: ['ACTIVE', 'TRIALING'] } },
select: { plan: true, billingPeriod: true },
});
}
async function getBillingStatusCounts() {
const [activeCount, trialingCount, pastDueCount, cancelledCount] = await Promise.all([
prisma_1.prisma.subscription.count({ where: { status: 'ACTIVE' } }),
prisma_1.prisma.subscription.count({ where: { status: 'TRIALING' } }),
prisma_1.prisma.subscription.count({ where: { status: 'PAST_DUE' } }),
prisma_1.prisma.subscription.count({ where: { status: 'CANCELLED' } }),
]);
return { activeCount, trialingCount, pastDueCount, cancelledCount };
}
async function listCompanyInvoicesPage(companyId, query) {
const [data, total] = await Promise.all([
prisma_1.prisma.subscriptionInvoice.findMany({
where: { companyId },
orderBy: { createdAt: 'desc' },
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
}),
prisma_1.prisma.subscriptionInvoice.count({ where: { companyId } }),
]);
return { data, total };
}
function getInvoicePdfRecord(invoiceId) {
return prisma_1.prisma.subscriptionInvoice.findUniqueOrThrow({
where: { id: invoiceId },
include: {
company: { select: { name: true, email: true, phone: true, address: true } },
subscription: {
select: {
plan: true,
billingPeriod: true,
currency: true,
currentPeriodStart: true,
currentPeriodEnd: true,
},
},
},
});
}
function listPricingConfigs() {
return prisma_1.prisma.pricingConfig.findMany({ orderBy: [{ plan: 'asc' }, { billingPeriod: 'asc' }] });
}
function upsertPricingConfig(plan, billingPeriod, amount, updatedBy) {
return prisma_1.prisma.pricingConfig.upsert({
where: { plan_billingPeriod: { plan, billingPeriod } },
update: { amount, updatedBy },
create: { id: `prc_${plan.toLowerCase()}_${billingPeriod.toLowerCase()}`, plan, billingPeriod, amount, updatedBy },
});
}
function listPlanFeatures() {
return prisma_1.prisma.planFeature.findMany({
orderBy: [{ plan: 'asc' }, { sortOrder: 'asc' }, { createdAt: 'asc' }],
});
}
function createPlanFeature(data) {
return prisma_1.prisma.planFeature.create({
data: {
plan: data.plan,
label: data.label,
sortOrder: data.sortOrder ?? 0,
},
});
}
function updatePlanFeature(id, data) {
return prisma_1.prisma.planFeature.update({
where: { id },
data,
});
}
function deletePlanFeature(id) {
return prisma_1.prisma.planFeature.delete({ where: { id } });
}
// ─── Pricing promotions ────────────────────────────────────────────────────
function listPromotions() {
return prisma_1.prisma.pricingPromotion.findMany({ orderBy: { createdAt: 'desc' } });
}
function createPromotion(data) {
return prisma_1.prisma.pricingPromotion.create({
data: {
...data,
validFrom: new Date(data.validFrom),
validUntil: data.validUntil ? new Date(data.validUntil) : null,
},
});
}
function updatePromotion(id, data) {
const { validFrom, validUntil, ...rest } = data;
return prisma_1.prisma.pricingPromotion.update({
where: { id },
data: {
...rest,
...(validFrom ? { validFrom: new Date(validFrom) } : {}),
...(validUntil !== undefined ? { validUntil: validUntil ? new Date(validUntil) : null } : {}),
},
});
}
function deletePromotion(id) {
return prisma_1.prisma.pricingPromotion.delete({ where: { id } });
}
async function listNotificationsPage(query) {
const where = {};
if (query.channel)
where.channel = query.channel;
if (query.status)
where.status = query.status;
if (query.companyId)
where.companyId = query.companyId;
const [data, total] = await Promise.all([
prisma_1.prisma.notification.findMany({
where,
orderBy: { createdAt: 'desc' },
skip: (query.page - 1) * query.pageSize,
take: query.pageSize,
include: { company: { select: { name: true } } },
}),
prisma_1.prisma.notification.count({ where }),
]);
return { data, total };
}
//# sourceMappingURL=admin.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=admin.routes.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin.routes.ts"],"names":[],"mappings":"AAsCA,QAAA,MAAM,MAAM,4CAAW,CAAA;AAwhBvB,eAAe,MAAM,CAAA"}
+720
View File
@@ -0,0 +1,720 @@
"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 requireAdminAuth_1 = require("../../middleware/requireAdminAuth");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const sessionCookies_1 = require("../../security/sessionCookies");
const service = __importStar(require("./admin.service"));
const subService = __importStar(require("../subscriptions/subscription.service"));
const menuService = __importStar(require("../menu/menu.service"));
const admin_presenter_1 = require("./admin.presenter");
const admin_schemas_1 = require("./admin.schemas");
const zod_1 = require("zod");
const adminSubOverrideSchema = zod_1.z.object({
reason: zod_1.z.string().min(1).max(500),
});
const adminExtendTrialSchema = zod_1.z.object({
extraDays: zod_1.z.number().int().positive(),
reason: zod_1.z.string().min(1).max(500),
});
const adminImpersonationSchema = zod_1.z.object({
reason: zod_1.z.string().min(5).max(500),
durationMinutes: zod_1.z.number().int().min(1).max(30).default(15),
});
const subIdParamSchema = zod_1.z.object({ subscriptionId: zod_1.z.string() });
const router = (0, express_1.Router)();
// ─── Auth (public) ─────────────────────────────────────────────
router.post('/auth/login', async (req, res, next) => {
try {
const { email, password, totpCode, recoveryCode } = (0, validate_1.parseBody)(admin_schemas_1.loginSchema, req);
const result = await service.login(email, password, totpCode, recoveryCode);
if (!result)
return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 });
if ('totpRequired' in result)
return res.status(401).json({ error: 'totp_required', message: '2FA code required', statusCode: 401 });
if ('invalidTotp' in result)
return res.status(401).json({ error: 'invalid_totp', message: 'Invalid 2FA code', statusCode: 401 });
(0, sessionCookies_1.setSessionCookie)(res, 'admin', result.token, 8 * 60 * 60 * 1000);
(0, respond_1.ok)(res, result);
}
catch (err) {
next(err);
}
});
router.post('/auth/logout', (_req, res) => {
(0, sessionCookies_1.clearSessionCookie)(res, 'admin');
(0, respond_1.ok)(res, { success: true });
});
router.post('/auth/forgot-password', async (req, res, next) => {
try {
const { email } = (0, validate_1.parseBody)(admin_schemas_1.forgotPasswordSchema, req);
await service.forgotPassword(email);
(0, respond_1.ok)(res, { message: 'If that email is registered, a reset link has been sent.' });
}
catch (err) {
next(err);
}
});
router.post('/auth/reset-password', async (req, res, next) => {
try {
const { token, password } = (0, validate_1.parseBody)(admin_schemas_1.resetPasswordSchema, req);
const ok2 = await service.resetPassword(token, password);
if (!ok2)
return res.status(400).json({ error: 'invalid_token', message: 'Reset link is invalid or has expired.', statusCode: 400 });
(0, respond_1.ok)(res, { message: 'Password updated successfully. You can now sign in.' });
}
catch (err) {
next(err);
}
});
// ─── Auth (protected) ──────────────────────────────────────────
router.get('/auth/me', requireAdminAuth_1.requireAdminAuth, (req, res) => {
(0, respond_1.ok)(res, (0, admin_presenter_1.presentAdminUser)(req.admin));
});
router.post('/auth/2fa/setup', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.setupTotp(req.admin.id, req.admin.email));
}
catch (err) {
next(err);
}
});
router.post('/auth/2fa/verify', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
const { code } = (0, validate_1.parseBody)(admin_schemas_1.totpVerifySchema, req);
const result = await service.verifyTotp(req.admin.id, code);
if (!result)
return res.status(400).json({ error: 'invalid_code', message: 'Invalid 2FA code', statusCode: 400 });
(0, sessionCookies_1.setSessionCookie)(res, 'admin', result.token, 8 * 60 * 60 * 1000);
(0, respond_1.ok)(res, { success: true, admin: result.admin, recoveryCodes: result.recoveryCodes });
}
catch (err) {
next(err);
}
});
router.post('/auth/2fa/recovery-codes/regenerate', requireAdminAuth_1.requireAdminAuth, requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.regenerateRecoveryCodes(req.admin.id));
}
catch (err) {
next(err);
}
});
// ─── Companies ─────────────────────────────────────────────────
router.get('/companies', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listCompanies((0, validate_1.parseQuery)(admin_schemas_1.companiesQuerySchema, req)));
}
catch (err) {
next(err);
}
});
router.get('/companies/:id', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
(0, respond_1.ok)(res, await service.getCompany(id));
}
catch (err) {
next(err);
}
});
router.patch('/companies/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const body = (0, validate_1.parseBody)(admin_schemas_1.adminCompanyUpdateSchema, req);
(0, respond_1.ok)(res, await service.updateCompany(id, body, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.patch('/companies/:id/status', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { status, reason } = (0, validate_1.parseBody)(admin_schemas_1.companyStatusSchema, req);
(0, respond_1.ok)(res, await service.setCompanyStatus(id, status, reason, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.delete('/companies/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
await service.deleteCompany(id, req.admin.id, req.ip);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.post('/companies/:id/impersonate', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { reason, durationMinutes } = (0, validate_1.parseBody)(adminImpersonationSchema, req);
(0, respond_1.ok)(res, await service.impersonateCompany(id, req.admin.id, req.ip, reason, durationMinutes));
}
catch (err) {
next(err);
}
});
// ─── Menu management ──────────────────────────────────────────
router.get('/menu-items', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (_req, res, next) => {
try {
(0, respond_1.ok)(res, await menuService.listMenuItems());
}
catch (err) {
next(err);
}
});
router.post('/menu-items', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
(0, respond_1.created)(res, { data: await menuService.createMenuItem((0, validate_1.parseBody)(admin_schemas_1.menuItemSchema, req), req.admin) });
}
catch (err) {
next(err);
}
});
router.get('/menu-items/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
(0, respond_1.ok)(res, await menuService.getMenuItem(id));
}
catch (err) {
next(err);
}
});
router.put('/menu-items/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
(0, respond_1.ok)(res, await menuService.updateMenuItem(id, (0, validate_1.parseBody)(admin_schemas_1.menuItemSchema, req), req.admin));
}
catch (err) {
next(err);
}
});
router.patch('/menu-items/:id/status', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { isActive } = (0, validate_1.parseBody)(admin_schemas_1.menuItemStatusSchema, req);
(0, respond_1.ok)(res, await menuService.setMenuItemStatus(id, isActive, req.admin));
}
catch (err) {
next(err);
}
});
router.delete('/menu-items/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
await menuService.deleteMenuItem(id, req.admin);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.post('/menu-items/:id/subscriptions', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { assignments } = (0, validate_1.parseBody)(admin_schemas_1.menuPlanAssignmentsSchema, req);
(0, respond_1.ok)(res, await menuService.assignMenuItemToPlans(id, assignments, req.admin));
}
catch (err) {
next(err);
}
});
router.delete('/menu-items/:id/subscriptions/:plan', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id, plan } = (0, validate_1.parseParams)(admin_schemas_1.menuPlanParamSchema, req);
(0, respond_1.ok)(res, await menuService.removeMenuItemFromPlan(id, plan, req.admin));
}
catch (err) {
next(err);
}
});
router.post('/menu-items/:id/companies', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { assignments } = (0, validate_1.parseBody)(admin_schemas_1.menuCompanyAssignmentsSchema, req);
(0, respond_1.ok)(res, await menuService.assignMenuItemToCompanies(id, assignments, req.admin));
}
catch (err) {
next(err);
}
});
router.delete('/menu-items/:id/companies/:companyId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
const { id, companyId } = (0, validate_1.parseParams)(admin_schemas_1.menuCompanyParamSchema, req);
(0, respond_1.ok)(res, await menuService.removeMenuItemFromCompany(id, companyId, req.admin));
}
catch (err) {
next(err);
}
});
router.post('/menu-preview', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await menuService.previewCompanyMenu((0, validate_1.parseBody)(admin_schemas_1.menuPreviewSchema, req)));
}
catch (err) {
next(err);
}
});
router.get('/menu-audit-logs', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await menuService.listMenuAuditLogs((0, validate_1.parseQuery)(admin_schemas_1.menuAuditLogQuerySchema, req)));
}
catch (err) {
next(err);
}
});
// ─── Renters ───────────────────────────────────────────────────
router.get('/renters', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listRenters((0, validate_1.parseQuery)(admin_schemas_1.rentersQuerySchema, req)));
}
catch (err) {
next(err);
}
});
router.post('/renters/:id/block', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
await service.setRenterActive(id, false);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.post('/renters/:id/unblock', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
await service.setRenterActive(id, true);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
// ─── Metrics ───────────────────────────────────────────────────
router.get('/metrics', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getPlatformMetrics());
}
catch (err) {
next(err);
}
});
// ─── Notifications ─────────────────────────────────────────────
router.get('/notifications', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listNotifications((0, validate_1.parseQuery)(admin_schemas_1.notificationsQuerySchema, req)));
}
catch (err) {
next(err);
}
});
// ─── Audit logs ────────────────────────────────────────────────
router.get('/audit-logs', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('ADMIN'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getAuditLogs((0, validate_1.parseQuery)(admin_schemas_1.auditLogQuerySchema, req)));
}
catch (err) {
next(err);
}
});
// ─── Admin users ───────────────────────────────────────────────
router.get('/admins', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listAdmins());
}
catch (err) {
next(err);
}
});
router.post('/admins', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
(0, respond_1.created)(res, { data: await service.createAdmin((0, validate_1.parseBody)(admin_schemas_1.createAdminSchema, req)) });
}
catch (err) {
next(err);
}
});
router.patch('/admins/:id', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
(0, respond_1.ok)(res, await service.updateAdmin(id, (0, validate_1.parseBody)(admin_schemas_1.updateAdminSchema, req)));
}
catch (err) {
next(err);
}
});
router.patch('/admins/:id/role', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { role } = (0, validate_1.parseBody)(admin_schemas_1.adminRoleSchema, req);
await service.updateAdminRole(id, role);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.patch('/admins/:id/permissions', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPER_ADMIN'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(admin_schemas_1.idParamSchema, req);
const { permissions } = (0, validate_1.parseBody)(admin_schemas_1.adminPermissionsSchema, req);
(0, respond_1.ok)(res, await service.updateAdminPermissions(id, permissions));
}
catch (err) {
next(err);
}
});
// ─── Billing ───────────────────────────────────────────────────
router.get('/billing', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getBilling((0, validate_1.parseQuery)(admin_schemas_1.billingQuerySchema, req)));
}
catch (err) {
next(err);
}
});
router.get('/billing/invoices/:invoiceId/pdf', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
const { pdfBuffer, invoiceNumber } = await service.getInvoicePdf(invoiceId);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="${invoiceNumber}.pdf"`);
res.setHeader('Content-Length', pdfBuffer.length);
res.end(pdfBuffer);
}
catch (err) {
next(err);
}
});
router.get('/billing/:companyId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { companyId } = (0, validate_1.parseParams)(admin_schemas_1.companyIdParamSchema, req);
(0, respond_1.ok)(res, await service.getBillingAccountDetail(companyId));
}
catch (err) {
next(err);
}
});
router.get('/billing/:companyId/invoices', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { companyId } = (0, validate_1.parseParams)(admin_schemas_1.companyIdParamSchema, req);
(0, respond_1.ok)(res, await service.getCompanyInvoices(companyId, (0, validate_1.parseQuery)(admin_schemas_1.invoicesQuerySchema, req)));
}
catch (err) {
next(err);
}
});
router.patch('/billing/accounts/:billingAccountId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { billingAccountId } = (0, validate_1.parseParams)(admin_schemas_1.billingAccountIdParamSchema, req);
(0, respond_1.ok)(res, await service.updateBillingAccount(billingAccountId, (0, validate_1.parseBody)(admin_schemas_1.billingAccountUpdateSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/accounts/:billingAccountId/pause-dunning', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { billingAccountId } = (0, validate_1.parseParams)(admin_schemas_1.billingAccountIdParamSchema, req);
(0, respond_1.ok)(res, await service.setDunningPaused(billingAccountId, true, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/accounts/:billingAccountId/resume-dunning', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { billingAccountId } = (0, validate_1.parseParams)(admin_schemas_1.billingAccountIdParamSchema, req);
(0, respond_1.ok)(res, await service.setDunningPaused(billingAccountId, false, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/accounts/:billingAccountId/invoices', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { billingAccountId } = (0, validate_1.parseParams)(admin_schemas_1.billingAccountIdParamSchema, req);
(0, respond_1.created)(res, await service.createBillingInvoice(billingAccountId, (0, validate_1.parseBody)(admin_schemas_1.createBillingInvoiceSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/finalize', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
(0, respond_1.ok)(res, await service.finalizeBillingInvoice(invoiceId, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/pay', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
(0, respond_1.ok)(res, await service.payBillingInvoice(invoiceId, (0, validate_1.parseBody)(admin_schemas_1.payBillingInvoiceSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/retry-payment', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
(0, respond_1.ok)(res, await service.retryBillingInvoicePayment(invoiceId, (0, validate_1.parseBody)(admin_schemas_1.retryBillingInvoiceSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/void', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(admin_schemas_1.billingReasonSchema, req);
(0, respond_1.ok)(res, await service.voidBillingInvoice(invoiceId, reason, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/uncollectible', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(admin_schemas_1.billingReasonSchema, req);
(0, respond_1.ok)(res, await service.markBillingInvoiceUncollectible(invoiceId, reason, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/credit-notes', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
(0, respond_1.ok)(res, await service.issueBillingCreditNote(invoiceId, (0, validate_1.parseBody)(admin_schemas_1.billingCreditNoteSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.post('/billing/invoices/:invoiceId/refunds', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { invoiceId } = (0, validate_1.parseParams)(admin_schemas_1.invoiceIdParamSchema, req);
(0, respond_1.ok)(res, await service.issueBillingRefund(invoiceId, (0, validate_1.parseBody)(admin_schemas_1.billingRefundSchema, req), req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
// ─── Pricing config ────────────────────────────────────────────
router.get('/pricing', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getPricingConfigs());
}
catch (err) {
next(err);
}
});
router.patch('/pricing', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { entries } = (0, validate_1.parseBody)(admin_schemas_1.pricingUpdateSchema, req);
(0, respond_1.ok)(res, await service.updatePricingConfigs(entries, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.get('/pricing/features', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (_req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listPlanFeatures());
}
catch (err) {
next(err);
}
});
router.post('/pricing/features', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const data = (0, validate_1.parseBody)(admin_schemas_1.planFeatureCreateSchema, req);
(0, respond_1.created)(res, await service.createPlanFeature(data, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.patch('/pricing/features/:featureId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { featureId } = (0, validate_1.parseParams)(admin_schemas_1.planFeatureIdParamSchema, req);
const data = (0, validate_1.parseBody)(admin_schemas_1.planFeatureUpdateSchema, req);
(0, respond_1.ok)(res, await service.updatePlanFeature(featureId, data, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.delete('/pricing/features/:featureId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { featureId } = (0, validate_1.parseParams)(admin_schemas_1.planFeatureIdParamSchema, req);
await service.deletePlanFeature(featureId, req.admin.id, req.ip);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
// ─── Promotions ────────────────────────────────────────────────────────────
router.get('/pricing/promotions', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listPromotions());
}
catch (err) {
next(err);
}
});
router.post('/pricing/promotions', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const data = (0, validate_1.parseBody)(admin_schemas_1.promotionCreateSchema, req);
(0, respond_1.created)(res, await service.createPromotion(data, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.patch('/pricing/promotions/:promotionId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { promotionId } = (0, validate_1.parseParams)(admin_schemas_1.promotionIdParamSchema, req);
const data = (0, validate_1.parseBody)(admin_schemas_1.promotionUpdateSchema, req);
(0, respond_1.ok)(res, await service.updatePromotion(promotionId, data, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
router.delete('/pricing/promotions/:promotionId', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('FINANCE'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { promotionId } = (0, validate_1.parseParams)(admin_schemas_1.promotionIdParamSchema, req);
await service.deletePromotion(promotionId, req.admin.id, req.ip);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
// ─── Subscription admin overrides ─────────────────────────────
router.get('/subscriptions/:subscriptionId/events', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
(0, respond_1.ok)(res, await subService.getEventsBySubscriptionId(subscriptionId));
}
catch (err) {
next(err);
}
});
router.post('/subscriptions/:subscriptionId/extend-trial', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
const { extraDays, reason } = (0, validate_1.parseBody)(adminExtendTrialSchema, req);
(0, respond_1.ok)(res, await subService.adminExtendTrial(subscriptionId, extraDays, req.admin.id, reason));
}
catch (err) {
next(err);
}
});
router.post('/subscriptions/:subscriptionId/extend-grace-period', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(adminSubOverrideSchema, req);
(0, respond_1.ok)(res, await subService.adminExtendGracePeriod(subscriptionId, 7, req.admin.id, reason));
}
catch (err) {
next(err);
}
});
router.post('/subscriptions/:subscriptionId/suspend', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(adminSubOverrideSchema, req);
(0, respond_1.ok)(res, await subService.adminSuspendSubscription(subscriptionId, req.admin.id, reason));
}
catch (err) {
next(err);
}
});
router.post('/subscriptions/:subscriptionId/reactivate', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(adminSubOverrideSchema, req);
(0, respond_1.ok)(res, await subService.adminReactivateSubscription(subscriptionId, req.admin.id, reason));
}
catch (err) {
next(err);
}
});
router.post('/subscriptions/:subscriptionId/cancel', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), requireAdminAuth_1.requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = (0, validate_1.parseParams)(subIdParamSchema, req);
const { reason } = (0, validate_1.parseBody)(adminSubOverrideSchema, req);
(0, respond_1.ok)(res, await subService.adminCancelSubscription(subscriptionId, req.admin.id, reason));
}
catch (err) {
next(err);
}
});
// ─── Site config ───────────────────────────────────────────────
router.get('/site-config/marketplace-homepage', requireAdminAuth_1.requireAdminAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getMarketplaceHomepage());
}
catch (err) {
next(err);
}
});
router.patch('/site-config/marketplace-homepage', requireAdminAuth_1.requireAdminAuth, (0, requireAdminAuth_1.requireAdminRole)('SUPPORT'), async (req, res, next) => {
try {
const { homepage } = (0, validate_1.parseBody)(admin_schemas_1.homepageUpdateSchema, req);
(0, respond_1.ok)(res, await service.updateMarketplaceHomepage(homepage, req.admin.id, req.ip));
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=admin.routes.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin.schemas.ts"],"names":[],"mappings":"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"}
+361
View File
@@ -0,0 +1,361 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.menuCompanyParamSchema = exports.menuPlanParamSchema = exports.menuItemIdParamSchema = exports.menuAuditLogQuerySchema = exports.menuPreviewSchema = exports.menuCompanyAssignmentsSchema = exports.menuPlanAssignmentsSchema = exports.menuItemStatusSchema = exports.menuItemSchema = exports.promotionIdParamSchema = exports.planFeatureIdParamSchema = exports.promotionUpdateSchema = exports.promotionCreateSchema = exports.planFeatureUpdateSchema = exports.planFeatureCreateSchema = exports.pricingUpdateSchema = exports.homepageUpdateSchema = exports.billingRefundSchema = exports.billingCreditNoteSchema = exports.billingReasonSchema = exports.retryBillingInvoiceSchema = exports.payBillingInvoiceSchema = exports.createBillingInvoiceSchema = exports.billingLineItemInputSchema = exports.billingAccountUpdateSchema = exports.invoiceIdParamSchema = exports.billingAccountIdParamSchema = exports.companyIdParamSchema = exports.idParamSchema = exports.marketplaceHomepageConfigSchema = exports.adminCompanyUpdateSchema = exports.companyStatusSchema = exports.adminPermissionsSchema = exports.adminRoleSchema = exports.updateAdminSchema = exports.createAdminSchema = exports.permissionSchema = exports.invoicesQuerySchema = exports.billingQuerySchema = exports.notificationsQuerySchema = exports.auditLogQuerySchema = exports.rentersQuerySchema = exports.companiesQuerySchema = exports.totpVerifySchema = exports.resetPasswordSchema = exports.forgotPasswordSchema = exports.loginSchema = void 0;
const zod_1 = require("zod");
exports.loginSchema = zod_1.z.object({
email: zod_1.z.string().email().max(255).trim().toLowerCase(),
password: zod_1.z.string().max(128),
totpCode: zod_1.z.string().length(6).optional(),
recoveryCode: zod_1.z.string().min(8).max(32).optional(),
});
exports.forgotPasswordSchema = zod_1.z.object({
email: zod_1.z.string().email().max(255).trim().toLowerCase(),
});
exports.resetPasswordSchema = zod_1.z.object({
token: zod_1.z.string().min(1),
password: zod_1.z.string().min(8).max(128),
});
exports.totpVerifySchema = zod_1.z.object({
code: zod_1.z.string().length(6),
});
exports.companiesQuerySchema = zod_1.z.object({
q: zod_1.z.string().optional(),
status: zod_1.z.string().optional(),
plan: zod_1.z.string().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.rentersQuerySchema = zod_1.z.object({
q: zod_1.z.string().optional(),
blocked: zod_1.z.string().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.auditLogQuerySchema = zod_1.z.object({
adminId: zod_1.z.string().optional(),
action: zod_1.z.string().optional(),
companyId: zod_1.z.string().optional(),
entityId: zod_1.z.string().optional(),
page: zod_1.z.coerce.number().int().min(1).default(1),
pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(50),
});
exports.notificationsQuerySchema = zod_1.z.object({
channel: zod_1.z.string().optional(),
status: zod_1.z.string().optional(),
companyId: zod_1.z.string().optional(),
page: zod_1.z.coerce.number().int().min(1).default(1),
pageSize: zod_1.z.coerce.number().int().min(1).max(200).default(50),
});
exports.billingQuerySchema = zod_1.z.object({
q: zod_1.z.string().optional(),
status: zod_1.z.string().optional(),
plan: zod_1.z.string().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.invoicesQuerySchema = zod_1.z.object({
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.permissionSchema = zod_1.z.object({
resource: zod_1.z.enum(['COMPANIES', 'COMPANY_EMPLOYEES', 'SUBSCRIPTIONS', 'PAYMENTS', 'OFFERS', 'RENTERS', 'ADMIN_USERS', 'AUDIT_LOGS', 'PLATFORM_METRICS']),
actions: zod_1.z.array(zod_1.z.enum(['READ', 'CREATE', 'UPDATE', 'DELETE', 'SUSPEND', 'IMPERSONATE'])).min(1),
});
exports.createAdminSchema = zod_1.z.object({
email: zod_1.z.string().email().trim().toLowerCase(),
firstName: zod_1.z.string().min(1).max(100).trim(),
lastName: zod_1.z.string().min(1).max(100).trim(),
role: zod_1.z.enum(['SUPER_ADMIN', 'ADMIN', 'SUPPORT', 'FINANCE', 'VIEWER']),
password: zod_1.z.string().min(8),
permissions: zod_1.z.array(exports.permissionSchema).optional(),
});
exports.updateAdminSchema = zod_1.z.object({
email: zod_1.z.string().email().trim().toLowerCase().optional(),
firstName: zod_1.z.string().min(1).max(100).trim().optional(),
lastName: zod_1.z.string().min(1).max(100).trim().optional(),
role: zod_1.z.enum(['SUPER_ADMIN', 'ADMIN', 'SUPPORT', 'FINANCE', 'VIEWER']).optional(),
password: zod_1.z.string().min(8).optional(),
isActive: zod_1.z.boolean().optional(),
});
exports.adminRoleSchema = zod_1.z.object({
role: zod_1.z.enum(['SUPER_ADMIN', 'ADMIN', 'SUPPORT', 'FINANCE', 'VIEWER']),
});
exports.adminPermissionsSchema = zod_1.z.object({
permissions: zod_1.z.array(exports.permissionSchema),
});
exports.companyStatusSchema = zod_1.z.object({
status: zod_1.z.enum(['ACTIVE', 'SUSPENDED', 'CANCELLED']),
reason: zod_1.z.string().optional(),
});
const nullableString = zod_1.z.union([zod_1.z.string(), zod_1.z.null()]).optional();
const nullableEmail = zod_1.z.union([zod_1.z.string().email(), zod_1.z.null()]).optional();
const nullableUrl = zod_1.z.union([zod_1.z.string().url(), zod_1.z.null()]).optional();
const nullableDate = zod_1.z.union([zod_1.z.string().datetime(), zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/), zod_1.z.null()]).optional();
exports.adminCompanyUpdateSchema = zod_1.z.object({
company: zod_1.z.object({
name: zod_1.z.string().min(1).optional(), slug: zod_1.z.string().min(1).optional(),
email: zod_1.z.string().email().optional(), phone: nullableString,
status: zod_1.z.enum(['PENDING', 'TRIALING', 'ACTIVE', 'PAST_DUE', 'SUSPENDED', 'CANCELLED']).optional(),
subscriptionPaymentRef: nullableString,
address: zod_1.z.object({
streetAddress: nullableString,
city: nullableString,
country: nullableString,
zipCode: nullableString,
legalName: nullableString,
legalForm: nullableString,
companyEmail: nullableEmail,
managerName: nullableString,
iceNumber: nullableString,
operatingLicenseNumber: nullableString,
operatingLicenseIssuedAt: nullableString,
operatingLicenseIssuedBy: nullableString,
fax: nullableString,
yearsActive: nullableString,
representativeName: nullableString,
representativeTitle: nullableString,
responsibleName: nullableString,
responsibleRole: nullableString,
responsibleIdentityNumber: nullableString,
responsibleQualification: nullableString,
responsiblePhone: nullableString,
responsibleEmail: nullableEmail,
}).optional(),
}).optional(),
subscription: zod_1.z.object({
plan: zod_1.z.enum(['STARTER', 'GROWTH', 'PRO']).optional(),
billingPeriod: zod_1.z.enum(['MONTHLY', 'ANNUAL']).optional(),
status: zod_1.z.enum(['TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELLED', 'UNPAID']).optional(),
currency: zod_1.z.literal('MAD').optional(),
trialStartAt: nullableDate, trialEndAt: nullableDate,
currentPeriodStart: nullableDate, currentPeriodEnd: nullableDate,
cancelledAt: nullableDate, cancelAtPeriodEnd: zod_1.z.boolean().optional(),
}).optional(),
brand: zod_1.z.object({
displayName: zod_1.z.string().min(1).optional(), tagline: nullableString,
subdomain: zod_1.z.string().min(1).optional(), customDomain: nullableString,
publicEmail: nullableEmail, publicPhone: nullableString, publicAddress: nullableString,
publicCity: nullableString, publicCountry: nullableString, websiteUrl: nullableUrl,
whatsappNumber: nullableString, defaultLocale: zod_1.z.string().min(2).optional(),
defaultCurrency: zod_1.z.literal('MAD').optional(),
isListedOnMarketplace: zod_1.z.boolean().optional(),
homePageConfig: zod_1.z.any().optional(),
menuConfig: zod_1.z.any().optional(),
}).optional(),
contractSettings: zod_1.z.object({
legalName: nullableString, registrationNumber: nullableString, taxId: nullableString,
terms: zod_1.z.string().optional(),
fuelPolicyType: zod_1.z.enum(['FULL_TO_FULL', 'FULL_TO_EMPTY', 'SAME_TO_SAME', 'PREPAID', 'FREE']).optional(),
lateFeePerHour: zod_1.z.number().int().nullable().optional(), taxRate: zod_1.z.number().nullable().optional(),
signatureRequired: zod_1.z.boolean().optional(), showTax: zod_1.z.boolean().optional(),
}).optional(),
accountingSettings: zod_1.z.object({
reportingPeriod: zod_1.z.enum(['WEEKLY', 'MONTHLY', 'QUARTERLY', 'ANNUAL']).optional(),
fiscalYearStart: zod_1.z.number().int().min(1).max(12).optional(),
currency: zod_1.z.literal('MAD').optional(),
accountantEmail: nullableEmail, accountantName: nullableString,
autoSendReport: zod_1.z.boolean().optional(),
reportFormat: zod_1.z.enum(['PDF', 'CSV', 'BOTH']).optional(),
}).optional(),
});
const marketplaceMetricSchema = zod_1.z.object({ value: zod_1.z.string().min(1), label: zod_1.z.string().min(1) });
const marketplacePillarSchema = zod_1.z.object({ title: zod_1.z.string().min(1), body: zod_1.z.string().min(1) });
const marketplaceStepSchema = zod_1.z.object({ step: zod_1.z.string().min(1), title: zod_1.z.string().min(1), body: zod_1.z.string().min(1) });
const marketplaceSectionSchema = zod_1.z.enum(['hero', 'surface', 'pillars', 'audiences', 'features', 'steps', 'closing']);
const marketplaceHomepageContentSchema = zod_1.z.object({
sections: zod_1.z.array(marketplaceSectionSchema).min(1),
heroKicker: zod_1.z.string().min(1), heroTitle: zod_1.z.string().min(1), heroBody: zod_1.z.string().min(1),
startTrial: zod_1.z.string().min(1), exploreVehicles: zod_1.z.string().min(1),
surfaceLabel: zod_1.z.string().min(1), surfaceTitle: zod_1.z.string().min(1), surfaceBody: zod_1.z.string().min(1),
liveLabel: zod_1.z.string().min(1), trustedFleets: zod_1.z.string().min(1), brandedFlows: zod_1.z.string().min(1), multiTenant: zod_1.z.string().min(1),
companyKicker: zod_1.z.string().min(1), companyTitle: zod_1.z.string().min(1), companyBody: zod_1.z.string().min(1),
renterKicker: zod_1.z.string().min(1), renterTitle: zod_1.z.string().min(1), renterBody: zod_1.z.string().min(1),
pillars: zod_1.z.array(marketplacePillarSchema).length(3),
metrics: zod_1.z.array(marketplaceMetricSchema).length(3),
featureLabel: zod_1.z.string().min(1), features: zod_1.z.array(zod_1.z.string().min(1)).min(1),
stepsTitle: zod_1.z.string().min(1), steps: zod_1.z.array(marketplaceStepSchema).length(3), stepLabel: zod_1.z.string().min(1),
readyKicker: zod_1.z.string().min(1), readyTitle: zod_1.z.string().min(1), readyBody: zod_1.z.string().min(1),
viewPricing: zod_1.z.string().min(1), createWorkspace: zod_1.z.string().min(1),
});
exports.marketplaceHomepageConfigSchema = zod_1.z.object({
en: marketplaceHomepageContentSchema,
fr: marketplaceHomepageContentSchema,
ar: marketplaceHomepageContentSchema,
});
exports.idParamSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
});
exports.companyIdParamSchema = zod_1.z.object({
companyId: zod_1.z.string().min(1),
});
exports.billingAccountIdParamSchema = zod_1.z.object({
billingAccountId: zod_1.z.string().min(1),
});
exports.invoiceIdParamSchema = zod_1.z.object({
invoiceId: zod_1.z.string().min(1),
});
exports.billingAccountUpdateSchema = zod_1.z.object({
legalName: zod_1.z.string().min(1).max(255).optional(),
billingEmail: zod_1.z.string().email().optional(),
billingAddress: zod_1.z.any().optional(),
taxId: zod_1.z.union([zod_1.z.string().max(120), zod_1.z.null()]).optional(),
taxExempt: zod_1.z.boolean().optional(),
invoiceTerms: zod_1.z.enum(['DUE_ON_RECEIPT', 'NET_7', 'NET_15', 'NET_30', 'NET_45', 'NET_60']).optional(),
netTermsDays: zod_1.z.number().int().min(0).max(365).optional(),
});
exports.billingLineItemInputSchema = zod_1.z.object({
type: zod_1.z.enum([
'SUBSCRIPTION_FEE',
'SEAT_FEE',
'USAGE_FEE',
'SETUP_FEE',
'DISCOUNT',
'TAX',
'CREDIT',
'PRORATION',
'REFUND_ADJUSTMENT',
'MANUAL_ADJUSTMENT',
'PROFESSIONAL_SERVICES',
'CANCELLATION_FEE',
]),
description: zod_1.z.string().min(1).max(255),
quantity: zod_1.z.number().int().positive().max(100000),
unitAmount: zod_1.z.number().int(),
periodStart: nullableDate,
periodEnd: nullableDate,
});
exports.createBillingInvoiceSchema = zod_1.z.object({
subscriptionId: zod_1.z.union([zod_1.z.string(), zod_1.z.null()]).optional(),
invoiceType: zod_1.z.enum([
'SUBSCRIPTION_INITIAL',
'SUBSCRIPTION_RENEWAL',
'TRIAL_CONVERSION',
'SUBSCRIPTION_UPGRADE',
'SUBSCRIPTION_DOWNGRADE_CREDIT',
'USAGE_BASED',
'ONE_TIME',
'MANUAL',
'CORRECTION',
'CANCELLATION_FEE',
]),
currency: zod_1.z.string().min(3).max(8).optional(),
dueAt: nullableDate,
isSubscriptionBlocking: zod_1.z.boolean().optional(),
adminReason: zod_1.z.union([zod_1.z.string().max(500), zod_1.z.null()]).optional(),
lineItems: zod_1.z.array(exports.billingLineItemInputSchema).min(1),
});
exports.payBillingInvoiceSchema = zod_1.z.object({
amount: zod_1.z.number().int().positive().optional(),
paymentMethodId: zod_1.z.union([zod_1.z.string(), zod_1.z.null()]).optional(),
providerPaymentId: zod_1.z.union([zod_1.z.string(), zod_1.z.null()]).optional(),
});
exports.retryBillingInvoiceSchema = zod_1.z.object({
paymentMethodId: zod_1.z.union([zod_1.z.string(), zod_1.z.null()]).optional(),
});
exports.billingReasonSchema = zod_1.z.object({
reason: zod_1.z.string().min(1).max(500),
});
exports.billingCreditNoteSchema = zod_1.z.object({
amount: zod_1.z.number().int().positive(),
reason: zod_1.z.string().min(1).max(500),
});
exports.billingRefundSchema = zod_1.z.object({
amount: zod_1.z.number().int().positive(),
reason: zod_1.z.string().min(1).max(500),
});
exports.homepageUpdateSchema = zod_1.z.object({
homepage: exports.marketplaceHomepageConfigSchema,
});
exports.pricingUpdateSchema = zod_1.z.object({
entries: zod_1.z.array(zod_1.z.object({
plan: zod_1.z.enum(['STARTER', 'GROWTH', 'PRO']),
billingPeriod: zod_1.z.enum(['MONTHLY', 'ANNUAL']),
amount: zod_1.z.number().int().positive(),
})).min(1),
});
const planFeatureSchema = zod_1.z.object({
plan: zod_1.z.enum(['STARTER', 'GROWTH', 'PRO']),
label: zod_1.z.string().min(1).max(120),
sortOrder: zod_1.z.number().int().min(0).default(0),
});
exports.planFeatureCreateSchema = planFeatureSchema;
exports.planFeatureUpdateSchema = planFeatureSchema.partial();
exports.promotionCreateSchema = zod_1.z.object({
code: zod_1.z.string().min(2).max(30).regex(/^[A-Z0-9_-]+$/, 'Code must be uppercase A-Z, 0-9, _ or -'),
name: zod_1.z.string().min(1).max(100),
description: zod_1.z.string().max(500).optional(),
discountType: zod_1.z.enum(['PERCENTAGE', 'FIXED']),
discountValue: zod_1.z.number().int().positive(),
plans: zod_1.z.array(zod_1.z.enum(['STARTER', 'GROWTH', 'PRO'])),
periods: zod_1.z.array(zod_1.z.enum(['MONTHLY', 'ANNUAL'])),
maxUses: zod_1.z.number().int().positive().nullable().optional(),
validFrom: zod_1.z.string().datetime(),
validUntil: zod_1.z.string().datetime().nullable().optional(),
isActive: zod_1.z.boolean().default(true),
});
exports.promotionUpdateSchema = exports.promotionCreateSchema.partial();
exports.planFeatureIdParamSchema = zod_1.z.object({ featureId: zod_1.z.string().min(1) });
exports.promotionIdParamSchema = zod_1.z.object({ promotionId: zod_1.z.string().min(1) });
const employeeRoleSchema = zod_1.z.enum(['OWNER', 'MANAGER', 'AGENT']);
const planSchema = zod_1.z.enum(['STARTER', 'GROWTH', 'PRO']);
const menuItemTypeSchema = zod_1.z.enum(['INTERNAL_PAGE', 'EXTERNAL_LINK', 'PARENT_MENU', 'SECTION_LABEL', 'DIVIDER']);
exports.menuItemSchema = zod_1.z.object({
systemKey: zod_1.z.string().trim().min(1).max(120).optional().nullable(),
label: zod_1.z.string().trim().min(1).max(120),
itemType: menuItemTypeSchema,
routeOrUrl: zod_1.z.string().trim().max(400).optional().nullable(),
icon: zod_1.z.string().trim().max(120).optional().nullable(),
parentId: zod_1.z.string().trim().min(1).optional().nullable(),
displayOrder: zod_1.z.number().int().min(0).default(0),
openInNewTab: zod_1.z.boolean().default(false),
isRequired: zod_1.z.boolean().default(false),
isActive: zod_1.z.boolean().default(true),
roles: zod_1.z.array(employeeRoleSchema).default([]),
subscriptionPlans: zod_1.z.array(zod_1.z.object({
plan: planSchema,
displayOrder: zod_1.z.number().int().min(0).optional(),
isActive: zod_1.z.boolean().optional(),
})).default([]),
companyAssignments: zod_1.z.array(zod_1.z.object({
companyId: zod_1.z.string().trim().min(1),
displayOrder: zod_1.z.number().int().min(0).optional(),
isActive: zod_1.z.boolean().optional(),
})).default([]),
});
exports.menuItemStatusSchema = zod_1.z.object({
isActive: zod_1.z.boolean(),
});
exports.menuPlanAssignmentsSchema = zod_1.z.object({
assignments: zod_1.z.array(zod_1.z.object({
plan: planSchema,
displayOrder: zod_1.z.number().int().min(0).optional(),
isActive: zod_1.z.boolean().optional(),
})).min(1),
});
exports.menuCompanyAssignmentsSchema = zod_1.z.object({
assignments: zod_1.z.array(zod_1.z.object({
companyId: zod_1.z.string().trim().min(1),
displayOrder: zod_1.z.number().int().min(0).optional(),
isActive: zod_1.z.boolean().optional(),
})).min(1),
});
exports.menuPreviewSchema = zod_1.z.object({
companyId: zod_1.z.string().trim().min(1),
role: employeeRoleSchema,
});
exports.menuAuditLogQuerySchema = zod_1.z.object({
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.menuItemIdParamSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
});
exports.menuPlanParamSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
plan: planSchema,
});
exports.menuCompanyParamSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
companyId: zod_1.z.string().min(1),
});
//# sourceMappingURL=admin.schemas.js.map
File diff suppressed because one or more lines are too long
+174
View File
@@ -0,0 +1,174 @@
import * as repo from './admin.repo';
import * as billingService from './admin.billing.service';
export declare function login(email: string, password: string, totpCode?: string, recoveryCode?: string): Promise<{
token: string;
admin: Omit<any, "passwordHash" | "totpSecret">;
} | {
readonly totpRequired: true;
readonly invalidTotp?: undefined;
} | {
readonly invalidTotp: true;
readonly totpRequired?: undefined;
} | null>;
export declare function setupTotp(adminId: string, email: string): Promise<{
secret: string;
qrCode: string;
}>;
export declare function verifyTotp(adminId: string, code: string): Promise<false | {
recoveryCodes: string[];
token: string;
admin: Omit<any, "passwordHash" | "totpSecret">;
}>;
export declare function regenerateRecoveryCodes(adminId: string): Promise<{
recoveryCodes: string[];
}>;
export declare function forgotPassword(email: string): Promise<void>;
export declare function resetPassword(token: string, password: string): Promise<boolean>;
export declare function listCompanies(query: {
q?: string;
status?: string;
plan?: string;
page: number;
pageSize: number;
}): Promise<{
data: unknown[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function getCompany(id: string): any;
export declare function updateCompany(id: string, body: any, adminId: string, ip?: string): Promise<any>;
export declare function setCompanyStatus(id: string, status: string, reason: string | undefined, adminId: string, ip?: string): Promise<any>;
export declare function deleteCompany(id: string, adminId: string, ip?: string): Promise<void>;
export declare function impersonateCompany(id: string, adminId: string, ip?: string, reason?: string, durationMinutes?: number): Promise<{
token: string;
expiresIn: number;
impersonation: {
companyId: string;
reason: string | undefined;
durationMinutes: number;
};
}>;
export declare function listRenters(query: {
q?: string;
blocked?: string;
page: number;
pageSize: number;
}): Promise<{
data: unknown[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function setRenterActive(id: string, isActive: boolean): any;
export declare function getPlatformMetrics(): Promise<{
totalCompanies: any;
activeCompanies: any;
trialingCompanies: any;
suspendedCompanies: any;
totalRenters: any;
totalReservations: any;
}>;
export declare function listNotifications(query: {
channel?: string;
status?: string;
companyId?: string;
page: number;
pageSize: number;
}): Promise<{
data: unknown[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function getAuditLogs(query: {
adminId?: string;
action?: string;
companyId?: string;
entityId?: string;
page: number;
pageSize: number;
}): Promise<{
data: unknown[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function listAdmins(): Promise<any>;
export declare function createAdmin(body: {
email: string;
firstName: string;
lastName: string;
role: string;
password: string;
permissions?: any[];
}): Promise<Omit<any, "passwordHash" | "totpSecret">>;
export declare function updateAdmin(id: string, body: {
email?: string;
firstName?: string;
lastName?: string;
role?: string;
password?: string;
isActive?: boolean;
}): Promise<Omit<any, "passwordHash" | "totpSecret">>;
export declare function updateAdminRole(id: string, role: string): any;
export declare function updateAdminPermissions(id: string, permissions: any[]): Promise<Omit<any, "passwordHash" | "totpSecret">>;
export declare function getBilling(query: {
q?: string;
status?: string;
plan?: string;
page: number;
pageSize: number;
}): Promise<{
data: any[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function getCompanyInvoices(companyId: string, query: {
page: number;
pageSize: number;
}): Promise<{
data: unknown[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}>;
export declare function getInvoicePdf(invoiceId: string): Promise<{
pdfBuffer: Buffer<ArrayBufferLike>;
invoiceNumber: any;
}>;
export declare function getBillingAccountDetail(companyId: string): Promise<any>;
export declare function updateBillingAccount(billingAccountId: string, data: Parameters<typeof billingService.updateBillingAccount>[1], adminId: string, ip?: string): Promise<any>;
export declare function setDunningPaused(billingAccountId: string, paused: boolean, adminId: string, ip?: string): Promise<any>;
export declare function createBillingInvoice(billingAccountId: string, data: Parameters<typeof billingService.createDraftInvoice>[1], adminId: string, ip?: string): Promise<any>;
export declare function finalizeBillingInvoice(invoiceId: string, adminId: string, ip?: string): Promise<any>;
export declare function payBillingInvoice(invoiceId: string, data: Parameters<typeof billingService.payInvoice>[1], adminId: string, ip?: string): Promise<any>;
export declare function retryBillingInvoicePayment(invoiceId: string, data: Parameters<typeof billingService.retryInvoicePayment>[1], adminId: string, ip?: string): Promise<any>;
export declare function voidBillingInvoice(invoiceId: string, reason: string, adminId: string, ip?: string): Promise<any>;
export declare function markBillingInvoiceUncollectible(invoiceId: string, reason: string, adminId: string, ip?: string): Promise<any>;
export declare function issueBillingCreditNote(invoiceId: string, data: Parameters<typeof billingService.issueCreditNote>[1], adminId: string, ip?: string): Promise<any>;
export declare function issueBillingRefund(invoiceId: string, data: Parameters<typeof billingService.issueRefund>[1], adminId: string, ip?: string): Promise<any>;
export declare function getMarketplaceHomepage(): Promise<import("@rentaldrivego/types").MarketplaceHomepageConfig>;
export declare function updateMarketplaceHomepage(homepage: any, adminId: string, ip?: string): Promise<import("@rentaldrivego/types").MarketplaceHomepageConfig>;
export declare function getPricingConfigs(): any;
export declare function updatePricingConfigs(entries: {
plan: string;
billingPeriod: string;
amount: number;
}[], adminId: string, ip?: string): Promise<any>;
export declare function listPlanFeatures(): any;
export declare function createPlanFeature(data: Parameters<typeof repo.createPlanFeature>[0], adminId: string, ip?: string): Promise<any>;
export declare function updatePlanFeature(id: string, data: Parameters<typeof repo.updatePlanFeature>[1], adminId: string, ip?: string): Promise<any>;
export declare function deletePlanFeature(id: string, adminId: string, ip?: string): Promise<void>;
export declare function listPromotions(): any;
export declare function createPromotion(data: Parameters<typeof repo.createPromotion>[0], adminId: string, ip?: string): Promise<any>;
export declare function updatePromotion(id: string, data: Parameters<typeof repo.updatePromotion>[1], adminId: string, ip?: string): Promise<any>;
export declare function deletePromotion(id: string, adminId: string, ip?: string): Promise<void>;
//# sourceMappingURL=admin.service.d.ts.map
File diff suppressed because one or more lines are too long
+494
View File
@@ -0,0 +1,494 @@
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.login = login;
exports.setupTotp = setupTotp;
exports.verifyTotp = verifyTotp;
exports.regenerateRecoveryCodes = regenerateRecoveryCodes;
exports.forgotPassword = forgotPassword;
exports.resetPassword = resetPassword;
exports.listCompanies = listCompanies;
exports.getCompany = getCompany;
exports.updateCompany = updateCompany;
exports.setCompanyStatus = setCompanyStatus;
exports.deleteCompany = deleteCompany;
exports.impersonateCompany = impersonateCompany;
exports.listRenters = listRenters;
exports.setRenterActive = setRenterActive;
exports.getPlatformMetrics = getPlatformMetrics;
exports.listNotifications = listNotifications;
exports.getAuditLogs = getAuditLogs;
exports.listAdmins = listAdmins;
exports.createAdmin = createAdmin;
exports.updateAdmin = updateAdmin;
exports.updateAdminRole = updateAdminRole;
exports.updateAdminPermissions = updateAdminPermissions;
exports.getBilling = getBilling;
exports.getCompanyInvoices = getCompanyInvoices;
exports.getInvoicePdf = getInvoicePdf;
exports.getBillingAccountDetail = getBillingAccountDetail;
exports.updateBillingAccount = updateBillingAccount;
exports.setDunningPaused = setDunningPaused;
exports.createBillingInvoice = createBillingInvoice;
exports.finalizeBillingInvoice = finalizeBillingInvoice;
exports.payBillingInvoice = payBillingInvoice;
exports.retryBillingInvoicePayment = retryBillingInvoicePayment;
exports.voidBillingInvoice = voidBillingInvoice;
exports.markBillingInvoiceUncollectible = markBillingInvoiceUncollectible;
exports.issueBillingCreditNote = issueBillingCreditNote;
exports.issueBillingRefund = issueBillingRefund;
exports.getMarketplaceHomepage = getMarketplaceHomepage;
exports.updateMarketplaceHomepage = updateMarketplaceHomepage;
exports.getPricingConfigs = getPricingConfigs;
exports.updatePricingConfigs = updatePricingConfigs;
exports.listPlanFeatures = listPlanFeatures;
exports.createPlanFeature = createPlanFeature;
exports.updatePlanFeature = updatePlanFeature;
exports.deletePlanFeature = deletePlanFeature;
exports.listPromotions = listPromotions;
exports.createPromotion = createPromotion;
exports.updatePromotion = updatePromotion;
exports.deletePromotion = deletePromotion;
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const crypto_1 = __importDefault(require("crypto"));
const otplib_1 = require("otplib");
const tokens_1 = require("../../security/tokens");
const qrcode_1 = __importDefault(require("qrcode"));
const platformContentService_1 = require("../../services/platformContentService");
const notificationService_1 = require("../../services/notificationService");
const presenter = __importStar(require("./admin.presenter"));
const repo = __importStar(require("./admin.repo"));
const billingService = __importStar(require("./admin.billing.service"));
const ADMIN_RESET_TTL_MINUTES = 60;
const ADMIN_RECOVERY_CODE_COUNT = 10;
function generateRecoveryCode() {
const raw = crypto_1.default.randomBytes(9).toString('base64url').replace(/[^a-zA-Z0-9]/g, '').toUpperCase().slice(0, 12);
return `${raw.slice(0, 4)}-${raw.slice(4, 8)}-${raw.slice(8, 12)}`;
}
async function issueAdminRecoveryCodes(adminId) {
const codes = Array.from({ length: ADMIN_RECOVERY_CODE_COUNT }, generateRecoveryCode);
const hashes = await Promise.all(codes.map((code) => bcryptjs_1.default.hash(code, 12)));
await repo.replaceAdminRecoveryCodes(adminId, hashes);
await repo.createAuditLog({
adminUserId: adminId,
action: 'ADMIN_2FA_RECOVERY_CODES_ISSUED',
resource: 'AdminUser',
resourceId: adminId,
});
return codes;
}
async function consumeAdminRecoveryCode(adminId, code) {
const normalized = code.trim().toUpperCase();
if (!normalized)
return false;
const codes = await repo.listUnusedAdminRecoveryCodes(adminId);
for (const candidate of codes) {
if (await bcryptjs_1.default.compare(normalized, candidate.codeHash)) {
await repo.markAdminRecoveryCodeUsed(candidate.id);
await repo.createAuditLog({
adminUserId: adminId,
action: 'ADMIN_2FA_RECOVERY_CODE_USED',
resource: 'AdminUser',
resourceId: adminId,
});
return true;
}
}
return false;
}
function signAdminToken(adminId, last2faAt) {
return (0, tokens_1.signActorToken)(adminId, 'admin', { expiresIn: '8h', last2faAt });
}
function toAuditJson(value) {
return JSON.parse(JSON.stringify(value));
}
function ensureAdminBasePath(baseUrl) {
try {
const url = new URL(baseUrl);
const pathname = url.pathname.replace(/\/$/, '');
if (!pathname.endsWith('/admin'))
url.pathname = `${pathname}/admin`;
return url.toString().replace(/\/$/, '');
}
catch {
const trimmed = baseUrl.replace(/\/$/, '');
return trimmed.endsWith('/admin') ? trimmed : `${trimmed}/admin`;
}
}
async function login(email, password, totpCode, recoveryCode) {
const admin = await repo.findAdminByEmail(email);
if (!admin || !admin.isActive)
return null;
const valid = await bcryptjs_1.default.compare(password, admin.passwordHash);
if (!valid)
return null;
if (admin.totpEnabled) {
if (!totpCode && !recoveryCode)
return { totpRequired: true };
const validTotp = totpCode
? otplib_1.authenticator.verify({ token: totpCode, secret: admin.totpSecret })
: false;
const validRecoveryCode = !validTotp && recoveryCode
? await consumeAdminRecoveryCode(admin.id, recoveryCode)
: false;
if (!validTotp && !validRecoveryCode) {
return { invalidTotp: true };
}
}
await repo.updateAdminLastLogin(admin.id);
await repo.createAuditLog({
adminUserId: admin.id,
action: 'ADMIN_LOGIN',
resource: 'AdminUser',
resourceId: admin.id,
});
return presenter.presentAdminSession(admin, signAdminToken(admin.id, admin.totpEnabled ? Date.now() : undefined));
}
async function setupTotp(adminId, email) {
const secret = otplib_1.authenticator.generateSecret();
await repo.updateAdminTotpSecret(adminId, secret);
const otpauth = otplib_1.authenticator.keyuri(email, 'RentalDriveGo Admin', secret);
const qrCode = await qrcode_1.default.toDataURL(otpauth);
return { secret, qrCode };
}
async function verifyTotp(adminId, code) {
const admin = await repo.findAdminByIdOrThrow(adminId);
if (!admin.totpSecret)
return false;
const valid = otplib_1.authenticator.verify({ token: code, secret: admin.totpSecret });
if (!valid)
return false;
await repo.enableAdminTotp(adminId);
await repo.createAuditLog({
adminUserId: adminId,
action: 'ADMIN_2FA_VERIFIED',
resource: 'AdminUser',
resourceId: adminId,
});
const recoveryCodes = await issueAdminRecoveryCodes(adminId);
return {
...presenter.presentAdminSession({ ...admin, totpEnabled: true }, signAdminToken(adminId, Date.now())),
recoveryCodes,
};
}
async function regenerateRecoveryCodes(adminId) {
return { recoveryCodes: await issueAdminRecoveryCodes(adminId) };
}
async function forgotPassword(email) {
const admin = await repo.findAdminByEmail(email);
if (!admin || !admin.isActive)
return;
const rawToken = crypto_1.default.randomBytes(32).toString('hex');
const expiresAt = new Date(Date.now() + ADMIN_RESET_TTL_MINUTES * 60 * 1000);
await repo.setAdminPasswordReset(admin.id, rawToken, expiresAt);
const adminUrl = ensureAdminBasePath(process.env.ADMIN_URL ?? process.env.NEXT_PUBLIC_ADMIN_URL ?? 'http://localhost:3000/admin');
const resetUrl = `${adminUrl}/reset-password?token=${rawToken}`;
await (0, notificationService_1.sendTransactionalEmail)({
to: admin.email,
subject: 'Reset your RentalDriveGo admin password',
html: `<p>Hi ${admin.firstName},</p><p><a href="${resetUrl}">Reset password</a></p><p>Expires in ${ADMIN_RESET_TTL_MINUTES} minutes.</p>`,
text: `Hi ${admin.firstName},\n\nReset here: ${resetUrl}\n\nExpires in ${ADMIN_RESET_TTL_MINUTES} minutes.`,
}).catch((err) => console.error('[AdminForgotPassword]', err?.message));
}
async function resetPassword(token, password) {
const admin = await repo.findAdminByResetToken(token);
if (!admin)
return false;
await repo.updateAdminPassword(admin.id, await bcryptjs_1.default.hash(password, 12));
return true;
}
async function listCompanies(query) {
const { data, total } = await repo.listCompaniesPage(query);
return presenter.presentPaginated(data, total, query.page, query.pageSize);
}
function getCompany(id) {
return repo.getCompanyDetail(id);
}
async function updateCompany(id, body, adminId, ip) {
const before = await repo.getCompanyUpdateSnapshot(id);
const updated = await repo.applyCompanyUpdate(id, body, before);
await repo.createAuditLog({
adminUserId: adminId,
action: 'UPDATE_COMPANY',
resource: 'Company',
resourceId: id,
companyId: id,
before: toAuditJson(before),
after: toAuditJson(body),
ipAddress: ip,
});
return updated;
}
async function setCompanyStatus(id, status, reason, adminId, ip) {
const before = await repo.getCompanyUpdateSnapshot(id);
const updated = await repo.updateCompanyStatus(id, status);
await repo.createAuditLog({
adminUserId: adminId,
action: `SET_COMPANY_STATUS_${status}`,
resource: 'Company',
resourceId: id,
companyId: id,
before: { status: before.status },
after: { status },
note: reason,
ipAddress: ip,
});
return updated;
}
async function deleteCompany(id, adminId, ip) {
await repo.deleteCompany(id);
await repo.createAuditLog({
adminUserId: adminId,
action: 'DELETE_COMPANY',
resource: 'Company',
resourceId: id,
ipAddress: ip,
});
}
async function impersonateCompany(id, adminId, ip, reason, durationMinutes = 15) {
const company = await repo.getCompanyForImpersonation(id);
const ttlMinutes = Math.min(Math.max(durationMinutes, 1), 30);
const employeeId = company.employees[0]?.id;
if (!employeeId)
throw new Error('Company has no employee account to impersonate');
const token = (0, tokens_1.signActorToken)(employeeId, 'employee', { expiresIn: `${ttlMinutes}m` });
await repo.createAuditLog({
adminUserId: adminId,
action: 'IMPERSONATE_COMPANY',
resource: 'Company',
resourceId: id,
companyId: id,
note: reason,
before: { originalAdminId: adminId },
after: { targetCompanyId: id, durationMinutes: ttlMinutes },
ipAddress: ip,
});
return { token, expiresIn: ttlMinutes * 60, impersonation: { companyId: id, reason, durationMinutes: ttlMinutes } };
}
async function listRenters(query) {
const { data, total } = await repo.listRentersPage(query);
return presenter.presentPaginated(data, total, query.page, query.pageSize);
}
function setRenterActive(id, isActive) {
return repo.updateRenterActive(id, isActive);
}
function getPlatformMetrics() {
return repo.getPlatformMetricCounts();
}
async function listNotifications(query) {
const { data, total } = await repo.listNotificationsPage(query);
return presenter.presentPaginated(data, total, query.page, query.pageSize);
}
async function getAuditLogs(query) {
const { data, total } = await repo.listAuditLogsPage(query);
return presenter.presentPaginated(data, total, query.page, query.pageSize);
}
async function listAdmins() {
const admins = await repo.listAdmins();
return admins.map((admin) => presenter.presentAdminUser(admin));
}
async function createAdmin(body) {
const admin = await repo.createAdmin({
...body,
passwordHash: await bcryptjs_1.default.hash(body.password, 12),
});
return presenter.presentAdminUser(admin);
}
async function updateAdmin(id, body) {
const admin = await repo.updateAdmin(id, {
...(body.email !== undefined ? { email: body.email } : {}),
...(body.firstName !== undefined ? { firstName: body.firstName } : {}),
...(body.lastName !== undefined ? { lastName: body.lastName } : {}),
...(body.role !== undefined ? { role: body.role } : {}),
...(body.isActive !== undefined ? { isActive: body.isActive } : {}),
...(body.password ? { passwordHash: await bcryptjs_1.default.hash(body.password, 12) } : {}),
});
return presenter.presentAdminUser(admin);
}
function updateAdminRole(id, role) {
return repo.updateAdminRole(id, role);
}
async function updateAdminPermissions(id, permissions) {
const admin = await repo.replaceAdminPermissions(id, permissions);
return presenter.presentAdminUser(admin);
}
async function getBilling(query) {
const { data, total, stats } = await billingService.listBillingAccounts(query);
return presenter.presentPaginated(data, total, query.page, query.pageSize, { stats });
}
async function getCompanyInvoices(companyId, query) {
const detail = await billingService.getBillingAccountDetail(companyId);
const start = (query.page - 1) * query.pageSize;
const end = start + query.pageSize;
const data = detail.invoices.slice(start, end);
const total = detail.invoices.length;
return presenter.presentPaginated(data, total, query.page, query.pageSize);
}
async function getInvoicePdf(invoiceId) {
return billingService.getInvoicePdf(invoiceId);
}
function getBillingAccountDetail(companyId) {
return billingService.getBillingAccountDetail(companyId);
}
function updateBillingAccount(billingAccountId, data, adminId, ip) {
return billingService.updateBillingAccount(billingAccountId, data, adminId, ip);
}
function setDunningPaused(billingAccountId, paused, adminId, ip) {
return billingService.setDunningPaused(billingAccountId, paused, adminId, ip);
}
function createBillingInvoice(billingAccountId, data, adminId, ip) {
return billingService.createDraftInvoice(billingAccountId, data, adminId, ip);
}
function finalizeBillingInvoice(invoiceId, adminId, ip) {
return billingService.finalizeInvoice(invoiceId, adminId, ip);
}
function payBillingInvoice(invoiceId, data, adminId, ip) {
return billingService.payInvoice(invoiceId, data, adminId, ip);
}
function retryBillingInvoicePayment(invoiceId, data, adminId, ip) {
return billingService.retryInvoicePayment(invoiceId, data, adminId, ip);
}
function voidBillingInvoice(invoiceId, reason, adminId, ip) {
return billingService.voidInvoice(invoiceId, reason, adminId, ip);
}
function markBillingInvoiceUncollectible(invoiceId, reason, adminId, ip) {
return billingService.markInvoiceUncollectible(invoiceId, reason, adminId, ip);
}
function issueBillingCreditNote(invoiceId, data, adminId, ip) {
return billingService.issueCreditNote(invoiceId, data, adminId, ip);
}
function issueBillingRefund(invoiceId, data, adminId, ip) {
return billingService.issueRefund(invoiceId, data, adminId, ip);
}
function getMarketplaceHomepage() {
return (0, platformContentService_1.getMarketplaceHomepageContent)();
}
async function updateMarketplaceHomepage(homepage, adminId, ip) {
const saved = await (0, platformContentService_1.saveMarketplaceHomepageContent)(homepage);
await repo.createAuditLog({
adminUserId: adminId,
action: 'UPDATE',
resource: 'MarketplaceHomepage',
after: toAuditJson(saved),
ipAddress: ip,
userAgent: undefined,
});
return saved;
}
function getPricingConfigs() {
return repo.listPricingConfigs();
}
async function updatePricingConfigs(entries, adminId, ip) {
const results = await Promise.all(entries.map((e) => repo.upsertPricingConfig(e.plan, e.billingPeriod, e.amount, adminId)));
await repo.createAuditLog({
adminUserId: adminId,
action: 'UPDATE',
resource: 'PricingConfig',
after: toAuditJson(results),
ipAddress: ip,
userAgent: undefined,
});
return results;
}
function listPlanFeatures() {
return repo.listPlanFeatures();
}
async function createPlanFeature(data, adminId, ip) {
const feature = await repo.createPlanFeature(data);
await repo.createAuditLog({
adminUserId: adminId,
action: 'CREATE',
resource: 'PlanFeature',
after: toAuditJson(feature),
ipAddress: ip,
userAgent: undefined,
});
return feature;
}
async function updatePlanFeature(id, data, adminId, ip) {
const feature = await repo.updatePlanFeature(id, data);
await repo.createAuditLog({
adminUserId: adminId,
action: 'UPDATE',
resource: 'PlanFeature',
resourceId: id,
after: toAuditJson(feature),
ipAddress: ip,
userAgent: undefined,
});
return feature;
}
async function deletePlanFeature(id, adminId, ip) {
await repo.deletePlanFeature(id);
await repo.createAuditLog({
adminUserId: adminId,
action: 'DELETE',
resource: 'PlanFeature',
resourceId: id,
ipAddress: ip,
userAgent: undefined,
});
}
// ─── Promotions ────────────────────────────────────────────────────────────
function listPromotions() {
return repo.listPromotions();
}
async function createPromotion(data, adminId, ip) {
const promo = await repo.createPromotion({ ...data, createdBy: adminId });
await repo.createAuditLog({
adminUserId: adminId, action: 'CREATE', resource: 'PricingPromotion',
after: toAuditJson(promo), ipAddress: ip, userAgent: undefined,
});
return promo;
}
async function updatePromotion(id, data, adminId, ip) {
const promo = await repo.updatePromotion(id, data);
await repo.createAuditLog({
adminUserId: adminId, action: 'UPDATE', resource: 'PricingPromotion',
after: toAuditJson(promo), ipAddress: ip, userAgent: undefined,
});
return promo;
}
async function deletePromotion(id, adminId, ip) {
await repo.deletePromotion(id);
await repo.createAuditLog({
adminUserId: adminId, action: 'DELETE', resource: 'PricingPromotion',
entityId: id, ipAddress: ip, userAgent: undefined,
});
}
//# sourceMappingURL=admin.service.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=analytics.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"analytics.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/analytics/analytics.routes.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAA;AAsCvB,eAAe,MAAM,CAAA"}
+90
View File
@@ -0,0 +1,90 @@
"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 requireRole_1 = require("../../middleware/requireRole");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const service = __importStar(require("./analytics.service"));
const analytics_schemas_1 = require("./analytics.schemas");
const router = (0, express_1.Router)();
router.use(requireCompanyAuth_1.requireCompanyAuth, requireTenant_1.requireTenant, requireSubscription_1.requireSubscription);
router.get('/summary', async (req, res, next) => {
try {
const { period } = (0, validate_1.parseQuery)(analytics_schemas_1.summaryQuerySchema, req);
(0, respond_1.ok)(res, await service.getSummary(req.companyId, period));
}
catch (err) {
next(err);
}
});
router.get('/dashboard', async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getDashboard(req.companyId));
}
catch (err) {
next(err);
}
});
router.get('/sources', async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getSources(req.companyId));
}
catch (err) {
next(err);
}
});
router.get('/report', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const query = (0, validate_1.parseQuery)(analytics_schemas_1.reportQuerySchema, req);
const result = await service.getReport(req.companyId, query);
if (result.format === 'CSV') {
const start = result.startDate.toISOString().slice(0, 10);
const end = result.endDate.toISOString().slice(0, 10);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename="report-${start}-${end}.csv"`);
return res.send(result.csv);
}
(0, respond_1.ok)(res, result.report);
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=analytics.routes.js.map
@@ -0,0 +1 @@
{"version":3,"file":"analytics.routes.js","sourceRoot":"","sources":["../../../src/modules/analytics/analytics.routes.ts"],"names":[],"mappings":"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"}
+25
View File
@@ -0,0 +1,25 @@
import { z } from 'zod';
export declare const summaryQuerySchema: z.ZodObject<{
period: z.ZodDefault<z.ZodString>;
}, "strip", z.ZodTypeAny, {
period: string;
}, {
period?: string | undefined;
}>;
export declare const reportQuerySchema: z.ZodObject<{
from: z.ZodOptional<z.ZodString>;
to: z.ZodOptional<z.ZodString>;
format: z.ZodDefault<z.ZodString>;
period: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
format: string;
from?: string | undefined;
to?: string | undefined;
period?: string | undefined;
}, {
format?: string | undefined;
from?: string | undefined;
to?: string | undefined;
period?: string | undefined;
}>;
//# sourceMappingURL=analytics.schemas.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"analytics.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/analytics/analytics.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,kBAAkB;;;;;;EAE7B,CAAA;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAA"}
+14
View File
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reportQuerySchema = exports.summaryQuerySchema = void 0;
const zod_1 = require("zod");
exports.summaryQuerySchema = zod_1.z.object({
period: zod_1.z.string().default('30d'),
});
exports.reportQuerySchema = zod_1.z.object({
from: zod_1.z.string().optional(),
to: zod_1.z.string().optional(),
format: zod_1.z.string().default('JSON'),
period: zod_1.z.string().optional(),
});
//# sourceMappingURL=analytics.schemas.js.map
@@ -0,0 +1 @@
{"version":3,"file":"analytics.schemas.js","sourceRoot":"","sources":["../../../src/modules/analytics/analytics.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAClC,CAAC,CAAA;AAEW,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,IAAI,EAAI,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAM,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAA"}
+70
View File
@@ -0,0 +1,70 @@
export declare function getSummary(companyId: string, period: string): Promise<{
totalReservations: any;
activeVehicles: any;
totalRevenue: any;
totalCustomers: any;
period: string;
}>;
export declare function getDashboard(companyId: string): Promise<{
kpis: {
totalBookings: any;
activeVehicles: any;
monthlyRevenue: any;
totalCustomers: any;
bookingsChange: number;
vehiclesChange: number;
revenueChange: number;
customersChange: number;
};
recentReservations: {
id: any;
bookingRef: any;
customerName: string;
vehicleName: string;
startDate: any;
endDate: any;
status: any;
totalAmount: any;
}[];
sourceBreakdown: {
source: any;
count: any;
revenue: any;
}[];
subscription: {
status: any;
planName: any;
trialEndsAt: any;
} | null;
}>;
export declare function getSources(companyId: string): Promise<any>;
export declare function getReport(companyId: string, query: {
from?: string;
to?: string;
format: string;
period?: string;
}): Promise<{
report: {
summary: {
totalReservations: any;
totalRentalRevenue: any;
totalDiscounts: any;
totalInsurance: any;
totalAdditionalDrivers: any;
totalPricingRulesAdj: any;
totalDeposits: any;
totalCollected: any;
averageRentalDays: number;
};
rows: any;
period: {
startDate: Date;
endDate: Date;
};
};
startDate: Date;
endDate: Date;
format: string;
csv: string | null;
}>;
//# sourceMappingURL=analytics.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"analytics.service.d.ts","sourceRoot":"","sources":["../../../src/modules/analytics/analytics.service.ts"],"names":[],"mappings":"AAkBA,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;GAUjE;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDnD;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,gBAEjD;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;GAOxH"}
+96
View File
@@ -0,0 +1,96 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSummary = getSummary;
exports.getDashboard = getDashboard;
exports.getSources = getSources;
exports.getReport = getReport;
const prisma_1 = require("../../lib/prisma");
const financialReportService_1 = require("../../services/financialReportService");
function getRangeFromPeriod(period) {
const now = new Date();
switch (period) {
case 'WEEKLY': return { from: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000), to: now };
case 'QUARTERLY': return { from: new Date(now.getFullYear(), now.getMonth() - 2, 1), to: now };
case 'ANNUAL': return { from: new Date(now.getFullYear(), 0, 1), to: now };
default: return { from: new Date(now.getFullYear(), now.getMonth(), 1), to: now };
}
}
function pct(current, previous) {
if (previous === 0)
return current > 0 ? 100 : 0;
return Math.round(((current - previous) / previous) * 100);
}
async function getSummary(companyId, period) {
const days = parseInt(period.replace('d', '')) || 30;
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
const [totalReservations, activeVehicles, totalRevenue, totalCustomers] = await Promise.all([
prisma_1.prisma.reservation.count({ where: { companyId, createdAt: { gte: since } } }),
prisma_1.prisma.vehicle.count({ where: { companyId, status: 'AVAILABLE' } }),
prisma_1.prisma.reservation.aggregate({ where: { companyId, status: { in: ['COMPLETED'] }, createdAt: { gte: since } }, _sum: { totalAmount: true } }),
prisma_1.prisma.customer.count({ where: { companyId } }),
]);
return { totalReservations, activeVehicles, totalRevenue: totalRevenue._sum.totalAmount ?? 0, totalCustomers, period };
}
async function getDashboard(companyId) {
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const prevMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const prevMonthEnd = new Date(monthStart.getTime() - 1);
const [totalBookings, previousBookings, activeVehicles, previousActiveVehicles, totalCustomers, previousCustomers, monthlyRevenueAgg, previousRevenueAgg, recentReservations, sourceGroups, subscription,] = await Promise.all([
prisma_1.prisma.reservation.count({ where: { companyId, createdAt: { gte: monthStart } } }),
prisma_1.prisma.reservation.count({ where: { companyId, createdAt: { gte: prevMonthStart, lte: prevMonthEnd } } }),
prisma_1.prisma.vehicle.count({ where: { companyId, status: { in: ['AVAILABLE', 'RENTED'] } } }),
prisma_1.prisma.vehicle.count({ where: { companyId, createdAt: { lte: prevMonthEnd }, status: { in: ['AVAILABLE', 'RENTED'] } } }),
prisma_1.prisma.customer.count({ where: { companyId } }),
prisma_1.prisma.customer.count({ where: { companyId, createdAt: { lte: prevMonthEnd } } }),
prisma_1.prisma.reservation.aggregate({ where: { companyId, status: { in: ['CONFIRMED', 'ACTIVE', 'COMPLETED'] }, createdAt: { gte: monthStart } }, _sum: { totalAmount: true } }),
prisma_1.prisma.reservation.aggregate({ where: { companyId, status: { in: ['CONFIRMED', 'ACTIVE', 'COMPLETED'] }, createdAt: { gte: prevMonthStart, lte: prevMonthEnd } }, _sum: { totalAmount: true } }),
prisma_1.prisma.reservation.findMany({ where: { companyId }, include: { customer: true, vehicle: true }, orderBy: { createdAt: 'desc' }, take: 8 }),
prisma_1.prisma.reservation.groupBy({ by: ['source'], where: { companyId }, _count: { id: true }, _sum: { totalAmount: true } }),
prisma_1.prisma.subscription.findUnique({ where: { companyId } }),
]);
return {
kpis: {
totalBookings,
activeVehicles,
monthlyRevenue: monthlyRevenueAgg._sum.totalAmount ?? 0,
totalCustomers,
bookingsChange: pct(totalBookings, previousBookings),
vehiclesChange: pct(activeVehicles, previousActiveVehicles),
revenueChange: pct(monthlyRevenueAgg._sum.totalAmount ?? 0, previousRevenueAgg._sum.totalAmount ?? 0),
customersChange: pct(totalCustomers, previousCustomers),
},
recentReservations: recentReservations.map((r) => ({
id: r.id,
bookingRef: r.contractNumber ?? r.id.slice(-8).toUpperCase(),
customerName: `${r.customer.firstName} ${r.customer.lastName}`,
vehicleName: `${r.vehicle.make} ${r.vehicle.model}`,
startDate: r.startDate,
endDate: r.endDate,
status: r.status,
totalAmount: r.totalAmount,
})),
sourceBreakdown: sourceGroups.map((g) => ({
source: g.source,
count: g._count.id,
revenue: g._sum.totalAmount ?? 0,
})),
subscription: subscription ? {
status: subscription.status,
planName: subscription.plan,
trialEndsAt: subscription.trialEndAt,
} : null,
};
}
async function getSources(companyId) {
return prisma_1.prisma.reservation.groupBy({ by: ['source'], where: { companyId }, _count: { id: true } });
}
async function getReport(companyId, query) {
const accountingSettings = await prisma_1.prisma.accountingSettings.findUnique({ where: { companyId } });
const derivedRange = getRangeFromPeriod(query.period || accountingSettings?.reportingPeriod || 'MONTHLY');
const startDate = query.from ? new Date(query.from) : derivedRange.from;
const endDate = query.to ? new Date(query.to) : derivedRange.to;
const report = await (0, financialReportService_1.generateFinancialReport)(companyId, startDate, endDate);
return { report, startDate, endDate, format: query.format, csv: query.format === 'CSV' ? (0, financialReportService_1.toCsv)(report.rows) : null };
}
//# sourceMappingURL=analytics.service.js.map
File diff suppressed because one or more lines are too long
+50
View File
@@ -0,0 +1,50 @@
import { prisma } from '../../lib/prisma';
type DbClient = Pick<typeof prisma, 'company' | 'brandSettings' | 'contractSettings' | 'subscription' | 'employee'>;
type CompanySignupInput = {
companyName: string;
legalName: string;
slug: string;
ownerEmail: string;
companyEmail: string;
companyPhone: string;
streetAddress: string;
city: string;
country: string;
zipCode: string;
legalForm: string;
managerName: string;
iceNumber: string;
taxId: string;
operatingLicenseNumber: string;
operatingLicenseIssuedAt: string;
operatingLicenseIssuedBy: string;
fax?: string;
yearsActive: string;
representativeName?: string;
representativeTitle?: string;
responsibleName: string;
responsibleRole: string;
responsibleIdentityNumber: string;
responsibleQualification?: string;
responsiblePhone: string;
responsibleEmail: string;
currency: 'MAD';
registrationNumber: string;
plan: 'STARTER' | 'GROWTH' | 'PRO';
billingPeriod: 'MONTHLY' | 'ANNUAL';
preferredLanguage: 'en' | 'fr' | 'ar';
firstName: string;
lastName: string;
passwordHash: string;
now: Date;
trialEndAt: Date;
};
export declare function findCompanyBySlug(slug: string): any;
export declare function findCompanyByEmail(email: string): any;
export declare function findEmployeeByEmail(email: string): any;
export declare function createCompanySignup(db: DbClient, input: CompanySignupInput): Promise<{
company: any;
employee: any;
}>;
export {};
//# sourceMappingURL=auth.company.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,KAAK,QAAQ,GAAG,IAAI,CAAC,OAAO,MAAM,EAAE,SAAS,GAAG,eAAe,GAAG,kBAAkB,GAAG,cAAc,GAAG,UAAU,CAAC,CAAA;AAEnH,KAAK,kBAAkB,GAAG;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,sBAAsB,EAAE,MAAM,CAAA;IAC9B,wBAAwB,EAAE,MAAM,CAAA;IAChC,wBAAwB,EAAE,MAAM,CAAA;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,yBAAyB,EAAE,MAAM,CAAA;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAA;IACf,kBAAkB,EAAE,MAAM,CAAA;IAC1B,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;IAClC,aAAa,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,iBAAiB,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,IAAI,CAAA;IACT,UAAU,EAAE,IAAI,CAAA;CACjB,CAAA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,OAE7C;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,OAE/C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,OAEhD;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,kBAAkB;;;GAyFhF"}
+102
View File
@@ -0,0 +1,102 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findCompanyBySlug = findCompanyBySlug;
exports.findCompanyByEmail = findCompanyByEmail;
exports.findEmployeeByEmail = findEmployeeByEmail;
exports.createCompanySignup = createCompanySignup;
const prisma_1 = require("../../lib/prisma");
function findCompanyBySlug(slug) {
return prisma_1.prisma.company.findUnique({ where: { slug } });
}
function findCompanyByEmail(email) {
return prisma_1.prisma.company.findUnique({ where: { email } });
}
function findEmployeeByEmail(email) {
return prisma_1.prisma.employee.findFirst({ where: { email } });
}
async function createCompanySignup(db, input) {
const company = await db.company.create({
data: {
name: input.companyName,
slug: input.slug,
email: input.companyEmail,
phone: input.companyPhone,
address: {
streetAddress: input.streetAddress,
city: input.city,
country: input.country,
zipCode: input.zipCode,
legalName: input.legalName,
legalForm: input.legalForm,
companyEmail: input.companyEmail,
managerName: input.managerName,
iceNumber: input.iceNumber,
operatingLicenseNumber: input.operatingLicenseNumber,
operatingLicenseIssuedAt: input.operatingLicenseIssuedAt,
operatingLicenseIssuedBy: input.operatingLicenseIssuedBy,
fax: input.fax || null,
yearsActive: input.yearsActive,
representativeName: input.representativeName || null,
representativeTitle: input.representativeTitle || null,
responsibleName: input.responsibleName,
responsibleRole: input.responsibleRole,
responsibleIdentityNumber: input.responsibleIdentityNumber,
responsibleQualification: input.responsibleQualification || null,
responsiblePhone: input.responsiblePhone,
responsibleEmail: input.responsibleEmail,
},
status: 'TRIALING',
},
});
await db.brandSettings.create({
data: {
companyId: company.id,
displayName: input.companyName,
subdomain: input.slug,
publicEmail: input.companyEmail,
publicPhone: input.companyPhone,
publicAddress: input.streetAddress,
publicCity: input.city,
publicCountry: input.country,
defaultLocale: input.preferredLanguage,
defaultCurrency: input.currency,
},
});
await db.contractSettings.create({
data: {
companyId: company.id,
legalName: input.legalName,
registrationNumber: input.registrationNumber,
taxId: input.taxId,
},
});
await db.subscription.create({
data: {
companyId: company.id,
plan: input.plan,
billingPeriod: input.billingPeriod,
currency: input.currency,
status: 'TRIALING',
trialStartAt: input.now,
trialEndAt: input.trialEndAt,
currentPeriodStart: input.now,
currentPeriodEnd: input.trialEndAt,
},
});
const employee = await db.employee.create({
data: {
companyId: company.id,
clerkUserId: `local_owner_${company.id}`,
firstName: input.firstName,
lastName: input.lastName,
email: input.ownerEmail,
phone: input.companyPhone,
passwordHash: input.passwordHash,
role: 'OWNER',
preferredLanguage: input.preferredLanguage,
isActive: true,
},
});
return { company, employee };
}
//# sourceMappingURL=auth.company.repo.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.repo.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.repo.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=auth.company.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.routes.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,MAAM,4CAAW,CAAA;AAmBvB,eAAe,MAAM,CAAA"}
+60
View File
@@ -0,0 +1,60 @@
"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 validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const auth_company_schemas_1 = require("./auth.company.schemas");
const service = __importStar(require("./auth.company.service"));
const router = (0, express_1.Router)();
router.post('/signup', async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(auth_company_schemas_1.companySignupSchema, req);
(0, respond_1.created)(res, await service.signup(body));
}
catch (err) {
next(err);
}
});
router.post('/complete-signup', (_req, res) => {
service.completeSignupDisabled();
res.end();
});
router.post('/verify-email', (_req, res) => {
service.verifyEmailDisabled();
res.end();
});
exports.default = router;
//# sourceMappingURL=auth.company.routes.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.routes.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.routes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAgC;AAChC,kDAA+C;AAC/C,gDAA4C;AAC5C,iEAA4D;AAC5D,gEAAiD;AAEjD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAA;AAEvB,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,oBAAS,EAAC,0CAAmB,EAAE,GAAG,CAAC,CAAA;QAChD,IAAA,iBAAO,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC5C,OAAO,CAAC,sBAAsB,EAAE,CAAA;IAChC,GAAG,CAAC,GAAG,EAAE,CAAA;AACX,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACzC,OAAO,CAAC,mBAAmB,EAAE,CAAA;IAC7B,GAAG,CAAC,GAAG,EAAE,CAAA;AACX,CAAC,CAAC,CAAA;AAEF,kBAAe,MAAM,CAAA"}
+108
View File
@@ -0,0 +1,108 @@
import { z } from 'zod';
export declare const companySignupSchema: z.ZodObject<{
firstName: z.ZodString;
lastName: z.ZodString;
email: z.ZodString;
password: z.ZodString;
companyName: z.ZodString;
legalName: z.ZodString;
legalForm: z.ZodString;
registrationNumber: z.ZodString;
iceNumber: z.ZodString;
taxId: z.ZodString;
operatingLicenseNumber: z.ZodString;
operatingLicenseIssuedAt: z.ZodString;
operatingLicenseIssuedBy: z.ZodString;
streetAddress: z.ZodString;
city: z.ZodString;
country: z.ZodString;
zipCode: z.ZodString;
companyPhone: z.ZodString;
companyEmail: z.ZodString;
fax: z.ZodOptional<z.ZodString>;
yearsActive: z.ZodString;
representativeName: z.ZodOptional<z.ZodString>;
representativeTitle: z.ZodOptional<z.ZodString>;
responsibleName: z.ZodString;
responsibleRole: z.ZodString;
responsibleIdentityNumber: z.ZodString;
responsibleQualification: z.ZodOptional<z.ZodString>;
responsiblePhone: z.ZodString;
responsibleEmail: z.ZodString;
preferredLanguage: z.ZodDefault<z.ZodEnum<["en", "fr", "ar"]>>;
plan: z.ZodEnum<["STARTER", "GROWTH", "PRO"]>;
billingPeriod: z.ZodEnum<["MONTHLY", "ANNUAL"]>;
currency: z.ZodLiteral<"MAD">;
paymentProvider: z.ZodEnum<["AMANPAY", "PAYPAL"]>;
}, "strip", z.ZodTypeAny, {
firstName: string;
lastName: string;
email: string;
currency: "MAD";
password: string;
companyName: string;
legalName: string;
legalForm: string;
registrationNumber: string;
iceNumber: string;
taxId: string;
operatingLicenseNumber: string;
operatingLicenseIssuedAt: string;
operatingLicenseIssuedBy: string;
streetAddress: string;
city: string;
country: string;
zipCode: string;
companyPhone: string;
companyEmail: string;
yearsActive: string;
responsibleName: string;
responsibleRole: string;
responsibleIdentityNumber: string;
responsiblePhone: string;
responsibleEmail: string;
preferredLanguage: "en" | "fr" | "ar";
plan: "STARTER" | "GROWTH" | "PRO";
billingPeriod: "MONTHLY" | "ANNUAL";
paymentProvider: "AMANPAY" | "PAYPAL";
fax?: string | undefined;
representativeName?: string | undefined;
representativeTitle?: string | undefined;
responsibleQualification?: string | undefined;
}, {
firstName: string;
lastName: string;
email: string;
currency: "MAD";
password: string;
companyName: string;
legalName: string;
legalForm: string;
registrationNumber: string;
iceNumber: string;
taxId: string;
operatingLicenseNumber: string;
operatingLicenseIssuedAt: string;
operatingLicenseIssuedBy: string;
streetAddress: string;
city: string;
country: string;
zipCode: string;
companyPhone: string;
companyEmail: string;
yearsActive: string;
responsibleName: string;
responsibleRole: string;
responsibleIdentityNumber: string;
responsiblePhone: string;
responsibleEmail: string;
plan: "STARTER" | "GROWTH" | "PRO";
billingPeriod: "MONTHLY" | "ANNUAL";
paymentProvider: "AMANPAY" | "PAYPAL";
fax?: string | undefined;
representativeName?: string | undefined;
representativeTitle?: string | undefined;
responsibleQualification?: string | undefined;
preferredLanguage?: "en" | "fr" | "ar" | undefined;
}>;
//# sourceMappingURL=auth.company.schemas.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmC9B,CAAA"}
+41
View File
@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.companySignupSchema = void 0;
const zod_1 = require("zod");
exports.companySignupSchema = zod_1.z.object({
firstName: zod_1.z.string().min(1).max(80),
lastName: zod_1.z.string().min(1).max(80),
email: zod_1.z.string().email(),
password: zod_1.z.string().min(8).max(128),
companyName: zod_1.z.string().min(2).max(120),
legalName: zod_1.z.string().min(2).max(160),
legalForm: zod_1.z.string().min(1).max(80),
registrationNumber: zod_1.z.string().min(1).max(120),
iceNumber: zod_1.z.string().min(1).max(120),
taxId: zod_1.z.string().min(1).max(120),
operatingLicenseNumber: zod_1.z.string().min(1).max(120),
operatingLicenseIssuedAt: zod_1.z.string().min(1).max(40),
operatingLicenseIssuedBy: zod_1.z.string().min(1).max(160),
streetAddress: zod_1.z.string().min(1).max(200),
city: zod_1.z.string().min(1).max(120),
country: zod_1.z.string().min(1).max(120),
zipCode: zod_1.z.string().min(1).max(40),
companyPhone: zod_1.z.string().min(1).max(80),
companyEmail: zod_1.z.string().email(),
fax: zod_1.z.string().max(80).optional(),
yearsActive: zod_1.z.string().min(1).max(80),
representativeName: zod_1.z.string().max(160).optional(),
representativeTitle: zod_1.z.string().max(120).optional(),
responsibleName: zod_1.z.string().min(1).max(160),
responsibleRole: zod_1.z.string().min(1).max(120),
responsibleIdentityNumber: zod_1.z.string().min(1).max(120),
responsibleQualification: zod_1.z.string().max(200).optional(),
responsiblePhone: zod_1.z.string().min(1).max(80),
responsibleEmail: zod_1.z.string().email(),
preferredLanguage: zod_1.z.enum(['en', 'fr', 'ar']).default('en'),
plan: zod_1.z.enum(['STARTER', 'GROWTH', 'PRO']),
billingPeriod: zod_1.z.enum(['MONTHLY', 'ANNUAL']),
currency: zod_1.z.literal('MAD'),
paymentProvider: zod_1.z.enum(['AMANPAY', 'PAYPAL']),
});
//# sourceMappingURL=auth.company.schemas.js.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.schemas.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.schemas.ts"],"names":[],"mappings":"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"}
+16
View File
@@ -0,0 +1,16 @@
import { companySignupSchema } from './auth.company.schemas';
import type { output } from 'zod';
type CompanySignupInput = output<typeof companySignupSchema>;
export declare function signup(body: CompanySignupInput): Promise<{
companyId: string;
companyName: string;
slug: string;
invitationId: null;
trialEndsAt: string;
nextStep: string;
emailDelivery: {};
}>;
export declare function completeSignupDisabled(): void;
export declare function verifyEmailDisabled(): void;
export {};
//# sourceMappingURL=auth.company.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.service.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAEjC,KAAK,kBAAkB,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAmB5D,wBAAsB,MAAM,CAAC,IAAI,EAAE,kBAAkB;;;;;;;;GAiFpD;AAED,wBAAgB,sBAAsB,SAErC;AAED,wBAAgB,mBAAmB,SAElC"}
+145
View File
@@ -0,0 +1,145 @@
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.signup = signup;
exports.completeSignupDisabled = completeSignupDisabled;
exports.verifyEmailDisabled = verifyEmailDisabled;
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const errors_1 = require("../../http/errors");
const prisma_1 = require("../../lib/prisma");
const notificationService_1 = require("../../services/notificationService");
const notificationLocalizationService_1 = require("../../services/notificationLocalizationService");
const auth_presenter_1 = require("./auth.presenter");
const repo = __importStar(require("./auth.company.repo"));
const TRIAL_PERIOD_DAYS = 90;
function slugify(value) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 50) || 'company';
}
async function generateUniqueSlug(baseName) {
const base = slugify(baseName);
for (let attempt = 0; attempt < 25; attempt++) {
const slug = attempt === 0 ? base : `${base}-${attempt + 1}`;
const existing = await repo.findCompanyBySlug(slug);
if (!existing)
return slug;
}
return `${base}-${Date.now().toString(36)}`;
}
async function signup(body) {
if (await repo.findCompanyByEmail(body.companyEmail)) {
throw new errors_1.AppError('A company account with this contact email already exists', 409, 'company_email_taken');
}
if (await repo.findEmployeeByEmail(body.email)) {
throw new errors_1.AppError('An employee account with this owner email already exists', 409, 'owner_email_taken');
}
const slug = await generateUniqueSlug(body.companyName);
const now = new Date();
const trialEndAt = new Date(now.getTime() + TRIAL_PERIOD_DAYS * 24 * 60 * 60 * 1000);
const passwordHash = await bcryptjs_1.default.hash(body.password, 12);
const result = await prisma_1.prisma.$transaction((tx) => repo.createCompanySignup(tx, {
companyName: body.companyName,
legalName: body.legalName,
slug,
ownerEmail: body.email,
companyEmail: body.companyEmail,
companyPhone: body.companyPhone,
streetAddress: body.streetAddress,
city: body.city,
country: body.country,
zipCode: body.zipCode,
legalForm: body.legalForm,
managerName: `${body.firstName} ${body.lastName}`.trim(),
iceNumber: body.iceNumber,
taxId: body.taxId,
operatingLicenseNumber: body.operatingLicenseNumber,
operatingLicenseIssuedAt: body.operatingLicenseIssuedAt,
operatingLicenseIssuedBy: body.operatingLicenseIssuedBy,
fax: body.fax,
yearsActive: body.yearsActive,
representativeName: body.representativeName,
representativeTitle: body.representativeTitle,
responsibleName: body.responsibleName,
responsibleRole: body.responsibleRole,
responsibleIdentityNumber: body.responsibleIdentityNumber,
responsibleQualification: body.responsibleQualification,
responsiblePhone: body.responsiblePhone,
responsibleEmail: body.responsibleEmail,
currency: body.currency,
registrationNumber: body.registrationNumber,
plan: body.plan,
billingPeriod: body.billingPeriod,
preferredLanguage: body.preferredLanguage,
firstName: body.firstName,
lastName: body.lastName,
passwordHash,
now,
trialEndAt,
}));
const lang = (0, notificationLocalizationService_1.coerceNotificationLocale)(body.preferredLanguage);
const emailResult = await (0, notificationService_1.sendNotification)({
type: 'ACCOUNT_CREATED',
companyId: result.company.id,
employeeId: result.employee.id,
email: body.email,
channels: ['EMAIL', 'IN_APP'],
locale: lang,
templateKey: 'account.created',
templateVariables: {
firstName: body.firstName,
companyName: body.companyName,
planName: (0, notificationLocalizationService_1.localizePlanName)(body.plan, lang),
billingPeriodLabel: (0, notificationLocalizationService_1.localizeBillingPeriod)(body.billingPeriod, lang),
currency: body.currency,
paymentProvider: body.paymentProvider,
trialEndDate: trialEndAt,
},
}).catch(() => []);
const emailDelivery = emailResult.find((entry) => entry.channel === 'EMAIL');
return (0, auth_presenter_1.presentCompanySignup)({
company: result.company,
trialEndAt,
emailDelivery,
});
}
function completeSignupDisabled() {
throw new errors_1.AppError('Clerk-based signup has been removed. Use /auth/company/signup instead.', 410, 'disabled');
}
function verifyEmailDisabled() {
throw new errors_1.AppError('Email verification resend via Clerk has been removed from this project.', 410, 'disabled');
}
//# sourceMappingURL=auth.company.service.js.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.company.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.company.service.ts"],"names":[],"mappings":"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"}
+9
View File
@@ -0,0 +1,9 @@
export declare function findEmployeeWithCompanyById(id: string): any;
export declare function findEmployeeById(id: string): any;
export declare function findEmployeeWithCompanyByEmail(email: string): any;
export declare function findActiveEmployeeByEmail(email: string): any;
export declare function setPasswordResetToken(id: string, passwordResetToken: string, passwordResetExpiresAt: Date): any;
export declare function updatePreferredLanguage(id: string, preferredLanguage: 'en' | 'fr' | 'ar'): any;
export declare function findEmployeeByResetToken(token: string): any;
export declare function resetPassword(id: string, passwordHash: string): any;
//# sourceMappingURL=auth.employee.repo.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.repo.ts"],"names":[],"mappings":"AAEA,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,OAKrD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,OAI1C;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,OAU3D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,OAUtD;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,sBAAsB,EAAE,IAAI,OAKzG;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,OAKxF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,OAOrD;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,OAS7D"}
+75
View File
@@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findEmployeeWithCompanyById = findEmployeeWithCompanyById;
exports.findEmployeeById = findEmployeeById;
exports.findEmployeeWithCompanyByEmail = findEmployeeWithCompanyByEmail;
exports.findActiveEmployeeByEmail = findActiveEmployeeByEmail;
exports.setPasswordResetToken = setPasswordResetToken;
exports.updatePreferredLanguage = updatePreferredLanguage;
exports.findEmployeeByResetToken = findEmployeeByResetToken;
exports.resetPassword = resetPassword;
const prisma_1 = require("../../lib/prisma");
function findEmployeeWithCompanyById(id) {
return prisma_1.prisma.employee.findUnique({
where: { id },
include: { company: true },
});
}
function findEmployeeById(id) {
return prisma_1.prisma.employee.findUnique({
where: { id },
});
}
function findEmployeeWithCompanyByEmail(email) {
return prisma_1.prisma.employee.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
include: { company: true },
});
}
function findActiveEmployeeByEmail(email) {
return prisma_1.prisma.employee.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
isActive: true,
},
});
}
function setPasswordResetToken(id, passwordResetToken, passwordResetExpiresAt) {
return prisma_1.prisma.employee.update({
where: { id },
data: { passwordResetToken, passwordResetExpiresAt },
});
}
function updatePreferredLanguage(id, preferredLanguage) {
return prisma_1.prisma.employee.update({
where: { id },
data: { preferredLanguage },
});
}
function findEmployeeByResetToken(token) {
return prisma_1.prisma.employee.findFirst({
where: {
passwordResetToken: token,
passwordResetExpiresAt: { gt: new Date() },
},
});
}
function resetPassword(id, passwordHash) {
return prisma_1.prisma.employee.update({
where: { id },
data: {
passwordHash,
passwordResetToken: null,
passwordResetExpiresAt: null,
},
});
}
//# sourceMappingURL=auth.employee.repo.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.repo.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.repo.ts"],"names":[],"mappings":";;AAEA,kEAKC;AAED,4CAIC;AAED,wEAUC;AAED,8DAUC;AAED,sDAKC;AAED,0DAKC;AAED,4DAOC;AAED,sCASC;AAvED,6CAAyC;AAEzC,SAAgB,2BAA2B,CAAC,EAAU;IACpD,OAAO,eAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAChC,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,EAAU;IACzC,OAAO,eAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAChC,KAAK,EAAE,EAAE,EAAE,EAAE;KACd,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,8BAA8B,CAAC,KAAa;IAC1D,OAAO,eAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC/B,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,aAAa;aACpB;SACF;QACD,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,yBAAyB,CAAC,KAAa;IACrD,OAAO,eAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC/B,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,aAAa;aACpB;YACD,QAAQ,EAAE,IAAI;SACf;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,qBAAqB,CAAC,EAAU,EAAE,kBAA0B,EAAE,sBAA4B;IACxG,OAAO,eAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI,EAAE,EAAE,kBAAkB,EAAE,sBAAsB,EAAE;KACrD,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,uBAAuB,CAAC,EAAU,EAAE,iBAAqC;IACvF,OAAO,eAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI,EAAE,EAAE,iBAAiB,EAAE;KAC5B,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,wBAAwB,CAAC,KAAa;IACpD,OAAO,eAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC/B,KAAK,EAAE;YACL,kBAAkB,EAAE,KAAK;YACzB,sBAAsB,EAAE,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE;SAC3C;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,aAAa,CAAC,EAAU,EAAE,YAAoB;IAC5D,OAAO,eAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI,EAAE;YACJ,YAAY;YACZ,kBAAkB,EAAE,IAAI;YACxB,sBAAsB,EAAE,IAAI;SAC7B;KACF,CAAC,CAAA;AACJ,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=auth.employee.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.routes.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,MAAM,4CAAW,CAAA;AAiDvB,eAAe,MAAM,CAAA"}
+105
View File
@@ -0,0 +1,105 @@
"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 validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const sessionCookies_1 = require("../../security/sessionCookies");
const menu_service_1 = require("../menu/menu.service");
const auth_employee_schemas_1 = require("./auth.employee.schemas");
const service = __importStar(require("./auth.employee.service"));
const router = (0, express_1.Router)();
router.get('/me', requireCompanyAuth_1.requireCompanyAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getMe(req.employee.id));
}
catch (err) {
next(err);
}
});
router.get('/menu', requireCompanyAuth_1.requireCompanyAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await (0, menu_service_1.getEmployeeMenu)(req.employee.id));
}
catch (err) {
next(err);
}
});
router.post('/login', async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(auth_employee_schemas_1.employeeLoginSchema, req);
const result = await service.login(body);
if ('token' in result)
(0, sessionCookies_1.setSessionCookie)(res, 'employee', result.token, 8 * 60 * 60 * 1000);
(0, respond_1.ok)(res, result);
}
catch (err) {
next(err);
}
});
router.post('/logout', (_req, res) => {
(0, sessionCookies_1.clearSessionCookie)(res, 'employee');
(0, respond_1.ok)(res, { success: true });
});
router.post('/forgot-password', async (req, res, next) => {
try {
const { email } = (0, validate_1.parseBody)(auth_employee_schemas_1.employeeForgotPasswordSchema, req);
(0, respond_1.ok)(res, await service.forgotPassword(email));
}
catch (err) {
next(err);
}
});
router.patch('/me/language', requireCompanyAuth_1.requireCompanyAuth, async (req, res, next) => {
try {
const { language } = (0, validate_1.parseBody)(auth_employee_schemas_1.employeeLanguageSchema, req);
(0, respond_1.ok)(res, await service.updateLanguage(req.employee.id, language));
}
catch (err) {
next(err);
}
});
router.post('/reset-password', async (req, res, next) => {
try {
const { token, password } = (0, validate_1.parseBody)(auth_employee_schemas_1.employeeResetPasswordSchema, req);
(0, respond_1.ok)(res, await service.resetPassword(token, password));
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=auth.employee.routes.js.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.routes.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.routes.ts"],"names":[],"mappings":"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"}
+36
View File
@@ -0,0 +1,36 @@
import { z } from 'zod';
export declare const employeeLoginSchema: z.ZodObject<{
email: z.ZodString;
password: z.ZodString;
}, "strip", z.ZodTypeAny, {
email: string;
password: string;
}, {
email: string;
password: string;
}>;
export declare const employeeForgotPasswordSchema: z.ZodObject<{
email: z.ZodString;
}, "strip", z.ZodTypeAny, {
email: string;
}, {
email: string;
}>;
export declare const employeeLanguageSchema: z.ZodObject<{
language: z.ZodEnum<["en", "fr", "ar"]>;
}, "strip", z.ZodTypeAny, {
language: "en" | "fr" | "ar";
}, {
language: "en" | "fr" | "ar";
}>;
export declare const employeeResetPasswordSchema: z.ZodObject<{
token: z.ZodString;
password: z.ZodString;
}, "strip", z.ZodTypeAny, {
password: string;
token: string;
}, {
password: string;
token: string;
}>;
//# sourceMappingURL=auth.employee.schemas.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;;;;EAEvC,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;EAEjC,CAAA;AAEF,eAAO,MAAM,2BAA2B;;;;;;;;;EAGtC,CAAA"}
+19
View File
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.employeeResetPasswordSchema = exports.employeeLanguageSchema = exports.employeeForgotPasswordSchema = exports.employeeLoginSchema = void 0;
const zod_1 = require("zod");
exports.employeeLoginSchema = zod_1.z.object({
email: zod_1.z.string().email().max(255).trim().toLowerCase(),
password: zod_1.z.string().max(128),
});
exports.employeeForgotPasswordSchema = zod_1.z.object({
email: zod_1.z.string().email().max(255).trim().toLowerCase(),
});
exports.employeeLanguageSchema = zod_1.z.object({
language: zod_1.z.enum(['en', 'fr', 'ar']),
});
exports.employeeResetPasswordSchema = zod_1.z.object({
token: zod_1.z.string().min(1),
password: zod_1.z.string().min(8).max(128),
});
//# sourceMappingURL=auth.employee.schemas.js.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.schemas.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1C,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;CAC9B,CAAC,CAAA;AAEW,QAAA,4BAA4B,GAAG,OAAC,CAAC,MAAM,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;CACxD,CAAC,CAAA;AAEW,QAAA,sBAAsB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC7C,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;CACrC,CAAC,CAAA;AAEW,QAAA,2BAA2B,GAAG,OAAC,CAAC,MAAM,CAAC;IAClD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;CACrC,CAAC,CAAA"}
+67
View File
@@ -0,0 +1,67 @@
import { employeeLanguageSchema, employeeLoginSchema } from './auth.employee.schemas';
import type { output } from 'zod';
type EmployeeLoginInput = output<typeof employeeLoginSchema>;
type EmployeeLanguageInput = output<typeof employeeLanguageSchema>;
export declare function getMe(employeeId: string): Promise<{
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
} | {
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
token: string;
}>;
export declare function login(body: EmployeeLoginInput): Promise<{
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
} | {
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
token: string;
}>;
export declare function forgotPassword(email: string): Promise<{
message: string;
}>;
export declare function updateLanguage(employeeId: string, language: EmployeeLanguageInput['language']): Promise<{
language: "en" | "fr" | "ar";
}>;
export declare function resetPassword(token: string, password: string): Promise<{
message: string;
}>;
export {};
//# sourceMappingURL=auth.employee.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.employee.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.employee.service.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAIjC,KAAK,kBAAkB,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC5D,KAAK,qBAAqB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAuElE,wBAAsB,KAAK,CAAC,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;GAQ7C;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;GAiBnD;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM;;GAwBjD;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC;;GAGnG;AAuBD,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;GASlE"}
+180
View File
@@ -0,0 +1,180 @@
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMe = getMe;
exports.login = login;
exports.forgotPassword = forgotPassword;
exports.updateLanguage = updateLanguage;
exports.resetPassword = resetPassword;
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const crypto_1 = __importDefault(require("crypto"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const tokens_1 = require("../../security/tokens");
const errors_1 = require("../../http/errors");
const notificationService_1 = require("../../services/notificationService");
const emailTranslations_1 = require("../../lib/emailTranslations");
const auth_presenter_1 = require("./auth.presenter");
const repo = __importStar(require("./auth.employee.repo"));
const RESET_TOKEN_TTL_MINUTES = 60;
function signEmployeeToken(employeeId) {
return (0, tokens_1.signActorToken)(employeeId, 'employee', {
expiresIn: (process.env.JWT_EXPIRY ?? '8h'),
});
}
function getEmployeePasswordResetVersion(passwordHash) {
return crypto_1.default
.createHash('sha256')
.update(`${process.env.JWT_SECRET}:${passwordHash ?? 'no-password-set'}`)
.digest('hex');
}
function signEmployeePasswordResetToken(employeeId, passwordHash) {
return jsonwebtoken_1.default.sign({
sub: employeeId,
type: 'employee_password_reset',
pwdv: getEmployeePasswordResetVersion(passwordHash),
}, process.env.JWT_SECRET, {
algorithm: 'HS256',
issuer: 'rentaldrivego-api',
audience: 'employee_password_reset',
expiresIn: `${RESET_TOKEN_TTL_MINUTES}m`,
});
}
function verifyEmployeePasswordResetToken(token) {
try {
const payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'rentaldrivego-api',
audience: 'employee_password_reset',
});
if (typeof payload.sub !== 'string' ||
payload.type !== 'employee_password_reset' ||
typeof payload.pwdv !== 'string') {
return null;
}
return payload;
}
catch {
return null;
}
}
function ensureDashboardBasePath(baseUrl) {
try {
const url = new URL(baseUrl);
const pathname = url.pathname.replace(/\/$/, '');
if (!pathname.endsWith('/dashboard'))
url.pathname = `${pathname}/dashboard`;
return url.toString().replace(/\/$/, '');
}
catch {
const trimmed = baseUrl.replace(/\/$/, '');
return trimmed.endsWith('/dashboard') ? trimmed : `${trimmed}/dashboard`;
}
}
async function getMe(employeeId) {
const employee = await repo.findEmployeeWithCompanyById(employeeId);
if (!employee || !employee.isActive) {
throw new errors_1.AppError('Employee account not found or inactive', 401, 'unauthenticated');
}
return (0, auth_presenter_1.presentEmployeeSession)(employee);
}
async function login(body) {
const employee = await repo.findEmployeeWithCompanyByEmail(body.email);
if (!employee || !employee.isActive) {
throw new errors_1.AppError('Invalid email or password', 401, 'invalid_credentials');
}
if (!employee.passwordHash) {
throw new errors_1.AppError('This account does not have a password yet. Use the invitation or reset email to set one first.', 401, 'password_not_set');
}
const validPassword = await bcryptjs_1.default.compare(body.password, employee.passwordHash);
if (!validPassword) {
throw new errors_1.AppError('Invalid email or password', 401, 'invalid_credentials');
}
return (0, auth_presenter_1.presentEmployeeSession)(employee, signEmployeeToken(employee.id));
}
async function forgotPassword(email) {
const employee = await repo.findActiveEmployeeByEmail(email);
if (employee) {
const resetToken = signEmployeePasswordResetToken(employee.id, employee.passwordHash);
const dashboardUrl = ensureDashboardBasePath(process.env.DASHBOARD_URL ?? process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3000/dashboard');
const resetUrl = `${dashboardUrl}/reset-password?token=${encodeURIComponent(resetToken)}`;
const lang = employee.preferredLanguage ?? 'fr';
await (0, notificationService_1.sendTransactionalEmail)({
to: employee.email,
subject: emailTranslations_1.resetPasswordEmail.subject(lang),
html: emailTranslations_1.resetPasswordEmail.html(resetUrl, employee.firstName, RESET_TOKEN_TTL_MINUTES, lang),
text: emailTranslations_1.resetPasswordEmail.text(resetUrl, employee.firstName, RESET_TOKEN_TTL_MINUTES, lang),
}).catch((err) => {
console.error('[ForgotPassword] Email delivery failed:', err?.message);
console.error('[ForgotPassword] Provider config — resendKey present:', !!process.env.RESEND_API_KEY, '| smtpHost:', process.env.MAIL_HOST ?? 'not set');
});
}
return { message: 'If that email is registered, a reset link has been sent.' };
}
async function updateLanguage(employeeId, language) {
await repo.updatePreferredLanguage(employeeId, language);
return { language };
}
async function findEmployeeFromResetToken(token) {
try {
const employee = await repo.findEmployeeByResetToken(token);
if (employee)
return employee;
}
catch (err) {
console.error('[EmployeeResetPassword] Stored token lookup failed:', err?.message ?? String(err));
}
const payload = verifyEmployeePasswordResetToken(token);
if (!payload)
return null;
const employee = await repo.findEmployeeById(payload.sub);
if (!employee || !employee.isActive)
return null;
if (payload.pwdv !== getEmployeePasswordResetVersion(employee.passwordHash)) {
return null;
}
return employee;
}
async function resetPassword(token, password) {
const employee = await findEmployeeFromResetToken(token);
if (!employee) {
throw new errors_1.AppError('Reset link is invalid or has expired.', 400, 'invalid_token');
}
await repo.resetPassword(employee.id, await bcryptjs_1.default.hash(password, 12));
return { message: 'Password updated successfully. You can now sign in.' };
}
//# sourceMappingURL=auth.employee.service.js.map
File diff suppressed because one or more lines are too long
+98
View File
@@ -0,0 +1,98 @@
type EmployeeWithCompany = {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage?: string | null;
companyId: string;
company: {
name: string;
slug: string;
};
};
type CompanySignupResult = {
company: {
id: string;
name: string;
slug: string;
};
trialEndAt: Date;
emailDelivery?: unknown;
};
type RenterProfile = {
id: string;
firstName: string;
lastName: string;
email: string;
phone: string | null;
preferredLocale: string | null;
preferredCurrency: string | null;
emailVerified: boolean;
savedCompanies: Array<{
companyId: string;
}>;
};
type SavedCompany = {
id: string;
brand: {
displayName: string | null;
subdomain: string | null;
logoUrl: string | null;
} | null;
};
export declare function presentEmployeeSession(employee: EmployeeWithCompany, token?: string): {
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
} | {
employee: {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
preferredLanguage: string | null | undefined;
companyId: string;
companyName: string;
companySlug: string;
};
token: string;
};
export declare function presentCompanySignup(result: CompanySignupResult): {
companyId: string;
companyName: string;
slug: string;
invitationId: null;
trialEndsAt: string;
nextStep: string;
emailDelivery: {};
};
export declare function presentRenterProfile(renter: RenterProfile, companies: SavedCompany[]): {
savedCompanies: {
id: string;
brand: {
displayName: string | null;
subdomain: string | null;
logoUrl: string | null;
} | null;
}[];
id: string;
firstName: string;
lastName: string;
email: string;
phone: string | null;
preferredLocale: string | null;
preferredCurrency: string | null;
emailVerified: boolean;
};
export {};
//# sourceMappingURL=auth.presenter.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.presenter.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.presenter.ts"],"names":[],"mappings":"AAAA,KAAK,mBAAmB,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,UAAU,EAAE,IAAI,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7C,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,GAAG,IAAI,CAAA;CACT,CAAA;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;EAgBnF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB;;;;;;;;EAU/D;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE;;;;yBApCpE,MAAM,GAAG,IAAI;uBACf,MAAM,GAAG,IAAI;qBACf,MAAM,GAAG,IAAI;;;QAhBpB,MAAM;eACC,MAAM;cACP,MAAM;WACT,MAAM;WACN,MAAM,GAAG,IAAI;qBACH,MAAM,GAAG,IAAI;uBACX,MAAM,GAAG,IAAI;mBACjB,OAAO;EAqDvB"}
+43
View File
@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.presentEmployeeSession = presentEmployeeSession;
exports.presentCompanySignup = presentCompanySignup;
exports.presentRenterProfile = presentRenterProfile;
function presentEmployeeSession(employee, token) {
const data = {
employee: {
id: employee.id,
email: employee.email,
firstName: employee.firstName,
lastName: employee.lastName,
role: employee.role,
preferredLanguage: employee.preferredLanguage,
companyId: employee.companyId,
companyName: employee.company.name,
companySlug: employee.company.slug,
},
};
return token ? { token, ...data } : data;
}
function presentCompanySignup(result) {
return {
companyId: result.company.id,
companyName: result.company.name,
slug: result.company.slug,
invitationId: null,
trialEndsAt: result.trialEndAt.toISOString(),
nextStep: 'workspace_created',
emailDelivery: result.emailDelivery ?? { attempted: false, success: false, error: null },
};
}
function presentRenterProfile(renter, companies) {
const companyMap = new Map(companies.map((company) => [company.id, company]));
return {
...renter,
savedCompanies: renter.savedCompanies.map((savedCompany) => ({
id: savedCompany.companyId,
brand: companyMap.get(savedCompany.companyId)?.brand ?? null,
})),
};
}
//# sourceMappingURL=auth.presenter.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.presenter.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.presenter.ts"],"names":[],"mappings":";;AA6CA,wDAgBC;AAED,oDAUC;AAED,oDAUC;AAxCD,SAAgB,sBAAsB,CAAC,QAA6B,EAAE,KAAc;IAClF,MAAM,IAAI,GAAG;QACX,QAAQ,EAAE;YACR,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;YAC7C,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI;YAClC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI;SACnC;KACF,CAAA;IAED,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1C,CAAC;AAED,SAAgB,oBAAoB,CAAC,MAA2B;IAC9D,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE;QAC5B,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QAChC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QACzB,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE;QAC5C,QAAQ,EAAE,mBAAmB;QAC7B,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;KACzF,CAAA;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,MAAqB,EAAE,SAAyB;IACnF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAE7E,OAAO;QACL,GAAG,MAAM;QACT,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YAC3D,EAAE,EAAE,YAAY,CAAC,SAAS;YAC1B,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,KAAK,IAAI,IAAI;SAC7D,CAAC,CAAC;KACJ,CAAA;AACH,CAAC"}
+9
View File
@@ -0,0 +1,9 @@
import type { output } from 'zod';
import { renterUpdateSchema } from './auth.renter.schemas';
type RenterUpdateInput = output<typeof renterUpdateSchema>;
export declare function findRenterProfile(id: string): any;
export declare function findSavedCompanies(companyIds: string[]): any;
export declare function updateRenterProfile(id: string, data: RenterUpdateInput): any;
export declare function updateRenterFcmToken(id: string, fcmToken: string): any;
export {};
//# sourceMappingURL=auth.renter.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.repo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAE1D,KAAK,iBAAiB,GAAG,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE1D,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,OAe3C;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAYtD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,OAKtE;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAKhE"}
+49
View File
@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findRenterProfile = findRenterProfile;
exports.findSavedCompanies = findSavedCompanies;
exports.updateRenterProfile = updateRenterProfile;
exports.updateRenterFcmToken = updateRenterFcmToken;
const prisma_1 = require("../../lib/prisma");
function findRenterProfile(id) {
return prisma_1.prisma.renter.findUniqueOrThrow({
where: { id },
select: {
id: true,
firstName: true,
lastName: true,
email: true,
phone: true,
preferredLocale: true,
preferredCurrency: true,
emailVerified: true,
savedCompanies: { select: { companyId: true } },
},
});
}
function findSavedCompanies(companyIds) {
return companyIds.length === 0
? Promise.resolve([])
: prisma_1.prisma.company.findMany({
where: { id: { in: companyIds } },
select: {
id: true,
brand: {
select: { displayName: true, subdomain: true, logoUrl: true },
},
},
});
}
function updateRenterProfile(id, data) {
return prisma_1.prisma.renter.update({
where: { id },
data,
});
}
function updateRenterFcmToken(id, fcmToken) {
return prisma_1.prisma.renter.update({
where: { id },
data: { fcmToken },
});
}
//# sourceMappingURL=auth.renter.repo.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.repo.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.repo.ts"],"names":[],"mappings":";;AAMA,8CAeC;AAED,gDAYC;AAED,kDAKC;AAED,oDAKC;AAjDD,6CAAyC;AAMzC,SAAgB,iBAAiB,CAAC,EAAU;IAC1C,OAAO,eAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;QACrC,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,IAAI;YACX,eAAe,EAAE,IAAI;YACrB,iBAAiB,EAAE,IAAI;YACvB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;SAChD;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,kBAAkB,CAAC,UAAoB;IACrD,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;QAC5B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrB,CAAC,CAAC,eAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtB,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;YACjC,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACL,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;iBAC9D;aACF;SACF,CAAC,CAAA;AACR,CAAC;AAED,SAAgB,mBAAmB,CAAC,EAAU,EAAE,IAAuB;IACrE,OAAO,eAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI;KACL,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,oBAAoB,CAAC,EAAU,EAAE,QAAgB;IAC/D,OAAO,eAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI,EAAE,EAAE,QAAQ,EAAE;KACnB,CAAC,CAAA;AACJ,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=auth.renter.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.routes.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,MAAM,4CAAW,CAAA;AAgCvB,eAAe,MAAM,CAAA"}
+78
View File
@@ -0,0 +1,78 @@
"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 requireRenterAuth_1 = require("../../middleware/requireRenterAuth");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const auth_renter_schemas_1 = require("./auth.renter.schemas");
const service = __importStar(require("./auth.renter.service"));
const router = (0, express_1.Router)();
router.post('/signup', (_req, res) => {
service.signupDisabled();
res.end();
});
router.post('/login', (_req, res) => {
service.loginDisabled();
res.end();
});
router.get('/me', requireRenterAuth_1.requireRenterAuth, async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.getMe(req.renterId));
}
catch (err) {
next(err);
}
});
router.patch('/me', requireRenterAuth_1.requireRenterAuth, async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(auth_renter_schemas_1.renterUpdateSchema, req);
(0, respond_1.ok)(res, await service.updateMe(req.renterId, body));
}
catch (err) {
next(err);
}
});
router.post('/me/fcm-token', requireRenterAuth_1.requireRenterAuth, async (req, res, next) => {
try {
const { fcmToken } = (0, validate_1.parseBody)(auth_renter_schemas_1.renterFcmTokenSchema, req);
(0, respond_1.ok)(res, await service.updateFcmToken(req.renterId, fcmToken));
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=auth.renter.routes.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.routes.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.routes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAgC;AAChC,0EAAsE;AACtE,kDAA+C;AAC/C,gDAAuC;AACvC,+DAAgF;AAChF,+DAAgD;AAEhD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAA;AAEvB,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACnC,OAAO,CAAC,cAAc,EAAE,CAAA;IACxB,GAAG,CAAC,GAAG,EAAE,CAAA;AACX,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAClC,OAAO,CAAC,aAAa,EAAE,CAAA;IACvB,GAAG,CAAC,GAAG,EAAE,CAAA;AACX,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,qCAAiB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC5D,IAAI,CAAC;QACH,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,qCAAiB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,oBAAS,EAAC,wCAAkB,EAAE,GAAG,CAAC,CAAA;QAC/C,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,qCAAiB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvE,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,oBAAS,EAAC,0CAAoB,EAAE,GAAG,CAAC,CAAA;QACzD,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,kBAAe,MAAM,CAAA"}
+28
View File
@@ -0,0 +1,28 @@
import { z } from 'zod';
export declare const renterUpdateSchema: z.ZodObject<{
firstName: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
preferredLocale: z.ZodOptional<z.ZodEnum<["en", "fr", "ar"]>>;
preferredCurrency: z.ZodOptional<z.ZodLiteral<"MAD">>;
}, "strip", z.ZodTypeAny, {
firstName?: string | undefined;
lastName?: string | undefined;
phone?: string | undefined;
preferredLocale?: "en" | "fr" | "ar" | undefined;
preferredCurrency?: "MAD" | undefined;
}, {
firstName?: string | undefined;
lastName?: string | undefined;
phone?: string | undefined;
preferredLocale?: "en" | "fr" | "ar" | undefined;
preferredCurrency?: "MAD" | undefined;
}>;
export declare const renterFcmTokenSchema: z.ZodObject<{
fcmToken: z.ZodString;
}, "strip", z.ZodTypeAny, {
fcmToken: string;
}, {
fcmToken: string;
}>;
//# sourceMappingURL=auth.renter.schemas.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAM7B,CAAA;AAEF,eAAO,MAAM,oBAAoB;;;;;;EAE/B,CAAA"}
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renterFcmTokenSchema = exports.renterUpdateSchema = void 0;
const zod_1 = require("zod");
exports.renterUpdateSchema = zod_1.z.object({
firstName: zod_1.z.string().min(1).max(100).trim().optional(),
lastName: zod_1.z.string().min(1).max(100).trim().optional(),
phone: zod_1.z.string().max(30).trim().optional(),
preferredLocale: zod_1.z.enum(['en', 'fr', 'ar']).optional(),
preferredCurrency: zod_1.z.literal('MAD').optional(),
});
exports.renterFcmTokenSchema = zod_1.z.object({
fcmToken: zod_1.z.string(),
});
//# sourceMappingURL=auth.renter.schemas.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.schemas.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IACtD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IAC3C,eAAe,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtD,iBAAiB,EAAE,OAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC,CAAA;AAEW,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAA"}
+29
View File
@@ -0,0 +1,29 @@
import type { output } from 'zod';
import { renterUpdateSchema } from './auth.renter.schemas';
type RenterUpdateInput = output<typeof renterUpdateSchema>;
export declare function signupDisabled(): void;
export declare function loginDisabled(): void;
export declare function getMe(renterId: string): Promise<{
savedCompanies: {
id: string;
brand: {
displayName: string | null;
subdomain: string | null;
logoUrl: string | null;
} | null;
}[];
id: string;
firstName: string;
lastName: string;
email: string;
phone: string | null;
preferredLocale: string | null;
preferredCurrency: string | null;
emailVerified: boolean;
}>;
export declare function updateMe(renterId: string, body: RenterUpdateInput): any;
export declare function updateFcmToken(renterId: string, fcmToken: string): Promise<{
success: boolean;
}>;
export {};
//# sourceMappingURL=auth.renter.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAE1D,KAAK,iBAAiB,GAAG,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE1D,wBAAgB,cAAc,SAE7B;AAED,wBAAgB,aAAa,SAE5B;AAED,wBAAsB,KAAK,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;GAI3C;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,OAEjE;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;GAGtE"}
+62
View File
@@ -0,0 +1,62 @@
"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.signupDisabled = signupDisabled;
exports.loginDisabled = loginDisabled;
exports.getMe = getMe;
exports.updateMe = updateMe;
exports.updateFcmToken = updateFcmToken;
const errors_1 = require("../../http/errors");
const auth_presenter_1 = require("./auth.presenter");
const repo = __importStar(require("./auth.renter.repo"));
function signupDisabled() {
throw new errors_1.AppError('Renter account creation is disabled. Only company owners can sign in to the platform.', 403, 'renter_signup_disabled');
}
function loginDisabled() {
throw new errors_1.AppError('Renter sign-in is disabled. Only company owners can sign in to the platform.', 403, 'renter_login_disabled');
}
async function getMe(renterId) {
const renter = await repo.findRenterProfile(renterId);
const savedCompanies = await repo.findSavedCompanies(renter.savedCompanies.map((savedCompany) => savedCompany.companyId));
return (0, auth_presenter_1.presentRenterProfile)(renter, savedCompanies);
}
function updateMe(renterId, body) {
return repo.updateRenterProfile(renterId, body);
}
async function updateFcmToken(renterId, fcmToken) {
await repo.updateRenterFcmToken(renterId, fcmToken);
return { success: true };
}
//# sourceMappingURL=auth.renter.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.renter.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.renter.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,wCAEC;AAED,sCAEC;AAED,sBAIC;AAED,4BAEC;AAED,wCAGC;AA7BD,8CAA4C;AAC5C,qDAAuD;AACvD,yDAA0C;AAM1C,SAAgB,cAAc;IAC5B,MAAM,IAAI,iBAAQ,CAAC,uFAAuF,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAA;AAC5I,CAAC;AAED,SAAgB,aAAa;IAC3B,MAAM,IAAI,iBAAQ,CAAC,8EAA8E,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAA;AAClI,CAAC;AAEM,KAAK,UAAU,KAAK,CAAC,QAAgB;IAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAA;IACrD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,YAAiB,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAA;IAC9H,OAAO,IAAA,qCAAoB,EAAC,MAAM,EAAE,cAAc,CAAC,CAAA;AACrD,CAAC;AAED,SAAgB,QAAQ,CAAC,QAAgB,EAAE,IAAuB;IAChE,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;AACjD,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IACrE,MAAM,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,3 @@
export declare function presentCompany(company: any): any;
export declare function presentBrand(brand: any): any;
//# sourceMappingURL=company.presenter.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"company.presenter.d.ts","sourceRoot":"","sources":["../../../src/modules/companies/company.presenter.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,OAAO,EAAE,GAAG,OAE1C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,OAQtC"}
+18
View File
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.presentCompany = presentCompany;
exports.presentBrand = presentBrand;
function presentCompany(company) {
return company;
}
function presentBrand(brand) {
if (!brand)
return brand;
const { amanpaySecretKey, amanpayMerchantId, paypalEmail, paypalMerchantId, ...safe } = brand;
return {
...safe,
amanpayConfigured: !!(amanpayMerchantId && amanpaySecretKey),
paypalConfigured: !!(paypalEmail || paypalMerchantId),
};
}
//# sourceMappingURL=company.presenter.js.map
@@ -0,0 +1 @@
{"version":3,"file":"company.presenter.js","sourceRoot":"","sources":["../../../src/modules/companies/company.presenter.ts"],"names":[],"mappings":";;AAAA,wCAEC;AAED,oCAQC;AAZD,SAAgB,cAAc,CAAC,OAAY;IACzC,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAgB,YAAY,CAAC,KAAU;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,MAAM,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IAC7F,OAAO;QACL,GAAG,IAAI;QACP,iBAAiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,IAAI,gBAAgB,CAAC;QAC5D,gBAAgB,EAAG,CAAC,CAAC,CAAC,WAAW,IAAI,gBAAgB,CAAC;KACvD,CAAA;AACH,CAAC"}
+30
View File
@@ -0,0 +1,30 @@
export declare function findCompany(companyId: string): Promise<any>;
export declare function updateCompany(companyId: string, data: any): Promise<any>;
export declare function findBrand(companyId: string): Promise<any>;
export declare function upsertBrand(companyId: string, updateData: any, createData: any): Promise<any>;
export declare function findContractSettings(companyId: string): Promise<any>;
export declare function upsertContractSettings(companyId: string, data: any): Promise<any>;
export declare function findInsurancePolicies(companyId: string): Promise<any>;
export declare function createInsurancePolicy(companyId: string, data: any): Promise<any>;
export declare function updateInsurancePolicy(id: string, companyId: string, data: any): Promise<any>;
export declare function findInsurancePolicyOrThrow(id: string): Promise<any>;
export declare function deleteInsurancePolicy(id: string, companyId: string): Promise<any>;
export declare function findPricingRules(companyId: string): Promise<any>;
export declare function createPricingRule(companyId: string, data: any): Promise<any>;
export declare function updatePricingRule(id: string, companyId: string, data: any): Promise<any>;
export declare function findPricingRuleOrThrow(id: string): Promise<any>;
export declare function deletePricingRule(id: string, companyId: string): Promise<any>;
export declare function findAccountingSettings(companyId: string): Promise<any>;
export declare function upsertAccountingSettings(companyId: string, data: any): Promise<any>;
export declare function findApiKey(companyId: string): Promise<{
apiKeys: any;
}>;
export declare function regenerateApiKey(companyId: string): Promise<{
apiKey: string;
prefix: string;
warning: string;
}>;
export declare function findBrandBySubdomain(subdomain: string, excludeCompanyId: string): Promise<any>;
export declare function findBrandByCustomDomain(customDomain: string, excludeCompanyId: string): Promise<any>;
export declare function clearCustomDomain(companyId: string): Promise<any>;
//# sourceMappingURL=company.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"company.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/companies/company.repo.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,gBASlD;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAE/D;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,gBAEhD;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,gBAMpF;AAED,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,gBAE3D;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAMxE;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,gBAK5D;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAEvE;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAEnF;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM,gBAE1D;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAExE;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,gBAKvD;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAEnE;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAE/E;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,gBAEtD;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAEpE;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,gBAE7D;AAED,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAM1E;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM;;GAQjD;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;;GAmBvD;AAED,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,gBAErF;AAED,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,gBAE3F;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,gBAKxD"}
+145
View File
@@ -0,0 +1,145 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findCompany = findCompany;
exports.updateCompany = updateCompany;
exports.findBrand = findBrand;
exports.upsertBrand = upsertBrand;
exports.findContractSettings = findContractSettings;
exports.upsertContractSettings = upsertContractSettings;
exports.findInsurancePolicies = findInsurancePolicies;
exports.createInsurancePolicy = createInsurancePolicy;
exports.updateInsurancePolicy = updateInsurancePolicy;
exports.findInsurancePolicyOrThrow = findInsurancePolicyOrThrow;
exports.deleteInsurancePolicy = deleteInsurancePolicy;
exports.findPricingRules = findPricingRules;
exports.createPricingRule = createPricingRule;
exports.updatePricingRule = updatePricingRule;
exports.findPricingRuleOrThrow = findPricingRuleOrThrow;
exports.deletePricingRule = deletePricingRule;
exports.findAccountingSettings = findAccountingSettings;
exports.upsertAccountingSettings = upsertAccountingSettings;
exports.findApiKey = findApiKey;
exports.regenerateApiKey = regenerateApiKey;
exports.findBrandBySubdomain = findBrandBySubdomain;
exports.findBrandByCustomDomain = findBrandByCustomDomain;
exports.clearCustomDomain = clearCustomDomain;
const prisma_1 = require("../../lib/prisma");
const apiKeys_1 = require("../../security/apiKeys");
async function findCompany(companyId) {
return prisma_1.prisma.company.findUniqueOrThrow({
where: { id: companyId },
include: {
brand: true,
subscription: true,
_count: { select: { vehicles: true, customers: true, reservations: true } },
},
});
}
async function updateCompany(companyId, data) {
return prisma_1.prisma.company.update({ where: { id: companyId }, data });
}
async function findBrand(companyId) {
return prisma_1.prisma.brandSettings.findUnique({ where: { companyId } });
}
async function upsertBrand(companyId, updateData, createData) {
return prisma_1.prisma.brandSettings.upsert({
where: { companyId },
update: updateData,
create: { companyId, ...createData },
});
}
async function findContractSettings(companyId) {
return prisma_1.prisma.contractSettings.findUnique({ where: { companyId } });
}
async function upsertContractSettings(companyId, data) {
return prisma_1.prisma.contractSettings.upsert({
where: { companyId },
update: data,
create: { companyId, ...data },
});
}
async function findInsurancePolicies(companyId) {
return prisma_1.prisma.insurancePolicy.findMany({
where: { companyId },
orderBy: [{ isRequired: 'desc' }, { sortOrder: 'asc' }, { name: 'asc' }],
});
}
async function createInsurancePolicy(companyId, data) {
return prisma_1.prisma.insurancePolicy.create({ data: { companyId, ...data } });
}
async function updateInsurancePolicy(id, companyId, data) {
return prisma_1.prisma.insurancePolicy.updateMany({ where: { id, companyId }, data });
}
async function findInsurancePolicyOrThrow(id) {
return prisma_1.prisma.insurancePolicy.findUniqueOrThrow({ where: { id } });
}
async function deleteInsurancePolicy(id, companyId) {
return prisma_1.prisma.insurancePolicy.deleteMany({ where: { id, companyId } });
}
async function findPricingRules(companyId) {
return prisma_1.prisma.pricingRule.findMany({
where: { companyId },
orderBy: [{ isActive: 'desc' }, { createdAt: 'asc' }],
});
}
async function createPricingRule(companyId, data) {
return prisma_1.prisma.pricingRule.create({ data: { companyId, ...data } });
}
async function updatePricingRule(id, companyId, data) {
return prisma_1.prisma.pricingRule.updateMany({ where: { id, companyId }, data });
}
async function findPricingRuleOrThrow(id) {
return prisma_1.prisma.pricingRule.findUniqueOrThrow({ where: { id } });
}
async function deletePricingRule(id, companyId) {
return prisma_1.prisma.pricingRule.deleteMany({ where: { id, companyId } });
}
async function findAccountingSettings(companyId) {
return prisma_1.prisma.accountingSettings.findUnique({ where: { companyId } });
}
async function upsertAccountingSettings(companyId, data) {
return prisma_1.prisma.accountingSettings.upsert({
where: { companyId },
update: data,
create: { companyId, ...data },
});
}
async function findApiKey(companyId) {
const apiKeys = await prisma_1.prisma.companyApiKey.findMany({
where: { companyId, revokedAt: null },
orderBy: { createdAt: 'desc' },
select: { id: true, name: true, prefix: true, lastUsedAt: true, createdAt: true, revokedAt: true },
});
return { apiKeys };
}
async function regenerateApiKey(companyId) {
const nextKey = (0, apiKeys_1.generateCompanyApiKey)();
await prisma_1.prisma.$transaction(async (tx) => {
await tx.companyApiKey.updateMany({
where: { companyId, revokedAt: null },
data: { revokedAt: new Date() },
});
await tx.companyApiKey.create({
data: {
companyId,
name: 'Default API key',
prefix: nextKey.prefix,
keyHash: nextKey.keyHash,
},
});
});
return { apiKey: nextKey.rawKey, prefix: nextKey.prefix, warning: 'This raw API key is shown once. Store it securely.' };
}
async function findBrandBySubdomain(subdomain, excludeCompanyId) {
return prisma_1.prisma.brandSettings.findFirst({ where: { subdomain, companyId: { not: excludeCompanyId } } });
}
async function findBrandByCustomDomain(customDomain, excludeCompanyId) {
return prisma_1.prisma.brandSettings.findFirst({ where: { customDomain, companyId: { not: excludeCompanyId } } });
}
async function clearCustomDomain(companyId) {
return prisma_1.prisma.brandSettings.updateMany({
where: { companyId },
data: { customDomain: null, customDomainVerified: false, customDomainAddedAt: null },
});
}
//# sourceMappingURL=company.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=company.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"company.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/companies/company.routes.ts"],"names":[],"mappings":"AAgBA,QAAA,MAAM,MAAM,4CAAW,CAAA;AA4LvB,eAAe,MAAM,CAAA"}
+282
View File
@@ -0,0 +1,282 @@
"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 requireRole_1 = require("../../middleware/requireRole");
const requireCompanyPolicy_1 = require("../../middleware/requireCompanyPolicy");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const upload_1 = require("../../http/upload");
const service = __importStar(require("./company.service"));
const company_schemas_1 = require("./company.schemas");
const router = (0, express_1.Router)();
router.use(requireCompanyAuth_1.requireCompanyAuth, requireTenant_1.requireTenant, requireSubscription_1.requireSubscription);
router.get('/me', async (req, res, next) => {
try {
const company = await service.getCompany(req.companyId);
(0, respond_1.ok)(res, company);
}
catch (err) {
next(err);
}
});
router.patch('/me', (0, requireRole_1.requireRole)('OWNER'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.companySchema, req);
const company = await service.updateCompany(req.companyId, body);
(0, respond_1.ok)(res, company);
}
catch (err) {
next(err);
}
});
router.get('/me/brand', async (req, res, next) => {
try {
const brand = await service.getBrand(req.companyId);
(0, respond_1.ok)(res, brand);
}
catch (err) {
next(err);
}
});
router.patch('/me/brand', (0, requireRole_1.requireRole)('OWNER'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.brandSchema, req);
const brand = await service.updateBrand(req.companyId, body, req.company.name, req.company.slug);
(0, respond_1.ok)(res, brand);
}
catch (err) {
next(err);
}
});
router.post('/me/brand/logo', (0, requireRole_1.requireRole)('OWNER'), upload_1.imageUpload.single('file'), async (req, res, next) => {
try {
(0, upload_1.assertImageFile)(req.file, 'logo');
const brand = await service.uploadLogo(req.companyId, req.company.name, req.company.slug, req.file.buffer);
(0, respond_1.ok)(res, brand);
}
catch (err) {
next(err);
}
});
router.post('/me/brand/hero', (0, requireRole_1.requireRole)('OWNER'), upload_1.imageUpload.single('file'), async (req, res, next) => {
try {
(0, upload_1.assertImageFile)(req.file, 'hero image');
const brand = await service.uploadHeroImage(req.companyId, req.company.name, req.company.slug, req.file.buffer);
(0, respond_1.ok)(res, brand);
}
catch (err) {
next(err);
}
});
router.post('/me/brand/subdomain/check', (0, requireRole_1.requireRole)('OWNER'), async (req, res, next) => {
try {
const { subdomain } = (0, validate_1.parseBody)(company_schemas_1.subdomainSchema, req);
const result = await service.checkSubdomainAvailability(subdomain, req.companyId);
(0, respond_1.ok)(res, result);
}
catch (err) {
next(err);
}
});
router.post('/me/brand/custom-domain', (0, requireRole_1.requireRole)('OWNER'), async (req, res, next) => {
try {
const { customDomain } = (0, validate_1.parseBody)(company_schemas_1.customDomainSchema, req);
const brand = await service.setCustomDomain(req.companyId, req.company.name, req.company.slug, customDomain);
(0, respond_1.ok)(res, brand);
}
catch (err) {
next(err);
}
});
router.get('/me/brand/custom-domain/status', async (req, res, next) => {
try {
const status = await service.getCustomDomainStatus(req.companyId);
(0, respond_1.ok)(res, status);
}
catch (err) {
next(err);
}
});
router.delete('/me/brand/custom-domain', (0, requireRole_1.requireRole)('OWNER'), async (req, res, next) => {
try {
const result = await service.removeCustomDomain(req.companyId);
(0, respond_1.ok)(res, result);
}
catch (err) {
next(err);
}
});
router.get('/me/contract-settings', async (req, res, next) => {
try {
const settings = await service.getContractSettings(req.companyId);
(0, respond_1.ok)(res, settings);
}
catch (err) {
next(err);
}
});
router.patch('/me/contract-settings', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.contractSettingsSchema, req);
const settings = await service.updateContractSettings(req.companyId, body);
(0, respond_1.ok)(res, settings);
}
catch (err) {
next(err);
}
});
router.get('/me/insurance-policies', async (req, res, next) => {
try {
const policies = await service.getInsurancePolicies(req.companyId);
(0, respond_1.ok)(res, policies);
}
catch (err) {
next(err);
}
});
router.post('/me/insurance-policies', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.insurancePolicySchema, req);
const policy = await service.createInsurancePolicy(req.companyId, body);
(0, respond_1.created)(res, policy);
}
catch (err) {
next(err);
}
});
router.patch('/me/insurance-policies/:id', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(company_schemas_1.idParamSchema, req);
const body = (0, validate_1.parseBody)(company_schemas_1.insurancePolicySchema.partial(), req);
const policy = await service.updateInsurancePolicy(id, req.companyId, body);
(0, respond_1.ok)(res, policy);
}
catch (err) {
next(err);
}
});
router.delete('/me/insurance-policies/:id', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(company_schemas_1.idParamSchema, req);
await service.deleteInsurancePolicy(id, req.companyId);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.get('/me/pricing-rules', async (req, res, next) => {
try {
const rules = await service.getPricingRules(req.companyId);
(0, respond_1.ok)(res, rules);
}
catch (err) {
next(err);
}
});
router.post('/me/pricing-rules', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.pricingRuleSchema, req);
const rule = await service.createPricingRule(req.companyId, body);
(0, respond_1.created)(res, rule);
}
catch (err) {
next(err);
}
});
router.patch('/me/pricing-rules/:id', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(company_schemas_1.idParamSchema, req);
const body = (0, validate_1.parseBody)(company_schemas_1.pricingRuleSchema.partial(), req);
const rule = await service.updatePricingRule(id, req.companyId, body);
(0, respond_1.ok)(res, rule);
}
catch (err) {
next(err);
}
});
router.delete('/me/pricing-rules/:id', (0, requireCompanyPolicy_1.requireCompanyPolicy)('deletePricingRule'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(company_schemas_1.idParamSchema, req);
await service.deletePricingRule(id, req.companyId);
(0, respond_1.ok)(res, { success: true });
}
catch (err) {
next(err);
}
});
router.get('/me/accounting-settings', async (req, res, next) => {
try {
const settings = await service.getAccountingSettings(req.companyId);
(0, respond_1.ok)(res, settings);
}
catch (err) {
next(err);
}
});
router.patch('/me/accounting-settings', (0, requireCompanyPolicy_1.requireCompanyPolicy)('updateAccountingSettings'), async (req, res, next) => {
try {
const body = (0, validate_1.parseBody)(company_schemas_1.accountingSettingsSchema, req);
const settings = await service.updateAccountingSettings(req.companyId, body);
(0, respond_1.ok)(res, settings);
}
catch (err) {
next(err);
}
});
router.get('/me/api-key', (0, requireCompanyPolicy_1.requireCompanyPolicy)('viewApiKey'), async (req, res, next) => {
try {
const company = await service.getApiKey(req.companyId);
(0, respond_1.ok)(res, company);
}
catch (err) {
next(err);
}
});
router.post('/me/api-key/regenerate', (0, requireCompanyPolicy_1.requireCompanyPolicy)('regenerateApiKey'), async (req, res, next) => {
try {
const company = await service.regenerateApiKey(req.companyId);
(0, respond_1.ok)(res, company);
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=company.routes.js.map
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More