fixed cors bug, separating rental confirmation for owner and renter, removing condition checks from my-listings

This commit is contained in:
jackiettran
2025-10-08 23:03:28 -04:00
parent 052781a0e6
commit 34c0ad2920
5 changed files with 249 additions and 94 deletions

View File

@@ -73,10 +73,7 @@ app.use(morgan("combined", { stream: logger.stream }));
// API request/response logging
app.use("/api/", apiLogger);
// General rate limiting for all routes
app.use("/api/", generalLimiter);
// CORS with security settings
// CORS with security settings (must come BEFORE rate limiter to ensure headers on all responses)
app.use(
cors({
origin: process.env.FRONTEND_URL || "http://localhost:3000",
@@ -85,6 +82,9 @@ app.use(
})
);
// General rate limiting for all routes
app.use("/api/", generalLimiter);
// Body parsing with size limits
app.use(
bodyParser.json({

View File

@@ -415,6 +415,11 @@ class EmailService {
}
async sendRentalConfirmationEmails(rental) {
const results = {
ownerEmailSent: false,
renterEmailSent: false,
};
try {
// Get owner and renter emails
const owner = await User.findByPk(rental.ownerId, {
@@ -444,30 +449,67 @@ class EmailService {
metadata: { rentalStart: rental.startDateTime },
};
// Send email to owner
// Send email to owner - independent error handling
if (owner?.email) {
await this.sendRentalConfirmation(
owner.email,
ownerNotification,
rental
);
console.log(`Rental confirmation email sent to owner: ${owner.email}`);
try {
const ownerResult = await this.sendRentalConfirmation(
owner.email,
ownerNotification,
rental
);
if (ownerResult.success) {
console.log(
`Rental confirmation email sent to owner: ${owner.email}`
);
results.ownerEmailSent = true;
} else {
console.error(
`Failed to send rental confirmation email to owner (${owner.email}):`,
ownerResult.error
);
}
} catch (error) {
console.error(
`Failed to send rental confirmation email to owner (${owner.email}):`,
error.message
);
}
}
// Send email to renter
// Send email to renter - independent error handling
if (renter?.email) {
await this.sendRentalConfirmation(
renter.email,
renterNotification,
rental
);
console.log(
`Rental confirmation email sent to renter: ${renter.email}`
);
try {
const renterResult = await this.sendRentalConfirmation(
renter.email,
renterNotification,
rental
);
if (renterResult.success) {
console.log(
`Rental confirmation email sent to renter: ${renter.email}`
);
results.renterEmailSent = true;
} else {
console.error(
`Failed to send rental confirmation email to renter (${renter.email}):`,
renterResult.error
);
}
} catch (error) {
console.error(
`Failed to send rental confirmation email to renter (${renter.email}):`,
error.message
);
}
}
} catch (error) {
console.error("Error sending rental confirmation emails:", error);
console.error(
"Error fetching user data for rental confirmation emails:",
error
);
}
return results;
}
}

View File

@@ -248,4 +248,172 @@ describe('EmailService', () => {
expect(result.success).toBe(true);
});
});
describe('sendRentalConfirmationEmails', () => {
const { User } = require('../../../models');
beforeEach(async () => {
mockSend.mockResolvedValue({ MessageId: 'test-message-id' });
await emailService.initialize();
});
it('should send emails to both owner and renter successfully', async () => {
const mockOwner = { email: 'owner@example.com' };
const mockRenter = { email: 'renter@example.com' };
User.findByPk
.mockResolvedValueOnce(mockOwner) // First call for owner
.mockResolvedValueOnce(mockRenter); // Second call for renter
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(true);
expect(results.renterEmailSent).toBe(true);
expect(mockSend).toHaveBeenCalledTimes(2);
});
it('should send renter email even if owner email fails', async () => {
const mockOwner = { email: 'owner@example.com' };
const mockRenter = { email: 'renter@example.com' };
User.findByPk
.mockResolvedValueOnce(mockOwner)
.mockResolvedValueOnce(mockRenter);
// First call (owner) fails, second call (renter) succeeds
mockSend
.mockRejectedValueOnce(new Error('SES Error for owner'))
.mockResolvedValueOnce({ MessageId: 'renter-message-id' });
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(false);
expect(results.renterEmailSent).toBe(true);
expect(mockSend).toHaveBeenCalledTimes(2);
});
it('should send owner email even if renter email fails', async () => {
const mockOwner = { email: 'owner@example.com' };
const mockRenter = { email: 'renter@example.com' };
User.findByPk
.mockResolvedValueOnce(mockOwner)
.mockResolvedValueOnce(mockRenter);
// First call (owner) succeeds, second call (renter) fails
mockSend
.mockResolvedValueOnce({ MessageId: 'owner-message-id' })
.mockRejectedValueOnce(new Error('SES Error for renter'));
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(true);
expect(results.renterEmailSent).toBe(false);
expect(mockSend).toHaveBeenCalledTimes(2);
});
it('should handle both emails failing gracefully', async () => {
const mockOwner = { email: 'owner@example.com' };
const mockRenter = { email: 'renter@example.com' };
User.findByPk
.mockResolvedValueOnce(mockOwner)
.mockResolvedValueOnce(mockRenter);
// Both calls fail
mockSend
.mockRejectedValueOnce(new Error('SES Error for owner'))
.mockRejectedValueOnce(new Error('SES Error for renter'));
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(false);
expect(results.renterEmailSent).toBe(false);
expect(mockSend).toHaveBeenCalledTimes(2);
});
it('should handle missing owner email', async () => {
const mockOwner = { email: null };
const mockRenter = { email: 'renter@example.com' };
User.findByPk
.mockResolvedValueOnce(mockOwner)
.mockResolvedValueOnce(mockRenter);
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(false);
expect(results.renterEmailSent).toBe(true);
expect(mockSend).toHaveBeenCalledTimes(1);
});
it('should handle missing renter email', async () => {
const mockOwner = { email: 'owner@example.com' };
const mockRenter = { email: null };
User.findByPk
.mockResolvedValueOnce(mockOwner)
.mockResolvedValueOnce(mockRenter);
const rental = {
id: 1,
ownerId: 10,
renterId: 20,
item: { name: 'Test Item' },
startDateTime: '2024-01-15T10:00:00Z',
endDateTime: '2024-01-17T10:00:00Z'
};
const results = await emailService.sendRentalConfirmationEmails(rental);
expect(results.ownerEmailSent).toBe(true);
expect(results.renterEmailSent).toBe(false);
expect(mockSend).toHaveBeenCalledTimes(1);
});
});
});