@xtr-dev/rondevu-server 0.5.15 → 0.5.16
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.js +25 -18
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/config.ts +4 -0
- package/src/rpc.ts +3 -1
- package/src/storage/d1.ts +14 -6
- package/src/storage/memory.ts +7 -1
- package/src/storage/mysql.ts +14 -6
- package/src/storage/postgres.ts +14 -6
- package/src/storage/sqlite.ts +9 -4
- package/src/storage/types.ts +2 -1
- package/wrangler.toml +2 -0
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface Config {
|
|
|
20
20
|
offerDefaultTtl: number;
|
|
21
21
|
offerMaxTtl: number;
|
|
22
22
|
offerMinTtl: number;
|
|
23
|
+
answeredOfferTtl: number; // TTL after offer is answered (for ICE exchange)
|
|
23
24
|
cleanupInterval: number;
|
|
24
25
|
maxOffersPerRequest: number;
|
|
25
26
|
maxBatchSize: number;
|
|
@@ -66,6 +67,7 @@ export function loadConfig(): Config {
|
|
|
66
67
|
offerDefaultTtl: parsePositiveInt(process.env.OFFER_DEFAULT_TTL, '60000', 'OFFER_DEFAULT_TTL', 1000),
|
|
67
68
|
offerMaxTtl: parsePositiveInt(process.env.OFFER_MAX_TTL, '86400000', 'OFFER_MAX_TTL', 1000),
|
|
68
69
|
offerMinTtl: parsePositiveInt(process.env.OFFER_MIN_TTL, '60000', 'OFFER_MIN_TTL', 1000),
|
|
70
|
+
answeredOfferTtl: parsePositiveInt(process.env.ANSWERED_OFFER_TTL, '30000', 'ANSWERED_OFFER_TTL', 1000),
|
|
69
71
|
cleanupInterval: parsePositiveInt(process.env.CLEANUP_INTERVAL, '60000', 'CLEANUP_INTERVAL', 1000),
|
|
70
72
|
maxOffersPerRequest: parsePositiveInt(process.env.MAX_OFFERS_PER_REQUEST, '100', 'MAX_OFFERS_PER_REQUEST', 1),
|
|
71
73
|
maxBatchSize: parsePositiveInt(process.env.MAX_BATCH_SIZE, '100', 'MAX_BATCH_SIZE', 1),
|
|
@@ -93,6 +95,7 @@ export const CONFIG_DEFAULTS = {
|
|
|
93
95
|
offerDefaultTtl: 60000,
|
|
94
96
|
offerMaxTtl: 86400000,
|
|
95
97
|
offerMinTtl: 60000,
|
|
98
|
+
answeredOfferTtl: 30000, // 30 seconds TTL after offer is answered
|
|
96
99
|
cleanupInterval: 60000,
|
|
97
100
|
maxOffersPerRequest: 100,
|
|
98
101
|
maxBatchSize: 100,
|
|
@@ -133,6 +136,7 @@ export function buildWorkerConfig(env: {
|
|
|
133
136
|
offerDefaultTtl: env.OFFER_DEFAULT_TTL ? parseInt(env.OFFER_DEFAULT_TTL, 10) : CONFIG_DEFAULTS.offerDefaultTtl,
|
|
134
137
|
offerMaxTtl: env.OFFER_MAX_TTL ? parseInt(env.OFFER_MAX_TTL, 10) : CONFIG_DEFAULTS.offerMaxTtl,
|
|
135
138
|
offerMinTtl: env.OFFER_MIN_TTL ? parseInt(env.OFFER_MIN_TTL, 10) : CONFIG_DEFAULTS.offerMinTtl,
|
|
139
|
+
answeredOfferTtl: CONFIG_DEFAULTS.answeredOfferTtl,
|
|
136
140
|
cleanupInterval: CONFIG_DEFAULTS.cleanupInterval,
|
|
137
141
|
maxOffersPerRequest: env.MAX_OFFERS_PER_REQUEST ? parseInt(env.MAX_OFFERS_PER_REQUEST, 10) : CONFIG_DEFAULTS.maxOffersPerRequest,
|
|
138
142
|
maxBatchSize: env.MAX_BATCH_SIZE ? parseInt(env.MAX_BATCH_SIZE, 10) : CONFIG_DEFAULTS.maxBatchSize,
|
package/src/rpc.ts
CHANGED
|
@@ -454,7 +454,9 @@ const handlers: Record<string, RpcHandler> = {
|
|
|
454
454
|
}
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
|
|
457
|
+
// Reduce TTL after answer for faster cleanup (answered offers no longer appear in discovery)
|
|
458
|
+
const newExpiresAt = Date.now() + config.answeredOfferTtl;
|
|
459
|
+
await storage.answerOffer(offerId, publicKey, sdp, matchedTags, newExpiresAt);
|
|
458
460
|
|
|
459
461
|
return { success: true, offerId };
|
|
460
462
|
},
|
package/src/storage/d1.ts
CHANGED
|
@@ -166,7 +166,8 @@ export class D1Storage implements Storage {
|
|
|
166
166
|
offerId: string,
|
|
167
167
|
answererPublicKey: string,
|
|
168
168
|
answerSdp: string,
|
|
169
|
-
matchedTags?: string[]
|
|
169
|
+
matchedTags?: string[],
|
|
170
|
+
newExpiresAt?: number
|
|
170
171
|
): Promise<{ success: boolean; error?: string }> {
|
|
171
172
|
const offer = await this.getOfferById(offerId);
|
|
172
173
|
|
|
@@ -178,12 +179,19 @@ export class D1Storage implements Storage {
|
|
|
178
179
|
return { success: false, error: 'Offer already answered' };
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
const now = Date.now();
|
|
181
183
|
const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
WHERE id = ? AND answerer_public_key IS NULL
|
|
186
|
-
|
|
184
|
+
|
|
185
|
+
// Optionally reduce TTL for faster cleanup after answer
|
|
186
|
+
const query = newExpiresAt
|
|
187
|
+
? `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?, expires_at = ? WHERE id = ? AND answerer_public_key IS NULL`
|
|
188
|
+
: `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ? WHERE id = ? AND answerer_public_key IS NULL`;
|
|
189
|
+
|
|
190
|
+
const params = newExpiresAt
|
|
191
|
+
? [answererPublicKey, answerSdp, now, matchedTagsJson, newExpiresAt, offerId]
|
|
192
|
+
: [answererPublicKey, answerSdp, now, matchedTagsJson, offerId];
|
|
193
|
+
|
|
194
|
+
const result = await this.db.prepare(query).bind(...params).run();
|
|
187
195
|
|
|
188
196
|
if ((result.meta.changes || 0) === 0) {
|
|
189
197
|
return { success: false, error: 'Offer already answered (race condition)' };
|
package/src/storage/memory.ts
CHANGED
|
@@ -135,7 +135,8 @@ export class MemoryStorage implements Storage {
|
|
|
135
135
|
offerId: string,
|
|
136
136
|
answererPublicKey: string,
|
|
137
137
|
answerSdp: string,
|
|
138
|
-
matchedTags?: string[]
|
|
138
|
+
matchedTags?: string[],
|
|
139
|
+
newExpiresAt?: number
|
|
139
140
|
): Promise<{ success: boolean; error?: string }> {
|
|
140
141
|
const offer = await this.getOfferById(offerId);
|
|
141
142
|
|
|
@@ -154,6 +155,11 @@ export class MemoryStorage implements Storage {
|
|
|
154
155
|
offer.answeredAt = now;
|
|
155
156
|
offer.matchedTags = matchedTags;
|
|
156
157
|
|
|
158
|
+
// Optionally reduce TTL for faster cleanup after answer
|
|
159
|
+
if (newExpiresAt !== undefined) {
|
|
160
|
+
offer.expiresAt = newExpiresAt;
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
// Update answerer index
|
|
158
164
|
if (!this.offersByAnswerer.has(answererPublicKey)) {
|
|
159
165
|
this.offersByAnswerer.set(answererPublicKey, new Set());
|
package/src/storage/mysql.ts
CHANGED
|
@@ -187,7 +187,8 @@ export class MySQLStorage implements Storage {
|
|
|
187
187
|
offerId: string,
|
|
188
188
|
answererPublicKey: string,
|
|
189
189
|
answerSdp: string,
|
|
190
|
-
matchedTags?: string[]
|
|
190
|
+
matchedTags?: string[],
|
|
191
|
+
newExpiresAt?: number
|
|
191
192
|
): Promise<{ success: boolean; error?: string }> {
|
|
192
193
|
const offer = await this.getOfferById(offerId);
|
|
193
194
|
|
|
@@ -199,12 +200,19 @@ export class MySQLStorage implements Storage {
|
|
|
199
200
|
return { success: false, error: 'Offer already answered' };
|
|
200
201
|
}
|
|
201
202
|
|
|
203
|
+
const now = Date.now();
|
|
202
204
|
const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
|
|
206
|
+
// Optionally reduce TTL for faster cleanup after answer
|
|
207
|
+
const query = newExpiresAt
|
|
208
|
+
? `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?, expires_at = ? WHERE id = ? AND answerer_public_key IS NULL`
|
|
209
|
+
: `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ? WHERE id = ? AND answerer_public_key IS NULL`;
|
|
210
|
+
|
|
211
|
+
const params = newExpiresAt
|
|
212
|
+
? [answererPublicKey, answerSdp, now, matchedTagsJson, newExpiresAt, offerId]
|
|
213
|
+
: [answererPublicKey, answerSdp, now, matchedTagsJson, offerId];
|
|
214
|
+
|
|
215
|
+
const [result] = await this.pool.query<ResultSetHeader>(query, params);
|
|
208
216
|
|
|
209
217
|
if (result.affectedRows === 0) {
|
|
210
218
|
return { success: false, error: 'Offer already answered (race condition)' };
|
package/src/storage/postgres.ts
CHANGED
|
@@ -189,7 +189,8 @@ export class PostgreSQLStorage implements Storage {
|
|
|
189
189
|
offerId: string,
|
|
190
190
|
answererPublicKey: string,
|
|
191
191
|
answerSdp: string,
|
|
192
|
-
matchedTags?: string[]
|
|
192
|
+
matchedTags?: string[],
|
|
193
|
+
newExpiresAt?: number
|
|
193
194
|
): Promise<{ success: boolean; error?: string }> {
|
|
194
195
|
const offer = await this.getOfferById(offerId);
|
|
195
196
|
|
|
@@ -201,12 +202,19 @@ export class PostgreSQLStorage implements Storage {
|
|
|
201
202
|
return { success: false, error: 'Offer already answered' };
|
|
202
203
|
}
|
|
203
204
|
|
|
205
|
+
const now = Date.now();
|
|
204
206
|
const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
|
|
208
|
+
// Optionally reduce TTL for faster cleanup after answer
|
|
209
|
+
const query = newExpiresAt
|
|
210
|
+
? `UPDATE offers SET answerer_public_key = $1, answer_sdp = $2, answered_at = $3, matched_tags = $4, expires_at = $5 WHERE id = $6 AND answerer_public_key IS NULL`
|
|
211
|
+
: `UPDATE offers SET answerer_public_key = $1, answer_sdp = $2, answered_at = $3, matched_tags = $4 WHERE id = $5 AND answerer_public_key IS NULL`;
|
|
212
|
+
|
|
213
|
+
const params = newExpiresAt
|
|
214
|
+
? [answererPublicKey, answerSdp, now, matchedTagsJson, newExpiresAt, offerId]
|
|
215
|
+
: [answererPublicKey, answerSdp, now, matchedTagsJson, offerId];
|
|
216
|
+
|
|
217
|
+
const result = await this.pool.query(query, params);
|
|
210
218
|
|
|
211
219
|
if ((result.rowCount ?? 0) === 0) {
|
|
212
220
|
return { success: false, error: 'Offer already answered (race condition)' };
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -193,7 +193,8 @@ export class SQLiteStorage implements Storage {
|
|
|
193
193
|
offerId: string,
|
|
194
194
|
answererPublicKey: string,
|
|
195
195
|
answerSdp: string,
|
|
196
|
-
matchedTags?: string[]
|
|
196
|
+
matchedTags?: string[],
|
|
197
|
+
newExpiresAt?: number
|
|
197
198
|
): Promise<{ success: boolean; error?: string }> {
|
|
198
199
|
// Check if offer exists and is not expired
|
|
199
200
|
const offer = await this.getOfferById(offerId);
|
|
@@ -213,15 +214,19 @@ export class SQLiteStorage implements Storage {
|
|
|
213
214
|
};
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
// Update offer with answer
|
|
217
|
+
// Update offer with answer (optionally reduce TTL for faster cleanup)
|
|
218
|
+
const now = Date.now();
|
|
217
219
|
const stmt = this.db.prepare(`
|
|
218
220
|
UPDATE offers
|
|
219
|
-
SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?
|
|
221
|
+
SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?${newExpiresAt ? ', expires_at = ?' : ''}
|
|
220
222
|
WHERE id = ? AND answerer_public_key IS NULL
|
|
221
223
|
`);
|
|
222
224
|
|
|
223
225
|
const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
|
|
224
|
-
const
|
|
226
|
+
const params = newExpiresAt
|
|
227
|
+
? [answererPublicKey, answerSdp, now, matchedTagsJson, newExpiresAt, offerId]
|
|
228
|
+
: [answererPublicKey, answerSdp, now, matchedTagsJson, offerId];
|
|
229
|
+
const result = stmt.run(...params);
|
|
225
230
|
|
|
226
231
|
if (result.changes === 0) {
|
|
227
232
|
return {
|
package/src/storage/types.ts
CHANGED
|
@@ -100,9 +100,10 @@ export interface Storage {
|
|
|
100
100
|
* @param answererPublicKey Answerer's public key
|
|
101
101
|
* @param answerSdp WebRTC answer SDP
|
|
102
102
|
* @param matchedTags Optional tags the answerer searched for to find this offer
|
|
103
|
+
* @param newExpiresAt Optional new expiry time (to reduce TTL after answer for faster cleanup)
|
|
103
104
|
* @returns Success status and optional error message
|
|
104
105
|
*/
|
|
105
|
-
answerOffer(offerId: string, answererPublicKey: string, answerSdp: string, matchedTags?: string[]): Promise<{
|
|
106
|
+
answerOffer(offerId: string, answererPublicKey: string, answerSdp: string, matchedTags?: string[], newExpiresAt?: number): Promise<{
|
|
106
107
|
success: boolean;
|
|
107
108
|
error?: string;
|
|
108
109
|
}>;
|
package/wrangler.toml
CHANGED
|
@@ -6,6 +6,8 @@ compatibility_flags = ["nodejs_compat"]
|
|
|
6
6
|
workers_dev = true
|
|
7
7
|
preview_urls = true
|
|
8
8
|
|
|
9
|
+
# Note: api.ronde.vu is a standalone VPS deployment (manual deployment)
|
|
10
|
+
# This worker is deployed to test.ronde.vu for testing purposes
|
|
9
11
|
routes = [{ pattern = "test.ronde.vu/*", zone_name = "ronde.vu" }]
|
|
10
12
|
|
|
11
13
|
# Cleanup runs every 5 minutes
|