@xtr-dev/rondevu-server 0.5.12 → 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,7 +133,7 @@ export class MemoryStorage implements Storage {
142
133
 
143
134
  async answerOffer(
144
135
  offerId: string,
145
- answererUsername: string,
136
+ answererPublicKey: string,
146
137
  answerSdp: string,
147
138
  matchedTags?: string[]
148
139
  ): Promise<{ success: boolean; error?: string }> {
@@ -152,35 +143,35 @@ export class MemoryStorage implements Storage {
152
143
  return { success: false, error: 'Offer not found or expired' };
153
144
  }
154
145
 
155
- if (offer.answererUsername) {
146
+ if (offer.answererPublicKey) {
156
147
  return { success: false, error: 'Offer already answered' };
157
148
  }
158
149
 
159
150
  // Update offer with answer
160
151
  const now = Date.now();
161
- offer.answererUsername = answererUsername;
152
+ offer.answererPublicKey = answererPublicKey;
162
153
  offer.answerSdp = answerSdp;
163
154
  offer.answeredAt = now;
164
155
  offer.matchedTags = matchedTags;
165
156
 
166
157
  // Update answerer index
167
- if (!this.offersByAnswerer.has(answererUsername)) {
168
- this.offersByAnswerer.set(answererUsername, new Set());
158
+ if (!this.offersByAnswerer.has(answererPublicKey)) {
159
+ this.offersByAnswerer.set(answererPublicKey, new Set());
169
160
  }
170
- this.offersByAnswerer.get(answererUsername)!.add(offerId);
161
+ this.offersByAnswerer.get(answererPublicKey)!.add(offerId);
171
162
 
172
163
  return { success: true };
173
164
  }
174
165
 
175
- async getAnsweredOffers(offererUsername: string): Promise<Offer[]> {
166
+ async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {
176
167
  const now = Date.now();
177
- const offerIds = this.offersByUsername.get(offererUsername);
168
+ const offerIds = this.offersByPublicKey.get(offererPublicKey);
178
169
  if (!offerIds) return [];
179
170
 
180
171
  const offers: Offer[] = [];
181
172
  for (const id of offerIds) {
182
173
  const offer = this.offers.get(id);
183
- if (offer && offer.answererUsername && offer.expiresAt > now) {
174
+ if (offer && offer.answererPublicKey && offer.expiresAt > now) {
184
175
  offers.push(offer);
185
176
  }
186
177
  }
@@ -188,9 +179,9 @@ export class MemoryStorage implements Storage {
188
179
  return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));
189
180
  }
190
181
 
191
- async getOffersAnsweredBy(answererUsername: string): Promise<Offer[]> {
182
+ async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {
192
183
  const now = Date.now();
193
- const offerIds = this.offersByAnswerer.get(answererUsername);
184
+ const offerIds = this.offersByAnswerer.get(answererPublicKey);
194
185
  if (!offerIds) return [];
195
186
 
196
187
  const offers: Offer[] = [];
@@ -208,7 +199,7 @@ export class MemoryStorage implements Storage {
208
199
 
209
200
  async discoverOffers(
210
201
  tags: string[],
211
- excludeUsername: string | null,
202
+ excludePublicKey: string | null,
212
203
  limit: number,
213
204
  offset: number
214
205
  ): Promise<Offer[]> {
@@ -234,8 +225,8 @@ export class MemoryStorage implements Storage {
234
225
  if (
235
226
  offer &&
236
227
  offer.expiresAt > now &&
237
- !offer.answererUsername &&
238
- (!excludeUsername || offer.username !== excludeUsername)
228
+ !offer.answererPublicKey &&
229
+ (!excludePublicKey || offer.publicKey !== excludePublicKey)
239
230
  ) {
240
231
  offers.push(offer);
241
232
  }
@@ -248,7 +239,7 @@ export class MemoryStorage implements Storage {
248
239
 
249
240
  async getRandomOffer(
250
241
  tags: string[],
251
- excludeUsername: string | null
242
+ excludePublicKey: string | null
252
243
  ): Promise<Offer | null> {
253
244
  if (tags.length === 0) return null;
254
245
 
@@ -272,8 +263,8 @@ export class MemoryStorage implements Storage {
272
263
  if (
273
264
  offer &&
274
265
  offer.expiresAt > now &&
275
- !offer.answererUsername &&
276
- (!excludeUsername || offer.username !== excludeUsername)
266
+ !offer.answererPublicKey &&
267
+ (!excludePublicKey || offer.publicKey !== excludePublicKey)
277
268
  ) {
278
269
  matchingOffers.push(offer);
279
270
  }
@@ -290,7 +281,7 @@ export class MemoryStorage implements Storage {
290
281
 
291
282
  async addIceCandidates(
292
283
  offerId: string,
293
- username: string,
284
+ publicKey: string,
294
285
  role: 'offerer' | 'answerer',
295
286
  candidates: any[]
296
287
  ): Promise<number> {
@@ -306,7 +297,7 @@ export class MemoryStorage implements Storage {
306
297
  const candidate: IceCandidate = {
307
298
  id: ++this.iceCandidateIdCounter,
308
299
  offerId,
309
- username,
300
+ publicKey,
310
301
  role,
311
302
  candidate: candidates[i],
312
303
  createdAt: baseTimestamp + i,
@@ -331,7 +322,7 @@ export class MemoryStorage implements Storage {
331
322
 
332
323
  async getIceCandidatesForMultipleOffers(
333
324
  offerIds: string[],
334
- username: string,
325
+ publicKey: string,
335
326
  since?: number
336
327
  ): Promise<Map<string, IceCandidate[]>> {
337
328
  const result = new Map<string, IceCandidate[]>();
@@ -349,8 +340,8 @@ export class MemoryStorage implements Storage {
349
340
 
350
341
  // Determine which role's candidates to return
351
342
  // If user is offerer, return answerer candidates and vice versa
352
- const isOfferer = offer.username === username;
353
- const isAnswerer = offer.answererUsername === username;
343
+ const isOfferer = offer.publicKey === publicKey;
344
+ const isAnswerer = offer.answererPublicKey === publicKey;
354
345
 
355
346
  if (!isOfferer && !isAnswerer) continue;
356
347
 
@@ -368,97 +359,6 @@ export class MemoryStorage implements Storage {
368
359
  return result;
369
360
  }
370
361
 
371
- // ===== Credential Management =====
372
-
373
- async generateCredentials(request: GenerateCredentialsRequest): Promise<Credential> {
374
- const now = Date.now();
375
- const expiresAt = request.expiresAt || (now + YEAR_IN_MS);
376
-
377
- const { generateCredentialName, generateSecret, encryptSecret } = await import('../crypto.ts');
378
-
379
- let name: string;
380
-
381
- if (request.name) {
382
- if (this.credentials.has(request.name)) {
383
- throw new Error('Username already taken');
384
- }
385
- name = request.name;
386
- } else {
387
- let attempts = 0;
388
- const maxAttempts = 100;
389
-
390
- while (attempts < maxAttempts) {
391
- name = generateCredentialName();
392
- if (!this.credentials.has(name)) break;
393
- attempts++;
394
- }
395
-
396
- if (attempts >= maxAttempts) {
397
- throw new Error(`Failed to generate unique credential name after ${maxAttempts} attempts`);
398
- }
399
- }
400
-
401
- const secret = generateSecret();
402
-
403
- // Encrypt secret before storing
404
- const encryptedSecret = await encryptSecret(secret, this.masterEncryptionKey);
405
-
406
- const credential: Credential = {
407
- name: name!,
408
- secret: encryptedSecret,
409
- createdAt: now,
410
- expiresAt,
411
- lastUsed: now,
412
- };
413
-
414
- this.credentials.set(name!, credential);
415
-
416
- // Return plaintext secret to user
417
- return {
418
- ...credential,
419
- secret, // Return plaintext, not encrypted
420
- };
421
- }
422
-
423
- async getCredential(name: string): Promise<Credential | null> {
424
- const credential = this.credentials.get(name);
425
- if (!credential || credential.expiresAt <= Date.now()) {
426
- return null;
427
- }
428
-
429
- try {
430
- const { decryptSecret } = await import('../crypto.ts');
431
- const decryptedSecret = await decryptSecret(credential.secret, this.masterEncryptionKey);
432
-
433
- return {
434
- ...credential,
435
- secret: decryptedSecret,
436
- };
437
- } catch (error) {
438
- console.error(`Failed to decrypt secret for credential '${name}':`, error);
439
- return null;
440
- }
441
- }
442
-
443
- async updateCredentialUsage(name: string, lastUsed: number, expiresAt: number): Promise<void> {
444
- const credential = this.credentials.get(name);
445
- if (credential) {
446
- credential.lastUsed = lastUsed;
447
- credential.expiresAt = expiresAt;
448
- }
449
- }
450
-
451
- async deleteExpiredCredentials(now: number): Promise<number> {
452
- let count = 0;
453
- for (const [name, credential] of this.credentials) {
454
- if (credential.expiresAt < now) {
455
- this.credentials.delete(name);
456
- count++;
457
- }
458
- }
459
- return count;
460
- }
461
-
462
362
  // ===== Rate Limiting =====
463
363
 
464
364
  async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {
@@ -514,12 +414,11 @@ export class MemoryStorage implements Storage {
514
414
 
515
415
  async close(): Promise<void> {
516
416
  // Clear all data
517
- this.credentials.clear();
518
417
  this.offers.clear();
519
418
  this.iceCandidates.clear();
520
419
  this.rateLimits.clear();
521
420
  this.nonces.clear();
522
- this.offersByUsername.clear();
421
+ this.offersByPublicKey.clear();
523
422
  this.offersByTag.clear();
524
423
  this.offersByAnswerer.clear();
525
424
  }
@@ -530,15 +429,11 @@ export class MemoryStorage implements Storage {
530
429
  return this.offers.size;
531
430
  }
532
431
 
533
- async getOfferCountByUsername(username: string): Promise<number> {
534
- const offerIds = this.offersByUsername.get(username);
432
+ async getOfferCountByPublicKey(publicKey: string): Promise<number> {
433
+ const offerIds = this.offersByPublicKey.get(publicKey);
535
434
  return offerIds ? offerIds.size : 0;
536
435
  }
537
436
 
538
- async getCredentialCount(): Promise<number> {
539
- return this.credentials.size;
540
- }
541
-
542
437
  async getIceCandidateCount(offerId: string): Promise<number> {
543
438
  const candidates = this.iceCandidates.get(offerId);
544
439
  return candidates ? candidates.length : 0;
@@ -547,12 +442,12 @@ export class MemoryStorage implements Storage {
547
442
  // ===== Helper Methods =====
548
443
 
549
444
  private removeOfferFromIndexes(offer: Offer): void {
550
- // Remove from username index
551
- const usernameOffers = this.offersByUsername.get(offer.username);
552
- if (usernameOffers) {
553
- usernameOffers.delete(offer.id);
554
- if (usernameOffers.size === 0) {
555
- 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);
556
451
  }
557
452
  }
558
453
 
@@ -568,12 +463,12 @@ export class MemoryStorage implements Storage {
568
463
  }
569
464
 
570
465
  // Remove from answerer index
571
- if (offer.answererUsername) {
572
- const answererOffers = this.offersByAnswerer.get(offer.answererUsername);
466
+ if (offer.answererPublicKey) {
467
+ const answererOffers = this.offersByAnswerer.get(offer.answererPublicKey);
573
468
  if (answererOffers) {
574
469
  answererOffers.delete(offer.id);
575
470
  if (answererOffers.size === 0) {
576
- this.offersByAnswerer.delete(offer.answererUsername);
471
+ this.offersByAnswerer.delete(offer.answererPublicKey);
577
472
  }
578
473
  }
579
474
  }