@xtr-dev/rondevu-server 0.5.11 → 0.5.13

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.
@@ -3,13 +3,9 @@ import {
3
3
  Offer,
4
4
  IceCandidate,
5
5
  CreateOfferRequest,
6
- Credential,
7
- GenerateCredentialsRequest,
8
6
  } from './types.ts';
9
7
  import { generateOfferHash } from './hash-id.ts';
10
8
 
11
- const YEAR_IN_MS = 365 * 24 * 60 * 60 * 1000;
12
-
13
9
  interface RateLimit {
14
10
  count: number;
15
11
  resetTime: number;
@@ -25,26 +21,21 @@ interface NonceEntry {
25
21
  * Best for development, testing, or ephemeral deployments
26
22
  */
27
23
  export class MemoryStorage implements Storage {
28
- private masterEncryptionKey: string;
29
-
30
24
  // Primary storage
31
- private credentials = new Map<string, Credential>();
32
25
  private offers = new Map<string, Offer>();
33
26
  private iceCandidates = new Map<string, IceCandidate[]>(); // offerId → candidates
34
27
  private rateLimits = new Map<string, RateLimit>();
35
28
  private nonces = new Map<string, NonceEntry>();
36
29
 
37
30
  // Secondary indexes for efficient lookups
38
- private offersByUsername = new Map<string, Set<string>>(); // username → offer IDs
31
+ private offersByPublicKey = new Map<string, Set<string>>(); // publicKey → offer IDs
39
32
  private offersByTag = new Map<string, Set<string>>(); // tag → offer IDs
40
- private offersByAnswerer = new Map<string, Set<string>>(); // answerer username → offer IDs
33
+ private offersByAnswerer = new Map<string, Set<string>>(); // answerer publicKey → offer IDs
41
34
 
42
35
  // Auto-increment counter for ICE candidates
43
36
  private iceCandidateIdCounter = 0;
44
37
 
45
- constructor(masterEncryptionKey: string) {
46
- this.masterEncryptionKey = masterEncryptionKey;
47
- }
38
+ constructor() {}
48
39
 
49
40
  // ===== Offer Management =====
50
41
 
@@ -57,7 +48,7 @@ export class MemoryStorage implements Storage {
57
48
 
58
49
  const offer: Offer = {
59
50
  id,
60
- username: request.username,
51
+ publicKey: request.publicKey,
61
52
  tags: request.tags,
62
53
  sdp: request.sdp,
63
54
  createdAt: now,
@@ -68,11 +59,11 @@ export class MemoryStorage implements Storage {
68
59
  // Store offer
69
60
  this.offers.set(id, offer);
70
61
 
71
- // Update username index
72
- if (!this.offersByUsername.has(request.username)) {
73
- this.offersByUsername.set(request.username, new Set());
62
+ // Update publicKey index
63
+ if (!this.offersByPublicKey.has(request.publicKey)) {
64
+ this.offersByPublicKey.set(request.publicKey, new Set());
74
65
  }
75
- this.offersByUsername.get(request.username)!.add(id);
66
+ this.offersByPublicKey.get(request.publicKey)!.add(id);
76
67
 
77
68
  // Update tag indexes
78
69
  for (const tag of request.tags) {
@@ -88,9 +79,9 @@ export class MemoryStorage implements Storage {
88
79
  return created;
89
80
  }
90
81
 
91
- async getOffersByUsername(username: string): Promise<Offer[]> {
82
+ async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {
92
83
  const now = Date.now();
93
- const offerIds = this.offersByUsername.get(username);
84
+ const offerIds = this.offersByPublicKey.get(publicKey);
94
85
  if (!offerIds) return [];
95
86
 
96
87
  const offers: Offer[] = [];
@@ -112,9 +103,9 @@ export class MemoryStorage implements Storage {
112
103
  return offer;
113
104
  }
114
105
 
115
- async deleteOffer(offerId: string, ownerUsername: string): Promise<boolean> {
106
+ async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {
116
107
  const offer = this.offers.get(offerId);
117
- if (!offer || offer.username !== ownerUsername) {
108
+ if (!offer || offer.publicKey !== ownerPublicKey) {
118
109
  return false;
119
110
  }
120
111
 
@@ -142,8 +133,9 @@ export class MemoryStorage implements Storage {
142
133
 
143
134
  async answerOffer(
144
135
  offerId: string,
145
- answererUsername: string,
146
- answerSdp: string
136
+ answererPublicKey: string,
137
+ answerSdp: string,
138
+ matchedTags?: string[]
147
139
  ): Promise<{ success: boolean; error?: string }> {
148
140
  const offer = await this.getOfferById(offerId);
149
141
 
@@ -151,34 +143,35 @@ export class MemoryStorage implements Storage {
151
143
  return { success: false, error: 'Offer not found or expired' };
152
144
  }
153
145
 
154
- if (offer.answererUsername) {
146
+ if (offer.answererPublicKey) {
155
147
  return { success: false, error: 'Offer already answered' };
156
148
  }
157
149
 
158
150
  // Update offer with answer
159
151
  const now = Date.now();
160
- offer.answererUsername = answererUsername;
152
+ offer.answererPublicKey = answererPublicKey;
161
153
  offer.answerSdp = answerSdp;
162
154
  offer.answeredAt = now;
155
+ offer.matchedTags = matchedTags;
163
156
 
164
157
  // Update answerer index
165
- if (!this.offersByAnswerer.has(answererUsername)) {
166
- this.offersByAnswerer.set(answererUsername, new Set());
158
+ if (!this.offersByAnswerer.has(answererPublicKey)) {
159
+ this.offersByAnswerer.set(answererPublicKey, new Set());
167
160
  }
168
- this.offersByAnswerer.get(answererUsername)!.add(offerId);
161
+ this.offersByAnswerer.get(answererPublicKey)!.add(offerId);
169
162
 
170
163
  return { success: true };
171
164
  }
172
165
 
173
- async getAnsweredOffers(offererUsername: string): Promise<Offer[]> {
166
+ async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {
174
167
  const now = Date.now();
175
- const offerIds = this.offersByUsername.get(offererUsername);
168
+ const offerIds = this.offersByPublicKey.get(offererPublicKey);
176
169
  if (!offerIds) return [];
177
170
 
178
171
  const offers: Offer[] = [];
179
172
  for (const id of offerIds) {
180
173
  const offer = this.offers.get(id);
181
- if (offer && offer.answererUsername && offer.expiresAt > now) {
174
+ if (offer && offer.answererPublicKey && offer.expiresAt > now) {
182
175
  offers.push(offer);
183
176
  }
184
177
  }
@@ -186,9 +179,9 @@ export class MemoryStorage implements Storage {
186
179
  return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));
187
180
  }
188
181
 
189
- async getOffersAnsweredBy(answererUsername: string): Promise<Offer[]> {
182
+ async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {
190
183
  const now = Date.now();
191
- const offerIds = this.offersByAnswerer.get(answererUsername);
184
+ const offerIds = this.offersByAnswerer.get(answererPublicKey);
192
185
  if (!offerIds) return [];
193
186
 
194
187
  const offers: Offer[] = [];
@@ -206,7 +199,7 @@ export class MemoryStorage implements Storage {
206
199
 
207
200
  async discoverOffers(
208
201
  tags: string[],
209
- excludeUsername: string | null,
202
+ excludePublicKey: string | null,
210
203
  limit: number,
211
204
  offset: number
212
205
  ): Promise<Offer[]> {
@@ -232,8 +225,8 @@ export class MemoryStorage implements Storage {
232
225
  if (
233
226
  offer &&
234
227
  offer.expiresAt > now &&
235
- !offer.answererUsername &&
236
- (!excludeUsername || offer.username !== excludeUsername)
228
+ !offer.answererPublicKey &&
229
+ (!excludePublicKey || offer.publicKey !== excludePublicKey)
237
230
  ) {
238
231
  offers.push(offer);
239
232
  }
@@ -246,7 +239,7 @@ export class MemoryStorage implements Storage {
246
239
 
247
240
  async getRandomOffer(
248
241
  tags: string[],
249
- excludeUsername: string | null
242
+ excludePublicKey: string | null
250
243
  ): Promise<Offer | null> {
251
244
  if (tags.length === 0) return null;
252
245
 
@@ -270,8 +263,8 @@ export class MemoryStorage implements Storage {
270
263
  if (
271
264
  offer &&
272
265
  offer.expiresAt > now &&
273
- !offer.answererUsername &&
274
- (!excludeUsername || offer.username !== excludeUsername)
266
+ !offer.answererPublicKey &&
267
+ (!excludePublicKey || offer.publicKey !== excludePublicKey)
275
268
  ) {
276
269
  matchingOffers.push(offer);
277
270
  }
@@ -288,7 +281,7 @@ export class MemoryStorage implements Storage {
288
281
 
289
282
  async addIceCandidates(
290
283
  offerId: string,
291
- username: string,
284
+ publicKey: string,
292
285
  role: 'offerer' | 'answerer',
293
286
  candidates: any[]
294
287
  ): Promise<number> {
@@ -304,7 +297,7 @@ export class MemoryStorage implements Storage {
304
297
  const candidate: IceCandidate = {
305
298
  id: ++this.iceCandidateIdCounter,
306
299
  offerId,
307
- username,
300
+ publicKey,
308
301
  role,
309
302
  candidate: candidates[i],
310
303
  createdAt: baseTimestamp + i,
@@ -329,7 +322,7 @@ export class MemoryStorage implements Storage {
329
322
 
330
323
  async getIceCandidatesForMultipleOffers(
331
324
  offerIds: string[],
332
- username: string,
325
+ publicKey: string,
333
326
  since?: number
334
327
  ): Promise<Map<string, IceCandidate[]>> {
335
328
  const result = new Map<string, IceCandidate[]>();
@@ -347,8 +340,8 @@ export class MemoryStorage implements Storage {
347
340
 
348
341
  // Determine which role's candidates to return
349
342
  // If user is offerer, return answerer candidates and vice versa
350
- const isOfferer = offer.username === username;
351
- const isAnswerer = offer.answererUsername === username;
343
+ const isOfferer = offer.publicKey === publicKey;
344
+ const isAnswerer = offer.answererPublicKey === publicKey;
352
345
 
353
346
  if (!isOfferer && !isAnswerer) continue;
354
347
 
@@ -366,97 +359,6 @@ export class MemoryStorage implements Storage {
366
359
  return result;
367
360
  }
368
361
 
369
- // ===== Credential Management =====
370
-
371
- async generateCredentials(request: GenerateCredentialsRequest): Promise<Credential> {
372
- const now = Date.now();
373
- const expiresAt = request.expiresAt || (now + YEAR_IN_MS);
374
-
375
- const { generateCredentialName, generateSecret, encryptSecret } = await import('../crypto.ts');
376
-
377
- let name: string;
378
-
379
- if (request.name) {
380
- if (this.credentials.has(request.name)) {
381
- throw new Error('Username already taken');
382
- }
383
- name = request.name;
384
- } else {
385
- let attempts = 0;
386
- const maxAttempts = 100;
387
-
388
- while (attempts < maxAttempts) {
389
- name = generateCredentialName();
390
- if (!this.credentials.has(name)) break;
391
- attempts++;
392
- }
393
-
394
- if (attempts >= maxAttempts) {
395
- throw new Error(`Failed to generate unique credential name after ${maxAttempts} attempts`);
396
- }
397
- }
398
-
399
- const secret = generateSecret();
400
-
401
- // Encrypt secret before storing
402
- const encryptedSecret = await encryptSecret(secret, this.masterEncryptionKey);
403
-
404
- const credential: Credential = {
405
- name: name!,
406
- secret: encryptedSecret,
407
- createdAt: now,
408
- expiresAt,
409
- lastUsed: now,
410
- };
411
-
412
- this.credentials.set(name!, credential);
413
-
414
- // Return plaintext secret to user
415
- return {
416
- ...credential,
417
- secret, // Return plaintext, not encrypted
418
- };
419
- }
420
-
421
- async getCredential(name: string): Promise<Credential | null> {
422
- const credential = this.credentials.get(name);
423
- if (!credential || credential.expiresAt <= Date.now()) {
424
- return null;
425
- }
426
-
427
- try {
428
- const { decryptSecret } = await import('../crypto.ts');
429
- const decryptedSecret = await decryptSecret(credential.secret, this.masterEncryptionKey);
430
-
431
- return {
432
- ...credential,
433
- secret: decryptedSecret,
434
- };
435
- } catch (error) {
436
- console.error(`Failed to decrypt secret for credential '${name}':`, error);
437
- return null;
438
- }
439
- }
440
-
441
- async updateCredentialUsage(name: string, lastUsed: number, expiresAt: number): Promise<void> {
442
- const credential = this.credentials.get(name);
443
- if (credential) {
444
- credential.lastUsed = lastUsed;
445
- credential.expiresAt = expiresAt;
446
- }
447
- }
448
-
449
- async deleteExpiredCredentials(now: number): Promise<number> {
450
- let count = 0;
451
- for (const [name, credential] of this.credentials) {
452
- if (credential.expiresAt < now) {
453
- this.credentials.delete(name);
454
- count++;
455
- }
456
- }
457
- return count;
458
- }
459
-
460
362
  // ===== Rate Limiting =====
461
363
 
462
364
  async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {
@@ -512,12 +414,11 @@ export class MemoryStorage implements Storage {
512
414
 
513
415
  async close(): Promise<void> {
514
416
  // Clear all data
515
- this.credentials.clear();
516
417
  this.offers.clear();
517
418
  this.iceCandidates.clear();
518
419
  this.rateLimits.clear();
519
420
  this.nonces.clear();
520
- this.offersByUsername.clear();
421
+ this.offersByPublicKey.clear();
521
422
  this.offersByTag.clear();
522
423
  this.offersByAnswerer.clear();
523
424
  }
@@ -528,15 +429,11 @@ export class MemoryStorage implements Storage {
528
429
  return this.offers.size;
529
430
  }
530
431
 
531
- async getOfferCountByUsername(username: string): Promise<number> {
532
- const offerIds = this.offersByUsername.get(username);
432
+ async getOfferCountByPublicKey(publicKey: string): Promise<number> {
433
+ const offerIds = this.offersByPublicKey.get(publicKey);
533
434
  return offerIds ? offerIds.size : 0;
534
435
  }
535
436
 
536
- async getCredentialCount(): Promise<number> {
537
- return this.credentials.size;
538
- }
539
-
540
437
  async getIceCandidateCount(offerId: string): Promise<number> {
541
438
  const candidates = this.iceCandidates.get(offerId);
542
439
  return candidates ? candidates.length : 0;
@@ -545,12 +442,12 @@ export class MemoryStorage implements Storage {
545
442
  // ===== Helper Methods =====
546
443
 
547
444
  private removeOfferFromIndexes(offer: Offer): void {
548
- // Remove from username index
549
- const usernameOffers = this.offersByUsername.get(offer.username);
550
- if (usernameOffers) {
551
- usernameOffers.delete(offer.id);
552
- if (usernameOffers.size === 0) {
553
- this.offersByUsername.delete(offer.username);
445
+ // Remove from publicKey index
446
+ const publicKeyOffers = this.offersByPublicKey.get(offer.publicKey);
447
+ if (publicKeyOffers) {
448
+ publicKeyOffers.delete(offer.id);
449
+ if (publicKeyOffers.size === 0) {
450
+ this.offersByPublicKey.delete(offer.publicKey);
554
451
  }
555
452
  }
556
453
 
@@ -566,12 +463,12 @@ export class MemoryStorage implements Storage {
566
463
  }
567
464
 
568
465
  // Remove from answerer index
569
- if (offer.answererUsername) {
570
- const answererOffers = this.offersByAnswerer.get(offer.answererUsername);
466
+ if (offer.answererPublicKey) {
467
+ const answererOffers = this.offersByAnswerer.get(offer.answererPublicKey);
571
468
  if (answererOffers) {
572
469
  answererOffers.delete(offer.id);
573
470
  if (answererOffers.size === 0) {
574
- this.offersByAnswerer.delete(offer.answererUsername);
471
+ this.offersByAnswerer.delete(offer.answererPublicKey);
575
472
  }
576
473
  }
577
474
  }