@apart-tech/intelligence-core 1.15.2 → 1.17.0
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/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/__tests__/search-service.test.js +3 -3
- package/dist/services/__tests__/search-service.test.js.map +1 -1
- package/dist/services/billing-event-service.d.ts +22 -0
- package/dist/services/billing-event-service.d.ts.map +1 -0
- package/dist/services/billing-event-service.js +30 -0
- package/dist/services/billing-event-service.js.map +1 -0
- package/dist/services/document-service.d.ts +3 -1
- package/dist/services/document-service.d.ts.map +1 -1
- package/dist/services/document-service.js +7 -1
- package/dist/services/document-service.js.map +1 -1
- package/dist/services/quota-service.d.ts +43 -0
- package/dist/services/quota-service.d.ts.map +1 -0
- package/dist/services/quota-service.js +148 -0
- package/dist/services/quota-service.js.map +1 -0
- package/dist/services/quota-service.test.d.ts +2 -0
- package/dist/services/quota-service.test.d.ts.map +1 -0
- package/dist/services/quota-service.test.js +165 -0
- package/dist/services/quota-service.test.js.map +1 -0
- package/dist/services/search-service.d.ts +7 -0
- package/dist/services/search-service.d.ts.map +1 -1
- package/dist/services/search-service.js +68 -23
- package/dist/services/search-service.js.map +1 -1
- package/dist/services/search-service.test.d.ts +2 -0
- package/dist/services/search-service.test.d.ts.map +1 -0
- package/dist/services/search-service.test.js +65 -0
- package/dist/services/search-service.test.js.map +1 -0
- package/dist/services/usage-service.d.ts +49 -2
- package/dist/services/usage-service.d.ts.map +1 -1
- package/dist/services/usage-service.js +109 -19
- package/dist/services/usage-service.js.map +1 -1
- package/dist/services/usage-service.test.d.ts +2 -0
- package/dist/services/usage-service.test.d.ts.map +1 -0
- package/dist/services/usage-service.test.js +191 -0
- package/dist/services/usage-service.test.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +90 -5
|
@@ -3,11 +3,21 @@ const PLAN_LIMITS = {
|
|
|
3
3
|
maxNodes: 500,
|
|
4
4
|
maxApiCallsPerMonth: 1_000,
|
|
5
5
|
maxMembers: 3,
|
|
6
|
+
maxOcrPagesPerMonth: 25,
|
|
7
|
+
maxStorageBytes: 1_073_741_824, // 1 GB
|
|
8
|
+
maxAgentRunsPerMonth: 0,
|
|
9
|
+
maxFileSizeBytes: 10_485_760, // 10 MB
|
|
10
|
+
maxPagesPerDocument: 20,
|
|
6
11
|
},
|
|
7
12
|
pro: {
|
|
8
13
|
maxNodes: Infinity,
|
|
9
14
|
maxApiCallsPerMonth: 50_000,
|
|
10
15
|
maxMembers: Infinity,
|
|
16
|
+
maxOcrPagesPerMonth: 500, // per seat — multiplied at runtime
|
|
17
|
+
maxStorageBytes: 10_737_418_240, // 10 GB per seat — multiplied at runtime
|
|
18
|
+
maxAgentRunsPerMonth: 50, // per seat — multiplied at runtime
|
|
19
|
+
maxFileSizeBytes: 52_428_800, // 50 MB
|
|
20
|
+
maxPagesPerDocument: Infinity,
|
|
11
21
|
},
|
|
12
22
|
};
|
|
13
23
|
/**
|
|
@@ -22,18 +32,27 @@ export class UsageService {
|
|
|
22
32
|
constructor(db) {
|
|
23
33
|
this.db = db;
|
|
24
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the base plan limits for a given plan name.
|
|
37
|
+
*/
|
|
38
|
+
static getPlanLimits(plan) {
|
|
39
|
+
return PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
40
|
+
}
|
|
25
41
|
/**
|
|
26
42
|
* Get the full usage snapshot for an organization.
|
|
27
43
|
*/
|
|
28
44
|
async getUsage(organizationId) {
|
|
29
|
-
const [org, nodeCount, memberCount, apiCalls] = await Promise.all([
|
|
45
|
+
const [org, nodeCount, memberCount, apiCalls, ocrPages, storageBytes, agentRuns] = await Promise.all([
|
|
30
46
|
this.db.organization.findUnique({
|
|
31
47
|
where: { id: organizationId },
|
|
32
48
|
select: { plan: true, stripeCustomerId: true, stripeSubscriptionId: true, currentPeriodEnd: true },
|
|
33
49
|
}),
|
|
34
50
|
this.db.node.count({ where: { organizationId } }),
|
|
35
51
|
this.db.membership.count({ where: { organizationId } }),
|
|
36
|
-
this.
|
|
52
|
+
this.getMetricCount(organizationId, "api_calls"),
|
|
53
|
+
this.getMetricCount(organizationId, "ocr_pages"),
|
|
54
|
+
this.getStorageUsed(organizationId),
|
|
55
|
+
this.getMetricCount(organizationId, "agent_runs"),
|
|
37
56
|
]);
|
|
38
57
|
const plan = org?.plan ?? "free";
|
|
39
58
|
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
@@ -44,6 +63,9 @@ export class UsageService {
|
|
|
44
63
|
nodes: nodeCount,
|
|
45
64
|
apiCalls,
|
|
46
65
|
members: memberCount,
|
|
66
|
+
ocrPages,
|
|
67
|
+
storageBytes,
|
|
68
|
+
agentRuns,
|
|
47
69
|
},
|
|
48
70
|
stripeCustomerId: org?.stripeCustomerId ?? null,
|
|
49
71
|
stripeSubscriptionId: org?.stripeSubscriptionId ?? null,
|
|
@@ -55,18 +77,98 @@ export class UsageService {
|
|
|
55
77
|
* Uses upsert to create or increment atomically.
|
|
56
78
|
*/
|
|
57
79
|
async incrementApiCalls(organizationId, count = 1) {
|
|
80
|
+
await this.incrementMetric(organizationId, "api_calls", count);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Increment the OCR pages counter for the current month.
|
|
84
|
+
*/
|
|
85
|
+
async incrementOcrPages(organizationId, count = 1) {
|
|
86
|
+
await this.incrementMetric(organizationId, "ocr_pages", count);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Increment the agent runs counter for the current month.
|
|
90
|
+
*/
|
|
91
|
+
async incrementAgentRuns(organizationId, count = 1) {
|
|
92
|
+
await this.incrementMetric(organizationId, "agent_runs", count);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get OCR pages used in the current period.
|
|
96
|
+
*/
|
|
97
|
+
async getOcrPagesUsed(organizationId) {
|
|
98
|
+
return this.getMetricCount(organizationId, "ocr_pages");
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get agent runs used in the current period.
|
|
102
|
+
*/
|
|
103
|
+
async getAgentRunsUsed(organizationId) {
|
|
104
|
+
return this.getMetricCount(organizationId, "agent_runs");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get total storage used by an org, computed as SUM(fileSizeBytes) from documents.
|
|
108
|
+
*/
|
|
109
|
+
async getStorageUsed(organizationId) {
|
|
110
|
+
const result = await this.db.document.aggregate({
|
|
111
|
+
where: { organizationId },
|
|
112
|
+
_sum: { fileSizeBytes: true },
|
|
113
|
+
});
|
|
114
|
+
return result._sum.fileSizeBytes ?? 0;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get usage history for a metric over the last N months.
|
|
118
|
+
*/
|
|
119
|
+
async getUsageHistory(organizationId, metric, months = 6) {
|
|
120
|
+
const cutoff = new Date();
|
|
121
|
+
cutoff.setUTCMonth(cutoff.getUTCMonth() - months);
|
|
122
|
+
cutoff.setUTCDate(1);
|
|
123
|
+
cutoff.setUTCHours(0, 0, 0, 0);
|
|
124
|
+
const records = await this.db.usageRecord.findMany({
|
|
125
|
+
where: {
|
|
126
|
+
organizationId,
|
|
127
|
+
metric,
|
|
128
|
+
periodStart: { gte: cutoff },
|
|
129
|
+
},
|
|
130
|
+
orderBy: { periodStart: "asc" },
|
|
131
|
+
select: { periodStart: true, count: true },
|
|
132
|
+
});
|
|
133
|
+
return records;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check whether an organization has exceeded a specific limit.
|
|
137
|
+
*/
|
|
138
|
+
async isOverLimit(organizationId, metric) {
|
|
139
|
+
const snapshot = await this.getUsage(organizationId);
|
|
140
|
+
const limits = snapshot.limits;
|
|
141
|
+
switch (metric) {
|
|
142
|
+
case "nodes":
|
|
143
|
+
return snapshot.usage.nodes >= limits.maxNodes;
|
|
144
|
+
case "apiCalls":
|
|
145
|
+
return snapshot.usage.apiCalls >= limits.maxApiCallsPerMonth;
|
|
146
|
+
case "members":
|
|
147
|
+
return snapshot.usage.members >= limits.maxMembers;
|
|
148
|
+
case "ocrPages":
|
|
149
|
+
return snapshot.usage.ocrPages >= limits.maxOcrPagesPerMonth;
|
|
150
|
+
case "storageBytes":
|
|
151
|
+
return snapshot.usage.storageBytes >= limits.maxStorageBytes;
|
|
152
|
+
case "agentRuns":
|
|
153
|
+
return snapshot.usage.agentRuns >= limits.maxAgentRunsPerMonth;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Generic metric increment — upsert a usage_records row for the current period.
|
|
158
|
+
*/
|
|
159
|
+
async incrementMetric(organizationId, metric, count) {
|
|
58
160
|
const periodStart = this.currentPeriodStart();
|
|
59
161
|
await this.db.usageRecord.upsert({
|
|
60
162
|
where: {
|
|
61
163
|
organizationId_metric_periodStart: {
|
|
62
164
|
organizationId,
|
|
63
|
-
metric
|
|
165
|
+
metric,
|
|
64
166
|
periodStart,
|
|
65
167
|
},
|
|
66
168
|
},
|
|
67
169
|
create: {
|
|
68
170
|
organizationId,
|
|
69
|
-
metric
|
|
171
|
+
metric,
|
|
70
172
|
periodStart,
|
|
71
173
|
count,
|
|
72
174
|
},
|
|
@@ -77,27 +179,15 @@ export class UsageService {
|
|
|
77
179
|
});
|
|
78
180
|
}
|
|
79
181
|
/**
|
|
80
|
-
*
|
|
182
|
+
* Get the count for a metric in the current period.
|
|
81
183
|
*/
|
|
82
|
-
async
|
|
83
|
-
const snapshot = await this.getUsage(organizationId);
|
|
84
|
-
const limits = snapshot.limits;
|
|
85
|
-
switch (metric) {
|
|
86
|
-
case "nodes":
|
|
87
|
-
return snapshot.usage.nodes >= limits.maxNodes;
|
|
88
|
-
case "apiCalls":
|
|
89
|
-
return snapshot.usage.apiCalls >= limits.maxApiCallsPerMonth;
|
|
90
|
-
case "members":
|
|
91
|
-
return snapshot.usage.members >= limits.maxMembers;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async getApiCallCount(organizationId) {
|
|
184
|
+
async getMetricCount(organizationId, metric) {
|
|
95
185
|
const periodStart = this.currentPeriodStart();
|
|
96
186
|
const record = await this.db.usageRecord.findUnique({
|
|
97
187
|
where: {
|
|
98
188
|
organizationId_metric_periodStart: {
|
|
99
189
|
organizationId,
|
|
100
|
-
metric
|
|
190
|
+
metric,
|
|
101
191
|
periodStart,
|
|
102
192
|
},
|
|
103
193
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/services/usage-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/services/usage-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-service.test.d.ts","sourceRoot":"","sources":["../../src/services/usage-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
2
|
+
import { UsageService } from "./usage-service.js";
|
|
3
|
+
const ORG_ID = "org-1";
|
|
4
|
+
function makeMockDb() {
|
|
5
|
+
const usageStore = {};
|
|
6
|
+
const nodeCount = 42;
|
|
7
|
+
const memberCount = 2;
|
|
8
|
+
const orgData = {
|
|
9
|
+
plan: "free",
|
|
10
|
+
stripeCustomerId: null,
|
|
11
|
+
stripeSubscriptionId: null,
|
|
12
|
+
currentPeriodEnd: null,
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
organization: {
|
|
16
|
+
findUnique: vi.fn(async () => orgData),
|
|
17
|
+
},
|
|
18
|
+
node: {
|
|
19
|
+
count: vi.fn(async () => nodeCount),
|
|
20
|
+
},
|
|
21
|
+
membership: {
|
|
22
|
+
count: vi.fn(async () => memberCount),
|
|
23
|
+
},
|
|
24
|
+
document: {
|
|
25
|
+
aggregate: vi.fn(async () => ({
|
|
26
|
+
_sum: { fileSizeBytes: 512_000 },
|
|
27
|
+
})),
|
|
28
|
+
},
|
|
29
|
+
usageRecord: {
|
|
30
|
+
findUnique: vi.fn(async ({ where }) => {
|
|
31
|
+
const key = where.organizationId_metric_periodStart.metric;
|
|
32
|
+
return usageStore[key] ?? null;
|
|
33
|
+
}),
|
|
34
|
+
findMany: vi.fn(async () => []),
|
|
35
|
+
upsert: vi.fn(async ({ where, create, update }) => {
|
|
36
|
+
const key = where.organizationId_metric_periodStart.metric;
|
|
37
|
+
if (usageStore[key]) {
|
|
38
|
+
usageStore[key].count += update.count.increment;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
usageStore[key] = { count: create.count };
|
|
42
|
+
}
|
|
43
|
+
return usageStore[key];
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
_usageStore: usageStore,
|
|
47
|
+
_orgData: orgData,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
describe("UsageService", () => {
|
|
51
|
+
let db;
|
|
52
|
+
let svc;
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
db = makeMockDb();
|
|
55
|
+
svc = new UsageService(db);
|
|
56
|
+
});
|
|
57
|
+
describe("getUsage", () => {
|
|
58
|
+
it("returns full snapshot with all metrics", async () => {
|
|
59
|
+
const snapshot = await svc.getUsage(ORG_ID);
|
|
60
|
+
expect(snapshot.plan).toBe("free");
|
|
61
|
+
expect(snapshot.usage.nodes).toBe(42);
|
|
62
|
+
expect(snapshot.usage.members).toBe(2);
|
|
63
|
+
expect(snapshot.usage.apiCalls).toBe(0);
|
|
64
|
+
expect(snapshot.usage.ocrPages).toBe(0);
|
|
65
|
+
expect(snapshot.usage.storageBytes).toBe(512_000);
|
|
66
|
+
expect(snapshot.usage.agentRuns).toBe(0);
|
|
67
|
+
expect(snapshot.limits.maxNodes).toBe(500);
|
|
68
|
+
expect(snapshot.limits.maxOcrPagesPerMonth).toBe(25);
|
|
69
|
+
expect(snapshot.limits.maxStorageBytes).toBe(1_073_741_824);
|
|
70
|
+
expect(snapshot.limits.maxAgentRunsPerMonth).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it("returns pro limits for pro plan", async () => {
|
|
73
|
+
db._orgData.plan = "pro";
|
|
74
|
+
const snapshot = await svc.getUsage(ORG_ID);
|
|
75
|
+
expect(snapshot.plan).toBe("pro");
|
|
76
|
+
expect(snapshot.limits.maxNodes).toBe(Infinity);
|
|
77
|
+
expect(snapshot.limits.maxOcrPagesPerMonth).toBe(500);
|
|
78
|
+
expect(snapshot.limits.maxAgentRunsPerMonth).toBe(50);
|
|
79
|
+
});
|
|
80
|
+
it("falls back to free limits for unknown plan", async () => {
|
|
81
|
+
db._orgData.plan = "enterprise";
|
|
82
|
+
const snapshot = await svc.getUsage(ORG_ID);
|
|
83
|
+
expect(snapshot.limits.maxNodes).toBe(500);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe("incrementApiCalls", () => {
|
|
87
|
+
it("creates a new record on first call", async () => {
|
|
88
|
+
await svc.incrementApiCalls(ORG_ID, 5);
|
|
89
|
+
expect(db.usageRecord.upsert).toHaveBeenCalledTimes(1);
|
|
90
|
+
expect(db._usageStore["api_calls"]?.count).toBe(5);
|
|
91
|
+
});
|
|
92
|
+
it("increments existing record", async () => {
|
|
93
|
+
await svc.incrementApiCalls(ORG_ID, 3);
|
|
94
|
+
await svc.incrementApiCalls(ORG_ID, 7);
|
|
95
|
+
expect(db._usageStore["api_calls"]?.count).toBe(10);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe("incrementOcrPages", () => {
|
|
99
|
+
it("tracks OCR page usage", async () => {
|
|
100
|
+
await svc.incrementOcrPages(ORG_ID, 10);
|
|
101
|
+
expect(db._usageStore["ocr_pages"]?.count).toBe(10);
|
|
102
|
+
await svc.incrementOcrPages(ORG_ID, 5);
|
|
103
|
+
expect(db._usageStore["ocr_pages"]?.count).toBe(15);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("incrementAgentRuns", () => {
|
|
107
|
+
it("tracks agent run usage", async () => {
|
|
108
|
+
await svc.incrementAgentRuns(ORG_ID);
|
|
109
|
+
expect(db._usageStore["agent_runs"]?.count).toBe(1);
|
|
110
|
+
await svc.incrementAgentRuns(ORG_ID);
|
|
111
|
+
expect(db._usageStore["agent_runs"]?.count).toBe(2);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe("getStorageUsed", () => {
|
|
115
|
+
it("returns sum of fileSizeBytes from documents", async () => {
|
|
116
|
+
const result = await svc.getStorageUsed(ORG_ID);
|
|
117
|
+
expect(result).toBe(512_000);
|
|
118
|
+
expect(db.document.aggregate).toHaveBeenCalledWith({
|
|
119
|
+
where: { organizationId: ORG_ID },
|
|
120
|
+
_sum: { fileSizeBytes: true },
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
it("returns 0 when no documents", async () => {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
db.document.aggregate.mockResolvedValueOnce({ _sum: { fileSizeBytes: null } });
|
|
126
|
+
const result = await svc.getStorageUsed(ORG_ID);
|
|
127
|
+
expect(result).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe("isOverLimit", () => {
|
|
131
|
+
it("returns true when nodes at limit", async () => {
|
|
132
|
+
// Free limit is 500 nodes; mock returns 42
|
|
133
|
+
const result = await svc.isOverLimit(ORG_ID, "nodes");
|
|
134
|
+
expect(result).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
it("detects OCR over limit", async () => {
|
|
137
|
+
// Free limit is 25 OCR pages
|
|
138
|
+
db._usageStore["ocr_pages"] = { count: 30 };
|
|
139
|
+
const result = await svc.isOverLimit(ORG_ID, "ocrPages");
|
|
140
|
+
expect(result).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
it("detects storage over limit", async () => {
|
|
143
|
+
// Free limit is 1 GB
|
|
144
|
+
db.document.aggregate.mockResolvedValueOnce({
|
|
145
|
+
_sum: { fileSizeBytes: 2_000_000_000 },
|
|
146
|
+
});
|
|
147
|
+
const result = await svc.isOverLimit(ORG_ID, "storageBytes");
|
|
148
|
+
expect(result).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
it("detects agent runs at zero limit for free tier", async () => {
|
|
151
|
+
// Free tier has 0 agent runs
|
|
152
|
+
db._usageStore["agent_runs"] = { count: 1 };
|
|
153
|
+
const result = await svc.isOverLimit(ORG_ID, "agentRuns");
|
|
154
|
+
expect(result).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
it("free tier blocks agent runs even at 0", async () => {
|
|
157
|
+
const result = await svc.isOverLimit(ORG_ID, "agentRuns");
|
|
158
|
+
// 0 >= 0 is true
|
|
159
|
+
expect(result).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe("getUsageHistory", () => {
|
|
163
|
+
it("queries usage records for the metric", async () => {
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
165
|
+
db.usageRecord.findMany.mockResolvedValueOnce([
|
|
166
|
+
{ periodStart: new Date("2026-01-01"), count: 100 },
|
|
167
|
+
{ periodStart: new Date("2026-02-01"), count: 150 },
|
|
168
|
+
]);
|
|
169
|
+
const history = await svc.getUsageHistory(ORG_ID, "api_calls", 3);
|
|
170
|
+
expect(history).toHaveLength(2);
|
|
171
|
+
expect(db.usageRecord.findMany).toHaveBeenCalledTimes(1);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe("getPlanLimits (static)", () => {
|
|
175
|
+
it("returns correct free limits", () => {
|
|
176
|
+
const limits = UsageService.getPlanLimits("free");
|
|
177
|
+
expect(limits.maxNodes).toBe(500);
|
|
178
|
+
expect(limits.maxOcrPagesPerMonth).toBe(25);
|
|
179
|
+
});
|
|
180
|
+
it("returns correct pro limits", () => {
|
|
181
|
+
const limits = UsageService.getPlanLimits("pro");
|
|
182
|
+
expect(limits.maxNodes).toBe(Infinity);
|
|
183
|
+
expect(limits.maxOcrPagesPerMonth).toBe(500);
|
|
184
|
+
});
|
|
185
|
+
it("falls back to free for unknown plan", () => {
|
|
186
|
+
const limits = UsageService.getPlanLimits("unknown");
|
|
187
|
+
expect(limits.maxNodes).toBe(500);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=usage-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-service.test.js","sourceRoot":"","sources":["../../src/services/usage-service.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -35,11 +35,15 @@ model Organization {
|
|
|
35
35
|
orgAgentTypes OrgAgentType[]
|
|
36
36
|
orgMcpServers OrgMcpServer[]
|
|
37
37
|
usageRecords UsageRecord[]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
quotaOverrides QuotaOverride[]
|
|
39
|
+
onboardingPackages OnboardingPackage[]
|
|
40
|
+
billingEvents BillingEvent[]
|
|
41
|
+
searchMetrics SearchMetric[]
|
|
42
|
+
tagDefinitions TagDefinition[]
|
|
43
|
+
nodeTags NodeTag[]
|
|
44
|
+
nodeChunks NodeChunk[]
|
|
45
|
+
piiQueryLogs PiiQueryLog[]
|
|
46
|
+
documents Document[]
|
|
43
47
|
|
|
44
48
|
@@map("organizations")
|
|
45
49
|
}
|
|
@@ -55,6 +59,7 @@ model User {
|
|
|
55
59
|
claudeOauthTokenEncrypted String? @map("claude_oauth_token_encrypted") @db.Text
|
|
56
60
|
apartRefreshTokenEncrypted String? @map("apart_refresh_token_encrypted") @db.Text
|
|
57
61
|
agentSecretsEncrypted String? @map("agent_secrets_encrypted") @db.Text
|
|
62
|
+
isSuperAdmin Boolean @default(false) @map("is_super_admin")
|
|
58
63
|
|
|
59
64
|
memberships Membership[]
|
|
60
65
|
createdAgentTypes OrgAgentType[] @relation("OrgAgentTypeCreatedBy")
|
|
@@ -616,6 +621,24 @@ model NodeTagAudit {
|
|
|
616
621
|
@@map("node_tag_audit")
|
|
617
622
|
}
|
|
618
623
|
|
|
624
|
+
// ── Quota Overrides ─────────────────────────────────────────────────────────
|
|
625
|
+
|
|
626
|
+
model QuotaOverride {
|
|
627
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
628
|
+
organizationId String @map("organization_id") @db.Uuid
|
|
629
|
+
resource String @db.VarChar(50)
|
|
630
|
+
overrideLimit Int @map("override_limit")
|
|
631
|
+
reason String @default("") @db.Text
|
|
632
|
+
grantedBy String @map("granted_by") @db.VarChar(255)
|
|
633
|
+
expiresAt DateTime? @map("expires_at") @db.Timestamptz
|
|
634
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
635
|
+
|
|
636
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
637
|
+
|
|
638
|
+
@@index([organizationId], map: "idx_quota_overrides_org")
|
|
639
|
+
@@map("quota_overrides")
|
|
640
|
+
}
|
|
641
|
+
|
|
619
642
|
// ── Document Archive ────────────────────────────────────────────────────────
|
|
620
643
|
|
|
621
644
|
model Document {
|
|
@@ -643,3 +666,65 @@ model Document {
|
|
|
643
666
|
@@index([organizationId, status], map: "idx_documents_org_status")
|
|
644
667
|
@@map("documents")
|
|
645
668
|
}
|
|
669
|
+
|
|
670
|
+
// ── Onboarding Packages ──────────────────────────────────────────────────────
|
|
671
|
+
|
|
672
|
+
model OnboardingPackage {
|
|
673
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
674
|
+
organizationId String @map("organization_id") @db.Uuid
|
|
675
|
+
status String @default("pending") @db.VarChar(20)
|
|
676
|
+
ocrPagesAllotment Int @default(10000) @map("ocr_pages_allotment")
|
|
677
|
+
ocrPagesUsed Int @default(0) @map("ocr_pages_used")
|
|
678
|
+
storageBytesAllotment BigInt @default(26843545600) @map("storage_bytes_allotment")
|
|
679
|
+
proSeats Int @default(3) @map("pro_seats")
|
|
680
|
+
proExpiresAt DateTime? @map("pro_expires_at") @db.Timestamptz
|
|
681
|
+
stripePaymentIntentId String? @map("stripe_payment_intent_id") @db.VarChar(255)
|
|
682
|
+
purchasedAt DateTime? @map("purchased_at") @db.Timestamptz
|
|
683
|
+
completedAt DateTime? @map("completed_at") @db.Timestamptz
|
|
684
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
685
|
+
|
|
686
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
687
|
+
|
|
688
|
+
@@index([organizationId], map: "idx_onboarding_packages_org")
|
|
689
|
+
@@map("onboarding_packages")
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// ── Billing Events (Audit Trail) ────────────────────────────────────────────
|
|
693
|
+
|
|
694
|
+
model BillingEvent {
|
|
695
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
696
|
+
organizationId String @map("organization_id") @db.Uuid
|
|
697
|
+
eventType String @map("event_type") @db.VarChar(50)
|
|
698
|
+
detail Json @default("{}") @db.JsonB
|
|
699
|
+
actor String @default("system") @db.VarChar(255)
|
|
700
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
701
|
+
|
|
702
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
703
|
+
|
|
704
|
+
@@index([organizationId], map: "idx_billing_events_org")
|
|
705
|
+
@@index([eventType], map: "idx_billing_events_type")
|
|
706
|
+
@@map("billing_events")
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ── Search Performance Metrics ──────────────────────────────────────────────
|
|
710
|
+
|
|
711
|
+
model SearchMetric {
|
|
712
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
713
|
+
organizationId String @map("organization_id") @db.Uuid
|
|
714
|
+
queryHash String @map("query_hash") @db.VarChar(64)
|
|
715
|
+
chunkSemanticMs Float @map("chunk_semantic_ms")
|
|
716
|
+
nodeSemanticMs Float @map("node_semantic_ms")
|
|
717
|
+
keywordMs Float @map("keyword_ms")
|
|
718
|
+
substringMs Float @map("substring_ms")
|
|
719
|
+
trigramMs Float @map("trigram_ms")
|
|
720
|
+
totalMs Float @map("total_ms")
|
|
721
|
+
resultCount Int @map("result_count")
|
|
722
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
723
|
+
|
|
724
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
725
|
+
|
|
726
|
+
@@index([organizationId], map: "idx_search_metrics_org")
|
|
727
|
+
@@index([createdAt], map: "idx_search_metrics_created_at")
|
|
728
|
+
@@index([organizationId, createdAt], map: "idx_search_metrics_org_time")
|
|
729
|
+
@@map("search_metrics")
|
|
730
|
+
}
|