evaldoc 1.0.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.
Files changed (139) hide show
  1. package/.env.staging.example +19 -0
  2. package/.firebaserc +6 -0
  3. package/.github/workflows/auto-merge-to-main.yml +28 -0
  4. package/.github/workflows/ci.yml +27 -0
  5. package/.github/workflows/npxhub-publish.yml +170 -0
  6. package/.prettierignore +4 -0
  7. package/.prettierrc.json +7 -0
  8. package/README.md +142 -0
  9. package/SRS.md +448 -0
  10. package/apphosting.staging.yaml +48 -0
  11. package/apphosting.yaml +50 -0
  12. package/bin/evaldoc.cjs +105 -0
  13. package/cors.json +8 -0
  14. package/docs/ARCHITECTURE.md +769 -0
  15. package/docs/ARCHITECTURE_SUMMARY.md +323 -0
  16. package/docs/DEPLOYMENT.md +86 -0
  17. package/docs/DEPLOYMENT_STEPS.md +373 -0
  18. package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
  19. package/docs/DEPLOY_STORAGE_RULES.md +82 -0
  20. package/docs/DOCTOR_TUTORIAL.md +467 -0
  21. package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
  22. package/docs/FIXES_APPLIED.md +128 -0
  23. package/docs/IMPLEMENTATION.md +498 -0
  24. package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
  25. package/docs/LLM-SETUP.md +216 -0
  26. package/docs/MANUAL_TEST_GUIDE.md +327 -0
  27. package/docs/PATIENT_GUIDE.md +451 -0
  28. package/docs/PLATFORM_OVERVIEW.md +194 -0
  29. package/docs/QUICKSTART.md +252 -0
  30. package/docs/QUICK_REFERENCE.md +222 -0
  31. package/docs/READY_TO_TEST.md +383 -0
  32. package/docs/RUN.md +237 -0
  33. package/docs/START_TESTING.md +130 -0
  34. package/docs/STRIPE_CONNECT_SETUP.md +454 -0
  35. package/docs/SYSTEM_READY.md +345 -0
  36. package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
  37. package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
  38. package/docs/firebasefiles.md +21 -0
  39. package/docs/homepage.md +1208 -0
  40. package/docs/payments.md +159 -0
  41. package/eslint.config.js +76 -0
  42. package/firebase.json +27 -0
  43. package/firestore.indexes.json +88 -0
  44. package/firestore.rules +125 -0
  45. package/functions/package-lock.json +2884 -0
  46. package/functions/package.json +20 -0
  47. package/functions/src/analytics.js +101 -0
  48. package/functions/src/audit.js +28 -0
  49. package/functions/src/auth.js +19 -0
  50. package/functions/src/drafts.js +128 -0
  51. package/functions/src/feedback.js +164 -0
  52. package/functions/src/firebase-init.js +29 -0
  53. package/functions/src/i18n.js +134 -0
  54. package/functions/src/limits.js +27 -0
  55. package/functions/src/llm.js +271 -0
  56. package/functions/src/ner.js +162 -0
  57. package/functions/src/notifications.js +273 -0
  58. package/functions/src/orgs.js +127 -0
  59. package/functions/src/patient-api.js +545 -0
  60. package/functions/src/payment.js +830 -0
  61. package/functions/src/safety.js +188 -0
  62. package/functions/src/stripe-config.js +33 -0
  63. package/functions/src/topics.js +94 -0
  64. package/functions/src/triage.js +191 -0
  65. package/functions/src/utils.js +28 -0
  66. package/functions/src/validate.js +177 -0
  67. package/functions/test-firebase.js +36 -0
  68. package/jest.config.js +18 -0
  69. package/package.json +52 -0
  70. package/public/404.html +93 -0
  71. package/public/app/analytics.html +336 -0
  72. package/public/app/analytics.js +177 -0
  73. package/public/app/app.css +1400 -0
  74. package/public/app/app.js +1754 -0
  75. package/public/app/config.js +14 -0
  76. package/public/app/inbox-threads.js +843 -0
  77. package/public/app/inbox.html +598 -0
  78. package/public/app/inbox.js +397 -0
  79. package/public/app/index.html +612 -0
  80. package/public/assets/logo.jpg +0 -0
  81. package/public/chat.html +137 -0
  82. package/public/css/chat.css +870 -0
  83. package/public/css/landing-enhance.css +298 -0
  84. package/public/css/styles.css +1198 -0
  85. package/public/favicon.svg +5 -0
  86. package/public/find-doctor.html +220 -0
  87. package/public/index.html +399 -0
  88. package/public/js/chat-threads.js +513 -0
  89. package/public/js/chat.js +832 -0
  90. package/public/js/search.js +41 -0
  91. package/public/privacy.html +101 -0
  92. package/public/robots.txt +6 -0
  93. package/public/site.webmanifest +12 -0
  94. package/public/terms.html +110 -0
  95. package/scripts/create-test-provider.js +55 -0
  96. package/scripts/deploy-staging.sh +51 -0
  97. package/scripts/fix-provider.js +39 -0
  98. package/scripts/quick-test.sh +37 -0
  99. package/scripts/test-chat.sh +54 -0
  100. package/scripts/test-multi-turn.js +137 -0
  101. package/scripts/test-ner-simple.js +28 -0
  102. package/scripts/test-ner.js +60 -0
  103. package/scripts/test-stripe-connect.js +304 -0
  104. package/scripts/test-webhook-secret.sh +24 -0
  105. package/server.js +3864 -0
  106. package/storage.rules +22 -0
  107. package/test-api.json +1 -0
  108. package/tests/emulator/README.md +38 -0
  109. package/tests/emulator/counters.emulator.test.js +81 -0
  110. package/tests/integration/api-account-selfserve.test.js +312 -0
  111. package/tests/integration/api-admin-digest.test.js +202 -0
  112. package/tests/integration/api-chat-satisfaction.test.js +259 -0
  113. package/tests/integration/api-doctor-drafts.test.js +236 -0
  114. package/tests/integration/api-doctor-inbox.test.js +401 -0
  115. package/tests/integration/api-org.test.js +328 -0
  116. package/tests/integration/api-payment.test.js +664 -0
  117. package/tests/integration/api-provider-settings.test.js +475 -0
  118. package/tests/integration/api-qr.test.js +121 -0
  119. package/tests/integration/api-security.test.js +241 -0
  120. package/tests/integration/api-stripe-search.test.js +303 -0
  121. package/tests/integration/api-webhook-deletion.test.js +231 -0
  122. package/tests/mocks/firebase-admin.js +220 -0
  123. package/tests/unit/analytics.test.js +151 -0
  124. package/tests/unit/audit-bugs.test.js +203 -0
  125. package/tests/unit/chat-ui-logic.test.js +283 -0
  126. package/tests/unit/drafts.test.js +104 -0
  127. package/tests/unit/feedback.test.js +148 -0
  128. package/tests/unit/i18n.test.js +229 -0
  129. package/tests/unit/llm-retry.test.js +122 -0
  130. package/tests/unit/notifications.test.js +155 -0
  131. package/tests/unit/orgs.test.js +328 -0
  132. package/tests/unit/payment.test.js +889 -0
  133. package/tests/unit/safety-edge-cases.test.js +509 -0
  134. package/tests/unit/safety-srs-compliance.test.js +252 -0
  135. package/tests/unit/safety.test.js +291 -0
  136. package/tests/unit/server-helpers.test.js +774 -0
  137. package/tests/unit/topics-limits.test.js +158 -0
  138. package/tests/unit/triage.test.js +152 -0
  139. package/tests/unit/validate.test.js +408 -0
