diff --git a/README.md b/README.md index 4c1679b..8def015 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,8 @@ Find offers by topic with optional bloom filter exclusion "topics": ["movie-xyz", "hd-content"], "expiresAt": 1234567890, "lastSeen": 1234567890, - "hasSecret": true // Indicates if secret is required to answer + "hasSecret": true, // Indicates if secret is required to answer + "info": "Looking for peers in EU region" // Public info field (optional) } ], "total": 42, @@ -121,6 +122,7 @@ Find offers by topic with optional bloom filter exclusion **Notes:** - `hasSecret`: Boolean flag indicating whether a secret is required to answer this offer. The actual secret is never exposed in public endpoints. +- `info`: Optional public metadata field (max 128 characters) visible to all peers. #### `GET /peers/:peerId/offers` View all offers from a specific peer @@ -140,7 +142,8 @@ Create one or more offers "sdp": "v=0...", "topics": ["movie-xyz", "hd-content"], "ttl": 300000, - "secret": "my-secret-password" // Optional: protect offer (max 128 chars) + "secret": "my-secret-password", // Optional: protect offer (max 128 chars) + "info": "Looking for peers in EU region" // Optional: public info (max 128 chars) } ] } @@ -148,6 +151,7 @@ Create one or more offers **Notes:** - `secret` (optional): Protect the offer with a secret. Answerers must provide the correct secret to connect. +- `info` (optional): Public metadata visible to all peers (max 128 characters). Useful for describing the offer or connection requirements. #### `GET /offers/mine` List all offers owned by authenticated peer diff --git a/src/app.ts b/src/app.ts index 59682dd..8e1d899 100644 --- a/src/app.ts +++ b/src/app.ts @@ -151,6 +151,16 @@ export function createApp(storage: Storage, config: Config) { } } + // Validate info if provided + if (offer.info !== undefined) { + if (typeof offer.info !== 'string') { + return c.json({ error: 'Info must be a string' }, 400); + } + if (offer.info.length > 128) { + return c.json({ error: 'Info must be 128 characters or less' }, 400); + } + } + // Validate topics if (!Array.isArray(offer.topics) || offer.topics.length === 0) { return c.json({ error: 'Each offer must have a non-empty topics array' }, 400); @@ -182,6 +192,7 @@ export function createApp(storage: Storage, config: Config) { topics: offer.topics, expiresAt: Date.now() + ttl, secret: offer.secret, + info: offer.info, }); } @@ -254,7 +265,8 @@ export function createApp(storage: Storage, config: Config) { topics: o.topics, expiresAt: o.expiresAt, lastSeen: o.lastSeen, - hasSecret: !!o.secret // Indicate if secret is required without exposing it + hasSecret: !!o.secret, // Indicate if secret is required without exposing it + info: o.info // Public info field })), total: bloomParam ? total + excludePeerIds.length : total, returned: offers.length @@ -321,7 +333,8 @@ export function createApp(storage: Storage, config: Config) { topics: o.topics, expiresAt: o.expiresAt, lastSeen: o.lastSeen, - hasSecret: !!o.secret // Indicate if secret is required without exposing it + hasSecret: !!o.secret, // Indicate if secret is required without exposing it + info: o.info // Public info field })), topics: Array.from(topicsSet) }, 200); @@ -351,6 +364,7 @@ export function createApp(storage: Storage, config: Config) { expiresAt: o.expiresAt, lastSeen: o.lastSeen, secret: o.secret, // Owner can see the secret + info: o.info, // Owner can see the info answererPeerId: o.answererPeerId, answeredAt: o.answeredAt })) diff --git a/src/storage/types.ts b/src/storage/types.ts index 76841ac..eefaf08 100644 --- a/src/storage/types.ts +++ b/src/storage/types.ts @@ -10,6 +10,7 @@ export interface Offer { expiresAt: number; lastSeen: number; secret?: string; + info?: string; answererPeerId?: string; answerSdp?: string; answeredAt?: number; @@ -46,6 +47,7 @@ export interface CreateOfferRequest { topics: string[]; expiresAt: number; secret?: string; + info?: string; } /**