diff --git a/src/storage/d1.ts b/src/storage/d1.ts index 97a5f99..c09af3f 100644 --- a/src/storage/d1.ts +++ b/src/storage/d1.ts @@ -1,4 +1,4 @@ -import { Storage, Offer, IceCandidate, CreateOfferRequest, TopicInfo } from './types.ts'; +import { Storage, Offer, IceCandidate, CreateOfferRequest, TopicInfo, RTCIceCandidateInit } from './types.ts'; // Generate a UUID v4 function generateUUID(): string { @@ -58,9 +58,7 @@ export class D1Storage implements Storage { offer_id TEXT NOT NULL, peer_id TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')), - candidate TEXT NOT NULL, - sdp_mid TEXT, - sdp_m_line_index INTEGER, + candidate TEXT NOT NULL, -- JSON: RTCIceCandidateInit object created_at INTEGER NOT NULL, FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE ); @@ -244,24 +242,18 @@ export class D1Storage implements Storage { offerId: string, peerId: string, role: 'offerer' | 'answerer', - candidates: Array<{ - candidate: string; - sdpMid?: string | null; - sdpMLineIndex?: number | null; - }> + candidates: RTCIceCandidateInit[] ): Promise { // D1 doesn't have transactions, so insert one by one for (const cand of candidates) { await this.db.prepare(` - INSERT INTO ice_candidates (offer_id, peer_id, role, candidate, sdp_mid, sdp_m_line_index, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO ice_candidates (offer_id, peer_id, role, candidate, created_at) + VALUES (?, ?, ?, ?, ?) `).bind( offerId, peerId, role, - cand.candidate, - cand.sdpMid ?? null, - cand.sdpMLineIndex ?? null, + JSON.stringify(cand), // Store full object as JSON Date.now() ).run(); } @@ -299,9 +291,7 @@ export class D1Storage implements Storage { offerId: row.offer_id, peerId: row.peer_id, role: row.role, - candidate: row.candidate, - sdpMid: row.sdp_mid, - sdpMLineIndex: row.sdp_m_line_index, + candidate: JSON.parse(row.candidate), // Parse JSON back to object createdAt: row.created_at, })); } diff --git a/src/storage/sqlite.ts b/src/storage/sqlite.ts index 87b20dd..42bfe6b 100644 --- a/src/storage/sqlite.ts +++ b/src/storage/sqlite.ts @@ -1,6 +1,6 @@ import Database from 'better-sqlite3'; import { randomUUID } from 'crypto'; -import { Storage, Offer, IceCandidate, CreateOfferRequest, TopicInfo } from './types.ts'; +import { Storage, Offer, IceCandidate, CreateOfferRequest, TopicInfo, RTCIceCandidateInit } from './types.ts'; /** * SQLite storage adapter for topic-based offer management @@ -55,9 +55,7 @@ export class SQLiteStorage implements Storage { offer_id TEXT NOT NULL, peer_id TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')), - candidate TEXT NOT NULL, - sdp_mid TEXT, - sdp_m_line_index INTEGER, + candidate TEXT NOT NULL, -- JSON: RTCIceCandidateInit object created_at INTEGER NOT NULL, FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE ); @@ -254,26 +252,20 @@ export class SQLiteStorage implements Storage { offerId: string, peerId: string, role: 'offerer' | 'answerer', - candidates: Array<{ - candidate: string; - sdpMid?: string | null; - sdpMLineIndex?: number | null; - }> + candidates: RTCIceCandidateInit[] ): Promise { const stmt = this.db.prepare(` - INSERT INTO ice_candidates (offer_id, peer_id, role, candidate, sdp_mid, sdp_m_line_index, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO ice_candidates (offer_id, peer_id, role, candidate, created_at) + VALUES (?, ?, ?, ?, ?) `); - const transaction = this.db.transaction((candidates: typeof candidates) => { + const transaction = this.db.transaction((candidates: RTCIceCandidateInit[]) => { for (const cand of candidates) { stmt.run( offerId, peerId, role, - cand.candidate, - cand.sdpMid ?? null, - cand.sdpMLineIndex ?? null, + JSON.stringify(cand), // Store full object as JSON Date.now() ); } @@ -310,9 +302,7 @@ export class SQLiteStorage implements Storage { offerId: row.offer_id, peerId: row.peer_id, role: row.role, - candidate: row.candidate, - sdpMid: row.sdp_mid, - sdpMLineIndex: row.sdp_m_line_index, + candidate: JSON.parse(row.candidate), // Parse JSON back to object createdAt: row.created_at, })); } diff --git a/src/storage/types.ts b/src/storage/types.ts index 185213d..238fe6f 100644 --- a/src/storage/types.ts +++ b/src/storage/types.ts @@ -16,18 +16,27 @@ export interface Offer { /** * Represents an ICE candidate for WebRTC signaling + * Stores the complete RTCIceCandidateInit object */ export interface IceCandidate { id: number; offerId: string; peerId: string; role: 'offerer' | 'answerer'; - candidate: string; - sdpMid: string | null; - sdpMLineIndex: number | null; + candidate: RTCIceCandidateInit; // Full candidate object as JSON createdAt: number; } +/** + * RTCIceCandidateInit interface for TypeScript environments without WebRTC + */ +export interface RTCIceCandidateInit { + candidate?: string; + sdpMid?: string | null; + sdpMLineIndex?: number | null; + usernameFragment?: string | null; +} + /** * Represents a topic with active peer count */ @@ -127,18 +136,14 @@ export interface Storage { * @param offerId Offer identifier * @param peerId Peer ID posting the candidates * @param role Role of the peer (offerer or answerer) - * @param candidates Array of ICE candidate objects + * @param candidates Array of RTCIceCandidateInit objects * @returns Number of candidates added */ addIceCandidates( offerId: string, peerId: string, role: 'offerer' | 'answerer', - candidates: Array<{ - candidate: string; - sdpMid?: string | null; - sdpMLineIndex?: number | null; - }> + candidates: RTCIceCandidateInit[] ): Promise; /**