Refactor: Consolidate service/offer architecture

## Breaking Changes

### Server
- Services can now have multiple offers instead of single offer
- POST /users/:username/services accepts `offers` array instead of `sdp`
- GET /users/:username/services/:fqn returns `offers` array in response
- GET /services/:uuid returns `offers` array in response
- Database schema: removed `offer_id` from services table, added `service_id` to offers table
- Added `batchCreateServices()` and `getOffersForService()` methods

### Client
- `PublishServiceOptions` interface: `offers` array instead of `sdp` string
- `Service` interface: `offers` array instead of `offerId` and `sdp`
- `ServiceRequest` interface: `offers` array instead of `sdp`
- RondevuSignaler.setOffer() sends offers array to server
- Updated to extract offerId from first offer in service response

## New Features
- Support for multiple simultaneous offers per service (connection pooling)
- Batch service creation endpoint for reduced server load
- Proper one-to-many relationship between services and offers

## Implementation Details

### Server Changes (src/storage/)
- sqlite.ts: Added service_id column to offers, removed offer_id from services
- d1.ts: Updated to match new interface
- types.ts: Updated interfaces for Service, Offer, CreateServiceRequest
- app.ts: Updated all service endpoints to handle offers array

### Client Changes (src/)
- api.ts: Added OfferRequest and ServiceOffer interfaces
- rondevu-service.ts: Updated PublishServiceOptions to use offers array
- rondevu-signaler.ts: Updated to send/receive offers array

## Migration Notes
- No backwards compatibility - this is a breaking change
- Services published with old API will not work with new server
- Clients must update to new API to work with updated server

🤖 Generated with Claude Code
This commit is contained in:
2025-12-07 21:49:23 +01:00
parent 3efed6e9d2
commit ca3db47009
7 changed files with 427 additions and 221 deletions

View File

@@ -77,15 +77,28 @@ Generates a cryptographically random 128-bit peer ID.
}
```
### Username Management
### User Management (RESTful)
#### `POST /usernames/claim`
#### `GET /users/:username`
Check username availability and claim status
**Response:**
```json
{
"username": "alice",
"available": false,
"claimedAt": 1733404800000,
"expiresAt": 1765027200000,
"publicKey": "..."
}
```
#### `POST /users/:username`
Claim a username with cryptographic proof
**Request:**
```json
{
"username": "alice",
"publicKey": "base64-encoded-ed25519-public-key",
"signature": "base64-encoded-signature",
"message": "claim:alice:1733404800000"
@@ -107,21 +120,7 @@ Claim a username with cryptographic proof
- Timestamp must be within 5 minutes (replay protection)
- Expires after 365 days, auto-renewed on use
#### `GET /usernames/:username`
Check username availability and claim status
**Response:**
```json
{
"username": "alice",
"available": false,
"claimedAt": 1733404800000,
"expiresAt": 1765027200000,
"publicKey": "..."
}
```
#### `GET /usernames/:username/services`
#### `GET /users/:username/services`
List all services for a username (privacy-preserving)
**Response:**
@@ -143,9 +142,28 @@ List all services for a username (privacy-preserving)
}
```
### Service Management
#### `GET /users/:username/services/:fqn`
Get specific service by username and FQN (single request)
#### `POST /services`
**Response:**
```json
{
"uuid": "abc123",
"serviceId": "service-id",
"username": "alice",
"serviceFqn": "chat.app@1.0.0",
"offerId": "offer-hash",
"sdp": "v=0...",
"isPublic": true,
"metadata": {},
"createdAt": 1733404800000,
"expiresAt": 1733405100000
}
```
### Service Management (RESTful)
#### `POST /users/:username/services`
Publish a service (requires authentication and username signature)
**Headers:**
@@ -154,7 +172,6 @@ Publish a service (requires authentication and username signature)
**Request:**
```json
{
"username": "alice",
"serviceFqn": "com.example.chat@1.0.0",
"sdp": "v=0...",
"ttl": 300000,
@@ -165,12 +182,18 @@ Publish a service (requires authentication and username signature)
}
```
**Response:**
**Response (Full service details):**
```json
{
"serviceId": "uuid-v4",
"uuid": "uuid-v4-for-index",
"serviceId": "uuid-v4",
"username": "alice",
"serviceFqn": "com.example.chat@1.0.0",
"offerId": "offer-hash-id",
"sdp": "v=0...",
"isPublic": false,
"metadata": { "description": "Chat service" },
"createdAt": 1733404800000,
"expiresAt": 1733405100000
}
```
@@ -203,7 +226,7 @@ Get service details by UUID
}
```
#### `DELETE /services/:serviceId`
#### `DELETE /users/:username/services/:fqn`
Unpublish a service (requires authentication and ownership)
**Headers:**
@@ -275,8 +298,8 @@ Answer an offer (locks it to answerer)
}
```
#### `GET /offers/answers`
Poll for answers to your offers
#### `GET /offers/:offerId/answer`
Get answer for a specific offer
#### `POST /offers/:offerId/ice-candidates`
Post ICE candidates for an offer