@nordsym/apiclaw 1.7.2 → 1.7.4
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/convex/_generated/api.d.ts +115 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +60 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/adminActivate.d.ts +3 -0
- package/convex/adminActivate.d.ts.map +1 -0
- package/convex/adminActivate.js +47 -0
- package/convex/adminActivate.js.map +1 -0
- package/convex/adminActivate.ts +54 -0
- package/convex/adminStats.d.ts +3 -0
- package/convex/adminStats.d.ts.map +1 -0
- package/convex/adminStats.js +42 -0
- package/convex/adminStats.js.map +1 -0
- package/convex/adminStats.ts +44 -0
- package/convex/agents.d.ts +76 -0
- package/convex/agents.d.ts.map +1 -0
- package/convex/agents.js +699 -0
- package/convex/agents.js.map +1 -0
- package/convex/agents.ts +814 -0
- package/convex/analytics.d.ts +5 -0
- package/convex/analytics.d.ts.map +1 -0
- package/convex/analytics.js +166 -0
- package/convex/analytics.js.map +1 -0
- package/convex/analytics.ts +186 -0
- package/convex/billing.d.ts +88 -0
- package/convex/billing.d.ts.map +1 -0
- package/convex/billing.js +655 -0
- package/convex/billing.js.map +1 -0
- package/convex/billing.ts +791 -0
- package/convex/capabilities.d.ts +9 -0
- package/convex/capabilities.d.ts.map +1 -0
- package/convex/capabilities.js +145 -0
- package/convex/capabilities.js.map +1 -0
- package/convex/capabilities.ts +157 -0
- package/convex/chains.d.ts +68 -0
- package/convex/chains.d.ts.map +1 -0
- package/convex/chains.js +1105 -0
- package/convex/chains.js.map +1 -0
- package/convex/chains.ts +1318 -0
- package/convex/credits.d.ts +25 -0
- package/convex/credits.d.ts.map +1 -0
- package/convex/credits.js +186 -0
- package/convex/credits.js.map +1 -0
- package/convex/credits.ts +211 -0
- package/convex/crons.d.ts +3 -0
- package/convex/crons.d.ts.map +1 -0
- package/convex/crons.js +17 -0
- package/convex/crons.js.map +1 -0
- package/convex/crons.ts +28 -0
- package/convex/directCall.d.ts +72 -0
- package/convex/directCall.d.ts.map +1 -0
- package/convex/directCall.js +627 -0
- package/convex/directCall.js.map +1 -0
- package/convex/directCall.ts +678 -0
- package/convex/earnProgress.d.ts +58 -0
- package/convex/earnProgress.d.ts.map +1 -0
- package/convex/earnProgress.js +649 -0
- package/convex/earnProgress.js.map +1 -0
- package/convex/earnProgress.ts +753 -0
- package/convex/email.d.ts +14 -0
- package/convex/email.d.ts.map +1 -0
- package/convex/email.js +300 -0
- package/convex/email.js.map +1 -0
- package/convex/email.ts +329 -0
- package/convex/feedback.d.ts +7 -0
- package/convex/feedback.d.ts.map +1 -0
- package/convex/feedback.js +227 -0
- package/convex/feedback.js.map +1 -0
- package/convex/feedback.ts +265 -0
- package/convex/http.d.ts +3 -0
- package/convex/http.d.ts.map +1 -0
- package/convex/http.js +1405 -0
- package/convex/http.js.map +1 -0
- package/convex/http.ts +1577 -0
- package/convex/inbound.d.ts +2 -0
- package/convex/inbound.d.ts.map +1 -0
- package/convex/inbound.js +32 -0
- package/convex/inbound.js.map +1 -0
- package/convex/inbound.ts +32 -0
- package/convex/logs.d.ts +38 -0
- package/convex/logs.d.ts.map +1 -0
- package/convex/logs.js +487 -0
- package/convex/logs.js.map +1 -0
- package/convex/logs.ts +550 -0
- package/convex/mou.d.ts +6 -0
- package/convex/mou.d.ts.map +1 -0
- package/convex/mou.js +82 -0
- package/convex/mou.js.map +1 -0
- package/convex/mou.ts +91 -0
- package/convex/providerKeys.d.ts +31 -0
- package/convex/providerKeys.d.ts.map +1 -0
- package/convex/providerKeys.js +257 -0
- package/convex/providerKeys.js.map +1 -0
- package/convex/providerKeys.ts +289 -0
- package/convex/providers.d.ts +32 -0
- package/convex/providers.d.ts.map +1 -0
- package/convex/providers.js +814 -0
- package/convex/providers.js.map +1 -0
- package/convex/providers.ts +909 -0
- package/convex/purchases.d.ts +7 -0
- package/convex/purchases.d.ts.map +1 -0
- package/convex/purchases.js +157 -0
- package/convex/purchases.js.map +1 -0
- package/convex/purchases.ts +183 -0
- package/convex/ratelimit.d.ts +4 -0
- package/convex/ratelimit.d.ts.map +1 -0
- package/convex/ratelimit.js +91 -0
- package/convex/ratelimit.js.map +1 -0
- package/convex/ratelimit.ts +104 -0
- package/convex/schema.ts +802 -0
- package/convex/searchLogs.d.ts +4 -0
- package/convex/searchLogs.d.ts.map +1 -0
- package/convex/searchLogs.js +129 -0
- package/convex/searchLogs.js.map +1 -0
- package/convex/searchLogs.ts +146 -0
- package/convex/seedAPILayerAPIs.d.ts +7 -0
- package/convex/seedAPILayerAPIs.d.ts.map +1 -0
- package/convex/seedAPILayerAPIs.js +177 -0
- package/convex/seedAPILayerAPIs.js.map +1 -0
- package/convex/seedAPILayerAPIs.ts +191 -0
- package/convex/seedDirectCallConfigs.d.ts +2 -0
- package/convex/seedDirectCallConfigs.d.ts.map +1 -0
- package/convex/seedDirectCallConfigs.js +324 -0
- package/convex/seedDirectCallConfigs.js.map +1 -0
- package/convex/seedDirectCallConfigs.ts +336 -0
- package/convex/seedPratham.d.ts +6 -0
- package/convex/seedPratham.d.ts.map +1 -0
- package/convex/seedPratham.js +150 -0
- package/convex/seedPratham.js.map +1 -0
- package/convex/seedPratham.ts +161 -0
- package/convex/spendAlerts.d.ts +36 -0
- package/convex/spendAlerts.d.ts.map +1 -0
- package/convex/spendAlerts.js +380 -0
- package/convex/spendAlerts.js.map +1 -0
- package/convex/spendAlerts.ts +442 -0
- package/convex/stripeActions.d.ts +19 -0
- package/convex/stripeActions.d.ts.map +1 -0
- package/convex/stripeActions.js +411 -0
- package/convex/stripeActions.js.map +1 -0
- package/convex/stripeActions.ts +512 -0
- package/convex/teams.d.ts +21 -0
- package/convex/teams.d.ts.map +1 -0
- package/convex/teams.js +215 -0
- package/convex/teams.js.map +1 -0
- package/convex/teams.ts +243 -0
- package/convex/telemetry.d.ts +4 -0
- package/convex/telemetry.d.ts.map +1 -0
- package/convex/telemetry.js +74 -0
- package/convex/telemetry.js.map +1 -0
- package/convex/telemetry.ts +81 -0
- package/convex/tsconfig.json +25 -0
- package/convex/updateAPIStatus.d.ts +6 -0
- package/convex/updateAPIStatus.d.ts.map +1 -0
- package/convex/updateAPIStatus.js +40 -0
- package/convex/updateAPIStatus.js.map +1 -0
- package/convex/updateAPIStatus.ts +45 -0
- package/convex/usage.d.ts +27 -0
- package/convex/usage.d.ts.map +1 -0
- package/convex/usage.js +229 -0
- package/convex/usage.js.map +1 -0
- package/convex/usage.ts +260 -0
- package/convex/waitlist.d.ts +4 -0
- package/convex/waitlist.d.ts.map +1 -0
- package/convex/waitlist.js +49 -0
- package/convex/waitlist.js.map +1 -0
- package/convex/waitlist.ts +55 -0
- package/convex/webhooks.d.ts +12 -0
- package/convex/webhooks.d.ts.map +1 -0
- package/convex/webhooks.js +410 -0
- package/convex/webhooks.js.map +1 -0
- package/convex/webhooks.ts +494 -0
- package/convex/workspaces.d.ts +31 -0
- package/convex/workspaces.d.ts.map +1 -0
- package/convex/workspaces.js +975 -0
- package/convex/workspaces.js.map +1 -0
- package/convex/workspaces.ts +1130 -0
- package/dist/bin.js +0 -0
- package/dist/capability-router.js +1 -1
- package/dist/capability-router.js.map +1 -1
- package/dist/execute.d.ts +2 -0
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +18 -4
- package/dist/execute.js.map +1 -1
- package/dist/http-api.js +1 -1
- package/dist/http-api.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-analytics.d.ts +32 -0
- package/dist/mcp-analytics.d.ts.map +1 -0
- package/dist/mcp-analytics.js +130 -0
- package/dist/mcp-analytics.js.map +1 -0
- package/package.json +1 -1
- package/dist/chain-types.d.ts +0 -187
- package/dist/chain-types.d.ts.map +0 -1
- package/dist/chain-types.js +0 -33
- package/dist/chain-types.js.map +0 -1
- package/dist/registry/apis.json.bak +0 -248811
- package/dist/src/bin.js +0 -17
- package/dist/src/capability-router.js +0 -240
- package/dist/src/chainExecutor.js +0 -451
- package/dist/src/chainResolver.js +0 -518
- package/dist/src/cli/commands/doctor.js +0 -324
- package/dist/src/cli/commands/mcp-install.js +0 -255
- package/dist/src/cli/commands/restore.js +0 -259
- package/dist/src/cli/commands/setup.js +0 -205
- package/dist/src/cli/commands/uninstall.js +0 -188
- package/dist/src/cli/index.js +0 -111
- package/dist/src/cli.js +0 -302
- package/dist/src/confirmation.js +0 -240
- package/dist/src/credentials.js +0 -357
- package/dist/src/credits.js +0 -260
- package/dist/src/crypto.js +0 -66
- package/dist/src/discovery.js +0 -504
- package/dist/src/enterprise/env.js +0 -123
- package/dist/src/enterprise/script-generator.js +0 -460
- package/dist/src/execute-dynamic.js +0 -473
- package/dist/src/execute.js +0 -1727
- package/dist/src/index.js +0 -2062
- package/dist/src/metered.js +0 -80
- package/dist/src/open-apis.js +0 -276
- package/dist/src/proxy.js +0 -28
- package/dist/src/session.js +0 -86
- package/dist/src/stripe.js +0 -407
- package/dist/src/telemetry.js +0 -49
- package/dist/src/types.js +0 -2
- package/dist/src/utils/backup.js +0 -181
- package/dist/src/utils/config.js +0 -220
- package/dist/src/utils/os.js +0 -105
- package/dist/src/utils/paths.js +0 -159
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const purchaseAccess: any;
|
|
2
|
+
export declare const getAgentPurchases: any;
|
|
3
|
+
export declare const getActivePurchase: any;
|
|
4
|
+
export declare const getUsage: any;
|
|
5
|
+
export declare const recordUsage: any;
|
|
6
|
+
export declare const getBalanceSummary: any;
|
|
7
|
+
//# sourceMappingURL=purchases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purchases.d.ts","sourceRoot":"","sources":["purchases.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,cAAc,KAoDzB,CAAC;AAGH,eAAO,MAAM,iBAAiB,KAQ5B,CAAC;AAGH,eAAO,MAAM,iBAAiB,KAe5B,CAAC;AAGH,eAAO,MAAM,QAAQ,KAQnB,CAAC;AAGH,eAAO,MAAM,WAAW,KAsCtB,CAAC;AAGH,eAAO,MAAM,iBAAiB,KA0B5B,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
// Provider pricing (credits per dollar)
|
|
4
|
+
const CREDITS_PER_DOLLAR = {
|
|
5
|
+
"46elks": 30, // ~30 SMS per dollar
|
|
6
|
+
twilio: 25, // ~25 SMS per dollar
|
|
7
|
+
resend: 1000, // ~1000 emails per dollar
|
|
8
|
+
brave_search: 200, // ~200 searches per dollar
|
|
9
|
+
openrouter: 100, // ~100k tokens per dollar
|
|
10
|
+
elevenlabs: 3333, // ~3333 characters per dollar
|
|
11
|
+
};
|
|
12
|
+
// Calculate credits for a provider purchase
|
|
13
|
+
function calculateCredits(providerId, amountUsd) {
|
|
14
|
+
const rate = CREDITS_PER_DOLLAR[providerId] || 100;
|
|
15
|
+
return Math.floor(amountUsd * rate);
|
|
16
|
+
}
|
|
17
|
+
// Purchase API access
|
|
18
|
+
export const purchaseAccess = mutation({
|
|
19
|
+
args: {
|
|
20
|
+
agentId: v.string(),
|
|
21
|
+
providerId: v.string(),
|
|
22
|
+
amountUsd: v.number(),
|
|
23
|
+
credentials: v.any(), // Credentials passed from server side
|
|
24
|
+
},
|
|
25
|
+
handler: async (ctx, args) => {
|
|
26
|
+
// Check balance
|
|
27
|
+
const credits = await ctx.db
|
|
28
|
+
.query("agentCredits")
|
|
29
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
30
|
+
.first();
|
|
31
|
+
if (!credits || credits.balanceUsd < args.amountUsd) {
|
|
32
|
+
throw new Error(`Insufficient balance: have $${(credits?.balanceUsd || 0).toFixed(2)}, need $${args.amountUsd.toFixed(2)}`);
|
|
33
|
+
}
|
|
34
|
+
// Deduct credits
|
|
35
|
+
await ctx.db.patch(credits._id, {
|
|
36
|
+
balanceUsd: credits.balanceUsd - args.amountUsd,
|
|
37
|
+
updatedAt: Date.now(),
|
|
38
|
+
});
|
|
39
|
+
// Calculate credits granted
|
|
40
|
+
const creditsGranted = calculateCredits(args.providerId, args.amountUsd);
|
|
41
|
+
// Create purchase record
|
|
42
|
+
const purchaseId = await ctx.db.insert("purchases", {
|
|
43
|
+
agentId: args.agentId,
|
|
44
|
+
providerId: args.providerId,
|
|
45
|
+
amountUsd: args.amountUsd,
|
|
46
|
+
creditsGranted,
|
|
47
|
+
status: "active",
|
|
48
|
+
credentials: args.credentials,
|
|
49
|
+
createdAt: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
// Initialize usage tracking
|
|
52
|
+
await ctx.db.insert("usage", {
|
|
53
|
+
purchaseId,
|
|
54
|
+
providerId: args.providerId,
|
|
55
|
+
unitsUsed: 0,
|
|
56
|
+
unitsRemaining: creditsGranted,
|
|
57
|
+
costIncurredUsd: 0,
|
|
58
|
+
lastUsedAt: Date.now(),
|
|
59
|
+
});
|
|
60
|
+
return await ctx.db.get(purchaseId);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
// Get all purchases for an agent
|
|
64
|
+
export const getAgentPurchases = query({
|
|
65
|
+
args: { agentId: v.string() },
|
|
66
|
+
handler: async (ctx, args) => {
|
|
67
|
+
return await ctx.db
|
|
68
|
+
.query("purchases")
|
|
69
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
70
|
+
.collect();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
// Get active purchase for a provider
|
|
74
|
+
export const getActivePurchase = query({
|
|
75
|
+
args: {
|
|
76
|
+
agentId: v.string(),
|
|
77
|
+
providerId: v.string(),
|
|
78
|
+
},
|
|
79
|
+
handler: async (ctx, args) => {
|
|
80
|
+
const purchases = await ctx.db
|
|
81
|
+
.query("purchases")
|
|
82
|
+
.withIndex("by_agentId_providerId", (q) => q.eq("agentId", args.agentId).eq("providerId", args.providerId))
|
|
83
|
+
.collect();
|
|
84
|
+
return purchases.find((p) => p.status === "active") || null;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// Get usage for a purchase
|
|
88
|
+
export const getUsage = query({
|
|
89
|
+
args: { purchaseId: v.id("purchases") },
|
|
90
|
+
handler: async (ctx, args) => {
|
|
91
|
+
return await ctx.db
|
|
92
|
+
.query("usage")
|
|
93
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
94
|
+
.first();
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
// Record usage
|
|
98
|
+
export const recordUsage = mutation({
|
|
99
|
+
args: {
|
|
100
|
+
purchaseId: v.id("purchases"),
|
|
101
|
+
unitsUsed: v.number(),
|
|
102
|
+
costUsd: v.number(),
|
|
103
|
+
},
|
|
104
|
+
handler: async (ctx, args) => {
|
|
105
|
+
const usage = await ctx.db
|
|
106
|
+
.query("usage")
|
|
107
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
108
|
+
.first();
|
|
109
|
+
if (!usage) {
|
|
110
|
+
throw new Error("Usage record not found");
|
|
111
|
+
}
|
|
112
|
+
const newUnitsRemaining = Math.max(0, usage.unitsRemaining - args.unitsUsed);
|
|
113
|
+
await ctx.db.patch(usage._id, {
|
|
114
|
+
unitsUsed: usage.unitsUsed + args.unitsUsed,
|
|
115
|
+
unitsRemaining: newUnitsRemaining,
|
|
116
|
+
costIncurredUsd: usage.costIncurredUsd + args.costUsd,
|
|
117
|
+
lastUsedAt: Date.now(),
|
|
118
|
+
});
|
|
119
|
+
// Update purchase status if depleted
|
|
120
|
+
if (newUnitsRemaining === 0) {
|
|
121
|
+
const purchase = await ctx.db.get(args.purchaseId);
|
|
122
|
+
if (purchase) {
|
|
123
|
+
await ctx.db.patch(args.purchaseId, { status: "exhausted" });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return await ctx.db
|
|
127
|
+
.query("usage")
|
|
128
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
129
|
+
.first();
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// Get balance summary for an agent
|
|
133
|
+
export const getBalanceSummary = query({
|
|
134
|
+
args: { agentId: v.string() },
|
|
135
|
+
handler: async (ctx, args) => {
|
|
136
|
+
const credits = await ctx.db
|
|
137
|
+
.query("agentCredits")
|
|
138
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
139
|
+
.first();
|
|
140
|
+
const purchases = await ctx.db
|
|
141
|
+
.query("purchases")
|
|
142
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
143
|
+
.collect();
|
|
144
|
+
const activePurchases = purchases.filter((p) => p.status === "active");
|
|
145
|
+
const totalSpent = purchases.reduce((sum, p) => sum + p.amountUsd, 0);
|
|
146
|
+
return {
|
|
147
|
+
credits: credits || {
|
|
148
|
+
agentId: args.agentId,
|
|
149
|
+
balanceUsd: 0,
|
|
150
|
+
currency: "USD",
|
|
151
|
+
},
|
|
152
|
+
activePurchases,
|
|
153
|
+
totalSpentUsd: totalSpent,
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=purchases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purchases.js","sourceRoot":"","sources":["purchases.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Provider pricing (credits per dollar)
|
|
5
|
+
const CREDITS_PER_DOLLAR: Record<string, number> = {
|
|
6
|
+
"46elks": 30, // ~30 SMS per dollar
|
|
7
|
+
twilio: 25, // ~25 SMS per dollar
|
|
8
|
+
resend: 1000, // ~1000 emails per dollar
|
|
9
|
+
brave_search: 200, // ~200 searches per dollar
|
|
10
|
+
openrouter: 100, // ~100k tokens per dollar
|
|
11
|
+
elevenlabs: 3333, // ~3333 characters per dollar
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Calculate credits for a provider purchase
|
|
15
|
+
function calculateCredits(providerId: string, amountUsd: number): number {
|
|
16
|
+
const rate = CREDITS_PER_DOLLAR[providerId] || 100;
|
|
17
|
+
return Math.floor(amountUsd * rate);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Purchase API access
|
|
21
|
+
export const purchaseAccess = mutation({
|
|
22
|
+
args: {
|
|
23
|
+
agentId: v.string(),
|
|
24
|
+
providerId: v.string(),
|
|
25
|
+
amountUsd: v.number(),
|
|
26
|
+
credentials: v.any(), // Credentials passed from server side
|
|
27
|
+
},
|
|
28
|
+
handler: async (ctx, args) => {
|
|
29
|
+
// Check balance
|
|
30
|
+
const credits = await ctx.db
|
|
31
|
+
.query("agentCredits")
|
|
32
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
33
|
+
.first();
|
|
34
|
+
|
|
35
|
+
if (!credits || credits.balanceUsd < args.amountUsd) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Insufficient balance: have $${(credits?.balanceUsd || 0).toFixed(2)}, need $${args.amountUsd.toFixed(2)}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Deduct credits
|
|
42
|
+
await ctx.db.patch(credits._id, {
|
|
43
|
+
balanceUsd: credits.balanceUsd - args.amountUsd,
|
|
44
|
+
updatedAt: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Calculate credits granted
|
|
48
|
+
const creditsGranted = calculateCredits(args.providerId, args.amountUsd);
|
|
49
|
+
|
|
50
|
+
// Create purchase record
|
|
51
|
+
const purchaseId = await ctx.db.insert("purchases", {
|
|
52
|
+
agentId: args.agentId,
|
|
53
|
+
providerId: args.providerId,
|
|
54
|
+
amountUsd: args.amountUsd,
|
|
55
|
+
creditsGranted,
|
|
56
|
+
status: "active",
|
|
57
|
+
credentials: args.credentials,
|
|
58
|
+
createdAt: Date.now(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Initialize usage tracking
|
|
62
|
+
await ctx.db.insert("usage", {
|
|
63
|
+
purchaseId,
|
|
64
|
+
providerId: args.providerId,
|
|
65
|
+
unitsUsed: 0,
|
|
66
|
+
unitsRemaining: creditsGranted,
|
|
67
|
+
costIncurredUsd: 0,
|
|
68
|
+
lastUsedAt: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return await ctx.db.get(purchaseId);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Get all purchases for an agent
|
|
76
|
+
export const getAgentPurchases = query({
|
|
77
|
+
args: { agentId: v.string() },
|
|
78
|
+
handler: async (ctx, args) => {
|
|
79
|
+
return await ctx.db
|
|
80
|
+
.query("purchases")
|
|
81
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
82
|
+
.collect();
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Get active purchase for a provider
|
|
87
|
+
export const getActivePurchase = query({
|
|
88
|
+
args: {
|
|
89
|
+
agentId: v.string(),
|
|
90
|
+
providerId: v.string(),
|
|
91
|
+
},
|
|
92
|
+
handler: async (ctx, args) => {
|
|
93
|
+
const purchases = await ctx.db
|
|
94
|
+
.query("purchases")
|
|
95
|
+
.withIndex("by_agentId_providerId", (q) =>
|
|
96
|
+
q.eq("agentId", args.agentId).eq("providerId", args.providerId)
|
|
97
|
+
)
|
|
98
|
+
.collect();
|
|
99
|
+
|
|
100
|
+
return purchases.find((p) => p.status === "active") || null;
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Get usage for a purchase
|
|
105
|
+
export const getUsage = query({
|
|
106
|
+
args: { purchaseId: v.id("purchases") },
|
|
107
|
+
handler: async (ctx, args) => {
|
|
108
|
+
return await ctx.db
|
|
109
|
+
.query("usage")
|
|
110
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
111
|
+
.first();
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Record usage
|
|
116
|
+
export const recordUsage = mutation({
|
|
117
|
+
args: {
|
|
118
|
+
purchaseId: v.id("purchases"),
|
|
119
|
+
unitsUsed: v.number(),
|
|
120
|
+
costUsd: v.number(),
|
|
121
|
+
},
|
|
122
|
+
handler: async (ctx, args) => {
|
|
123
|
+
const usage = await ctx.db
|
|
124
|
+
.query("usage")
|
|
125
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
126
|
+
.first();
|
|
127
|
+
|
|
128
|
+
if (!usage) {
|
|
129
|
+
throw new Error("Usage record not found");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const newUnitsRemaining = Math.max(0, usage.unitsRemaining - args.unitsUsed);
|
|
133
|
+
|
|
134
|
+
await ctx.db.patch(usage._id, {
|
|
135
|
+
unitsUsed: usage.unitsUsed + args.unitsUsed,
|
|
136
|
+
unitsRemaining: newUnitsRemaining,
|
|
137
|
+
costIncurredUsd: usage.costIncurredUsd + args.costUsd,
|
|
138
|
+
lastUsedAt: Date.now(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Update purchase status if depleted
|
|
142
|
+
if (newUnitsRemaining === 0) {
|
|
143
|
+
const purchase = await ctx.db.get(args.purchaseId);
|
|
144
|
+
if (purchase) {
|
|
145
|
+
await ctx.db.patch(args.purchaseId, { status: "exhausted" });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return await ctx.db
|
|
150
|
+
.query("usage")
|
|
151
|
+
.withIndex("by_purchaseId", (q) => q.eq("purchaseId", args.purchaseId))
|
|
152
|
+
.first();
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Get balance summary for an agent
|
|
157
|
+
export const getBalanceSummary = query({
|
|
158
|
+
args: { agentId: v.string() },
|
|
159
|
+
handler: async (ctx, args) => {
|
|
160
|
+
const credits = await ctx.db
|
|
161
|
+
.query("agentCredits")
|
|
162
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
163
|
+
.first();
|
|
164
|
+
|
|
165
|
+
const purchases = await ctx.db
|
|
166
|
+
.query("purchases")
|
|
167
|
+
.withIndex("by_agentId", (q) => q.eq("agentId", args.agentId))
|
|
168
|
+
.collect();
|
|
169
|
+
|
|
170
|
+
const activePurchases = purchases.filter((p) => p.status === "active");
|
|
171
|
+
const totalSpent = purchases.reduce((sum, p) => sum + p.amountUsd, 0);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
credits: credits || {
|
|
175
|
+
agentId: args.agentId,
|
|
176
|
+
balanceUsd: 0,
|
|
177
|
+
currency: "USD",
|
|
178
|
+
},
|
|
179
|
+
activePurchases,
|
|
180
|
+
totalSpentUsd: totalSpent,
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.d.ts","sourceRoot":"","sources":["ratelimit.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,UAAU,KAsCrB,CAAC;AAGH,eAAO,MAAM,QAAQ,KAuBnB,CAAC;AAGH,eAAO,MAAM,OAAO,KAgBlB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { mutation, query } from "./_generated/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
// Rate limit config per tier
|
|
4
|
+
const LIMITS = {
|
|
5
|
+
free: {
|
|
6
|
+
discovery: 100, // searches per hour
|
|
7
|
+
instant: 10, // API calls per hour
|
|
8
|
+
},
|
|
9
|
+
subscriber: {
|
|
10
|
+
discovery: 1000,
|
|
11
|
+
instant: 100,
|
|
12
|
+
},
|
|
13
|
+
provider: {
|
|
14
|
+
discovery: 10000,
|
|
15
|
+
instant: 1000,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
// Check and increment rate limit
|
|
19
|
+
export const checkLimit = mutation({
|
|
20
|
+
args: {
|
|
21
|
+
identifier: v.string(), // IP or agentId
|
|
22
|
+
action: v.union(v.literal("discovery"), v.literal("instant")),
|
|
23
|
+
tier: v.optional(v.union(v.literal("free"), v.literal("subscriber"), v.literal("provider"))),
|
|
24
|
+
},
|
|
25
|
+
handler: async (ctx, args) => {
|
|
26
|
+
const tier = args.tier || "free";
|
|
27
|
+
const limit = LIMITS[tier][args.action];
|
|
28
|
+
const hourKey = Math.floor(Date.now() / 3600000); // Hour bucket
|
|
29
|
+
const key = `${args.identifier}:${args.action}:${hourKey}`;
|
|
30
|
+
// Get current count
|
|
31
|
+
const existing = await ctx.db
|
|
32
|
+
.query("rateLimits")
|
|
33
|
+
.withIndex("by_key", (q) => q.eq("key", key))
|
|
34
|
+
.first();
|
|
35
|
+
if (existing) {
|
|
36
|
+
if (existing.count >= limit) {
|
|
37
|
+
return { allowed: false, remaining: 0, resetIn: 3600 - (Date.now() % 3600000) / 1000 };
|
|
38
|
+
}
|
|
39
|
+
await ctx.db.patch(existing._id, { count: existing.count + 1 });
|
|
40
|
+
return { allowed: true, remaining: limit - existing.count - 1 };
|
|
41
|
+
}
|
|
42
|
+
// Create new entry
|
|
43
|
+
await ctx.db.insert("rateLimits", {
|
|
44
|
+
key,
|
|
45
|
+
identifier: args.identifier,
|
|
46
|
+
action: args.action,
|
|
47
|
+
count: 1,
|
|
48
|
+
hourBucket: hourKey,
|
|
49
|
+
createdAt: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
return { allowed: true, remaining: limit - 1 };
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
// Get current usage stats
|
|
55
|
+
export const getUsage = query({
|
|
56
|
+
args: {
|
|
57
|
+
identifier: v.string(),
|
|
58
|
+
},
|
|
59
|
+
handler: async (ctx, args) => {
|
|
60
|
+
const hourKey = Math.floor(Date.now() / 3600000);
|
|
61
|
+
const discovery = await ctx.db
|
|
62
|
+
.query("rateLimits")
|
|
63
|
+
.withIndex("by_key", (q) => q.eq("key", `${args.identifier}:discovery:${hourKey}`))
|
|
64
|
+
.first();
|
|
65
|
+
const instant = await ctx.db
|
|
66
|
+
.query("rateLimits")
|
|
67
|
+
.withIndex("by_key", (q) => q.eq("key", `${args.identifier}:instant:${hourKey}`))
|
|
68
|
+
.first();
|
|
69
|
+
return {
|
|
70
|
+
discovery: discovery?.count || 0,
|
|
71
|
+
instant: instant?.count || 0,
|
|
72
|
+
limits: LIMITS.free,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
// Cleanup old rate limit entries (run via cron)
|
|
77
|
+
export const cleanup = mutation({
|
|
78
|
+
args: {},
|
|
79
|
+
handler: async (ctx) => {
|
|
80
|
+
const hourAgo = Math.floor(Date.now() / 3600000) - 2; // Keep 2 hours
|
|
81
|
+
const old = await ctx.db
|
|
82
|
+
.query("rateLimits")
|
|
83
|
+
.filter((q) => q.lt(q.field("hourBucket"), hourAgo))
|
|
84
|
+
.take(100);
|
|
85
|
+
for (const entry of old) {
|
|
86
|
+
await ctx.db.delete(entry._id);
|
|
87
|
+
}
|
|
88
|
+
return { deleted: old.length };
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=ratelimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.js","sourceRoot":"","sources":["ratelimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,6BAA6B;AAC7B,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,SAAS,EAAE,GAAG,EAAK,oBAAoB;QACvC,OAAO,EAAE,EAAE,EAAQ,qBAAqB;KACzC;IACD,UAAU,EAAE;QACV,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,GAAG;KACb;IACD,QAAQ,EAAE;QACR,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,IAAI;KACd;CACF,CAAC;AAEF,iCAAiC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;IACjC,IAAI,EAAE;QACJ,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAG,gBAAgB;QACzC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7D,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;KAC7F;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,cAAc;QAChE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;QAE3D,oBAAoB;QACpB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE;aAC1B,KAAK,CAAC,YAAY,CAAC;aACnB,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;aAC5C,KAAK,EAAE,CAAC;QAEX,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,QAAQ,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;YACzF,CAAC;YACD,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;YAChE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QAClE,CAAC;QAED,mBAAmB;QACnB,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,EAAE;YAChC,GAAG;YACH,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,CAAC;YACR,UAAU,EAAE,OAAO;YACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC,CAAC;QAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;IACjD,CAAC;CACF,CAAC,CAAC;AAEH,0BAA0B;AAC1B,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC;IAC5B,IAAI,EAAE;QACJ,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;KACvB;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;QAEjD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,EAAE;aAC3B,KAAK,CAAC,YAAY,CAAC;aACnB,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,UAAU,cAAc,OAAO,EAAE,CAAC,CAAC;aAClF,KAAK,EAAE,CAAC;QAEX,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,EAAE;aACzB,KAAK,CAAC,YAAY,CAAC;aACnB,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,UAAU,YAAY,OAAO,EAAE,CAAC,CAAC;aAChF,KAAK,EAAE,CAAC;QAEX,OAAO;YACL,SAAS,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;YAC5B,MAAM,EAAE,MAAM,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe;QAErE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,EAAE;aACrB,KAAK,CAAC,YAAY,CAAC;aACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;aACnD,IAAI,CAAC,GAAG,CAAC,CAAC;QAEb,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;YACxB,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mutation, query } from "./_generated/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
// Rate limit config per tier
|
|
5
|
+
const LIMITS = {
|
|
6
|
+
free: {
|
|
7
|
+
discovery: 100, // searches per hour
|
|
8
|
+
instant: 10, // API calls per hour
|
|
9
|
+
},
|
|
10
|
+
subscriber: {
|
|
11
|
+
discovery: 1000,
|
|
12
|
+
instant: 100,
|
|
13
|
+
},
|
|
14
|
+
provider: {
|
|
15
|
+
discovery: 10000,
|
|
16
|
+
instant: 1000,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Check and increment rate limit
|
|
21
|
+
export const checkLimit = mutation({
|
|
22
|
+
args: {
|
|
23
|
+
identifier: v.string(), // IP or agentId
|
|
24
|
+
action: v.union(v.literal("discovery"), v.literal("instant")),
|
|
25
|
+
tier: v.optional(v.union(v.literal("free"), v.literal("subscriber"), v.literal("provider"))),
|
|
26
|
+
},
|
|
27
|
+
handler: async (ctx, args) => {
|
|
28
|
+
const tier = args.tier || "free";
|
|
29
|
+
const limit = LIMITS[tier][args.action];
|
|
30
|
+
const hourKey = Math.floor(Date.now() / 3600000); // Hour bucket
|
|
31
|
+
const key = `${args.identifier}:${args.action}:${hourKey}`;
|
|
32
|
+
|
|
33
|
+
// Get current count
|
|
34
|
+
const existing = await ctx.db
|
|
35
|
+
.query("rateLimits")
|
|
36
|
+
.withIndex("by_key", (q) => q.eq("key", key))
|
|
37
|
+
.first();
|
|
38
|
+
|
|
39
|
+
if (existing) {
|
|
40
|
+
if (existing.count >= limit) {
|
|
41
|
+
return { allowed: false, remaining: 0, resetIn: 3600 - (Date.now() % 3600000) / 1000 };
|
|
42
|
+
}
|
|
43
|
+
await ctx.db.patch(existing._id, { count: existing.count + 1 });
|
|
44
|
+
return { allowed: true, remaining: limit - existing.count - 1 };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create new entry
|
|
48
|
+
await ctx.db.insert("rateLimits", {
|
|
49
|
+
key,
|
|
50
|
+
identifier: args.identifier,
|
|
51
|
+
action: args.action,
|
|
52
|
+
count: 1,
|
|
53
|
+
hourBucket: hourKey,
|
|
54
|
+
createdAt: Date.now(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return { allowed: true, remaining: limit - 1 };
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Get current usage stats
|
|
62
|
+
export const getUsage = query({
|
|
63
|
+
args: {
|
|
64
|
+
identifier: v.string(),
|
|
65
|
+
},
|
|
66
|
+
handler: async (ctx, args) => {
|
|
67
|
+
const hourKey = Math.floor(Date.now() / 3600000);
|
|
68
|
+
|
|
69
|
+
const discovery = await ctx.db
|
|
70
|
+
.query("rateLimits")
|
|
71
|
+
.withIndex("by_key", (q) => q.eq("key", `${args.identifier}:discovery:${hourKey}`))
|
|
72
|
+
.first();
|
|
73
|
+
|
|
74
|
+
const instant = await ctx.db
|
|
75
|
+
.query("rateLimits")
|
|
76
|
+
.withIndex("by_key", (q) => q.eq("key", `${args.identifier}:instant:${hourKey}`))
|
|
77
|
+
.first();
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
discovery: discovery?.count || 0,
|
|
81
|
+
instant: instant?.count || 0,
|
|
82
|
+
limits: LIMITS.free,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Cleanup old rate limit entries (run via cron)
|
|
88
|
+
export const cleanup = mutation({
|
|
89
|
+
args: {},
|
|
90
|
+
handler: async (ctx) => {
|
|
91
|
+
const hourAgo = Math.floor(Date.now() / 3600000) - 2; // Keep 2 hours
|
|
92
|
+
|
|
93
|
+
const old = await ctx.db
|
|
94
|
+
.query("rateLimits")
|
|
95
|
+
.filter((q) => q.lt(q.field("hourBucket"), hourAgo))
|
|
96
|
+
.take(100);
|
|
97
|
+
|
|
98
|
+
for (const entry of old) {
|
|
99
|
+
await ctx.db.delete(entry._id);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { deleted: old.length };
|
|
103
|
+
},
|
|
104
|
+
});
|