codebuff 1.0.219 → 1.0.221
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checkpoints/checkpoint-manager.d.ts +1 -1
- package/dist/checkpoints/checkpoint-manager.js +8 -6
- package/dist/checkpoints/checkpoint-manager.js.map +1 -1
- package/dist/cli-handlers/api-key.d.ts +25 -0
- package/dist/cli-handlers/api-key.js +66 -0
- package/dist/cli-handlers/api-key.js.map +1 -0
- package/dist/cli-handlers/checkpoint.d.ts +18 -0
- package/dist/cli-handlers/checkpoint.js +195 -0
- package/dist/cli-handlers/checkpoint.js.map +1 -0
- package/dist/cli-handlers/diff.d.ts +2 -0
- package/dist/cli-handlers/diff.js +31 -0
- package/dist/cli-handlers/diff.js.map +1 -0
- package/dist/cli-handlers/easter-egg.d.ts +1 -0
- package/dist/cli-handlers/easter-egg.js +126 -0
- package/dist/cli-handlers/easter-egg.js.map +1 -0
- package/dist/cli.d.ts +0 -18
- package/dist/cli.js +70 -397
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +11 -11
- package/dist/client.js +123 -45
- package/dist/client.js.map +1 -1
- package/dist/code-map/tsconfig.tsbuildinfo +1 -1
- package/dist/common/actions.d.ts +288 -264
- package/dist/common/actions.js +10 -4
- package/dist/common/actions.js.map +1 -1
- package/dist/common/advanced-analyzer.d.ts +19 -0
- package/dist/common/advanced-analyzer.js +140 -0
- package/dist/common/advanced-analyzer.js.map +1 -0
- package/dist/common/billing/auto-topup.d.ts +8 -0
- package/dist/common/billing/auto-topup.js +192 -0
- package/dist/common/billing/auto-topup.js.map +1 -0
- package/dist/common/billing/balance-calculator.d.ts +55 -0
- package/dist/common/billing/balance-calculator.js +207 -0
- package/dist/common/billing/balance-calculator.js.map +1 -0
- package/dist/common/billing/check-auto-topup.d.ts +12 -0
- package/dist/common/billing/check-auto-topup.js +50 -0
- package/dist/common/billing/check-auto-topup.js.map +1 -0
- package/dist/common/billing/conversion.d.ts +9 -0
- package/dist/common/billing/conversion.js +20 -0
- package/dist/common/billing/conversion.js.map +1 -0
- package/dist/common/billing/credit-check.d.ts +8 -0
- package/dist/common/billing/credit-check.js +45 -0
- package/dist/common/billing/credit-check.js.map +1 -0
- package/dist/common/billing/credit-conversion.d.ts +24 -0
- package/dist/common/billing/credit-conversion.js +48 -0
- package/dist/common/billing/credit-conversion.js.map +1 -0
- package/dist/common/billing/grant-credits.d.ts +28 -0
- package/dist/common/billing/grant-credits.js +201 -0
- package/dist/common/billing/grant-credits.js.map +1 -0
- package/dist/common/billing/plans.d.ts +13 -0
- package/dist/common/billing/plans.js +44 -0
- package/dist/common/billing/plans.js.map +1 -0
- package/dist/common/billing/rollover-logic.d.ts +13 -0
- package/dist/common/billing/rollover-logic.js +174 -0
- package/dist/common/billing/rollover-logic.js.map +1 -0
- package/dist/common/billing/stripe-api.d.ts +31 -0
- package/dist/common/billing/stripe-api.js +104 -0
- package/dist/common/billing/stripe-api.js.map +1 -0
- package/dist/common/browser-actions.d.ts +62 -62
- package/dist/common/constants/grant-priorities.d.ts +2 -0
- package/dist/common/constants/grant-priorities.js +10 -0
- package/dist/common/constants/grant-priorities.js.map +1 -0
- package/dist/common/constants.d.ts +28 -1
- package/dist/common/constants.js +36 -7
- package/dist/common/constants.js.map +1 -1
- package/dist/common/db/schema.d.ts +323 -86
- package/dist/common/db/schema.js +55 -13
- package/dist/common/db/schema.js.map +1 -1
- package/dist/common/env.mjs +4 -1
- package/dist/common/env.mjs.map +1 -1
- package/dist/common/message-image-handling.d.ts +41 -0
- package/dist/common/message-image-handling.js +57 -0
- package/dist/common/message-image-handling.js.map +1 -0
- package/dist/common/types/agent-state.d.ts +8 -8
- package/dist/common/types/grant.d.ts +2 -0
- package/dist/common/types/grant.js +10 -0
- package/dist/common/types/grant.js.map +1 -0
- package/dist/common/types/usage.d.ts +37 -18
- package/dist/common/types/usage.js +9 -6
- package/dist/common/types/usage.js.map +1 -1
- package/dist/common/util/__tests__/saxy.test.d.ts +1 -0
- package/dist/common/util/__tests__/saxy.test.js +262 -0
- package/dist/common/util/__tests__/saxy.test.js.map +1 -0
- package/dist/common/util/credentials.d.ts +2 -2
- package/dist/common/util/dates.d.ts +10 -1
- package/dist/common/util/dates.js +11 -2
- package/dist/common/util/dates.js.map +1 -1
- package/dist/common/util/logger.js +32 -14
- package/dist/common/util/logger.js.map +1 -1
- package/dist/common/util/process-stream.d.ts +8 -0
- package/dist/common/util/process-stream.js +102 -0
- package/dist/common/util/process-stream.js.map +1 -0
- package/dist/common/util/promise.d.ts +1 -1
- package/dist/common/util/promise.js +2 -2
- package/dist/common/util/promise.js.map +1 -1
- package/dist/common/util/referral-credits.d.ts +1 -0
- package/dist/common/util/referral-credits.js +48 -0
- package/dist/common/util/referral-credits.js.map +1 -0
- package/dist/common/util/saxy.d.ts +8 -0
- package/dist/common/util/saxy.js +35 -12
- package/dist/common/util/saxy.js.map +1 -1
- package/dist/common/util/sync-failure.d.ts +1 -0
- package/dist/common/util/sync-failure.js +57 -0
- package/dist/common/util/sync-failure.js.map +1 -0
- package/dist/common/websockets/websocket-schema.d.ts +530 -480
- package/dist/index.js +1 -1
- package/dist/utils/__tests__/xml-stream-parser.test.js +6 -10
- package/dist/utils/__tests__/xml-stream-parser.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/common/logger.d.ts +0 -1
- package/dist/common/logger.js +0 -7
- package/dist/common/logger.js.map +0 -1
- package/dist/common/util/constants.d.ts +0 -1
- package/dist/common/util/constants.js +0 -7
- package/dist/common/util/constants.js.map +0 -1
- package/dist/common/util/helpers.d.ts +0 -1
- package/dist/common/util/helpers.js +0 -6
- package/dist/common/util/helpers.js.map +0 -1
- package/dist/common/util/token-counter.d.ts +0 -3
- package/dist/common/util/token-counter.js +0 -27
- package/dist/common/util/token-counter.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.getPreviousFreeGrantAmount = getPreviousFreeGrantAmount;
|
|
30
|
+
exports.calculateTotalReferralBonus = calculateTotalReferralBonus;
|
|
31
|
+
exports.processAndGrantCredit = processAndGrantCredit;
|
|
32
|
+
exports.revokeGrantByOperationId = revokeGrantByOperationId;
|
|
33
|
+
const db_1 = __importDefault(require("../db"));
|
|
34
|
+
const schema = __importStar(require("../db/schema"));
|
|
35
|
+
const logger_1 = require("../util/logger");
|
|
36
|
+
const grant_priorities_1 = require("../constants/grant-priorities");
|
|
37
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
38
|
+
const constants_1 = require("../constants");
|
|
39
|
+
const promise_1 = require("../util/promise");
|
|
40
|
+
const sync_failure_1 = require("../util/sync-failure");
|
|
41
|
+
/**
|
|
42
|
+
* Finds the amount of the most recent expired 'free' grant for a user.
|
|
43
|
+
* If no expired 'free' grant is found, returns the default free limit.
|
|
44
|
+
* @param userId The ID of the user.
|
|
45
|
+
* @returns The amount of the last expired free grant or the default.
|
|
46
|
+
*/
|
|
47
|
+
async function getPreviousFreeGrantAmount(userId) {
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const lastExpiredFreeGrant = await db_1.default
|
|
50
|
+
.select({
|
|
51
|
+
principal: schema.creditLedger.principal,
|
|
52
|
+
})
|
|
53
|
+
.from(schema.creditLedger)
|
|
54
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditLedger.user_id, userId), (0, drizzle_orm_1.eq)(schema.creditLedger.type, 'free'), (0, drizzle_orm_1.lte)(schema.creditLedger.expires_at, now) // Grant has expired
|
|
55
|
+
))
|
|
56
|
+
.orderBy((0, drizzle_orm_1.desc)(schema.creditLedger.expires_at)) // Most recent expiry first
|
|
57
|
+
.limit(1);
|
|
58
|
+
if (lastExpiredFreeGrant.length > 0) {
|
|
59
|
+
logger_1.logger.debug({ userId, amount: lastExpiredFreeGrant[0].principal }, 'Found previous expired free grant amount.');
|
|
60
|
+
return lastExpiredFreeGrant[0].principal;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logger_1.logger.debug({ userId, defaultAmount: constants_1.CREDITS_USAGE_LIMITS.FREE }, 'No previous expired free grant found. Using default.');
|
|
64
|
+
return constants_1.CREDITS_USAGE_LIMITS.FREE; // Default if no previous grant found
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Calculates the total referral bonus credits a user should receive based on
|
|
69
|
+
* their referral history (both as referrer and referred).
|
|
70
|
+
* @param userId The ID of the user.
|
|
71
|
+
* @returns The total referral bonus credits earned.
|
|
72
|
+
*/
|
|
73
|
+
async function calculateTotalReferralBonus(userId) {
|
|
74
|
+
try {
|
|
75
|
+
const result = await db_1.default
|
|
76
|
+
.select({
|
|
77
|
+
totalCredits: (0, drizzle_orm_1.sql) `COALESCE(SUM(${schema.referral.credits}), 0)`,
|
|
78
|
+
})
|
|
79
|
+
.from(schema.referral)
|
|
80
|
+
.where((0, drizzle_orm_1.or)((0, drizzle_orm_1.eq)(schema.referral.referrer_id, userId), (0, drizzle_orm_1.eq)(schema.referral.referred_id, userId)));
|
|
81
|
+
const totalBonus = parseInt(result[0]?.totalCredits ?? '0');
|
|
82
|
+
logger_1.logger.debug({ userId, totalBonus }, 'Calculated total referral bonus.');
|
|
83
|
+
return totalBonus;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger_1.logger.error({ userId, error }, 'Error calculating total referral bonus. Returning 0.');
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Core grant operation wrapped in a single DB transaction.
|
|
92
|
+
*/
|
|
93
|
+
async function grantCreditOperation(userId, amount, type, description, expiresAt, operationId) {
|
|
94
|
+
await db_1.default.transaction(async (tx) => {
|
|
95
|
+
const now = new Date();
|
|
96
|
+
// First check for any negative balances
|
|
97
|
+
const negativeGrants = await tx
|
|
98
|
+
.select()
|
|
99
|
+
.from(schema.creditLedger)
|
|
100
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditLedger.user_id, userId), (0, drizzle_orm_1.or)((0, drizzle_orm_1.isNull)(schema.creditLedger.expires_at), (0, drizzle_orm_1.gt)(schema.creditLedger.expires_at, now))))
|
|
101
|
+
.then((grants) => grants.filter((g) => g.balance < 0));
|
|
102
|
+
if (negativeGrants.length > 0) {
|
|
103
|
+
const totalDebt = negativeGrants.reduce((sum, g) => sum + Math.abs(g.balance), 0);
|
|
104
|
+
for (const grant of negativeGrants) {
|
|
105
|
+
await tx
|
|
106
|
+
.update(schema.creditLedger)
|
|
107
|
+
.set({ balance: 0 })
|
|
108
|
+
.where((0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, grant.operation_id));
|
|
109
|
+
}
|
|
110
|
+
const remainingAmount = Math.max(0, amount - totalDebt);
|
|
111
|
+
if (remainingAmount > 0) {
|
|
112
|
+
await tx.insert(schema.creditLedger).values({
|
|
113
|
+
operation_id: operationId,
|
|
114
|
+
user_id: userId,
|
|
115
|
+
principal: amount,
|
|
116
|
+
balance: remainingAmount,
|
|
117
|
+
type,
|
|
118
|
+
description: totalDebt > 0
|
|
119
|
+
? `${description} (${totalDebt} credits used to clear existing debt)`
|
|
120
|
+
: description,
|
|
121
|
+
priority: grant_priorities_1.GRANT_PRIORITIES[type],
|
|
122
|
+
expires_at: expiresAt,
|
|
123
|
+
created_at: now,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// No debt - create grant normally
|
|
129
|
+
await tx.insert(schema.creditLedger).values({
|
|
130
|
+
operation_id: operationId,
|
|
131
|
+
user_id: userId,
|
|
132
|
+
principal: amount,
|
|
133
|
+
balance: amount,
|
|
134
|
+
type,
|
|
135
|
+
description,
|
|
136
|
+
priority: grant_priorities_1.GRANT_PRIORITIES[type],
|
|
137
|
+
expires_at: expiresAt,
|
|
138
|
+
created_at: now,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
logger_1.logger.info({ userId, operationId, type, amount, expiresAt }, 'Created new credit grant');
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Processes a credit grant request with retries and failure logging.
|
|
146
|
+
*/
|
|
147
|
+
async function processAndGrantCredit(userId, amount, type, description, expiresAt, operationId) {
|
|
148
|
+
try {
|
|
149
|
+
await (0, promise_1.withRetry)(() => grantCreditOperation(userId, amount, type, description, expiresAt, operationId), {
|
|
150
|
+
maxRetries: 3,
|
|
151
|
+
retryIf: () => true,
|
|
152
|
+
onRetry: (error, attempt) => {
|
|
153
|
+
logger_1.logger.warn({ operationId, attempt, error }, `processAndGrantCredit retry ${attempt}`);
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
await (0, sync_failure_1.logSyncFailure)(operationId, error.message, 'internal');
|
|
159
|
+
logger_1.logger.error({ operationId, error }, 'processAndGrantCredit failed after retries, logged to sync_failure');
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Revokes credits from a specific grant by operation ID.
|
|
165
|
+
* This sets the balance to 0 and updates the description to indicate a refund.
|
|
166
|
+
*
|
|
167
|
+
* @param operationId The operation ID of the grant to revoke
|
|
168
|
+
* @param reason The reason for revoking the credits (e.g. refund)
|
|
169
|
+
* @returns true if the grant was found and revoked, false otherwise
|
|
170
|
+
*/
|
|
171
|
+
async function revokeGrantByOperationId(operationId, reason) {
|
|
172
|
+
return await db_1.default.transaction(async (tx) => {
|
|
173
|
+
const grant = await tx.query.creditLedger.findFirst({
|
|
174
|
+
where: (0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, operationId),
|
|
175
|
+
});
|
|
176
|
+
if (!grant) {
|
|
177
|
+
logger_1.logger.warn({ operationId }, 'Attempted to revoke non-existent grant');
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (grant.balance < 0) {
|
|
181
|
+
logger_1.logger.warn({ operationId, currentBalance: grant.balance }, 'Cannot revoke grant with negative balance - user has already spent these credits');
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
await tx
|
|
185
|
+
.update(schema.creditLedger)
|
|
186
|
+
.set({
|
|
187
|
+
principal: 0,
|
|
188
|
+
balance: 0,
|
|
189
|
+
description: `${grant.description} (Revoked: ${reason})`,
|
|
190
|
+
})
|
|
191
|
+
.where((0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, operationId));
|
|
192
|
+
logger_1.logger.info({
|
|
193
|
+
operationId,
|
|
194
|
+
userId: grant.user_id,
|
|
195
|
+
revokedAmount: grant.balance,
|
|
196
|
+
reason,
|
|
197
|
+
}, 'Revoked credit grant');
|
|
198
|
+
return true;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=grant-credits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-credits.js","sourceRoot":"","sources":["../../src/billing/grant-credits.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,gEAgCC;AAQD,kEA0BC;AAoFD,sDAsCC;AAUD,4DA2CC;AArQD,+CAAsB;AACtB,qDAAsC;AAEtC,2CAAuC;AAEvC,oEAAgE;AAChE,6CAAqE;AAErE,4CAAmD;AACnD,6CAA2C;AAC3C,uDAAqD;AAIrD;;;;;GAKG;AACI,KAAK,UAAU,0BAA0B,CAC9C,MAAc;IAEd,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,oBAAoB,GAAG,MAAM,YAAE;SAClC,MAAM,CAAC;QACN,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,SAAS;KACzC,CAAC;SACD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;SACzB,KAAK,CACJ,IAAA,iBAAG,EACD,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,EACvC,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EACpC,IAAA,iBAAG,EAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,oBAAoB;KAC9D,CACF;SACA,OAAO,CAAC,IAAA,kBAAI,EAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,2BAA2B;SACzE,KAAK,CAAC,CAAC,CAAC,CAAA;IAEX,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,eAAM,CAAC,KAAK,CACV,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,EACrD,2CAA2C,CAC5C,CAAA;QACD,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1C,CAAC;SAAM,CAAC;QACN,eAAM,CAAC,KAAK,CACV,EAAE,MAAM,EAAE,aAAa,EAAE,gCAAoB,CAAC,IAAI,EAAE,EACpD,sDAAsD,CACvD,CAAA;QACD,OAAO,gCAAoB,CAAC,IAAI,CAAA,CAAC,qCAAqC;IACxE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,2BAA2B,CAC/C,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAE;aACpB,MAAM,CAAC;YACN,YAAY,EAAE,IAAA,iBAAG,EAAQ,gBAAgB,MAAM,CAAC,QAAQ,CAAC,OAAO,OAAO;SACxE,CAAC;aACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;aACrB,KAAK,CACJ,IAAA,gBAAE,EACA,IAAA,gBAAE,EAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,EACvC,IAAA,gBAAE,EAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CACxC,CACF,CAAA;QAEH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,GAAG,CAAC,CAAA;QAC3D,eAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,kCAAkC,CAAC,CAAA;QACxE,OAAO,UAAU,CAAA;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CACV,EAAE,MAAM,EAAE,KAAK,EAAE,EACjB,sDAAsD,CACvD,CAAA;QACD,OAAO,CAAC,CAAA;IACV,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,oBAAoB,CACjC,MAAc,EACd,MAAc,EACd,IAAe,EACf,WAAmB,EACnB,SAAsB,EACtB,WAAmB;IAEnB,MAAM,YAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;QAEtB,wCAAwC;QACxC,MAAM,cAAc,GAAG,MAAM,EAAE;aAC5B,MAAM,EAAE;aACR,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;aACzB,KAAK,CACJ,IAAA,iBAAG,EACD,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,EACvC,IAAA,gBAAE,EACA,IAAA,oBAAM,EAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EACtC,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CACxC,CACF,CACF;aACA,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;QAExD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,CACrC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EACrC,CAAC,CACF,CAAA;YACD,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;gBACnC,MAAM,EAAE;qBACL,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;qBAC3B,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;qBACnB,KAAK,CAAC,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAA;YACpE,CAAC;YACD,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;YACvD,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;oBAC1C,YAAY,EAAE,WAAW;oBACzB,OAAO,EAAE,MAAM;oBACf,SAAS,EAAE,MAAM;oBACjB,OAAO,EAAE,eAAe;oBACxB,IAAI;oBACJ,WAAW,EACT,SAAS,GAAG,CAAC;wBACX,CAAC,CAAC,GAAG,WAAW,KAAK,SAAS,uCAAuC;wBACrE,CAAC,CAAC,WAAW;oBACjB,QAAQ,EAAE,mCAAgB,CAAC,IAAI,CAAC;oBAChC,UAAU,EAAE,SAAS;oBACrB,UAAU,EAAE,GAAG;iBAChB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,kCAAkC;YAClC,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;gBAC1C,YAAY,EAAE,WAAW;gBACzB,OAAO,EAAE,MAAM;gBACf,SAAS,EAAE,MAAM;gBACjB,OAAO,EAAE,MAAM;gBACf,IAAI;gBACJ,WAAW;gBACX,QAAQ,EAAE,mCAAgB,CAAC,IAAI,CAAC;gBAChC,UAAU,EAAE,SAAS;gBACrB,UAAU,EAAE,GAAG;aAChB,CAAC,CAAA;QACJ,CAAC;QAED,eAAM,CAAC,IAAI,CACT,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,EAChD,0BAA0B,CAC3B,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,qBAAqB,CACzC,MAAc,EACd,MAAc,EACd,IAAe,EACf,WAAmB,EACnB,SAAsB,EACtB,WAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,IAAA,mBAAS,EACb,GAAG,EAAE,CACH,oBAAoB,CAClB,MAAM,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,SAAS,EACT,WAAW,CACZ,EACH;YACE,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;YACnB,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC1B,eAAM,CAAC,IAAI,CACT,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,EAC/B,+BAA+B,OAAO,EAAE,CACzC,CAAA;YACH,CAAC;SACF,CACF,CAAA;IACH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,MAAM,IAAA,6BAAc,EAAC,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAC5D,eAAM,CAAC,KAAK,CACV,EAAE,WAAW,EAAE,KAAK,EAAE,EACtB,oEAAoE,CACrE,CAAA;QACD,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,wBAAwB,CAC5C,WAAmB,EACnB,MAAc;IAEd,OAAO,MAAM,YAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACvC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC;YAClD,KAAK,EAAE,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC;SACzD,CAAC,CAAA;QAEF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,eAAM,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,EAAE,wCAAwC,CAAC,CAAA;YACtE,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACtB,eAAM,CAAC,IAAI,CACT,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,CAAC,OAAO,EAAE,EAC9C,kFAAkF,CACnF,CAAA;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,EAAE;aACL,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;aAC3B,GAAG,CAAC;YACH,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,GAAG,KAAK,CAAC,WAAW,cAAc,MAAM,GAAG;SACzD,CAAC;aACD,KAAK,CAAC,IAAA,gBAAE,EAAC,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;QAE3D,eAAM,CAAC,IAAI,CACT;YACE,WAAW;YACX,MAAM,EAAE,KAAK,CAAC,OAAO;YACrB,aAAa,EAAE,KAAK,CAAC,OAAO;YAC5B,MAAM;SACP,EACD,sBAAsB,CACvB,CAAA;QAED,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { UsageLimits } from '../constants';
|
|
2
|
+
/**
|
|
3
|
+
* Determines the user's plan based on their Stripe Price ID.
|
|
4
|
+
* Defaults to FREE if no matching price ID is found.
|
|
5
|
+
* NOTE: Requires backend environment variables for Stripe Price IDs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getPlanFromPriceId(priceId: string | null | undefined, proPriceId?: string, moarProPriceId?: string): UsageLimits;
|
|
8
|
+
/**
|
|
9
|
+
* Gets the total monthly credit grant amount for a given plan and user.
|
|
10
|
+
* This includes both their plan's base credits and any referral bonuses,
|
|
11
|
+
* combined into a single number.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getMonthlyGrantForPlan(plan: UsageLimits, userId?: string): Promise<number>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPlanFromPriceId = getPlanFromPriceId;
|
|
4
|
+
exports.getMonthlyGrantForPlan = getMonthlyGrantForPlan;
|
|
5
|
+
const constants_1 = require("../constants");
|
|
6
|
+
const grant_credits_1 = require("./grant-credits");
|
|
7
|
+
const logger_1 = require("../util/logger");
|
|
8
|
+
/**
|
|
9
|
+
* Determines the user's plan based on their Stripe Price ID.
|
|
10
|
+
* Defaults to FREE if no matching price ID is found.
|
|
11
|
+
* NOTE: Requires backend environment variables for Stripe Price IDs.
|
|
12
|
+
*/
|
|
13
|
+
function getPlanFromPriceId(priceId, proPriceId, moarProPriceId) {
|
|
14
|
+
if (!priceId) {
|
|
15
|
+
return constants_1.UsageLimits.FREE;
|
|
16
|
+
}
|
|
17
|
+
if (proPriceId && priceId === proPriceId) {
|
|
18
|
+
return constants_1.UsageLimits.PRO;
|
|
19
|
+
}
|
|
20
|
+
if (moarProPriceId && priceId === moarProPriceId) {
|
|
21
|
+
return constants_1.UsageLimits.MOAR_PRO;
|
|
22
|
+
}
|
|
23
|
+
return constants_1.UsageLimits.FREE; // Default to FREE for unknown or non-matching IDs
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Gets the total monthly credit grant amount for a given plan and user.
|
|
27
|
+
* This includes both their plan's base credits and any referral bonuses,
|
|
28
|
+
* combined into a single number.
|
|
29
|
+
*/
|
|
30
|
+
async function getMonthlyGrantForPlan(plan, userId) {
|
|
31
|
+
// If no userId provided, just return the plan's base limit
|
|
32
|
+
if (!userId) {
|
|
33
|
+
return constants_1.PLAN_CONFIGS[plan]?.limit ?? constants_1.PLAN_CONFIGS[constants_1.UsageLimits.FREE].limit;
|
|
34
|
+
}
|
|
35
|
+
// Calculate total grant by adding base plan amount and referral bonuses
|
|
36
|
+
const [freeGrantAmount, referralBonus] = await Promise.all([
|
|
37
|
+
(0, grant_credits_1.getPreviousFreeGrantAmount)(userId),
|
|
38
|
+
(0, grant_credits_1.calculateTotalReferralBonus)(userId),
|
|
39
|
+
]);
|
|
40
|
+
const totalGrant = freeGrantAmount + referralBonus;
|
|
41
|
+
logger_1.logger.debug({ userId, plan, freeGrantAmount, referralBonus, totalGrant }, 'Calculated total monthly grant amount');
|
|
42
|
+
return totalGrant;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=plans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plans.js","sourceRoot":"","sources":["../../src/billing/plans.ts"],"names":[],"mappings":";;AASA,gDAiBC;AAOD,wDAqBC;AAtDD,4CAAwD;AACxD,mDAAyF;AACzF,2CAAuC;AAEvC;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,OAAkC,EAClC,UAAmB,EACnB,cAAuB;IAEvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,uBAAW,CAAC,IAAI,CAAA;IACzB,CAAC;IAED,IAAI,UAAU,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QACzC,OAAO,uBAAW,CAAC,GAAG,CAAA;IACxB,CAAC;IACD,IAAI,cAAc,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;QACjD,OAAO,uBAAW,CAAC,QAAQ,CAAA;IAC7B,CAAC;IAED,OAAO,uBAAW,CAAC,IAAI,CAAA,CAAC,kDAAkD;AAC5E,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,sBAAsB,CAC1C,IAAiB,EACjB,MAAe;IAEf,2DAA2D;IAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,wBAAY,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,wBAAY,CAAC,uBAAW,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;IAC1E,CAAC;IAED,wEAAwE;IACxE,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzD,IAAA,0CAA0B,EAAC,MAAM,CAAC;QAClC,IAAA,2CAA2B,EAAC,MAAM,CAAC;KACpC,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,eAAe,GAAG,aAAa,CAAA;IAClD,eAAM,CAAC,KAAK,CACV,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,EAC5D,uCAAuC,CACxC,CAAA;IACD,OAAO,UAAU,CAAA;AACnB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the rollover amount for a user at the end of their billing cycle
|
|
3
|
+
* and applies the necessary database updates (creates rollover grant, resets usage, updates reset date).
|
|
4
|
+
*
|
|
5
|
+
* NOTE: This function performs database writes and should be called within a transaction
|
|
6
|
+
* if invoked alongside other database operations. It also depends on the `credit_grants`
|
|
7
|
+
* table and the modified `user` table schema being in place.
|
|
8
|
+
*
|
|
9
|
+
* @param userId The ID of the user whose cycle is ending.
|
|
10
|
+
* @param cycleEndDate The exact date and time the billing cycle ended.
|
|
11
|
+
* @returns A Promise resolving when the process is complete.
|
|
12
|
+
*/
|
|
13
|
+
export declare function calculateAndApplyRollover(userId: string, cycleEndDate: Date): Promise<void>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.calculateAndApplyRollover = calculateAndApplyRollover;
|
|
30
|
+
const db_1 = __importDefault(require("../db"));
|
|
31
|
+
const schema = __importStar(require("../db/schema"));
|
|
32
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
33
|
+
const balance_calculator_1 = require("./balance-calculator");
|
|
34
|
+
const logger_1 = require("../util/logger");
|
|
35
|
+
const dates_1 = require("../util/dates"); // Assuming this utility exists/is created
|
|
36
|
+
// Define which grant types contribute to rollover
|
|
37
|
+
const ROLLOVER_GRANT_TYPES = ['purchase', 'rollover'];
|
|
38
|
+
/**
|
|
39
|
+
* Calculates the rollover amount for a user at the end of their billing cycle
|
|
40
|
+
* and applies the necessary database updates (creates rollover grant, resets usage, updates reset date).
|
|
41
|
+
*
|
|
42
|
+
* NOTE: This function performs database writes and should be called within a transaction
|
|
43
|
+
* if invoked alongside other database operations. It also depends on the `credit_grants`
|
|
44
|
+
* table and the modified `user` table schema being in place.
|
|
45
|
+
*
|
|
46
|
+
* @param userId The ID of the user whose cycle is ending.
|
|
47
|
+
* @param cycleEndDate The exact date and time the billing cycle ended.
|
|
48
|
+
* @returns A Promise resolving when the process is complete.
|
|
49
|
+
*/
|
|
50
|
+
async function calculateAndApplyRollover(userId, cycleEndDate) {
|
|
51
|
+
logger_1.logger.info({ userId, cycleEndDate }, 'Starting end-of-cycle rollover process');
|
|
52
|
+
try {
|
|
53
|
+
// 1. Fetch the user's usage for the cycle that just ended
|
|
54
|
+
// We need the user record to get the usage *before* resetting it.
|
|
55
|
+
const user = await db_1.default.query.user.findFirst({
|
|
56
|
+
where: (0, drizzle_orm_1.eq)(schema.user.id, userId),
|
|
57
|
+
columns: { usage: true, next_quota_reset: true }, // Also get current reset date
|
|
58
|
+
});
|
|
59
|
+
if (!user) {
|
|
60
|
+
logger_1.logger.error({ userId }, 'User not found during rollover calculation.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Ensure we don't run rollover multiple times for the same cycle
|
|
64
|
+
if (user.next_quota_reset && user.next_quota_reset > cycleEndDate) {
|
|
65
|
+
logger_1.logger.warn({ userId, cycleEndDate, nextReset: user.next_quota_reset }, 'Rollover attempted for a cycle that has not yet ended or already processed. Skipping.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const endedCycleUsage = user.usage;
|
|
69
|
+
// Calculate the start date of the ended cycle (approximate)
|
|
70
|
+
const cycleStartDate = new Date(cycleEndDate);
|
|
71
|
+
cycleStartDate.setMonth(cycleStartDate.getMonth() - 1); // Approximation
|
|
72
|
+
// 2. Fetch all grants that were active *during* the ended cycle
|
|
73
|
+
// Active during cycle: created_at < cycleEndDate AND (expires_at IS NULL OR expires_at > cycleStartDate)
|
|
74
|
+
const grantsActiveDuringCycle = await db_1.default
|
|
75
|
+
.select()
|
|
76
|
+
.from(schema.creditGrants)
|
|
77
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditGrants.user_id, userId), (0, drizzle_orm_1.lte)(schema.creditGrants.created_at, cycleEndDate), // Created before or exactly at cycle end
|
|
78
|
+
(0, drizzle_orm_1.or)((0, drizzle_orm_1.isNull)(schema.creditGrants.expires_at), // Never expires
|
|
79
|
+
(0, drizzle_orm_1.gt)(schema.creditGrants.expires_at, cycleStartDate) // Expired after cycle start
|
|
80
|
+
)))
|
|
81
|
+
// 3. Order grants by priority ASC, then created_at ASC
|
|
82
|
+
.orderBy((0, drizzle_orm_1.asc)(schema.creditGrants.priority), (0, drizzle_orm_1.asc)(schema.creditGrants.created_at));
|
|
83
|
+
// 4. Initialize rollover amount
|
|
84
|
+
let rolloverAmount = 0;
|
|
85
|
+
// 5. Initialize usage to account for from the ended cycle
|
|
86
|
+
let usageToAccountFor = endedCycleUsage;
|
|
87
|
+
logger_1.logger.debug({ userId, endedCycleUsage, grantsCount: grantsActiveDuringCycle.length }, 'Simulating consumption for ended cycle');
|
|
88
|
+
// 6. Simulate Cycle Consumption
|
|
89
|
+
for (const grant of grantsActiveDuringCycle) {
|
|
90
|
+
const consumedFromThisGrant = Math.min(grant.amount, usageToAccountFor);
|
|
91
|
+
const remainingInThisGrant = grant.amount - consumedFromThisGrant;
|
|
92
|
+
usageToAccountFor -= consumedFromThisGrant;
|
|
93
|
+
logger_1.logger.trace({
|
|
94
|
+
userId,
|
|
95
|
+
grantId: grant.operation_id,
|
|
96
|
+
grantType: grant.type,
|
|
97
|
+
grantAmount: grant.amount,
|
|
98
|
+
consumed: consumedFromThisGrant,
|
|
99
|
+
remainingInGrant: remainingInThisGrant,
|
|
100
|
+
usageLeftToAccount: usageToAccountFor,
|
|
101
|
+
}, 'Processing grant during rollover simulation');
|
|
102
|
+
// 7. Check for Rollover Contribution
|
|
103
|
+
if (remainingInThisGrant > 0 &&
|
|
104
|
+
ROLLOVER_GRANT_TYPES.includes(grant.type)) {
|
|
105
|
+
rolloverAmount += remainingInThisGrant;
|
|
106
|
+
logger_1.logger.trace({
|
|
107
|
+
userId,
|
|
108
|
+
grantId: grant.operation_id,
|
|
109
|
+
addedToRollover: remainingInThisGrant,
|
|
110
|
+
newTotalRollover: rolloverAmount,
|
|
111
|
+
}, 'Grant contributed to rollover amount');
|
|
112
|
+
}
|
|
113
|
+
if (usageToAccountFor <= 0) {
|
|
114
|
+
// If usage is covered, check remaining grants for rollover contributions
|
|
115
|
+
const remainingGrants = grantsActiveDuringCycle.slice(grantsActiveDuringCycle.indexOf(grant) + 1);
|
|
116
|
+
for (const remainingGrant of remainingGrants) {
|
|
117
|
+
if (ROLLOVER_GRANT_TYPES.includes(remainingGrant.type)) {
|
|
118
|
+
rolloverAmount += remainingGrant.amount;
|
|
119
|
+
logger_1.logger.trace({
|
|
120
|
+
userId,
|
|
121
|
+
grantId: remainingGrant.operation_id,
|
|
122
|
+
addedToRollover: remainingGrant.amount,
|
|
123
|
+
newTotalRollover: rolloverAmount,
|
|
124
|
+
}, 'Untouched grant contributed to rollover amount');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break; // All usage accounted for
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
logger_1.logger.info({ userId, endedCycleUsage, calculatedRollover: rolloverAmount }, 'Rollover calculation complete');
|
|
131
|
+
// 8. Database Updates (Perform as a transaction)
|
|
132
|
+
await db_1.default.transaction(async (tx) => {
|
|
133
|
+
// Insert new 'rollover' grant if amount > 0
|
|
134
|
+
if (rolloverAmount > 0) {
|
|
135
|
+
const rolloverGrantId = `rollover-${userId}-${cycleEndDate.toISOString()}`;
|
|
136
|
+
await tx
|
|
137
|
+
.insert(schema.creditGrants)
|
|
138
|
+
.values({
|
|
139
|
+
operation_id: rolloverGrantId,
|
|
140
|
+
user_id: userId,
|
|
141
|
+
amount: rolloverAmount,
|
|
142
|
+
amount_remaining: rolloverAmount, // Initialize amount_remaining to the full amount
|
|
143
|
+
type: 'rollover', // Use string literal directly
|
|
144
|
+
priority: balance_calculator_1.GRANT_PRIORITIES.rollover, // Use defined priority
|
|
145
|
+
expires_at: null, // Rollover credits don't expire
|
|
146
|
+
description: `Rollover from cycle ending ${cycleEndDate.toLocaleDateString()}`,
|
|
147
|
+
// stripe_grant_id is NULL for local grants
|
|
148
|
+
})
|
|
149
|
+
.onConflictDoNothing(); // Avoid duplicate rollovers if run concurrently
|
|
150
|
+
logger_1.logger.debug({ userId, rolloverAmount }, 'Inserted rollover grant');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
logger_1.logger.debug({ userId }, 'No rollover amount to grant.');
|
|
154
|
+
}
|
|
155
|
+
// Update the user: reset usage and set next reset date
|
|
156
|
+
const nextResetDate = (0, dates_1.getNextQuotaReset)(cycleEndDate); // Calculate the next reset date based on the cycle end
|
|
157
|
+
await tx
|
|
158
|
+
.update(schema.user)
|
|
159
|
+
.set({
|
|
160
|
+
usage: 0,
|
|
161
|
+
next_quota_reset: nextResetDate,
|
|
162
|
+
})
|
|
163
|
+
.where((0, drizzle_orm_1.eq)(schema.user.id, userId));
|
|
164
|
+
logger_1.logger.info({ userId, nextResetDate: nextResetDate.toISOString() }, 'User usage reset and next reset date updated');
|
|
165
|
+
});
|
|
166
|
+
logger_1.logger.info({ userId }, 'Rollover process completed successfully.');
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
logger_1.logger.error({ userId, cycleEndDate, error }, 'Error during rollover process');
|
|
170
|
+
// Depending on trigger mechanism, might need error handling/retry logic here
|
|
171
|
+
throw error; // Re-throw to indicate failure if called from a job
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=rollover-logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollover-logic.js","sourceRoot":"","sources":["../../src/billing/rollover-logic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CreditType } from './balance-calculator';
|
|
2
|
+
import Stripe from 'stripe';
|
|
3
|
+
export declare const stripeApi: {
|
|
4
|
+
credits: {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a customer balance transaction in Stripe to grant credits.
|
|
7
|
+
* NOTE: Database insertion for a local 'creditGrant' record is commented out
|
|
8
|
+
* as the 'creditGrant' table doesn't exist in the current schema.
|
|
9
|
+
*/
|
|
10
|
+
create({ userId, amount, type, expiresAt, description, metadata }: {
|
|
11
|
+
userId: string;
|
|
12
|
+
amount: number;
|
|
13
|
+
type: CreditType;
|
|
14
|
+
expiresAt?: Date;
|
|
15
|
+
description?: string;
|
|
16
|
+
metadata?: Record<string, string | number | null>;
|
|
17
|
+
}): Promise<Stripe.CustomerBalanceTransaction>;
|
|
18
|
+
/**
|
|
19
|
+
* Retrieves the customer's cash balance from Stripe.
|
|
20
|
+
* Note: This is the overall cash balance, typically negative if credits have been granted.
|
|
21
|
+
* It does NOT represent the sum of specific "credit grants".
|
|
22
|
+
*/
|
|
23
|
+
getBalance(userId: string): Promise<number>;
|
|
24
|
+
};
|
|
25
|
+
usage: {
|
|
26
|
+
/**
|
|
27
|
+
* Reports usage to a Stripe meter.
|
|
28
|
+
*/
|
|
29
|
+
report(userId: string, amount: number, metadata?: Record<string, any>): Promise<Stripe.Response<Stripe.Billing.MeterEvent>>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.stripeApi = void 0;
|
|
30
|
+
const db_1 = __importDefault(require("../db"));
|
|
31
|
+
const schema = __importStar(require("../db/schema"));
|
|
32
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
33
|
+
const stripe_1 = require("../util/stripe");
|
|
34
|
+
const balance_calculator_1 = require("./balance-calculator");
|
|
35
|
+
// Helper to get Stripe Customer ID
|
|
36
|
+
async function getCustomerId(userId) {
|
|
37
|
+
const user = await db_1.default.query.user.findFirst({
|
|
38
|
+
where: (0, drizzle_orm_1.eq)(schema.user.id, userId),
|
|
39
|
+
columns: { stripe_customer_id: true }
|
|
40
|
+
});
|
|
41
|
+
if (!user?.stripe_customer_id) {
|
|
42
|
+
throw new Error(`Stripe customer ID not found for user ${userId}`);
|
|
43
|
+
}
|
|
44
|
+
return user.stripe_customer_id;
|
|
45
|
+
}
|
|
46
|
+
exports.stripeApi = {
|
|
47
|
+
credits: {
|
|
48
|
+
/**
|
|
49
|
+
* Creates a customer balance transaction in Stripe to grant credits.
|
|
50
|
+
* NOTE: Database insertion for a local 'creditGrant' record is commented out
|
|
51
|
+
* as the 'creditGrant' table doesn't exist in the current schema.
|
|
52
|
+
*/
|
|
53
|
+
async create({ userId, amount, type, expiresAt, description, metadata = {} }) {
|
|
54
|
+
const customerId = await getCustomerId(userId);
|
|
55
|
+
const priority = balance_calculator_1.CREDIT_PRIORITIES[type];
|
|
56
|
+
const amountInCents = -Math.abs(amount);
|
|
57
|
+
const transaction = await stripe_1.stripeServer.customers.createBalanceTransaction(customerId, {
|
|
58
|
+
amount: amountInCents,
|
|
59
|
+
currency: 'usd',
|
|
60
|
+
description: description ?? `${type} credits granted`,
|
|
61
|
+
metadata: {
|
|
62
|
+
...metadata,
|
|
63
|
+
type: type,
|
|
64
|
+
priority: priority.toString(),
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return transaction;
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Retrieves the customer's cash balance from Stripe.
|
|
71
|
+
* Note: This is the overall cash balance, typically negative if credits have been granted.
|
|
72
|
+
* It does NOT represent the sum of specific "credit grants".
|
|
73
|
+
*/
|
|
74
|
+
async getBalance(userId) {
|
|
75
|
+
const customerId = await getCustomerId(userId);
|
|
76
|
+
const customer = await stripe_1.stripeServer.customers.retrieve(customerId);
|
|
77
|
+
if (customer.deleted) {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
return customer.balance ?? 0;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
usage: {
|
|
84
|
+
/**
|
|
85
|
+
* Reports usage to a Stripe meter.
|
|
86
|
+
*/
|
|
87
|
+
async report(userId, amount, metadata = {}) {
|
|
88
|
+
const customerId = await getCustomerId(userId);
|
|
89
|
+
const eventName = 'codebuff_credits_used';
|
|
90
|
+
return stripe_1.stripeServer.billing.meterEvents.create({
|
|
91
|
+
event_name: eventName,
|
|
92
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
93
|
+
payload: {
|
|
94
|
+
stripe_customer_id: customerId,
|
|
95
|
+
value: amount.toString(),
|
|
96
|
+
...metadata
|
|
97
|
+
}
|
|
98
|
+
}).catch((err) => {
|
|
99
|
+
throw err;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=stripe-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripe-api.js","sourceRoot":"","sources":["../../src/billing/stripe-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAsB;AACtB,qDAAsC;AACtC,6CAAgC;AAChC,2CAA6C;AAC7C,6DAAoE;AAGpE,mCAAmC;AACnC,KAAK,UAAU,aAAa,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,MAAM,YAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;QACvC,KAAK,EAAE,IAAA,gBAAE,EAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC;QACjC,OAAO,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE;KACxC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,EAAE,kBAAkB,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC,kBAAkB,CAAC;AACnC,CAAC;AAEY,QAAA,SAAS,GAAG;IACvB,OAAO,EAAE;QACP;;;;WAIG;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,GAAG,EAAE,EAOzE;YACC,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9C,MAAM,QAAQ,GAAG,sCAAiB,CAAC,IAAI,CAAC,CAAA;YAExC,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAExC,MAAM,WAAW,GAAG,MAAM,qBAAY,CAAC,SAAS,CAAC,wBAAwB,CACrE,UAAU,EACV;gBACI,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,WAAW,IAAI,GAAG,IAAI,kBAAkB;gBACrD,QAAQ,EAAE;oBACN,GAAG,QAAQ;oBACX,IAAI,EAAE,IAAI;oBACV,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE;iBAChC;aACJ,CACJ,CAAC;YAEF,OAAO,WAAW,CAAA;QACpB,CAAC;QAED;;;;WAIG;QACH,KAAK,CAAC,UAAU,CAAC,MAAc;YAC3B,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,MAAM,qBAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAEnE,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,CAAC,CAAC;YACb,CAAC;YAED,OAAO,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;QACjC,CAAC;KACF;IACD,KAAK,EAAE;QACL;;WAEG;QACH,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,WAAgC,EAAE;YAC7E,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9C,MAAM,SAAS,GAAG,uBAAuB,CAAC;YAE1C,OAAO,qBAAY,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC;gBAC7C,UAAU,EAAE,SAAS;gBACrB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;gBACxC,OAAO,EAAE;oBACP,kBAAkB,EAAE,UAAU;oBAC9B,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE;oBACxB,GAAG,QAAQ;iBACZ;aACF,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;gBACpB,MAAM,GAAG,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC;KACF;CACF,CAAA"}
|