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.
- package/.env.staging.example +19 -0
- package/.firebaserc +6 -0
- package/.github/workflows/auto-merge-to-main.yml +28 -0
- package/.github/workflows/ci.yml +27 -0
- package/.github/workflows/npxhub-publish.yml +170 -0
- package/.prettierignore +4 -0
- package/.prettierrc.json +7 -0
- package/README.md +142 -0
- package/SRS.md +448 -0
- package/apphosting.staging.yaml +48 -0
- package/apphosting.yaml +50 -0
- package/bin/evaldoc.cjs +105 -0
- package/cors.json +8 -0
- package/docs/ARCHITECTURE.md +769 -0
- package/docs/ARCHITECTURE_SUMMARY.md +323 -0
- package/docs/DEPLOYMENT.md +86 -0
- package/docs/DEPLOYMENT_STEPS.md +373 -0
- package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
- package/docs/DEPLOY_STORAGE_RULES.md +82 -0
- package/docs/DOCTOR_TUTORIAL.md +467 -0
- package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
- package/docs/FIXES_APPLIED.md +128 -0
- package/docs/IMPLEMENTATION.md +498 -0
- package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
- package/docs/LLM-SETUP.md +216 -0
- package/docs/MANUAL_TEST_GUIDE.md +327 -0
- package/docs/PATIENT_GUIDE.md +451 -0
- package/docs/PLATFORM_OVERVIEW.md +194 -0
- package/docs/QUICKSTART.md +252 -0
- package/docs/QUICK_REFERENCE.md +222 -0
- package/docs/READY_TO_TEST.md +383 -0
- package/docs/RUN.md +237 -0
- package/docs/START_TESTING.md +130 -0
- package/docs/STRIPE_CONNECT_SETUP.md +454 -0
- package/docs/SYSTEM_READY.md +345 -0
- package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
- package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
- package/docs/firebasefiles.md +21 -0
- package/docs/homepage.md +1208 -0
- package/docs/payments.md +159 -0
- package/eslint.config.js +76 -0
- package/firebase.json +27 -0
- package/firestore.indexes.json +88 -0
- package/firestore.rules +125 -0
- package/functions/package-lock.json +2884 -0
- package/functions/package.json +20 -0
- package/functions/src/analytics.js +101 -0
- package/functions/src/audit.js +28 -0
- package/functions/src/auth.js +19 -0
- package/functions/src/drafts.js +128 -0
- package/functions/src/feedback.js +164 -0
- package/functions/src/firebase-init.js +29 -0
- package/functions/src/i18n.js +134 -0
- package/functions/src/limits.js +27 -0
- package/functions/src/llm.js +271 -0
- package/functions/src/ner.js +162 -0
- package/functions/src/notifications.js +273 -0
- package/functions/src/orgs.js +127 -0
- package/functions/src/patient-api.js +545 -0
- package/functions/src/payment.js +830 -0
- package/functions/src/safety.js +188 -0
- package/functions/src/stripe-config.js +33 -0
- package/functions/src/topics.js +94 -0
- package/functions/src/triage.js +191 -0
- package/functions/src/utils.js +28 -0
- package/functions/src/validate.js +177 -0
- package/functions/test-firebase.js +36 -0
- package/jest.config.js +18 -0
- package/package.json +52 -0
- package/public/404.html +93 -0
- package/public/app/analytics.html +336 -0
- package/public/app/analytics.js +177 -0
- package/public/app/app.css +1400 -0
- package/public/app/app.js +1754 -0
- package/public/app/config.js +14 -0
- package/public/app/inbox-threads.js +843 -0
- package/public/app/inbox.html +598 -0
- package/public/app/inbox.js +397 -0
- package/public/app/index.html +612 -0
- package/public/assets/logo.jpg +0 -0
- package/public/chat.html +137 -0
- package/public/css/chat.css +870 -0
- package/public/css/landing-enhance.css +298 -0
- package/public/css/styles.css +1198 -0
- package/public/favicon.svg +5 -0
- package/public/find-doctor.html +220 -0
- package/public/index.html +399 -0
- package/public/js/chat-threads.js +513 -0
- package/public/js/chat.js +832 -0
- package/public/js/search.js +41 -0
- package/public/privacy.html +101 -0
- package/public/robots.txt +6 -0
- package/public/site.webmanifest +12 -0
- package/public/terms.html +110 -0
- package/scripts/create-test-provider.js +55 -0
- package/scripts/deploy-staging.sh +51 -0
- package/scripts/fix-provider.js +39 -0
- package/scripts/quick-test.sh +37 -0
- package/scripts/test-chat.sh +54 -0
- package/scripts/test-multi-turn.js +137 -0
- package/scripts/test-ner-simple.js +28 -0
- package/scripts/test-ner.js +60 -0
- package/scripts/test-stripe-connect.js +304 -0
- package/scripts/test-webhook-secret.sh +24 -0
- package/server.js +3864 -0
- package/storage.rules +22 -0
- package/test-api.json +1 -0
- package/tests/emulator/README.md +38 -0
- package/tests/emulator/counters.emulator.test.js +81 -0
- package/tests/integration/api-account-selfserve.test.js +312 -0
- package/tests/integration/api-admin-digest.test.js +202 -0
- package/tests/integration/api-chat-satisfaction.test.js +259 -0
- package/tests/integration/api-doctor-drafts.test.js +236 -0
- package/tests/integration/api-doctor-inbox.test.js +401 -0
- package/tests/integration/api-org.test.js +328 -0
- package/tests/integration/api-payment.test.js +664 -0
- package/tests/integration/api-provider-settings.test.js +475 -0
- package/tests/integration/api-qr.test.js +121 -0
- package/tests/integration/api-security.test.js +241 -0
- package/tests/integration/api-stripe-search.test.js +303 -0
- package/tests/integration/api-webhook-deletion.test.js +231 -0
- package/tests/mocks/firebase-admin.js +220 -0
- package/tests/unit/analytics.test.js +151 -0
- package/tests/unit/audit-bugs.test.js +203 -0
- package/tests/unit/chat-ui-logic.test.js +283 -0
- package/tests/unit/drafts.test.js +104 -0
- package/tests/unit/feedback.test.js +148 -0
- package/tests/unit/i18n.test.js +229 -0
- package/tests/unit/llm-retry.test.js +122 -0
- package/tests/unit/notifications.test.js +155 -0
- package/tests/unit/orgs.test.js +328 -0
- package/tests/unit/payment.test.js +889 -0
- package/tests/unit/safety-edge-cases.test.js +509 -0
- package/tests/unit/safety-srs-compliance.test.js +252 -0
- package/tests/unit/safety.test.js +291 -0
- package/tests/unit/server-helpers.test.js +774 -0
- package/tests/unit/topics-limits.test.js +158 -0
- package/tests/unit/triage.test.js +152 -0
- package/tests/unit/validate.test.js +408 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Provider Search Functionality
|
|
2
|
+
// Search is temporarily disabled before launch and routes users to early access.
|
|
3
|
+
|
|
4
|
+
const EARLY_ACCESS_PATH = '/#waitlist';
|
|
5
|
+
|
|
6
|
+
function redirectToEarlyAccess() {
|
|
7
|
+
window.location.href = EARLY_ACCESS_PATH;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Initialize disabled search UI when DOM loads
|
|
11
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
12
|
+
const searchInput = document.getElementById('providerSearch');
|
|
13
|
+
const resultsContainer = document.getElementById('quickResults');
|
|
14
|
+
|
|
15
|
+
if (searchInput) {
|
|
16
|
+
searchInput.disabled = true;
|
|
17
|
+
searchInput.placeholder = 'Provider search opens at launch';
|
|
18
|
+
searchInput.addEventListener('focus', redirectToEarlyAccess);
|
|
19
|
+
searchInput.addEventListener('keydown', (e) => {
|
|
20
|
+
if (e.key === 'Enter') {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
redirectToEarlyAccess();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (resultsContainer) {
|
|
28
|
+
resultsContainer.style.display = 'block';
|
|
29
|
+
resultsContainer.innerHTML = '<div class="search-loading">Provider search is coming soon. Request early access to join launch.</div>';
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Kept for existing inline onclick handlers
|
|
34
|
+
function searchProvider() {
|
|
35
|
+
redirectToEarlyAccess();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Kept for existing click handlers that may still call this
|
|
39
|
+
function selectProvider() {
|
|
40
|
+
redirectToEarlyAccess();
|
|
41
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Privacy Policy - EvalDoc</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<link rel="stylesheet" href="/css/styles.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<nav class="navbar">
|
|
14
|
+
<div class="nav-container">
|
|
15
|
+
<a href="/" class="logo">
|
|
16
|
+
<img src="/assets/logo.jpg" alt="Logo" class="logo-img-nav" />
|
|
17
|
+
<span>EvalDoc</span>
|
|
18
|
+
</a>
|
|
19
|
+
<div class="nav-links">
|
|
20
|
+
<a href="/">Home</a>
|
|
21
|
+
<a href="/app" class="nav-cta">Doctor Sign Up</a>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</nav>
|
|
25
|
+
|
|
26
|
+
<section class="section" style="max-width: 800px; margin: 100px auto; padding: 0 20px;">
|
|
27
|
+
<h1>Privacy Policy</h1>
|
|
28
|
+
<p><strong>Effective Date:</strong> June 26, 2026</p>
|
|
29
|
+
<p><strong>Last Updated:</strong> June 26, 2026</p>
|
|
30
|
+
|
|
31
|
+
<h2>1. Introduction</h2>
|
|
32
|
+
<p>EvalDoc, a product of Feyti Medical Group, Inc. ("we", "us", "our"), is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you use our platform.</p>
|
|
33
|
+
|
|
34
|
+
<h2>2. Information We Collect</h2>
|
|
35
|
+
<h3>For Providers (Doctors/Clinics)</h3>
|
|
36
|
+
<ul>
|
|
37
|
+
<li>Account information: name, email, specialty, country, license number</li>
|
|
38
|
+
<li>Branding assets: logo, colors, welcome message</li>
|
|
39
|
+
<li>Billing information (processed securely via Stripe)</li>
|
|
40
|
+
</ul>
|
|
41
|
+
<h3>For Patients</h3>
|
|
42
|
+
<ul>
|
|
43
|
+
<li>Chat messages (anonymized, no login required)</li>
|
|
44
|
+
<li>Session identifiers (browser-generated, not personally identifiable)</li>
|
|
45
|
+
<li>Optional satisfaction ratings</li>
|
|
46
|
+
</ul>
|
|
47
|
+
<h3>Automatically Collected</h3>
|
|
48
|
+
<ul>
|
|
49
|
+
<li>Device type, browser type, IP address (for security and analytics)</li>
|
|
50
|
+
<li>Usage patterns (pages visited, features used)</li>
|
|
51
|
+
</ul>
|
|
52
|
+
|
|
53
|
+
<h2>3. How We Use Information</h2>
|
|
54
|
+
<ul>
|
|
55
|
+
<li>To provide and maintain the EvalDoc service</li>
|
|
56
|
+
<li>To process payments and manage subscriptions</li>
|
|
57
|
+
<li>To detect and prevent safety risks (PII redaction, emergency escalation)</li>
|
|
58
|
+
<li>To generate anonymized analytics for providers</li>
|
|
59
|
+
<li>To improve our platform and safety systems</li>
|
|
60
|
+
</ul>
|
|
61
|
+
|
|
62
|
+
<h2>4. PII Protection</h2>
|
|
63
|
+
<p>EvalDoc automatically detects and redacts personally identifiable information (PII) in chat messages, including Social Security numbers, email addresses, phone numbers, and physical addresses. Redacted data is never stored or transmitted.</p>
|
|
64
|
+
|
|
65
|
+
<h2>5. Data Sharing</h2>
|
|
66
|
+
<p>We do not sell, rent, or trade your personal information. We may share data with:</p>
|
|
67
|
+
<ul>
|
|
68
|
+
<li>Payment processors (Stripe) to process transactions</li>
|
|
69
|
+
<li>AI service providers to generate safe, educational responses</li>
|
|
70
|
+
<li>Law enforcement when required by law</li>
|
|
71
|
+
</ul>
|
|
72
|
+
|
|
73
|
+
<h2>6. Data Retention</h2>
|
|
74
|
+
<p>Chat conversations are retained for provider review and audit purposes. Providers may request data deletion by contacting support. Patient session data is ephemeral and tied to browser storage.</p>
|
|
75
|
+
|
|
76
|
+
<h2>7. Security</h2>
|
|
77
|
+
<p>We implement industry-standard security measures including encrypted data transmission (TLS), secure authentication (Firebase Auth), and access controls. All safety events are logged for compliance auditing.</p>
|
|
78
|
+
|
|
79
|
+
<h2>8. International Data Transfers</h2>
|
|
80
|
+
<p>EvalDoc operates globally. Your data may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for cross-border transfers.</p>
|
|
81
|
+
|
|
82
|
+
<h2>9. Your Rights</h2>
|
|
83
|
+
<p>Depending on your jurisdiction, you may have the right to access, correct, delete, or port your data. Contact us at <a href="mailto:helpme@evaldoctor.ai">helpme@evaldoctor.ai</a> to exercise these rights.</p>
|
|
84
|
+
|
|
85
|
+
<h2>10. Changes to This Policy</h2>
|
|
86
|
+
<p>We may update this Privacy Policy from time to time. We will notify registered providers of material changes via email.</p>
|
|
87
|
+
|
|
88
|
+
<h2>11. Contact Us</h2>
|
|
89
|
+
<p>Feyti Medical Group, Inc.<br>
|
|
90
|
+
1111B S Governors Ave STE 21329<br>
|
|
91
|
+
Dover, DE 19904 US<br>
|
|
92
|
+
<a href="mailto:helpme@evaldoctor.ai">helpme@evaldoctor.ai</a></p>
|
|
93
|
+
</section>
|
|
94
|
+
|
|
95
|
+
<footer class="footer">
|
|
96
|
+
<div class="footer-bottom">
|
|
97
|
+
<p>© 2026 Feyti Medical Group, Inc. All rights reserved.</p>
|
|
98
|
+
</div>
|
|
99
|
+
</footer>
|
|
100
|
+
</body>
|
|
101
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "EvalDoc",
|
|
3
|
+
"short_name": "EvalDoc",
|
|
4
|
+
"description": "Safe, topic-based health chat with your provider.",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#0e1a14",
|
|
8
|
+
"theme_color": "#0e1a14",
|
|
9
|
+
"icons": [
|
|
10
|
+
{ "src": "/favicon.svg", "type": "image/svg+xml", "sizes": "any", "purpose": "any" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Terms of Service - EvalDoc</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<link rel="stylesheet" href="/css/styles.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<nav class="navbar">
|
|
14
|
+
<div class="nav-container">
|
|
15
|
+
<a href="/" class="logo">
|
|
16
|
+
<img src="/assets/logo.jpg" alt="Logo" class="logo-img-nav" />
|
|
17
|
+
<span>EvalDoc</span>
|
|
18
|
+
</a>
|
|
19
|
+
<div class="nav-links">
|
|
20
|
+
<a href="/">Home</a>
|
|
21
|
+
<a href="/app" class="nav-cta">Doctor Sign Up</a>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</nav>
|
|
25
|
+
|
|
26
|
+
<section class="section" style="max-width: 800px; margin: 100px auto; padding: 0 20px;">
|
|
27
|
+
<h1>Terms of Service</h1>
|
|
28
|
+
<p><strong>Effective Date:</strong> June 26, 2026</p>
|
|
29
|
+
<p><strong>Last Updated:</strong> June 26, 2026</p>
|
|
30
|
+
|
|
31
|
+
<h2>1. Acceptance of Terms</h2>
|
|
32
|
+
<p>By accessing or using EvalDoc ("the Platform"), you agree to be bound by these Terms of Service. EvalDoc is a product of Feyti Medical Group, Inc. ("Company"). If you do not agree to these terms, do not use the Platform.</p>
|
|
33
|
+
|
|
34
|
+
<h2>2. Service Description</h2>
|
|
35
|
+
<p>EvalDoc is a healthcare communication platform that provides:</p>
|
|
36
|
+
<ul>
|
|
37
|
+
<li>AI-powered health information chat for patients (educational only)</li>
|
|
38
|
+
<li>Provider dashboard for managing patient interactions</li>
|
|
39
|
+
<li>Safety pipeline to prevent clinical advice from AI responses</li>
|
|
40
|
+
</ul>
|
|
41
|
+
|
|
42
|
+
<h2>3. Medical Disclaimer</h2>
|
|
43
|
+
<p><strong>EvalDoc does not provide medical advice, diagnosis, or treatment.</strong> All AI-generated responses are educational and informational only. EvalDoc is not a substitute for professional medical advice, diagnosis, or treatment. Always seek the advice of your physician or other qualified health provider with any questions you may have regarding a medical condition.</p>
|
|
44
|
+
|
|
45
|
+
<h2>4. Provider Responsibilities</h2>
|
|
46
|
+
<p>Providers using EvalDoc agree to:</p>
|
|
47
|
+
<ul>
|
|
48
|
+
<li>Provide accurate professional credentials during registration</li>
|
|
49
|
+
<li>Complete the verification process before publishing their page</li>
|
|
50
|
+
<li>Review escalated conversations in a timely manner</li>
|
|
51
|
+
<li>Not use the platform to provide diagnoses, prescriptions, or treatment plans through the AI system</li>
|
|
52
|
+
<li>Comply with all applicable medical regulations in their jurisdiction</li>
|
|
53
|
+
</ul>
|
|
54
|
+
|
|
55
|
+
<h2>5. Patient/Visitor Terms</h2>
|
|
56
|
+
<p>Patients and visitors agree to:</p>
|
|
57
|
+
<ul>
|
|
58
|
+
<li>Use the platform for informational purposes only</li>
|
|
59
|
+
<li>Seek emergency services immediately for urgent medical situations</li>
|
|
60
|
+
<li>Not submit personal health information that is not relevant to the conversation</li>
|
|
61
|
+
<li>Understand that AI responses are not medical advice</li>
|
|
62
|
+
</ul>
|
|
63
|
+
|
|
64
|
+
<h2>6. Payments and Fees</h2>
|
|
65
|
+
<ul>
|
|
66
|
+
<li>Providers set their own pricing (per-chat, subscription, or free)</li>
|
|
67
|
+
<li>The Platform charges a 25% service fee on patient payments</li>
|
|
68
|
+
<li>All payments are processed securely through Stripe</li>
|
|
69
|
+
<li>Refund requests should be directed to the provider or to <a href="mailto:helpme@evaldoctor.ai">helpme@evaldoctor.ai</a></li>
|
|
70
|
+
</ul>
|
|
71
|
+
|
|
72
|
+
<h2>7. Prohibited Use</h2>
|
|
73
|
+
<p>You may not:</p>
|
|
74
|
+
<ul>
|
|
75
|
+
<li>Use the platform to provide or seek emergency medical care</li>
|
|
76
|
+
<li>Attempt to manipulate AI responses to generate clinical advice</li>
|
|
77
|
+
<li>Impersonate a healthcare provider</li>
|
|
78
|
+
<li>Use the platform for illegal activities</li>
|
|
79
|
+
<li>Reverse-engineer, scrape, or interfere with the platform's operation</li>
|
|
80
|
+
</ul>
|
|
81
|
+
|
|
82
|
+
<h2>8. Intellectual Property</h2>
|
|
83
|
+
<p>All platform content, design, and technology are owned by Feyti Medical Group, Inc. Providers retain ownership of their branding and uploaded content.</p>
|
|
84
|
+
|
|
85
|
+
<h2>9. Account Suspension</h2>
|
|
86
|
+
<p>We reserve the right to suspend or terminate accounts that violate these terms, fail verification, or pose a risk to patient safety.</p>
|
|
87
|
+
|
|
88
|
+
<h2>10. Limitation of Liability</h2>
|
|
89
|
+
<p>To the maximum extent permitted by law, Feyti Medical Group, Inc. shall not be liable for any indirect, incidental, special, or consequential damages arising from your use of the Platform. The Platform is provided "as is" without warranties of any kind.</p>
|
|
90
|
+
|
|
91
|
+
<h2>11. Governing Law</h2>
|
|
92
|
+
<p>These Terms are governed by the laws of the State of Delaware, United States, without regard to conflict of law principles.</p>
|
|
93
|
+
|
|
94
|
+
<h2>12. Changes to Terms</h2>
|
|
95
|
+
<p>We may modify these Terms at any time. Continued use after changes constitutes acceptance. Material changes will be communicated to registered providers via email.</p>
|
|
96
|
+
|
|
97
|
+
<h2>13. Contact Us</h2>
|
|
98
|
+
<p>Feyti Medical Group, Inc.<br>
|
|
99
|
+
1111B S Governors Ave STE 21329<br>
|
|
100
|
+
Dover, DE 19904 US<br>
|
|
101
|
+
<a href="mailto:helpme@evaldoctor.ai">helpme@evaldoctor.ai</a></p>
|
|
102
|
+
</section>
|
|
103
|
+
|
|
104
|
+
<footer class="footer">
|
|
105
|
+
<div class="footer-bottom">
|
|
106
|
+
<p>© 2026 Feyti Medical Group, Inc. All rights reserved.</p>
|
|
107
|
+
</div>
|
|
108
|
+
</footer>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Create a test provider in Firebase for testing
|
|
2
|
+
require('dotenv').config();
|
|
3
|
+
const admin = require('firebase-admin');
|
|
4
|
+
|
|
5
|
+
// Initialize Firebase Admin
|
|
6
|
+
const serviceAccount = require(process.env.GOOGLE_APPLICATION_CREDENTIALS);
|
|
7
|
+
admin.initializeApp({
|
|
8
|
+
credential: admin.credential.cert(serviceAccount)
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const db = admin.firestore();
|
|
12
|
+
|
|
13
|
+
async function createTestProvider() {
|
|
14
|
+
console.log('🔧 Creating test provider...\n');
|
|
15
|
+
|
|
16
|
+
const testProvider = {
|
|
17
|
+
handle: 'test-clinic',
|
|
18
|
+
displayName: 'Test Medical Clinic',
|
|
19
|
+
specialty: 'General Practice',
|
|
20
|
+
enabledTopics: [
|
|
21
|
+
'medication_info_safety',
|
|
22
|
+
'appointments_preparation',
|
|
23
|
+
'general_health_info',
|
|
24
|
+
'clinic_services_hours',
|
|
25
|
+
'insurance_billing_faq',
|
|
26
|
+
'post_procedure_guidance'
|
|
27
|
+
],
|
|
28
|
+
active: true,
|
|
29
|
+
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
30
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp()
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const docRef = await db.collection('providers').add(testProvider);
|
|
35
|
+
console.log('✅ Test provider created successfully!');
|
|
36
|
+
console.log(` Provider ID: ${docRef.id}`);
|
|
37
|
+
console.log(` Handle: ${testProvider.handle}`);
|
|
38
|
+
console.log(` Display Name: ${testProvider.displayName}`);
|
|
39
|
+
console.log(` Specialty: ${testProvider.specialty}\n`);
|
|
40
|
+
|
|
41
|
+
console.log('🧪 You can now test with:');
|
|
42
|
+
console.log(` Handle: "${testProvider.handle}"`);
|
|
43
|
+
console.log(` Example curl command:\n`);
|
|
44
|
+
console.log(` curl -X POST http://localhost:8080/api/chat \\`);
|
|
45
|
+
console.log(` -H "Content-Type: application/json" \\`);
|
|
46
|
+
console.log(` -d '{"handle":"${testProvider.handle}","sessionId":"test-001","message":"What are the symptoms of flu?"}'\n`);
|
|
47
|
+
|
|
48
|
+
process.exit(0);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('❌ Error creating provider:', error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
createTestProvider();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Deploy EvalDoc to staging environment
|
|
5
|
+
#
|
|
6
|
+
# Prerequisites:
|
|
7
|
+
# 1. Create a staging Firebase project: evaldocplatform-staging
|
|
8
|
+
# 2. Set up staging secrets:
|
|
9
|
+
# firebase apphosting:secrets:set GEMINI_API_KEY --project evaldocplatform-staging
|
|
10
|
+
# firebase apphosting:secrets:set DEEPSEEK_API_KEY --project evaldocplatform-staging
|
|
11
|
+
# firebase apphosting:secrets:set STRIPE_PUBLISHABLE_KEY_STAGING --project evaldocplatform-staging
|
|
12
|
+
# firebase apphosting:secrets:set STRIPE_SECRET_KEY_STAGING --project evaldocplatform-staging
|
|
13
|
+
# firebase apphosting:secrets:set STRIPE_WEBHOOK_SECRET_STAGING --project evaldocplatform-staging
|
|
14
|
+
# 3. Enable Firestore, Auth (Google), and Storage in staging project
|
|
15
|
+
|
|
16
|
+
echo "=== EvalDoc Staging Deployment ==="
|
|
17
|
+
echo ""
|
|
18
|
+
|
|
19
|
+
# Run tests first
|
|
20
|
+
echo "[1/4] Running tests..."
|
|
21
|
+
npm test
|
|
22
|
+
echo ""
|
|
23
|
+
|
|
24
|
+
# Switch to staging project
|
|
25
|
+
echo "[2/4] Switching to staging project..."
|
|
26
|
+
firebase use staging
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# Deploy Firestore and Storage rules
|
|
30
|
+
echo "[3/4] Deploying security rules..."
|
|
31
|
+
firebase deploy --only firestore:rules,storage --project evaldocplatform-staging
|
|
32
|
+
echo ""
|
|
33
|
+
|
|
34
|
+
# Deploy hosting + app hosting backend
|
|
35
|
+
echo "[4/4] Deploying application..."
|
|
36
|
+
firebase deploy --only hosting --project evaldocplatform-staging
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
echo "=== Staging deployment complete ==="
|
|
40
|
+
echo "URL: https://staging.evaldoctor.ai"
|
|
41
|
+
echo ""
|
|
42
|
+
echo "Post-deploy checklist:"
|
|
43
|
+
echo " [ ] Verify health check: curl https://staging.evaldoctor.ai/health"
|
|
44
|
+
echo " [ ] Test provider registration"
|
|
45
|
+
echo " [ ] Test patient chat flow"
|
|
46
|
+
echo " [ ] Test Stripe payment (use test card 4242 4242 4242 4242)"
|
|
47
|
+
echo " [ ] Verify security headers"
|
|
48
|
+
echo " [ ] Check audit logs in Firestore"
|
|
49
|
+
|
|
50
|
+
# Switch back to default project
|
|
51
|
+
firebase use default
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Fix the test provider to be active
|
|
2
|
+
require('dotenv').config();
|
|
3
|
+
const admin = require('firebase-admin');
|
|
4
|
+
|
|
5
|
+
const serviceAccount = require(process.env.GOOGLE_APPLICATION_CREDENTIALS);
|
|
6
|
+
admin.initializeApp({
|
|
7
|
+
credential: admin.credential.cert(serviceAccount)
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const db = admin.firestore();
|
|
11
|
+
|
|
12
|
+
async function fixProvider() {
|
|
13
|
+
console.log('🔧 Fixing test provider...\n');
|
|
14
|
+
|
|
15
|
+
const snapshot = await db.collection('providers').where('handle', '==', 'test-clinic').get();
|
|
16
|
+
|
|
17
|
+
if (snapshot.empty) {
|
|
18
|
+
console.log('❌ Provider not found');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const doc = snapshot.docs[0];
|
|
23
|
+
await doc.ref.update({
|
|
24
|
+
active: true,
|
|
25
|
+
status: 'active'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log('✅ Provider updated!');
|
|
29
|
+
console.log(' Provider ID:', doc.id);
|
|
30
|
+
console.log(' Status: active\n');
|
|
31
|
+
|
|
32
|
+
// Show the provider data
|
|
33
|
+
const data = doc.data();
|
|
34
|
+
console.log('Provider data:', JSON.stringify(data, null, 2));
|
|
35
|
+
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fixProvider().catch(console.error);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Quick Test - Stripe Connect Endpoints
|
|
4
|
+
# Tests that endpoints are responding correctly
|
|
5
|
+
|
|
6
|
+
BASE_URL="http://localhost:54112"
|
|
7
|
+
|
|
8
|
+
echo "🧪 Quick Endpoint Test"
|
|
9
|
+
echo "====================="
|
|
10
|
+
echo ""
|
|
11
|
+
|
|
12
|
+
echo "1️⃣ Testing health endpoint..."
|
|
13
|
+
curl -s "$BASE_URL/health" | jq . || echo "✗ Health check failed"
|
|
14
|
+
echo ""
|
|
15
|
+
|
|
16
|
+
echo "2️⃣ Testing provider config endpoint (public)..."
|
|
17
|
+
curl -s "$BASE_URL/api/payment/config/test-clinic" | jq . || echo "Endpoint responded"
|
|
18
|
+
echo ""
|
|
19
|
+
|
|
20
|
+
echo "3️⃣ Testing checkout session (should fail without valid provider)..."
|
|
21
|
+
curl -s -X POST "$BASE_URL/api/payment/create-checkout-session" \
|
|
22
|
+
-H "Content-Type: application/json" \
|
|
23
|
+
-d '{"handle":"test-clinic","sessionId":"test-123","interval":"month"}' \
|
|
24
|
+
| jq . || echo "Endpoint responded"
|
|
25
|
+
echo ""
|
|
26
|
+
|
|
27
|
+
echo "4️⃣ Testing access check..."
|
|
28
|
+
curl -s -X POST "$BASE_URL/api/payment/check-access" \
|
|
29
|
+
-H "Content-Type: application/json" \
|
|
30
|
+
-d '{"handle":"test-clinic","sessionId":"test-123"}' \
|
|
31
|
+
| jq . || echo "Endpoint responded"
|
|
32
|
+
echo ""
|
|
33
|
+
|
|
34
|
+
echo "✅ All endpoints are responding!"
|
|
35
|
+
echo ""
|
|
36
|
+
echo "📖 For full testing, see MANUAL_TEST_GUIDE.md"
|
|
37
|
+
echo " Open http://localhost:54112/app and use the browser console"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Simple chat API test script for EvalDoc
|
|
4
|
+
|
|
5
|
+
# Colors for output
|
|
6
|
+
GREEN='\033[0;32m'
|
|
7
|
+
RED='\033[0;31m'
|
|
8
|
+
YELLOW='\033[1;33m'
|
|
9
|
+
NC='\033[0m' # No Color
|
|
10
|
+
|
|
11
|
+
echo -e "${YELLOW}🧪 Testing EvalDoc Chat API${NC}\n"
|
|
12
|
+
|
|
13
|
+
# Test 1: Health check
|
|
14
|
+
echo -e "${YELLOW}1. Health Check${NC}"
|
|
15
|
+
curl -s http://localhost:8080/health | jq '.'
|
|
16
|
+
echo -e "\n"
|
|
17
|
+
|
|
18
|
+
# Test 2: Normal question (allowed)
|
|
19
|
+
echo -e "${YELLOW}2. Normal Question (Should work)${NC}"
|
|
20
|
+
echo "Question: 'What are the symptoms of flu?'"
|
|
21
|
+
curl -s -X POST http://localhost:8080/api/chat \
|
|
22
|
+
-H "Content-Type: application/json" \
|
|
23
|
+
-d '{
|
|
24
|
+
"handle": "test-clinic",
|
|
25
|
+
"sessionId": "test-session-001",
|
|
26
|
+
"message": "What are the symptoms of flu?"
|
|
27
|
+
}' | jq '.'
|
|
28
|
+
echo -e "\n"
|
|
29
|
+
|
|
30
|
+
# Test 3: Escalated question (chest pain)
|
|
31
|
+
echo -e "${YELLOW}3. Escalated Question (Should include warning + answer)${NC}"
|
|
32
|
+
echo "Question: 'I have chest pain'"
|
|
33
|
+
curl -s -X POST http://localhost:8080/api/chat \
|
|
34
|
+
-H "Content-Type: application/json" \
|
|
35
|
+
-d '{
|
|
36
|
+
"handle": "test-clinic",
|
|
37
|
+
"sessionId": "test-session-002",
|
|
38
|
+
"message": "I have chest pain"
|
|
39
|
+
}' | jq '.'
|
|
40
|
+
echo -e "\n"
|
|
41
|
+
|
|
42
|
+
# Test 4: Blocked question (diagnosis)
|
|
43
|
+
echo -e "${YELLOW}4. Blocked Question (Should refuse)${NC}"
|
|
44
|
+
echo "Question: 'Do I have diabetes?'"
|
|
45
|
+
curl -s -X POST http://localhost:8080/api/chat \
|
|
46
|
+
-H "Content-Type: application/json" \
|
|
47
|
+
-d '{
|
|
48
|
+
"handle": "test-clinic",
|
|
49
|
+
"sessionId": "test-session-003",
|
|
50
|
+
"message": "Do I have diabetes?"
|
|
51
|
+
}' | jq '.'
|
|
52
|
+
echo -e "\n"
|
|
53
|
+
|
|
54
|
+
echo -e "${GREEN}✅ Testing complete!${NC}"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Multi-turn conversation history is correctly built and passed to LLM
|
|
3
|
+
*
|
|
4
|
+
* Mocks the DeepSeek (OpenAI) client to capture the messages array
|
|
5
|
+
* and verify conversation history is included in the right format.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Mock OpenAI before requiring llm.js
|
|
9
|
+
let capturedCreateArgs = null;
|
|
10
|
+
|
|
11
|
+
const mockOpenAI = function () {
|
|
12
|
+
return {
|
|
13
|
+
chat: {
|
|
14
|
+
completions: {
|
|
15
|
+
create: async (args) => {
|
|
16
|
+
capturedCreateArgs = args;
|
|
17
|
+
return {
|
|
18
|
+
choices: [{ message: { content: 'Mock AI response about headaches.' } }]
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Replace the openai module with our mock
|
|
27
|
+
require.cache[require.resolve('openai')] = {
|
|
28
|
+
id: require.resolve('openai'),
|
|
29
|
+
exports: mockOpenAI,
|
|
30
|
+
loaded: true
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Set env var so the client gets initialized
|
|
34
|
+
process.env.DEEPSEEK_API_KEY = 'test-key';
|
|
35
|
+
|
|
36
|
+
const { generateResponse } = require('./functions/src/llm');
|
|
37
|
+
const { TOPICS, TOPIC_KEYS } = require('./functions/src/topics');
|
|
38
|
+
|
|
39
|
+
async function runTests() {
|
|
40
|
+
let passed = 0;
|
|
41
|
+
let failed = 0;
|
|
42
|
+
|
|
43
|
+
function assert(condition, testName) {
|
|
44
|
+
if (condition) {
|
|
45
|
+
console.log(` PASS: ${testName}`);
|
|
46
|
+
passed++;
|
|
47
|
+
} else {
|
|
48
|
+
console.log(` FAIL: ${testName}`);
|
|
49
|
+
failed++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const provider = { displayName: 'Dr. Amina', specialty: 'General Practice' };
|
|
54
|
+
const topicConfig = TOPICS['general_health_info'];
|
|
55
|
+
|
|
56
|
+
// ─── Test 1: No history (first message in conversation) ───
|
|
57
|
+
console.log('\n--- Test 1: First message (no history) ---');
|
|
58
|
+
capturedCreateArgs = null;
|
|
59
|
+
|
|
60
|
+
await generateResponse({
|
|
61
|
+
topicConfig,
|
|
62
|
+
message: 'I have a headache',
|
|
63
|
+
provider,
|
|
64
|
+
conversationHistory: []
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
assert(capturedCreateArgs !== null, 'DeepSeek was called');
|
|
68
|
+
assert(capturedCreateArgs.messages.length === 2, 'Messages array has 2 items (system + user)');
|
|
69
|
+
assert(capturedCreateArgs.messages[0].role === 'system', 'First message is system prompt');
|
|
70
|
+
assert(capturedCreateArgs.messages[1].role === 'user', 'Second message is user');
|
|
71
|
+
assert(capturedCreateArgs.messages[1].content === 'I have a headache', 'User message content is correct');
|
|
72
|
+
|
|
73
|
+
// ─── Test 2: With conversation history ───
|
|
74
|
+
console.log('\n--- Test 2: Multi-turn with 4 history messages ---');
|
|
75
|
+
capturedCreateArgs = null;
|
|
76
|
+
|
|
77
|
+
const history = [
|
|
78
|
+
{ role: 'user', content: 'Hi, I have been getting headaches' },
|
|
79
|
+
{ role: 'assistant', content: 'I can help with info about headaches. What kind of headache?' },
|
|
80
|
+
{ role: 'user', content: 'It is on the right side, throbbing' },
|
|
81
|
+
{ role: 'assistant', content: 'That sounds like it could be a migraine pattern. How long do they last?' }
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
await generateResponse({
|
|
85
|
+
topicConfig,
|
|
86
|
+
message: 'About 4 hours usually',
|
|
87
|
+
provider,
|
|
88
|
+
conversationHistory: history
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
assert(capturedCreateArgs !== null, 'DeepSeek was called');
|
|
92
|
+
assert(capturedCreateArgs.messages.length === 6, 'Messages array has 6 items (system + 4 history + user)');
|
|
93
|
+
assert(capturedCreateArgs.messages[0].role === 'system', 'First message is system prompt');
|
|
94
|
+
assert(capturedCreateArgs.messages[1].role === 'user', 'History[0] is user');
|
|
95
|
+
assert(capturedCreateArgs.messages[1].content === 'Hi, I have been getting headaches', 'History[0] content correct');
|
|
96
|
+
assert(capturedCreateArgs.messages[2].role === 'assistant', 'History[1] is assistant');
|
|
97
|
+
assert(capturedCreateArgs.messages[3].role === 'user', 'History[2] is user');
|
|
98
|
+
assert(capturedCreateArgs.messages[4].role === 'assistant', 'History[3] is assistant');
|
|
99
|
+
assert(capturedCreateArgs.messages[5].role === 'user', 'Current message is last');
|
|
100
|
+
assert(capturedCreateArgs.messages[5].content === 'About 4 hours usually', 'Current message content correct');
|
|
101
|
+
|
|
102
|
+
// ─── Test 3: System prompt contains provider info ───
|
|
103
|
+
console.log('\n--- Test 3: System prompt personalization ---');
|
|
104
|
+
const systemPrompt = capturedCreateArgs.messages[0].content;
|
|
105
|
+
assert(systemPrompt.includes('Dr. Amina'), 'System prompt includes provider name');
|
|
106
|
+
assert(systemPrompt.includes('General Practice'), 'System prompt includes specialty');
|
|
107
|
+
assert(systemPrompt.includes('General health info'), 'System prompt includes topic title');
|
|
108
|
+
|
|
109
|
+
// ─── Test 4: No history param (backward compat) ───
|
|
110
|
+
console.log('\n--- Test 4: No conversationHistory param (backward compat) ---');
|
|
111
|
+
capturedCreateArgs = null;
|
|
112
|
+
|
|
113
|
+
await generateResponse({
|
|
114
|
+
topicConfig,
|
|
115
|
+
message: 'What causes migraines?',
|
|
116
|
+
provider
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
assert(capturedCreateArgs.messages.length === 2, 'Messages array has 2 items (no history added)');
|
|
120
|
+
assert(capturedCreateArgs.messages[1].content === 'What causes migraines?', 'User message correct');
|
|
121
|
+
|
|
122
|
+
// ─── Test 5: max_tokens bumped to 300 ───
|
|
123
|
+
console.log('\n--- Test 5: Token limit ---');
|
|
124
|
+
assert(capturedCreateArgs.max_tokens === 300, 'max_tokens is 300 (bumped from 250)');
|
|
125
|
+
|
|
126
|
+
// ─── Results ───
|
|
127
|
+
console.log(`\n${'='.repeat(40)}`);
|
|
128
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
129
|
+
console.log('='.repeat(40));
|
|
130
|
+
|
|
131
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
runTests().catch(err => {
|
|
135
|
+
console.error('Test error:', err);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|