The monthly usage window in billing.service.ts is computed like this, including on the enforcement path in canPerformAction (~line 999):
const processedSmsLastMonth = await this.smsModel.countDocuments({
user: user._id,
createdAt: {
$gte: new Date(new Date().setMonth(new Date().getMonth() - 1)),
},
})
setMonth overflows on long months. Run on March 31, setMonth(getMonth() - 1) produces "Feb 31", which JS normalizes to March 3. The "last month" window collapses to 28 days, and since this feeds the quota check, usage from the start of the real window stops counting. The mirror problem exists at month starts for display counts. Subtracting 30 days explicitly avoids the overflow, or better, use the subscription's currentPeriodStart, which the schema already carries, so the window matches what the user is actually billed for.
The daily window right above it (~line 994) has its own quirk:
createdAt: { $gte: new Date(new Date().setHours(0, 0, 0, 0)) },
Midnight in the server's timezone, so daily limits reset at some arbitrary hour for everyone else. UTC (or the user's timezone) would at least be predictable.
The same date snippet appears three times in the file (~78, ~999, ~1146), so whatever the fix, it wants to be one small helper.
The monthly usage window in billing.service.ts is computed like this, including on the enforcement path in canPerformAction (~line 999):
setMonth overflows on long months. Run on March 31, setMonth(getMonth() - 1) produces "Feb 31", which JS normalizes to March 3. The "last month" window collapses to 28 days, and since this feeds the quota check, usage from the start of the real window stops counting. The mirror problem exists at month starts for display counts. Subtracting 30 days explicitly avoids the overflow, or better, use the subscription's currentPeriodStart, which the schema already carries, so the window matches what the user is actually billed for.
The daily window right above it (~line 994) has its own quirk:
Midnight in the server's timezone, so daily limits reset at some arbitrary hour for everyone else. UTC (or the user's timezone) would at least be predictable.
The same date snippet appears three times in the file (~78, ~999, ~1146), so whatever the fix, it wants to be one small helper.