@seaverse/data-service-sdk 0.1.0 → 0.2.0
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/README.md +345 -113
- package/dist/index.cjs +140 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +238 -34
- package/dist/index.js +140 -25
- package/dist/index.js.map +1 -1
- package/package.json +10 -11
package/README.md
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
# @seaverse/data-service-sdk
|
|
2
2
|
|
|
3
|
-
SeaVerse Data Service SDK for
|
|
3
|
+
SeaVerse Data Service SDK for accessing Firestore with secure token management and three-tier permission model.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
7
|
+
- 🔐 Secure Firestore token generation for authenticated users and guests
|
|
8
|
+
- 🎯 Three-tier permission model (publicRead, publicData, userData)
|
|
9
|
+
- 🚀 Direct Firestore access - no proxy server needed
|
|
10
|
+
- 🔒 Automatic data isolation by app_id
|
|
11
|
+
- 📝 TypeScript support with full type definitions
|
|
12
|
+
- 🤖 LLM-friendly documentation with clear examples
|
|
13
|
+
|
|
14
|
+
## Three-Tier Permission Model
|
|
15
|
+
|
|
16
|
+
SeaVerse organizes your Firestore data into three permission levels:
|
|
17
|
+
|
|
18
|
+
| Permission Level | Path Pattern | Read Access | Write Access | Use Case |
|
|
19
|
+
|-----------------|--------------|-------------|--------------|----------|
|
|
20
|
+
| **publicRead** | `appData/{app_id}/publicRead/...` | All authenticated users | Admin only | System configs, announcements, static content |
|
|
21
|
+
| **publicData** | `appData/{app_id}/publicData/...` | All authenticated users | All authenticated users | User posts, comments, shared content |
|
|
22
|
+
| **userData** | `appData/{app_id}/userData/{user_id}/...` | Owner only | Owner only | User settings, private notes, personal data |
|
|
23
|
+
|
|
24
|
+
### Required Fields
|
|
25
|
+
|
|
26
|
+
All Firestore documents MUST include these three fields:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
{
|
|
30
|
+
_appId: string, // Your application ID (for data isolation)
|
|
31
|
+
_createdAt: timestamp, // Server timestamp (use serverTimestamp())
|
|
32
|
+
_createdBy: string // User ID who created the document
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
These fields are enforced by Firestore Security Rules and ensure proper data isolation.
|
|
12
37
|
|
|
13
38
|
## Installation
|
|
14
39
|
|
|
@@ -18,29 +43,101 @@ npm install @seaverse/data-service-sdk
|
|
|
18
43
|
|
|
19
44
|
## Quick Start
|
|
20
45
|
|
|
46
|
+
### For Authenticated Users
|
|
47
|
+
|
|
21
48
|
```typescript
|
|
22
49
|
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
50
|
+
import { AuthClient } from '@seaverse/auth-sdk';
|
|
51
|
+
import { initializeApp } from 'firebase/app';
|
|
52
|
+
import { getAuth, signInWithCustomToken } from 'firebase/auth';
|
|
53
|
+
import { getFirestore, collection, addDoc, getDocs, serverTimestamp } from 'firebase/firestore';
|
|
54
|
+
|
|
55
|
+
// Step 1: Login user with Auth SDK
|
|
56
|
+
const authClient = new AuthClient({ appId: 'my-app-123' });
|
|
57
|
+
const loginResponse = await authClient.loginWithEmail({
|
|
58
|
+
email: 'user@example.com',
|
|
59
|
+
password: 'password123'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Step 2: Get Firestore token
|
|
63
|
+
const dataClient = new DataServiceClient();
|
|
64
|
+
const firestoreToken = await dataClient.generateFirestoreToken({
|
|
65
|
+
token: loginResponse.token, // JWT from auth
|
|
66
|
+
app_id: 'my-app-123'
|
|
67
|
+
});
|
|
23
68
|
|
|
24
|
-
//
|
|
25
|
-
const
|
|
69
|
+
// Step 3: Initialize Firebase
|
|
70
|
+
const app = initializeApp({ projectId: firestoreToken.project_id });
|
|
71
|
+
const auth = getAuth(app);
|
|
72
|
+
await signInWithCustomToken(auth, firestoreToken.id_token);
|
|
73
|
+
const db = getFirestore(app);
|
|
74
|
+
|
|
75
|
+
// Step 4: Use Firestore with proper paths
|
|
76
|
+
const appId = firestoreToken.app_id;
|
|
77
|
+
const userId = firestoreToken.user_id;
|
|
78
|
+
|
|
79
|
+
// Write to publicData (everyone can write)
|
|
80
|
+
await addDoc(collection(db, `appData/${appId}/publicData/posts`), {
|
|
81
|
+
_appId: appId, // REQUIRED
|
|
82
|
+
_createdAt: serverTimestamp(), // REQUIRED
|
|
83
|
+
_createdBy: userId, // REQUIRED
|
|
84
|
+
title: 'My First Post',
|
|
85
|
+
content: 'Hello world!'
|
|
86
|
+
});
|
|
26
87
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
88
|
+
// Read from publicData
|
|
89
|
+
const snapshot = await getDocs(collection(db, `appData/${appId}/publicData/posts`));
|
|
90
|
+
snapshot.forEach(doc => {
|
|
91
|
+
console.log(doc.id, doc.data());
|
|
31
92
|
});
|
|
32
93
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
94
|
+
// Write to userData (private)
|
|
95
|
+
await addDoc(collection(db, `appData/${appId}/userData/${userId}/notes`), {
|
|
96
|
+
_appId: appId, // REQUIRED
|
|
97
|
+
_createdAt: serverTimestamp(), // REQUIRED
|
|
98
|
+
_createdBy: userId, // REQUIRED
|
|
99
|
+
title: 'Private Note',
|
|
100
|
+
content: 'Only I can see this'
|
|
36
101
|
});
|
|
37
102
|
```
|
|
38
103
|
|
|
104
|
+
### For Guest Users
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
108
|
+
import { initializeApp } from 'firebase/app';
|
|
109
|
+
import { getAuth, signInWithCustomToken } from 'firebase/auth';
|
|
110
|
+
import { getFirestore, collection, addDoc, serverTimestamp } from 'firebase/firestore';
|
|
111
|
+
|
|
112
|
+
// Step 1: Get guest token (no authentication needed!)
|
|
113
|
+
const dataClient = new DataServiceClient();
|
|
114
|
+
const guestToken = await dataClient.generateGuestFirestoreToken({
|
|
115
|
+
app_id: 'my-app-123'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Step 2: Initialize Firebase
|
|
119
|
+
const app = initializeApp({ projectId: guestToken.project_id });
|
|
120
|
+
const auth = getAuth(app);
|
|
121
|
+
await signInWithCustomToken(auth, guestToken.id_token);
|
|
122
|
+
const db = getFirestore(app);
|
|
123
|
+
|
|
124
|
+
// Step 3: Guest can write to publicData
|
|
125
|
+
await addDoc(collection(db, `appData/${guestToken.app_id}/publicData/comments`), {
|
|
126
|
+
_appId: guestToken.app_id,
|
|
127
|
+
_createdAt: serverTimestamp(),
|
|
128
|
+
_createdBy: guestToken.user_id, // Guest user ID (e.g., 'guest-abc123')
|
|
129
|
+
comment: 'Great app!',
|
|
130
|
+
rating: 5
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Note: Guests CANNOT access userData
|
|
134
|
+
```
|
|
135
|
+
|
|
39
136
|
## API Reference
|
|
40
137
|
|
|
41
138
|
### DataServiceClient
|
|
42
139
|
|
|
43
|
-
The main client for
|
|
140
|
+
The main client for generating Firestore tokens.
|
|
44
141
|
|
|
45
142
|
#### Constructor
|
|
46
143
|
|
|
@@ -61,8 +158,8 @@ const client = new DataServiceClient({
|
|
|
61
158
|
baseURL: 'https://auth.seaverse.ai',
|
|
62
159
|
timeout: 15000,
|
|
63
160
|
headers: {
|
|
64
|
-
'X-Custom-Header': 'value'
|
|
65
|
-
}
|
|
161
|
+
'X-Custom-Header': 'value'
|
|
162
|
+
}
|
|
66
163
|
});
|
|
67
164
|
```
|
|
68
165
|
|
|
@@ -83,7 +180,7 @@ generateFirestoreToken(
|
|
|
83
180
|
|
|
84
181
|
```typescript
|
|
85
182
|
interface GenerateFirestoreTokenRequest {
|
|
86
|
-
token: string; // User's JWT token
|
|
183
|
+
token: string; // User's JWT token from Auth SDK
|
|
87
184
|
app_id: string; // Application ID
|
|
88
185
|
}
|
|
89
186
|
```
|
|
@@ -92,32 +189,28 @@ interface GenerateFirestoreTokenRequest {
|
|
|
92
189
|
|
|
93
190
|
```typescript
|
|
94
191
|
interface FirestoreTokenResponse {
|
|
95
|
-
id_token: string; // Firebase ID Token
|
|
192
|
+
id_token: string; // Firebase ID Token for signInWithCustomToken()
|
|
96
193
|
refresh_token?: string; // Firebase Refresh Token (optional)
|
|
97
|
-
project_id: string; // Firebase Project ID
|
|
194
|
+
project_id: string; // Firebase Project ID for initializeApp()
|
|
98
195
|
database_id: string; // Firestore Database ID
|
|
99
|
-
app_id?: string; // Application ID
|
|
100
|
-
user_id: string; // User ID
|
|
101
|
-
role?: string; // User role
|
|
102
|
-
expires_in: number; //
|
|
196
|
+
app_id?: string; // Application ID (use in Firestore paths)
|
|
197
|
+
user_id: string; // User ID (use in Firestore paths)
|
|
198
|
+
role?: string; // User role ('guest', 'user', 'admin')
|
|
199
|
+
expires_in: number; // Token expiration in seconds (typically 3600)
|
|
103
200
|
}
|
|
104
201
|
```
|
|
105
202
|
|
|
106
203
|
**Example:**
|
|
107
204
|
|
|
108
205
|
```typescript
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
206
|
+
const firestoreToken = await client.generateFirestoreToken({
|
|
207
|
+
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
208
|
+
app_id: 'my-app-123'
|
|
209
|
+
});
|
|
114
210
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error('Failed to generate Firestore token:', error);
|
|
120
|
-
}
|
|
211
|
+
console.log('Token:', firestoreToken.id_token);
|
|
212
|
+
console.log('User ID:', firestoreToken.user_id);
|
|
213
|
+
console.log('App ID:', firestoreToken.app_id);
|
|
121
214
|
```
|
|
122
215
|
|
|
123
216
|
#### generateGuestFirestoreToken
|
|
@@ -141,126 +234,141 @@ interface GenerateGuestFirestoreTokenRequest {
|
|
|
141
234
|
|
|
142
235
|
**Response:**
|
|
143
236
|
|
|
144
|
-
Same as `FirestoreTokenResponse` above, with `role`
|
|
237
|
+
Same as `FirestoreTokenResponse` above, with `role` set to `'guest'`.
|
|
145
238
|
|
|
146
239
|
**Example:**
|
|
147
240
|
|
|
148
241
|
```typescript
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
242
|
+
const guestToken = await client.generateGuestFirestoreToken({
|
|
243
|
+
app_id: 'my-app-123'
|
|
244
|
+
});
|
|
153
245
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error('Failed to generate guest Firestore token:', error);
|
|
159
|
-
}
|
|
246
|
+
console.log('Guest Token:', guestToken.id_token);
|
|
247
|
+
console.log('Guest User ID:', guestToken.user_id);
|
|
248
|
+
console.log('Role:', guestToken.role); // 'guest'
|
|
160
249
|
```
|
|
161
250
|
|
|
162
|
-
##
|
|
251
|
+
## Common Use Cases
|
|
163
252
|
|
|
164
|
-
###
|
|
253
|
+
### Use Case 1: Public Forum with Comments
|
|
165
254
|
|
|
166
255
|
```typescript
|
|
167
|
-
|
|
256
|
+
// Anyone (including guests) can post comments
|
|
257
|
+
await addDoc(collection(db, `appData/${appId}/publicData/comments`), {
|
|
258
|
+
_appId: appId,
|
|
259
|
+
_createdAt: serverTimestamp(),
|
|
260
|
+
_createdBy: userId,
|
|
261
|
+
postId: 'post-123',
|
|
262
|
+
comment: 'Great post!',
|
|
263
|
+
likes: 0
|
|
264
|
+
});
|
|
168
265
|
|
|
169
|
-
|
|
266
|
+
// Anyone can read comments
|
|
267
|
+
const comments = await getDocs(
|
|
268
|
+
collection(db, `appData/${appId}/publicData/comments`)
|
|
269
|
+
);
|
|
270
|
+
```
|
|
170
271
|
|
|
171
|
-
|
|
172
|
-
const userToken = localStorage.getItem('authToken');
|
|
272
|
+
### Use Case 2: User Private Settings
|
|
173
273
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
274
|
+
```typescript
|
|
275
|
+
// Only the user can write to their own settings
|
|
276
|
+
await setDoc(doc(db, `appData/${appId}/userData/${userId}/settings/preferences`), {
|
|
277
|
+
_appId: appId,
|
|
278
|
+
_createdAt: serverTimestamp(),
|
|
279
|
+
_createdBy: userId,
|
|
280
|
+
theme: 'dark',
|
|
281
|
+
notifications: true,
|
|
282
|
+
language: 'en'
|
|
178
283
|
});
|
|
179
284
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const firebaseApp = initializeApp({
|
|
186
|
-
projectId: firestoreToken.project_id,
|
|
187
|
-
// ... other Firebase config
|
|
188
|
-
});
|
|
285
|
+
// Only the user can read their own settings
|
|
286
|
+
const settings = await getDoc(
|
|
287
|
+
doc(db, `appData/${appId}/userData/${userId}/settings/preferences`)
|
|
288
|
+
);
|
|
289
|
+
```
|
|
189
290
|
|
|
190
|
-
|
|
191
|
-
await signInWithCustomToken(auth, firestoreToken.id_token);
|
|
291
|
+
### Use Case 3: System Announcements (Admin Only)
|
|
192
292
|
|
|
193
|
-
|
|
194
|
-
//
|
|
293
|
+
```typescript
|
|
294
|
+
// Only admins can write to publicRead
|
|
295
|
+
// Regular users and guests can only read
|
|
296
|
+
const announcements = await getDocs(
|
|
297
|
+
collection(db, `appData/${appId}/publicRead/announcements`)
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
announcements.forEach(doc => {
|
|
301
|
+
console.log('Announcement:', doc.data().message);
|
|
302
|
+
});
|
|
195
303
|
```
|
|
196
304
|
|
|
197
|
-
###
|
|
305
|
+
### Use Case 4: Querying Public Data
|
|
198
306
|
|
|
199
307
|
```typescript
|
|
200
|
-
import {
|
|
308
|
+
import { query, where, orderBy, limit } from 'firebase/firestore';
|
|
201
309
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
token: response.id_token,
|
|
213
|
-
projectId: response.project_id,
|
|
214
|
-
databaseId: response.database_id,
|
|
215
|
-
userId: response.user_id,
|
|
216
|
-
expiresIn: response.expires_in,
|
|
217
|
-
};
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.error('Error getting Firestore access:', error);
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
310
|
+
// Query posts created by a specific user
|
|
311
|
+
const userPosts = query(
|
|
312
|
+
collection(db, `appData/${appId}/publicData/posts`),
|
|
313
|
+
where('_createdBy', '==', userId),
|
|
314
|
+
orderBy('_createdAt', 'desc'),
|
|
315
|
+
limit(10)
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const snapshot = await getDocs(userPosts);
|
|
223
319
|
```
|
|
224
320
|
|
|
225
|
-
|
|
321
|
+
## Permission Examples
|
|
226
322
|
|
|
227
|
-
|
|
228
|
-
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
323
|
+
### What Users CAN Do
|
|
229
324
|
|
|
230
|
-
|
|
325
|
+
✅ **Authenticated Users:**
|
|
326
|
+
- Read `publicRead` data
|
|
327
|
+
- Read and write `publicData` (all users' data)
|
|
328
|
+
- Read and write their own `userData/{userId}` only
|
|
329
|
+
- Update/delete documents where `_createdBy == userId`
|
|
231
330
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
331
|
+
✅ **Guest Users:**
|
|
332
|
+
- Read `publicRead` data
|
|
333
|
+
- Read and write `publicData`
|
|
334
|
+
- **Cannot** access any `userData`
|
|
237
335
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
336
|
+
✅ **Admin Users:**
|
|
337
|
+
- Everything regular users can do
|
|
338
|
+
- Write to `publicRead` data
|
|
241
339
|
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
```
|
|
340
|
+
### What Users CANNOT Do
|
|
245
341
|
|
|
246
|
-
|
|
342
|
+
❌ **All Users:**
|
|
343
|
+
- Access data from a different `app_id`
|
|
344
|
+
- Create documents without required fields (`_appId`, `_createdAt`, `_createdBy`)
|
|
345
|
+
- Modify documents created by other users (except in special cases)
|
|
346
|
+
- Access another user's `userData`
|
|
347
|
+
|
|
348
|
+
❌ **Guest Users:**
|
|
349
|
+
- Access any `userData` paths
|
|
247
350
|
|
|
248
|
-
|
|
351
|
+
## Error Handling
|
|
249
352
|
|
|
250
353
|
```typescript
|
|
251
354
|
try {
|
|
252
355
|
const token = await client.generateFirestoreToken({
|
|
253
356
|
token: 'invalid-token',
|
|
254
|
-
app_id: 'my-app'
|
|
357
|
+
app_id: 'my-app'
|
|
255
358
|
});
|
|
256
359
|
} catch (error) {
|
|
257
360
|
if (error instanceof Error) {
|
|
258
361
|
console.error('Error:', error.message);
|
|
259
|
-
//
|
|
362
|
+
// Handle error (e.g., invalid token, network error)
|
|
260
363
|
}
|
|
261
364
|
}
|
|
262
365
|
```
|
|
263
366
|
|
|
367
|
+
Common errors:
|
|
368
|
+
- `Invalid token` - JWT token is invalid or expired
|
|
369
|
+
- `permission-denied` - Missing required fields or insufficient permissions
|
|
370
|
+
- `Missing or insufficient permissions` - Trying to access unauthorized data
|
|
371
|
+
|
|
264
372
|
## TypeScript Support
|
|
265
373
|
|
|
266
374
|
This SDK is written in TypeScript and provides full type definitions:
|
|
@@ -273,10 +381,129 @@ import type {
|
|
|
273
381
|
GenerateGuestFirestoreTokenRequest,
|
|
274
382
|
FirestoreTokenResponse,
|
|
275
383
|
ApiResponse,
|
|
276
|
-
ApiError
|
|
384
|
+
ApiError
|
|
277
385
|
} from '@seaverse/data-service-sdk';
|
|
278
386
|
```
|
|
279
387
|
|
|
388
|
+
## Best Practices for LLM
|
|
389
|
+
|
|
390
|
+
When using this SDK with LLM-generated code:
|
|
391
|
+
|
|
392
|
+
1. **Always include required fields:**
|
|
393
|
+
```typescript
|
|
394
|
+
{
|
|
395
|
+
_appId: appId, // From token response
|
|
396
|
+
_createdAt: serverTimestamp(), // Use serverTimestamp()
|
|
397
|
+
_createdBy: userId // From token response
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
2. **Use correct data paths:**
|
|
402
|
+
- publicRead: `appData/${appId}/publicRead/{collection}/{docId}`
|
|
403
|
+
- publicData: `appData/${appId}/publicData/{collection}/{docId}`
|
|
404
|
+
- userData: `appData/${appId}/userData/${userId}/{collection}/{docId}`
|
|
405
|
+
|
|
406
|
+
3. **Handle token expiration:**
|
|
407
|
+
- Tokens expire after 1 hour (3600 seconds)
|
|
408
|
+
- Check `expires_in` field and refresh when needed
|
|
409
|
+
|
|
410
|
+
4. **Use serverTimestamp() for timestamps:**
|
|
411
|
+
```typescript
|
|
412
|
+
import { serverTimestamp } from 'firebase/firestore';
|
|
413
|
+
|
|
414
|
+
{
|
|
415
|
+
_createdAt: serverTimestamp() // Not new Date()!
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
5. **Separate guest and authenticated flows:**
|
|
420
|
+
- Use `generateGuestFirestoreToken()` for anonymous users
|
|
421
|
+
- Use `generateFirestoreToken()` for logged-in users
|
|
422
|
+
|
|
423
|
+
## Complete Example
|
|
424
|
+
|
|
425
|
+
Here's a complete example combining authentication and data access:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
429
|
+
import { AuthClient } from '@seaverse/auth-sdk';
|
|
430
|
+
import { initializeApp } from 'firebase/app';
|
|
431
|
+
import { getAuth, signInWithCustomToken } from 'firebase/auth';
|
|
432
|
+
import { getFirestore, collection, addDoc, getDocs, query, where, serverTimestamp } from 'firebase/firestore';
|
|
433
|
+
|
|
434
|
+
async function completeExample() {
|
|
435
|
+
const appId = 'my-app-123';
|
|
436
|
+
|
|
437
|
+
// 1. Authenticate user
|
|
438
|
+
const authClient = new AuthClient({ appId });
|
|
439
|
+
const loginResponse = await authClient.loginWithEmail({
|
|
440
|
+
email: 'user@example.com',
|
|
441
|
+
password: 'password123'
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// 2. Get Firestore token
|
|
445
|
+
const dataClient = new DataServiceClient();
|
|
446
|
+
const firestoreToken = await dataClient.generateFirestoreToken({
|
|
447
|
+
token: loginResponse.token,
|
|
448
|
+
app_id: appId
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// 3. Initialize Firebase
|
|
452
|
+
const app = initializeApp({ projectId: firestoreToken.project_id });
|
|
453
|
+
const auth = getAuth(app);
|
|
454
|
+
await signInWithCustomToken(auth, firestoreToken.id_token);
|
|
455
|
+
const db = getFirestore(app);
|
|
456
|
+
|
|
457
|
+
const userId = firestoreToken.user_id;
|
|
458
|
+
|
|
459
|
+
// 4. Create a post (publicData)
|
|
460
|
+
const postRef = await addDoc(
|
|
461
|
+
collection(db, `appData/${appId}/publicData/posts`),
|
|
462
|
+
{
|
|
463
|
+
_appId: appId,
|
|
464
|
+
_createdAt: serverTimestamp(),
|
|
465
|
+
_createdBy: userId,
|
|
466
|
+
title: 'My First Post',
|
|
467
|
+
content: 'Hello world!',
|
|
468
|
+
tags: ['introduction', 'first-post']
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
console.log('Created post:', postRef.id);
|
|
472
|
+
|
|
473
|
+
// 5. Read all posts
|
|
474
|
+
const postsSnapshot = await getDocs(
|
|
475
|
+
collection(db, `appData/${appId}/publicData/posts`)
|
|
476
|
+
);
|
|
477
|
+
postsSnapshot.forEach(doc => {
|
|
478
|
+
console.log('Post:', doc.id, doc.data());
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// 6. Query user's own posts
|
|
482
|
+
const myPostsQuery = query(
|
|
483
|
+
collection(db, `appData/${appId}/publicData/posts`),
|
|
484
|
+
where('_createdBy', '==', userId)
|
|
485
|
+
);
|
|
486
|
+
const myPosts = await getDocs(myPostsQuery);
|
|
487
|
+
console.log('My posts count:', myPosts.size);
|
|
488
|
+
|
|
489
|
+
// 7. Save user preferences (private)
|
|
490
|
+
await addDoc(
|
|
491
|
+
collection(db, `appData/${appId}/userData/${userId}/preferences`),
|
|
492
|
+
{
|
|
493
|
+
_appId: appId,
|
|
494
|
+
_createdAt: serverTimestamp(),
|
|
495
|
+
_createdBy: userId,
|
|
496
|
+
theme: 'dark',
|
|
497
|
+
language: 'en',
|
|
498
|
+
notifications: true
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
console.log('Saved user preferences');
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
completeExample();
|
|
505
|
+
```
|
|
506
|
+
|
|
280
507
|
## API Endpoints
|
|
281
508
|
|
|
282
509
|
The SDK connects to the following endpoints by default:
|
|
@@ -294,3 +521,8 @@ MIT
|
|
|
294
521
|
For issues and questions, please visit:
|
|
295
522
|
- GitHub: https://github.com/seaverseai/sv-sdk
|
|
296
523
|
- Email: support@seaverse.com
|
|
524
|
+
|
|
525
|
+
## Related SDKs
|
|
526
|
+
|
|
527
|
+
- [@seaverse/auth-sdk](../auth-sdk) - User authentication and account management
|
|
528
|
+
- [@seaverse/payment-sdk](../payment-sdk) - Payment processing integration
|