@chaaskit/server 0.1.1 → 0.1.2
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/LICENSE +21 -0
- package/dist/api/admin.js +211 -0
- package/dist/api/admin.js.map +1 -1
- package/dist/api/auth.js +91 -6
- package/dist/api/auth.js.map +1 -1
- package/dist/api/chat.js +51 -3
- package/dist/api/chat.js.map +1 -1
- package/dist/api/config.js +25 -14
- package/dist/api/config.js.map +1 -1
- package/dist/api/credits.js +122 -0
- package/dist/api/credits.js.map +1 -0
- package/dist/api/payments.js +94 -11
- package/dist/api/payments.js.map +1 -1
- package/dist/api/user.js +3 -4
- package/dist/api/user.js.map +1 -1
- package/dist/app.js +15 -6
- package/dist/app.js.map +1 -1
- package/dist/config/loader.js +62 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/extensions/loader.js +1 -0
- package/dist/extensions/loader.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/apiKeyAuth.js +2 -1
- package/dist/middleware/apiKeyAuth.js.map +1 -1
- package/dist/middleware/auth.js +29 -5
- package/dist/middleware/auth.js.map +1 -1
- package/dist/queue/cli.js +0 -0
- package/dist/secrets/index.js +147 -0
- package/dist/secrets/index.js.map +1 -0
- package/dist/services/credits.js +206 -0
- package/dist/services/credits.js.map +1 -0
- package/dist/services/email/templates.js +116 -0
- package/dist/services/email/templates.js.map +1 -1
- package/dist/services/metering.js +24 -0
- package/dist/services/metering.js.map +1 -0
- package/dist/services/referrals.js +133 -0
- package/dist/services/referrals.js.map +1 -0
- package/dist/services/usage.js +42 -29
- package/dist/services/usage.js.map +1 -1
- package/dist/services/waitlist.js +142 -0
- package/dist/services/waitlist.js.map +1 -0
- package/package.json +26 -12
- package/dist/api/upload.js +0 -57
- package/dist/api/upload.js.map +0 -1
- package/dist/ssr/build.js +0 -90
- package/dist/ssr/build.js.map +0 -1
- package/dist/ssr/components/SSRMessageList.js +0 -120
- package/dist/ssr/components/SSRMessageList.js.map +0 -1
- package/dist/ssr/entry.client.js +0 -8
- package/dist/ssr/entry.client.js.map +0 -1
- package/dist/ssr/entry.server.js +0 -71
- package/dist/ssr/entry.server.js.map +0 -1
- package/dist/ssr/handler.js +0 -51
- package/dist/ssr/handler.js.map +0 -1
- package/dist/ssr/root.js +0 -184
- package/dist/ssr/root.js.map +0 -1
- package/dist/ssr/routes/login.js +0 -140
- package/dist/ssr/routes/login.js.map +0 -1
- package/dist/ssr/routes/pricing.js +0 -195
- package/dist/ssr/routes/pricing.js.map +0 -1
- package/dist/ssr/routes/privacy.js +0 -39
- package/dist/ssr/routes/privacy.js.map +0 -1
- package/dist/ssr/routes/register.js +0 -148
- package/dist/ssr/routes/register.js.map +0 -1
- package/dist/ssr/routes/shared.$shareId.js +0 -153
- package/dist/ssr/routes/shared.$shareId.js.map +0 -1
- package/dist/ssr/routes/terms.js +0 -39
- package/dist/ssr/routes/terms.js.map +0 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { db } from '@chaaskit/db';
|
|
3
|
+
import { getConfig } from '../config/loader.js';
|
|
4
|
+
import { grantCredits } from './credits.js';
|
|
5
|
+
async function generateReferralCode() {
|
|
6
|
+
return crypto.randomBytes(6).toString('hex');
|
|
7
|
+
}
|
|
8
|
+
export async function ensureReferralCodeForUser(userId) {
|
|
9
|
+
const existing = await db.referralCode.findFirst({
|
|
10
|
+
where: { userId },
|
|
11
|
+
select: { code: true },
|
|
12
|
+
});
|
|
13
|
+
if (existing) {
|
|
14
|
+
return existing.code;
|
|
15
|
+
}
|
|
16
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
17
|
+
const code = await generateReferralCode();
|
|
18
|
+
try {
|
|
19
|
+
const created = await db.referralCode.create({
|
|
20
|
+
data: {
|
|
21
|
+
code,
|
|
22
|
+
userId,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return created.code;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (attempt === 4) {
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error('Failed to generate referral code');
|
|
34
|
+
}
|
|
35
|
+
export async function createReferralFromCode(params) {
|
|
36
|
+
const { referredUserId, referralCode } = params;
|
|
37
|
+
if (!referralCode) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const normalized = referralCode.trim().toLowerCase();
|
|
41
|
+
const code = await db.referralCode.findUnique({
|
|
42
|
+
where: { code: normalized },
|
|
43
|
+
});
|
|
44
|
+
if (!code) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (code.userId === referredUserId) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const existing = await db.referral.findFirst({
|
|
51
|
+
where: {
|
|
52
|
+
referrerUserId: code.userId,
|
|
53
|
+
referredUserId,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
if (existing) {
|
|
57
|
+
return { referralId: existing.id, referrerUserId: existing.referrerUserId };
|
|
58
|
+
}
|
|
59
|
+
const referral = await db.referral.create({
|
|
60
|
+
data: {
|
|
61
|
+
referrerUserId: code.userId,
|
|
62
|
+
referredUserId,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
return { referralId: referral.id, referrerUserId: referral.referrerUserId };
|
|
66
|
+
}
|
|
67
|
+
export async function grantReferralCreditsIfEligible(params) {
|
|
68
|
+
const { referralId, event } = params;
|
|
69
|
+
const config = getConfig();
|
|
70
|
+
if (!config.credits?.enabled) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const triggers = config.credits.referralTriggers;
|
|
74
|
+
if (!triggers) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (event === 'signup' && !triggers.signup) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (event === 'first_message' && !triggers.firstMessage) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (event === 'paying' && !triggers.paying) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const referral = await db.referral.findUnique({
|
|
87
|
+
where: { id: referralId },
|
|
88
|
+
});
|
|
89
|
+
if (!referral) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const existingGrant = await db.referralGrant.findFirst({
|
|
93
|
+
where: {
|
|
94
|
+
referralId,
|
|
95
|
+
event,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
if (existingGrant) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const reward = config.credits.referralRewardCredits ?? 0;
|
|
102
|
+
if (reward <= 0) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
await db.$transaction(async (tx) => {
|
|
106
|
+
await tx.referralGrant.create({
|
|
107
|
+
data: {
|
|
108
|
+
referralId,
|
|
109
|
+
event,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
await grantCredits({
|
|
113
|
+
ownerType: 'user',
|
|
114
|
+
ownerId: referral.referrerUserId,
|
|
115
|
+
amount: reward,
|
|
116
|
+
reason: `referral_${event}`,
|
|
117
|
+
sourceType: 'referral',
|
|
118
|
+
metadata: {
|
|
119
|
+
referralId,
|
|
120
|
+
event,
|
|
121
|
+
},
|
|
122
|
+
tx,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
export async function getReferralForUser(referredUserId) {
|
|
128
|
+
return db.referral.findFirst({
|
|
129
|
+
where: { referredUserId },
|
|
130
|
+
select: { id: true },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=referrals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"referrals.js","sourceRoot":"","sources":["../../src/services/referrals.ts"],"names":[],"mappings":"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"}
|
package/dist/services/usage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { db } from '@chaaskit/db';
|
|
2
2
|
import { getConfig } from '../config/loader.js';
|
|
3
|
+
import { calculateCreditsCost, consumeCredits, getCreditsBalance } from './credits.js';
|
|
3
4
|
export function getPlanLimits(planId) {
|
|
4
5
|
const config = getConfig();
|
|
5
6
|
const plan = config.payments.plans.find((p) => p.id === planId);
|
|
@@ -46,22 +47,21 @@ export async function getBillingContext(userId, teamId) {
|
|
|
46
47
|
select: {
|
|
47
48
|
id: true,
|
|
48
49
|
plan: true,
|
|
49
|
-
credits: true,
|
|
50
50
|
messagesThisMonth: true,
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
53
|
if (team) {
|
|
54
54
|
const teamPlanConfig = config.payments.plans.find((p) => p.id === team.plan);
|
|
55
|
-
|
|
56
|
-
const teamHasActivePlan = team.plan !== 'free' || (teamPlanConfig?.scope === 'team' || teamPlanConfig?.scope === 'both');
|
|
55
|
+
const teamUsesPlan = team.plan !== 'free' || (teamPlanConfig?.scope === 'team' || teamPlanConfig?.scope === 'both');
|
|
57
56
|
// Check if team has a paid plan or if the team's free plan should be used
|
|
58
|
-
if (
|
|
57
|
+
if (teamUsesPlan) {
|
|
59
58
|
const limits = getPlanLimits(team.plan);
|
|
59
|
+
const credits = await getCreditsBalance('team', team.id);
|
|
60
60
|
return {
|
|
61
61
|
type: 'team',
|
|
62
62
|
entityId: team.id,
|
|
63
63
|
plan: team.plan,
|
|
64
|
-
credits:
|
|
64
|
+
credits: credits.balance,
|
|
65
65
|
messagesThisMonth: team.messagesThisMonth,
|
|
66
66
|
monthlyLimit: limits.monthlyMessageLimit,
|
|
67
67
|
};
|
|
@@ -74,7 +74,6 @@ export async function getBillingContext(userId, teamId) {
|
|
|
74
74
|
select: {
|
|
75
75
|
id: true,
|
|
76
76
|
plan: true,
|
|
77
|
-
credits: true,
|
|
78
77
|
messagesThisMonth: true,
|
|
79
78
|
},
|
|
80
79
|
});
|
|
@@ -82,11 +81,12 @@ export async function getBillingContext(userId, teamId) {
|
|
|
82
81
|
return null;
|
|
83
82
|
}
|
|
84
83
|
const limits = getPlanLimits(user.plan);
|
|
84
|
+
const credits = await getCreditsBalance('user', user.id);
|
|
85
85
|
return {
|
|
86
86
|
type: 'personal',
|
|
87
87
|
entityId: user.id,
|
|
88
88
|
plan: user.plan,
|
|
89
|
-
credits:
|
|
89
|
+
credits: credits.balance,
|
|
90
90
|
messagesThisMonth: user.messagesThisMonth,
|
|
91
91
|
monthlyLimit: limits.monthlyMessageLimit,
|
|
92
92
|
};
|
|
@@ -106,14 +106,17 @@ export async function checkUsageLimits(userId, teamId) {
|
|
|
106
106
|
}
|
|
107
107
|
// Unlimited plan
|
|
108
108
|
if (context.monthlyLimit === -1) {
|
|
109
|
-
|
|
109
|
+
if (!config.credits?.enabled) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return context.credits > 0;
|
|
110
113
|
}
|
|
111
114
|
// Check monthly limit
|
|
112
115
|
if (context.messagesThisMonth < context.monthlyLimit) {
|
|
113
116
|
return true;
|
|
114
117
|
}
|
|
115
118
|
// Check if entity has credits
|
|
116
|
-
if (context.credits > 0) {
|
|
119
|
+
if (config.credits?.enabled && context.credits > 0) {
|
|
117
120
|
return true;
|
|
118
121
|
}
|
|
119
122
|
return false;
|
|
@@ -123,7 +126,7 @@ export async function checkUsageLimits(userId, teamId) {
|
|
|
123
126
|
* For team threads with team billing, increments team's usage.
|
|
124
127
|
* Otherwise, increments user's personal usage.
|
|
125
128
|
*/
|
|
126
|
-
export async function incrementUsage(userId, teamId) {
|
|
129
|
+
export async function incrementUsage(userId, teamId, usage) {
|
|
127
130
|
const config = getConfig();
|
|
128
131
|
if (!config.payments.enabled) {
|
|
129
132
|
return;
|
|
@@ -132,6 +135,8 @@ export async function incrementUsage(userId, teamId) {
|
|
|
132
135
|
if (!context) {
|
|
133
136
|
return;
|
|
134
137
|
}
|
|
138
|
+
const totalTokens = (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0);
|
|
139
|
+
const creditsCost = calculateCreditsCost(totalTokens);
|
|
135
140
|
// If unlimited plan, increment monthly usage for tracking purposes
|
|
136
141
|
if (context.monthlyLimit === -1) {
|
|
137
142
|
if (context.type === 'team') {
|
|
@@ -150,6 +155,19 @@ export async function incrementUsage(userId, teamId) {
|
|
|
150
155
|
},
|
|
151
156
|
});
|
|
152
157
|
}
|
|
158
|
+
if (config.credits?.enabled) {
|
|
159
|
+
await consumeCredits({
|
|
160
|
+
ownerType: context.type === 'team' ? 'team' : 'user',
|
|
161
|
+
ownerId: context.entityId,
|
|
162
|
+
amount: creditsCost,
|
|
163
|
+
reason: 'usage',
|
|
164
|
+
sourceType: 'usage',
|
|
165
|
+
metadata: {
|
|
166
|
+
inputTokens: usage?.inputTokens ?? 0,
|
|
167
|
+
outputTokens: usage?.outputTokens ?? 0,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
153
171
|
return;
|
|
154
172
|
}
|
|
155
173
|
// If under monthly limit, increment monthly usage
|
|
@@ -173,23 +191,18 @@ export async function incrementUsage(userId, teamId) {
|
|
|
173
191
|
return;
|
|
174
192
|
}
|
|
175
193
|
// Otherwise, deduct a credit
|
|
176
|
-
if (context.credits > 0) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
data: {
|
|
189
|
-
credits: { decrement: 1 },
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
}
|
|
194
|
+
if (config.credits?.enabled && context.credits > 0) {
|
|
195
|
+
await consumeCredits({
|
|
196
|
+
ownerType: context.type === 'team' ? 'team' : 'user',
|
|
197
|
+
ownerId: context.entityId,
|
|
198
|
+
amount: creditsCost,
|
|
199
|
+
reason: 'usage',
|
|
200
|
+
sourceType: 'usage',
|
|
201
|
+
metadata: {
|
|
202
|
+
inputTokens: usage?.inputTokens ?? 0,
|
|
203
|
+
outputTokens: usage?.outputTokens ?? 0,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
208
|
/**
|
|
@@ -219,7 +232,6 @@ export async function getTeamSubscription(teamId) {
|
|
|
219
232
|
where: { id: teamId },
|
|
220
233
|
select: {
|
|
221
234
|
plan: true,
|
|
222
|
-
credits: true,
|
|
223
235
|
messagesThisMonth: true,
|
|
224
236
|
stripeCustomerId: true,
|
|
225
237
|
},
|
|
@@ -229,12 +241,13 @@ export async function getTeamSubscription(teamId) {
|
|
|
229
241
|
}
|
|
230
242
|
const limits = getPlanLimits(team.plan);
|
|
231
243
|
const planConfig = config.payments.plans.find((p) => p.id === team.plan);
|
|
244
|
+
const credits = await getCreditsBalance('team', teamId);
|
|
232
245
|
return {
|
|
233
246
|
plan: team.plan,
|
|
234
247
|
planName: planConfig?.name || 'Unknown',
|
|
235
248
|
messagesThisMonth: team.messagesThisMonth,
|
|
236
249
|
monthlyLimit: limits.monthlyMessageLimit,
|
|
237
|
-
credits:
|
|
250
|
+
credits: credits.balance,
|
|
238
251
|
hasStripeCustomer: !!team.stripeCustomerId,
|
|
239
252
|
};
|
|
240
253
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/services/usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/services/usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { db } from '@chaaskit/db';
|
|
3
|
+
import { getConfig } from '../config/loader.js';
|
|
4
|
+
export async function addToWaitlist(params) {
|
|
5
|
+
const { email, name } = params;
|
|
6
|
+
const existing = await db.waitlistEntry.findUnique({
|
|
7
|
+
where: { email },
|
|
8
|
+
});
|
|
9
|
+
if (existing) {
|
|
10
|
+
if (existing.status === 'removed') {
|
|
11
|
+
return db.waitlistEntry.update({
|
|
12
|
+
where: { id: existing.id },
|
|
13
|
+
data: {
|
|
14
|
+
status: 'pending',
|
|
15
|
+
name: name ?? existing.name,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return existing;
|
|
20
|
+
}
|
|
21
|
+
return db.waitlistEntry.create({
|
|
22
|
+
data: {
|
|
23
|
+
email,
|
|
24
|
+
name: name ?? undefined,
|
|
25
|
+
status: 'pending',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function getInviteExpiryDays() {
|
|
30
|
+
const config = getConfig();
|
|
31
|
+
return config.auth.gating?.inviteExpiryDays ?? 7;
|
|
32
|
+
}
|
|
33
|
+
function generateInviteToken() {
|
|
34
|
+
return crypto.randomBytes(24).toString('hex');
|
|
35
|
+
}
|
|
36
|
+
export async function createInviteTokenForEmail(params) {
|
|
37
|
+
const { email, createdByUserId } = params;
|
|
38
|
+
const expiresAt = new Date(Date.now() + getInviteExpiryDays() * 24 * 60 * 60 * 1000);
|
|
39
|
+
const token = generateInviteToken();
|
|
40
|
+
return db.inviteToken.create({
|
|
41
|
+
data: {
|
|
42
|
+
email,
|
|
43
|
+
token,
|
|
44
|
+
expiresAt,
|
|
45
|
+
createdByUserId: createdByUserId ?? undefined,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export async function createInviteForWaitlistEntry(params) {
|
|
50
|
+
const entry = await db.waitlistEntry.findUnique({
|
|
51
|
+
where: { id: params.entryId },
|
|
52
|
+
});
|
|
53
|
+
if (!entry) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const invite = await createInviteTokenForEmail({
|
|
57
|
+
email: entry.email,
|
|
58
|
+
createdByUserId: params.createdByUserId ?? undefined,
|
|
59
|
+
});
|
|
60
|
+
await db.waitlistEntry.update({
|
|
61
|
+
where: { id: entry.id },
|
|
62
|
+
data: {
|
|
63
|
+
status: 'invited',
|
|
64
|
+
invitedAt: new Date(),
|
|
65
|
+
invitedByUserId: params.createdByUserId ?? undefined,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return invite;
|
|
69
|
+
}
|
|
70
|
+
export async function validateInviteToken(params) {
|
|
71
|
+
const { token, email } = params;
|
|
72
|
+
const invite = await db.inviteToken.findUnique({
|
|
73
|
+
where: { token },
|
|
74
|
+
});
|
|
75
|
+
if (!invite) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (invite.usedAt) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (invite.expiresAt < new Date()) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (email && invite.email.toLowerCase() !== email.toLowerCase()) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return invite;
|
|
88
|
+
}
|
|
89
|
+
export async function consumeInviteToken(params) {
|
|
90
|
+
return db.inviteToken.update({
|
|
91
|
+
where: { token: params.token },
|
|
92
|
+
data: {
|
|
93
|
+
usedAt: new Date(),
|
|
94
|
+
usedByUserId: params.userId,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
export async function listWaitlist() {
|
|
99
|
+
const [entries, total] = await Promise.all([
|
|
100
|
+
db.waitlistEntry.findMany({
|
|
101
|
+
orderBy: { createdAt: 'desc' },
|
|
102
|
+
}),
|
|
103
|
+
db.waitlistEntry.count(),
|
|
104
|
+
]);
|
|
105
|
+
return { entries, total };
|
|
106
|
+
}
|
|
107
|
+
export function evaluateSignupGate(params) {
|
|
108
|
+
const config = getConfig();
|
|
109
|
+
const gating = config.auth.gating;
|
|
110
|
+
const waitlistEnabled = gating?.waitlistEnabled ?? false;
|
|
111
|
+
if (!gating || gating.mode === 'open') {
|
|
112
|
+
return { allowed: true, waitlistEnabled };
|
|
113
|
+
}
|
|
114
|
+
if (params.inviteValid) {
|
|
115
|
+
return { allowed: true, waitlistEnabled };
|
|
116
|
+
}
|
|
117
|
+
const now = new Date();
|
|
118
|
+
switch (gating.mode) {
|
|
119
|
+
case 'invite_only':
|
|
120
|
+
return { allowed: false, reason: 'invite_only', waitlistEnabled };
|
|
121
|
+
case 'closed':
|
|
122
|
+
return { allowed: false, reason: 'closed', waitlistEnabled };
|
|
123
|
+
case 'capacity_limit':
|
|
124
|
+
if (params.capacityReached) {
|
|
125
|
+
return { allowed: false, reason: 'capacity_limit', waitlistEnabled };
|
|
126
|
+
}
|
|
127
|
+
return { allowed: true, waitlistEnabled };
|
|
128
|
+
case 'timed_window': {
|
|
129
|
+
const start = gating.windowStart ? new Date(gating.windowStart) : null;
|
|
130
|
+
const end = gating.windowEnd ? new Date(gating.windowEnd) : null;
|
|
131
|
+
const withinStart = !start || now >= start;
|
|
132
|
+
const withinEnd = !end || now <= end;
|
|
133
|
+
if (withinStart && withinEnd) {
|
|
134
|
+
return { allowed: true, waitlistEnabled };
|
|
135
|
+
}
|
|
136
|
+
return { allowed: false, reason: 'timed_window', waitlistEnabled };
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
return { allowed: false, reason: 'closed', waitlistEnabled };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=waitlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waitlist.js","sourceRoot":"","sources":["../../src/services/waitlist.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaaskit/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Express.js backend server for ChaasKit AI chat applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Matt Ferrante <@ferrants>",
|
|
@@ -13,7 +13,16 @@
|
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/ferrants/chaaskit/issues"
|
|
15
15
|
},
|
|
16
|
-
"keywords": [
|
|
16
|
+
"keywords": [
|
|
17
|
+
"chaaskit",
|
|
18
|
+
"ai",
|
|
19
|
+
"chat",
|
|
20
|
+
"saas",
|
|
21
|
+
"server",
|
|
22
|
+
"express",
|
|
23
|
+
"anthropic",
|
|
24
|
+
"openai"
|
|
25
|
+
],
|
|
17
26
|
"type": "module",
|
|
18
27
|
"main": "./dist/index.js",
|
|
19
28
|
"types": "./dist/index.d.ts",
|
|
@@ -34,17 +43,10 @@
|
|
|
34
43
|
"files": [
|
|
35
44
|
"dist"
|
|
36
45
|
],
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "tsc",
|
|
39
|
-
"dev": "tsx watch --clear-screen=false --watch=../../config --watch=src src/bin/cli.ts",
|
|
40
|
-
"start": "node dist/bin/cli.js",
|
|
41
|
-
"typecheck": "tsc --noEmit",
|
|
42
|
-
"clean": "rm -rf dist"
|
|
43
|
-
},
|
|
44
46
|
"dependencies": {
|
|
45
47
|
"@anthropic-ai/sdk": "^0.71.0",
|
|
46
|
-
"@chaaskit/db": "^0.1.
|
|
47
|
-
"@chaaskit/shared": "^0.1.
|
|
48
|
+
"@chaaskit/db": "^0.1.2",
|
|
49
|
+
"@chaaskit/shared": "^0.1.2",
|
|
48
50
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
51
|
"bcryptjs": "^2.4.3",
|
|
50
52
|
"cookie-parser": "^1.4.6",
|
|
@@ -67,14 +69,19 @@
|
|
|
67
69
|
"zod": "^3.22.4"
|
|
68
70
|
},
|
|
69
71
|
"peerDependencies": {
|
|
72
|
+
"@aws-sdk/client-secrets-manager": "^3.0.0",
|
|
70
73
|
"@aws-sdk/client-sqs": "^3.0.0"
|
|
71
74
|
},
|
|
72
75
|
"peerDependenciesMeta": {
|
|
76
|
+
"@aws-sdk/client-secrets-manager": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
73
79
|
"@aws-sdk/client-sqs": {
|
|
74
80
|
"optional": true
|
|
75
81
|
}
|
|
76
82
|
},
|
|
77
83
|
"devDependencies": {
|
|
84
|
+
"@aws-sdk/client-secrets-manager": "^3.0.0",
|
|
78
85
|
"@aws-sdk/client-sqs": "^3.0.0",
|
|
79
86
|
"@types/bcryptjs": "^2.4.6",
|
|
80
87
|
"@types/cookie-parser": "^1.4.6",
|
|
@@ -89,5 +96,12 @@
|
|
|
89
96
|
"@types/uuid": "^9.0.8",
|
|
90
97
|
"tsx": "^4.7.0",
|
|
91
98
|
"typescript": "^5.3.3"
|
|
99
|
+
},
|
|
100
|
+
"scripts": {
|
|
101
|
+
"build": "tsc",
|
|
102
|
+
"dev": "tsx watch --clear-screen=false --watch=../../config --watch=src src/bin/cli.ts",
|
|
103
|
+
"start": "node dist/bin/cli.js",
|
|
104
|
+
"typecheck": "tsc --noEmit",
|
|
105
|
+
"clean": "rm -rf dist"
|
|
92
106
|
}
|
|
93
|
-
}
|
|
107
|
+
}
|
package/dist/api/upload.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import multer from 'multer';
|
|
3
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
-
import { HTTP_STATUS } from '@chat-saas/shared';
|
|
5
|
-
import { optionalAuth } from '../middleware/auth.js';
|
|
6
|
-
import { AppError } from '../middleware/errorHandler.js';
|
|
7
|
-
import { getConfig } from '../config/loader.js';
|
|
8
|
-
export const uploadRouter = Router();
|
|
9
|
-
// Configure multer for memory storage
|
|
10
|
-
const storage = multer.memoryStorage();
|
|
11
|
-
const upload = multer({
|
|
12
|
-
storage,
|
|
13
|
-
limits: {
|
|
14
|
-
fileSize: 10 * 1024 * 1024, // Default 10MB, will be checked against config
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
uploadRouter.post('/', optionalAuth, upload.array('files', 10), async (req, res, next) => {
|
|
18
|
-
try {
|
|
19
|
-
const config = getConfig();
|
|
20
|
-
if (!config.fileUpload.enabled) {
|
|
21
|
-
throw new AppError(HTTP_STATUS.BAD_REQUEST, 'File upload is disabled');
|
|
22
|
-
}
|
|
23
|
-
const files = req.files;
|
|
24
|
-
if (!files || files.length === 0) {
|
|
25
|
-
throw new AppError(HTTP_STATUS.BAD_REQUEST, 'No files uploaded');
|
|
26
|
-
}
|
|
27
|
-
const maxSizeBytes = config.fileUpload.maxSizeMB * 1024 * 1024;
|
|
28
|
-
const acceptedTypes = config.fileUpload.acceptedTypes;
|
|
29
|
-
const uploadedFiles = [];
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
// Check file size
|
|
32
|
-
if (file.size > maxSizeBytes) {
|
|
33
|
-
throw new AppError(HTTP_STATUS.BAD_REQUEST, `File ${file.originalname} exceeds max size of ${config.fileUpload.maxSizeMB}MB`);
|
|
34
|
-
}
|
|
35
|
-
// Check file type
|
|
36
|
-
const ext = '.' + file.originalname.split('.').pop()?.toLowerCase();
|
|
37
|
-
if (acceptedTypes.length > 0 && !acceptedTypes.includes(ext)) {
|
|
38
|
-
throw new AppError(HTTP_STATUS.BAD_REQUEST, `File type ${ext} is not allowed. Accepted types: ${acceptedTypes.join(', ')}`);
|
|
39
|
-
}
|
|
40
|
-
// For now, store files as base64 in memory
|
|
41
|
-
// In production, you would upload to S3, GCS, etc.
|
|
42
|
-
const fileData = {
|
|
43
|
-
id: uuidv4(),
|
|
44
|
-
name: file.originalname,
|
|
45
|
-
type: file.mimetype,
|
|
46
|
-
size: file.size,
|
|
47
|
-
content: file.buffer.toString('base64'),
|
|
48
|
-
};
|
|
49
|
-
uploadedFiles.push(fileData);
|
|
50
|
-
}
|
|
51
|
-
res.json({ files: uploadedFiles });
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
next(error);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
//# sourceMappingURL=upload.js.map
|
package/dist/api/upload.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"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"}
|