@@ -0,0 +1,345 @@
1
+ # Thread-Based Conversation System - Implementation Guide
2
+
3
+ ## Overview
4
+
5
+ This guide covers the implementation of a WhatsApp-style thread-based conversation system where patients can maintain persistent conversations with multiple doctors using Google Sign-In.
6
+
7
+ ## Architecture Summary
8
+
9
+ ```
10
+ Patient (Google Auth) ←→ Thread ←→ Doctor (Provider)
11
+ ↓ ↓ ↓
12
+ patients/ threads/ providers/
13
+ messages (subcollection)
14
+ ```
15
+
16
+ ## What's Been Created
17
+
18
+ ### 1. Frontend Changes
19
+
20
+ #### [public/chat.html](public/chat.html)
21
+ - ✅ Added Google Sign-In modal
22
+ - ✅ Added Firebase Auth scripts
23
+ - ✅ Added user info display in header
24
+
25
+ #### [public/js/chat-threads.js](public/js/chat-threads.js) (NEW)
26
+ - ✅ Google authentication flow
27
+ - ✅ Thread-based messaging
28
+ - ✅ Load conversation history
29
+ - ✅ Real-time message display
30
+ - ✅ Payment integration
31
+
32
+ ### 2. Backend Changes
33
+
34
+ #### [functions/src/patient-api.js](functions/src/patient-api.js) (NEW)
35
+ New API endpoints for patients:
36
+ - `GET /api/patient/me` - Get patient profile
37
+ - `POST /api/patient/create` - Create patient profile
38
+ - `POST /api/patient/thread` - Get or create thread with doctor
39
+ - `GET /api/patient/threads` - Get all patient threads
40
+ - `GET /api/patient/thread/:threadId/messages` - Get messages
41
+ - `POST /api/patient/message` - Send message
42
+ - `POST /api/patient/thread/:threadId/read` - Mark as read
43
+
44
+ ### 3. Security Rules
45
+
46
+ #### [firestore.rules](firestore.rules)
47
+ - ✅ Added `patients` collection rules
48
+ - ✅ Added `threads` collection rules
49
+ - ✅ Added `messages` subcollection rules
50
+
51
+ ## Implementation Steps
52
+
53
+ ### Step 1: Enable Google Sign-In in Firebase Console
54
+
55
+ 1. Go to Firebase Console → Authentication → Sign-in method
56
+ 2. Enable **Google** provider
57
+ 3. Add your authorized domains:
58
+ - `localhost` (for testing)
59
+ - Your production domain (e.g., `evaldoc.com`)
60
+
61
+ ### Step 2: Update Firebase Functions
62
+
63
+ Add the patient API to your main index.js:
64
+
65
+ ```javascript
66
+ // In functions/src/index.js
67
+ const { handlePatientAPI } = require("./patient-api");
68
+
69
+ exports.api = functions
70
+ .runWith({
71
+ secrets: ['GEMINI_API_KEY', 'DEEPSEEK_API_KEY']
72
+ })
73
+ .https.onRequest(async (req, res) => {
74
+ res.set("Access-Control-Allow-Origin", "*");
75
+ res.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
76
+ res.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
77
+
78
+ if (req.method === "OPTIONS") {
79
+ res.status(204).send("");
80
+ return;
81
+ }
82
+
83
+ // ... existing routes ...
84
+
85
+ // Patient API routes
86
+ if (req.path.startsWith("/patient/")) {
87
+ const user = await requireAuth(req, res);
88
+ if (!user) return;
89
+ await handlePatientAPI(req, res, user);
90
+ return;
91
+ }
92
+
93
+ // ... rest of routes ...
94
+ });
95
+ ```
96
+
97
+ ### Step 3: Update chat.html to use new script
98
+
99
+ Change the script tag in [chat.html](public/chat.html:120):
100
+
101
+ ```html
102
+ <!-- OLD -->
103
+ <script src="/js/chat.js?v=3"></script>
104
+
105
+ <!-- NEW -->
106
+ <script src="/js/chat-threads.js?v=1"></script>
107
+ ```
108
+
109
+ ### Step 4: Deploy Firestore Rules
110
+
111
+ ```bash
112
+ firebase deploy --only firestore:rules
113
+ ```
114
+
115
+ ### Step 5: Deploy Functions
116
+
117
+ ```bash
118
+ cd functions
119
+ npm install
120
+ cd ..
121
+ firebase deploy --only functions
122
+ ```
123
+
124
+ ### Step 6: Test the Flow
125
+
126
+ 1. Go to `localhost:3000/dr-john-cardiology` (or your doctor's handle)
127
+ 2. Try to send a message
128
+ 3. Should see "Sign in to continue" modal
129
+ 4. Click "Continue with Google"
130
+ 5. Sign in with Google account
131
+ 6. Message should send and AI should respond
132
+ 7. Refresh page - conversation should persist
133
+
134
+ ## Data Models
135
+
136
+ ### Patient Document
137
+ ```javascript
138
+ // firestore: patients/{patientId}
139
+ {
140
+ email: "john@gmail.com",
141
+ displayName: "John Doe",
142
+ photoURL: "https://...",
143
+ doctorIds: ["dr_john", "dr_jane"],
144
+ notificationsEnabled: true,
145
+ fcmToken: null,
146
+ createdAt: Timestamp,
147
+ lastActiveAt: Timestamp
148
+ }
149
+ ```
150
+
151
+ ### Thread Document
152
+ ```javascript
153
+ // firestore: threads/{thread_patientId_doctorId}
154
+ {
155
+ // Patient info
156
+ patientId: "google_uid_xyz",
157
+ patientEmail: "john@gmail.com",
158
+ patientName: "John Doe",
159
+ patientPhoto: "https://...",
160
+
161
+ // Doctor info
162
+ doctorId: "dr_john",
163
+ doctorName: "Dr. John Smith",
164
+ doctorHandle: "dr-john-cardiology",
165
+
166
+ // Status
167
+ status: "active",
168
+ isEscalated: false,
169
+ doctorOverride: false,
170
+
171
+ // Last message
172
+ lastMessageAt: Timestamp,
173
+ lastMessageText: "Thank you...",
174
+ lastMessageRole: "patient",
175
+
176
+ // Counts
177
+ unreadByPatient: 0,
178
+ unreadByDoctor: 2,
179
+ messageCount: 15,
180
+
181
+ // Current
182
+ currentTopic: "medication_info_safety",
183
+
184
+ // Billing
185
+ isPaid: true,
186
+ paymentId: "pi_xxx",
187
+
188
+ createdAt: Timestamp,
189
+ updatedAt: Timestamp
190
+ }
191
+ ```
192
+
193
+ ### Message Document
194
+ ```javascript
195
+ // firestore: threads/{threadId}/messages/{messageId}
196
+ {
197
+ threadId: "thread_googleuid_drjohn",
198
+ role: "patient", // patient, doctor, ai
199
+ message: "Hello doctor...",
200
+
201
+ // Doctor-specific
202
+ isDoctorManual: false,
203
+ doctorUserId: null,
204
+
205
+ // Read status
206
+ readByPatient: true,
207
+ readByDoctor: false,
208
+
209
+ // Safety
210
+ wasFiltered: false,
211
+ isAiGenerated: true,
212
+
213
+ createdAt: Timestamp
214
+ }
215
+ ```
216
+
217
+ ## Next Steps (TODO)
218
+
219
+ ### Phase 2: Doctor Inbox Updates
220
+
221
+ Update [public/app/inbox.html](public/app/inbox.html) and [inbox.js](public/app/inbox.js) to:
222
+ 1. Show threads instead of conversations
223
+ 2. Group by patient (not sessionId)
224
+ 3. Show patient name/email instead of sessionId
225
+ 4. Add "Reply" button for manual doctor responses
226
+
227
+ ### Phase 3: Doctor Manual Reply
228
+
229
+ Add to inbox:
230
+ ```javascript
231
+ // Enable doctor to reply manually
232
+ POST /api/doctor/message
233
+ Body: {
234
+ threadId: "thread_...",
235
+ message: "This is my response",
236
+ override: true // Disable AI for this thread
237
+ }
238
+ ```
239
+
240
+ ### Phase 4: Web Push Notifications
241
+
242
+ 1. Add FCM (Firebase Cloud Messaging) to frontend
243
+ 2. Request notification permission
244
+ 3. Save FCM token to patient profile
245
+ 4. Send notification when new message arrives
246
+
247
+ Example:
248
+ ```javascript
249
+ // In patient-api.js, after AI responds
250
+ const patient = await getPatient(thread.patientId);
251
+ if (patient.fcmToken && patient.notificationsEnabled) {
252
+ await admin.messaging().send({
253
+ token: patient.fcmToken,
254
+ notification: {
255
+ title: `New message from ${thread.doctorName}`,
256
+ body: aiResponse.substring(0, 100)
257
+ }
258
+ });
259
+ }
260
+ ```
261
+
262
+ ## Migration Strategy
263
+
264
+ ### Option A: Fresh Start (Recommended)
265
+ - Keep existing `conversations` collection as-is
266
+ - New chats use `threads` system
267
+ - Old conversations remain in old dashboard
268
+ - Clean separation
269
+
270
+ ### Option B: Gradual Migration
271
+ - Add migration script to convert conversations → threads
272
+ - Link by email/phone if available
273
+ - Otherwise create anonymous patient profiles
274
+
275
+ ## Testing Checklist
276
+
277
+ - [ ] Patient can sign in with Google
278
+ - [ ] Patient profile created on first sign-in
279
+ - [ ] Thread created when sending first message
280
+ - [ ] Messages persist after refresh
281
+ - [ ] AI responds correctly
282
+ - [ ] Escalation works
283
+ - [ ] Payment flow works (if enabled)
284
+ - [ ] Doctor can see threads in inbox
285
+ - [ ] Firestore rules prevent unauthorized access
286
+
287
+ ## Troubleshooting
288
+
289
+ ### "Unauthorized" error
290
+ - Check Firebase Auth is enabled
291
+ - Verify `Authorization: Bearer <token>` header is sent
292
+ - Check Firestore rules are deployed
293
+
294
+ ### Messages not persisting
295
+ - Check Firebase console → Firestore
296
+ - Verify `threads` and `messages` collections exist
297
+ - Check browser console for errors
298
+
299
+ ### Google Sign-In not working
300
+ - Verify Google provider is enabled in Firebase Console
301
+ - Check authorized domains include your testing domain
302
+ - Clear browser cache/cookies
303
+
304
+ ## Performance Considerations
305
+
306
+ 1. **Message pagination**: Currently loads last 50 messages. Add pagination for longer threads.
307
+ 2. **Real-time updates**: Use Firestore `onSnapshot()` for live updates instead of polling.
308
+ 3. **Indexes**: Create composite indexes for queries:
309
+ ```
310
+ threads: (patientId, lastMessageAt DESC)
311
+ threads: (doctorId, lastMessageAt DESC)
312
+ ```
313
+
314
+ ## Security Notes
315
+
316
+ 1. All writes go through backend API (Firestore rules block direct writes)
317
+ 2. Patients can only read their own threads
318
+ 3. Doctors can only read threads where they are the doctor
319
+ 4. Use custom claims for doctor authentication
320
+ 5. All messages are authenticated via Firebase Auth
321
+
322
+ ## Cost Estimation
323
+
324
+ **Firestore Operations per Chat:**
325
+ - Patient sends message: 3 writes (message, thread update, patient update)
326
+ - AI responds: 2 writes (message, thread update)
327
+ - Load conversation: 1 read (thread) + N reads (messages)
328
+
329
+ **Monthly costs (1000 active users, 10 messages each):**
330
+ - Writes: 50,000 writes = $0.58
331
+ - Reads: 100,000 reads = $0.18
332
+ - Total: ~$0.76/month
333
+
334
+ ## Support
335
+
336
+ For questions or issues:
337
+ 1. Check Firebase Console logs
338
+ 2. Check browser developer console
339
+ 3. Review Firestore rules
340
+ 4. Test API endpoints with Postman/curl
341
+
342
+ ---
343
+
344
+ **Status**: Core implementation complete ✅
345
+ **Next**: Update doctor inbox + manual replies + push notifications
@@ -0,0 +1,21 @@
1
+ // Import the functions you need from the SDKs you need
2
+ import { initializeApp } from "firebase/app";
3
+ import { getAnalytics } from "firebase/analytics";
4
+ // TODO: Add SDKs for Firebase products that you want to use
5
+ // https://firebase.google.com/docs/web/setup#available-libraries
6
+
7
+ // Your web app's Firebase configuration
8
+ // For Firebase JS SDK v7.20.0 and later, measurementId is optional
9
+ const firebaseConfig = {
10
+ apiKey: "AIzaSyCLkOkhDZhB2m9bO-VxNmvI4Y179DhjmcQ",
11
+ authDomain: "evaldocplatform.firebaseapp.com",
12
+ projectId: "evaldocplatform",
13
+ storageBucket: "evaldocplatform.firebasestorage.app",
14
+ messagingSenderId: "1028185105722",
15
+ appId: "1:1028185105722:web:4a306dc90dd8af8b2e9c03",
16
+ measurementId: "G-NPWYSPFSVG"
17
+ };
18
+
19
+ // Initialize Firebase
20
+ const app = initializeApp(firebaseConfig);
21
+ const analytics = getAnalytics(app);