@xtr-dev/rondevu-server 0.5.10 → 0.5.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-server",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "DNS-like WebRTC signaling server with credential-based authentication and service discovery",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/config.ts CHANGED
@@ -113,8 +113,8 @@ export function loadConfig(): Config {
113
113
  timestampMaxFuture: parsePositiveInt(process.env.TIMESTAMP_MAX_FUTURE, '60000', 'TIMESTAMP_MAX_FUTURE', 1000), // Min 1 second
114
114
  masterEncryptionKey,
115
115
  // Resource limits
116
- maxOffersPerUser: parsePositiveInt(process.env.MAX_OFFERS_PER_USER, '20', 'MAX_OFFERS_PER_USER', 1),
117
- maxTotalOffers: parsePositiveInt(process.env.MAX_TOTAL_OFFERS, '10000', 'MAX_TOTAL_OFFERS', 1),
116
+ maxOffersPerUser: parsePositiveInt(process.env.MAX_OFFERS_PER_USER, '1000', 'MAX_OFFERS_PER_USER', 1),
117
+ maxTotalOffers: parsePositiveInt(process.env.MAX_TOTAL_OFFERS, '100000', 'MAX_TOTAL_OFFERS', 1),
118
118
  maxTotalCredentials: parsePositiveInt(process.env.MAX_TOTAL_CREDENTIALS, '50000', 'MAX_TOTAL_CREDENTIALS', 1),
119
119
  maxIceCandidatesPerOffer: parsePositiveInt(process.env.MAX_ICE_CANDIDATES_PER_OFFER, '50', 'MAX_ICE_CANDIDATES_PER_OFFER', 1),
120
120
  credentialsPerIpPerSecond: parsePositiveInt(process.env.CREDENTIALS_PER_IP_PER_SECOND, '5', 'CREDENTIALS_PER_IP_PER_SECOND', 1),
package/src/rpc.ts CHANGED
@@ -168,6 +168,7 @@ export interface DeleteOfferParams {
168
168
  export interface AnswerOfferParams {
169
169
  offerId: string;
170
170
  sdp: string;
171
+ matchedTags?: string[]; // Tags the answerer searched for to find this offer
171
172
  }
172
173
 
173
174
  export interface GetOfferAnswerParams {
@@ -565,7 +566,7 @@ const handlers: Record<string, RpcHandler> = {
565
566
  * Answer an offer
566
567
  */
567
568
  async answerOffer(params: AnswerOfferParams, name, timestamp, signature, storage, config, request: RpcRequest) {
568
- const { offerId, sdp } = params;
569
+ const { offerId, sdp, matchedTags } = params;
569
570
 
570
571
  // Validate input parameters
571
572
  validateStringParam(offerId, 'offerId');
@@ -582,6 +583,11 @@ const handlers: Record<string, RpcHandler> = {
582
583
  throw new RpcError(ErrorCodes.SDP_TOO_LARGE, `SDP too large (max ${config.maxSdpSize} bytes)`);
583
584
  }
584
585
 
586
+ // Validate matchedTags if provided
587
+ if (matchedTags !== undefined && !Array.isArray(matchedTags)) {
588
+ throw new RpcError(ErrorCodes.INVALID_PARAMS, 'matchedTags must be an array');
589
+ }
590
+
585
591
  const offer = await storage.getOfferById(offerId);
586
592
  if (!offer) {
587
593
  throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');
@@ -591,7 +597,7 @@ const handlers: Record<string, RpcHandler> = {
591
597
  throw new RpcError(ErrorCodes.OFFER_ALREADY_ANSWERED, 'Offer already answered');
592
598
  }
593
599
 
594
- await storage.answerOffer(offerId, name, sdp);
600
+ await storage.answerOffer(offerId, name, sdp, matchedTags);
595
601
 
596
602
  return { success: true, offerId };
597
603
  },
package/src/storage/d1.ts CHANGED
@@ -45,7 +45,8 @@ export class D1Storage implements Storage {
45
45
  last_seen INTEGER NOT NULL,
46
46
  answerer_username TEXT,
47
47
  answer_sdp TEXT,
48
- answered_at INTEGER
48
+ answered_at INTEGER,
49
+ matched_tags TEXT
49
50
  );
50
51
 
51
52
  CREATE INDEX IF NOT EXISTS idx_offers_username ON offers(username);
@@ -176,7 +177,8 @@ export class D1Storage implements Storage {
176
177
  async answerOffer(
177
178
  offerId: string,
178
179
  answererUsername: string,
179
- answerSdp: string
180
+ answerSdp: string,
181
+ matchedTags?: string[]
180
182
  ): Promise<{ success: boolean; error?: string }> {
181
183
  // Check if offer exists and is not expired
182
184
  const offer = await this.getOfferById(offerId);
@@ -197,11 +199,12 @@ export class D1Storage implements Storage {
197
199
  }
198
200
 
199
201
  // Update offer with answer
202
+ const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
200
203
  const result = await this.db.prepare(`
201
204
  UPDATE offers
202
- SET answerer_username = ?, answer_sdp = ?, answered_at = ?
205
+ SET answerer_username = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?
203
206
  WHERE id = ? AND answerer_username IS NULL
204
- `).bind(answererUsername, answerSdp, Date.now(), offerId).run();
207
+ `).bind(answererUsername, answerSdp, Date.now(), matchedTagsJson, offerId).run();
205
208
 
206
209
  if ((result.meta.changes || 0) === 0) {
207
210
  return {
@@ -674,6 +677,7 @@ export class D1Storage implements Storage {
674
677
  answererUsername: row.answerer_username || undefined,
675
678
  answerSdp: row.answer_sdp || undefined,
676
679
  answeredAt: row.answered_at || undefined,
680
+ matchedTags: row.matched_tags ? JSON.parse(row.matched_tags) : undefined,
677
681
  };
678
682
  }
679
683
  }
@@ -143,7 +143,8 @@ export class MemoryStorage implements Storage {
143
143
  async answerOffer(
144
144
  offerId: string,
145
145
  answererUsername: string,
146
- answerSdp: string
146
+ answerSdp: string,
147
+ matchedTags?: string[]
147
148
  ): Promise<{ success: boolean; error?: string }> {
148
149
  const offer = await this.getOfferById(offerId);
149
150
 
@@ -160,6 +161,7 @@ export class MemoryStorage implements Storage {
160
161
  offer.answererUsername = answererUsername;
161
162
  offer.answerSdp = answerSdp;
162
163
  offer.answeredAt = now;
164
+ offer.matchedTags = matchedTags;
163
165
 
164
166
  // Update answerer index
165
167
  if (!this.offersByAnswerer.has(answererUsername)) {
@@ -64,6 +64,7 @@ export class MySQLStorage implements Storage {
64
64
  answerer_username VARCHAR(32),
65
65
  answer_sdp MEDIUMTEXT,
66
66
  answered_at BIGINT,
67
+ matched_tags JSON,
67
68
  INDEX idx_offers_username (username),
68
69
  INDEX idx_offers_expires (expires_at),
69
70
  INDEX idx_offers_last_seen (last_seen),
@@ -196,7 +197,8 @@ export class MySQLStorage implements Storage {
196
197
  async answerOffer(
197
198
  offerId: string,
198
199
  answererUsername: string,
199
- answerSdp: string
200
+ answerSdp: string,
201
+ matchedTags?: string[]
200
202
  ): Promise<{ success: boolean; error?: string }> {
201
203
  const offer = await this.getOfferById(offerId);
202
204
 
@@ -208,10 +210,11 @@ export class MySQLStorage implements Storage {
208
210
  return { success: false, error: 'Offer already answered' };
209
211
  }
210
212
 
213
+ const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
211
214
  const [result] = await this.pool.query<ResultSetHeader>(
212
- `UPDATE offers SET answerer_username = ?, answer_sdp = ?, answered_at = ?
215
+ `UPDATE offers SET answerer_username = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?
213
216
  WHERE id = ? AND answerer_username IS NULL`,
214
- [answererUsername, answerSdp, Date.now(), offerId]
217
+ [answererUsername, answerSdp, Date.now(), matchedTagsJson, offerId]
215
218
  );
216
219
 
217
220
  if (result.affectedRows === 0) {
@@ -600,6 +603,7 @@ export class MySQLStorage implements Storage {
600
603
  answererUsername: row.answerer_username || undefined,
601
604
  answerSdp: row.answer_sdp || undefined,
602
605
  answeredAt: row.answered_at ? Number(row.answered_at) : undefined,
606
+ matchedTags: row.matched_tags ? (typeof row.matched_tags === 'string' ? JSON.parse(row.matched_tags) : row.matched_tags) : undefined,
603
607
  };
604
608
  }
605
609
 
@@ -61,7 +61,8 @@ export class PostgreSQLStorage implements Storage {
61
61
  last_seen BIGINT NOT NULL,
62
62
  answerer_username VARCHAR(32),
63
63
  answer_sdp TEXT,
64
- answered_at BIGINT
64
+ answered_at BIGINT,
65
+ matched_tags JSONB
65
66
  )
66
67
  `);
67
68
 
@@ -199,7 +200,8 @@ export class PostgreSQLStorage implements Storage {
199
200
  async answerOffer(
200
201
  offerId: string,
201
202
  answererUsername: string,
202
- answerSdp: string
203
+ answerSdp: string,
204
+ matchedTags?: string[]
203
205
  ): Promise<{ success: boolean; error?: string }> {
204
206
  const offer = await this.getOfferById(offerId);
205
207
 
@@ -211,10 +213,11 @@ export class PostgreSQLStorage implements Storage {
211
213
  return { success: false, error: 'Offer already answered' };
212
214
  }
213
215
 
216
+ const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
214
217
  const result = await this.pool.query(
215
- `UPDATE offers SET answerer_username = $1, answer_sdp = $2, answered_at = $3
216
- WHERE id = $4 AND answerer_username IS NULL`,
217
- [answererUsername, answerSdp, Date.now(), offerId]
218
+ `UPDATE offers SET answerer_username = $1, answer_sdp = $2, answered_at = $3, matched_tags = $4
219
+ WHERE id = $5 AND answerer_username IS NULL`,
220
+ [answererUsername, answerSdp, Date.now(), matchedTagsJson, offerId]
218
221
  );
219
222
 
220
223
  if ((result.rowCount ?? 0) === 0) {
@@ -607,6 +610,7 @@ export class PostgreSQLStorage implements Storage {
607
610
  answererUsername: row.answerer_username || undefined,
608
611
  answerSdp: row.answer_sdp || undefined,
609
612
  answeredAt: row.answered_at ? Number(row.answered_at) : undefined,
613
+ matchedTags: row.matched_tags || undefined,
610
614
  };
611
615
  }
612
616
 
@@ -46,7 +46,8 @@ export class SQLiteStorage implements Storage {
46
46
  last_seen INTEGER NOT NULL,
47
47
  answerer_username TEXT,
48
48
  answer_sdp TEXT,
49
- answered_at INTEGER
49
+ answered_at INTEGER,
50
+ matched_tags TEXT
50
51
  );
51
52
 
52
53
  CREATE INDEX IF NOT EXISTS idx_offers_username ON offers(username);
@@ -199,7 +200,8 @@ export class SQLiteStorage implements Storage {
199
200
  async answerOffer(
200
201
  offerId: string,
201
202
  answererUsername: string,
202
- answerSdp: string
203
+ answerSdp: string,
204
+ matchedTags?: string[]
203
205
  ): Promise<{ success: boolean; error?: string }> {
204
206
  // Check if offer exists and is not expired
205
207
  const offer = await this.getOfferById(offerId);
@@ -222,11 +224,12 @@ export class SQLiteStorage implements Storage {
222
224
  // Update offer with answer
223
225
  const stmt = this.db.prepare(`
224
226
  UPDATE offers
225
- SET answerer_username = ?, answer_sdp = ?, answered_at = ?
227
+ SET answerer_username = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?
226
228
  WHERE id = ? AND answerer_username IS NULL
227
229
  `);
228
230
 
229
- const result = stmt.run(answererUsername, answerSdp, Date.now(), offerId);
231
+ const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;
232
+ const result = stmt.run(answererUsername, answerSdp, Date.now(), matchedTagsJson, offerId);
230
233
 
231
234
  if (result.changes === 0) {
232
235
  return {
@@ -681,6 +684,7 @@ export class SQLiteStorage implements Storage {
681
684
  answererUsername: row.answerer_username || undefined,
682
685
  answerSdp: row.answer_sdp || undefined,
683
686
  answeredAt: row.answered_at || undefined,
687
+ matchedTags: row.matched_tags ? JSON.parse(row.matched_tags) : undefined,
684
688
  };
685
689
  }
686
690
  }
@@ -12,6 +12,7 @@ export interface Offer {
12
12
  answererUsername?: string;
13
13
  answerSdp?: string;
14
14
  answeredAt?: number;
15
+ matchedTags?: string[]; // Tags the answerer searched for to find this offer
15
16
  }
16
17
 
17
18
  /**
@@ -118,9 +119,10 @@ export interface Storage {
118
119
  * @param offerId Offer identifier
119
120
  * @param answererUsername Answerer's username
120
121
  * @param answerSdp WebRTC answer SDP
122
+ * @param matchedTags Optional tags the answerer searched for to find this offer
121
123
  * @returns Success status and optional error message
122
124
  */
123
- answerOffer(offerId: string, answererUsername: string, answerSdp: string): Promise<{
125
+ answerOffer(offerId: string, answererUsername: string, answerSdp: string, matchedTags?: string[]): Promise<{
124
126
  success: boolean;
125
127
  error?: string;
126
128
  }>;