1 line
14 KiB
JSON
1 line
14 KiB
JSON
{"repo": "bowmanlee/cmatch", "files": {"README.md": {"b94febb0fe87f577c9dbfc8c4a42cbfa07baf594": {"t": 1785746437.321351, "n": 5383, "h": "502abf8b15899e11", "c": "# CMatch\n\n> Chinese Medicine practitioner matching via conversational AI intake.\n\nCMatch is a prototype web application that helps patients structure their Chinese Medicine concerns into safety-aware, explainable practitioner matches. It uses a conversational interface powered by DeepSeek AI to extract structured intake data and match against a curated practitioner database.\n\n---\n\n## Architecture\n\n```\n┌─────────────┐ ┌─────────────────┐ ┌──────────────┐\n│ React SPA │────▶│ Vercel Function │────▶│ DeepSeek API│\n│ (Vite) │ │ api/conversation│ │ (LLM) │\n└─────────────┘ └─────────────────┘ └──────────────┘\n │\n ▼\n ┌──────────────┐\n │ Cloudflare │\n │ Turnstile │\n │ (bot check) │\n └──────────────┘\n```\n\n- **Frontend**: React 19 + TypeScript + Vite, vanilla CSS\n- **Backend**: Vercel serverless function (`api/conversation.mjs`)\n- **Shared core**: `server/core.mjs` — single source of truth for prompts, schema normalization, and DeepSeek API logic (used by both local dev and production)\n- **Bot protection**: Cloudflare Turnstile (invisible challenge on first interaction)\n\n---\n\n## Local Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Start local dev (Vite client + API proxy)\nnpm run dev\n\n# Or run separately\nnpm run dev:client # Vite on http://localhost:5173\nnpm run dev:api # API proxy on http://127.0.0.1:8787\n```\n\nCreate a `.env` file (see `.env.example`):\n\n```bash\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_MODEL=deepseek-chat\nALLOWED_ORIGIN=https://your-project.vercel.app\nTURNSTILE_SECRET_KEY=0x...\n```\n\n---\n\n## Deployment to Vercel\n\n### 1. Rotate your DeepSeek API key\n\nIf this repository was ever shared or the key appeared in chat history, generate a new key at [platform.deepseek.com](https://platform.deepseek.com) before deploying.\n\n### 2. Connect repository\n\n1. Go to [vercel.com/new](https://vercel.com/new)\n2. Import `bowmanlee/CMatch`\n3. Framework preset: **Vite**\n\n### 3. Environment variables\n\nAdd these in the Vercel dashboard (**Settings → Environment Variables**):\n\n| Variable | Value | Secret? |\n|----------|-------|---------|\n| `DEEPSEEK_API_KEY` | `sk-...` | ✅ Yes |\n| `DEEPSEEK_MODEL` | `deepseek-chat` | No |\n| `ALLOWED_ORIGIN` | `https://your-project.vercel.app` | No |\n| `TURNSTILE_SECRET_KEY` | `0x...` | ✅ Yes |\n\n> **Note**: `ALLOWED_ORIGIN` must match your production domain exactly. Include `https://`.\n\n### 4. Deploy\n\nClick **Deploy**. Vercel will:\n- Build the SPA (`npm run build` → `dist/`)\n- Deploy `api/conversation.mjs` as a serverless function\n- Apply SPA rewrites from `vercel.json`\n\n### 5. Add Turnstile site key to frontend\n\n1. Create a Turnstile site at [dash.cloudflare.com](https://dash.cloudflare.com) → Turnstile\n2. Copy the **Site Key**\n3. In `src/App.tsx`, set `TURNSTILE_SITE_KEY` to your site key (this is public and safe to embed)\n\n---\n\n## Security Notes\n\n- **Zero-PHI logging**: Server logs never contain user message content, schema fields, or raw LLM responses. Only metadata (model, message count, status) is logged.\n- **Debug page**: Only loads in development (`import.meta.env.DEV`). Production builds tree-shake it out completely.\n- **CORS**: Exact-match allowlist, not prefix matching. Localhost origins are hardcoded; production origin comes from `ALLOWED_ORIGIN`.\n- **Request timeout**: All DeepSeek API calls use `AbortController` with a 30-second timeout.\n- **Bot protection**: Cloudflare Turnstile verifies every API request. Invalid tokens receive HTTP 403.\n- **No client-side secrets**: The broken `X-API-Key` approach was removed. Abuse protection comes from Turnstile + edge rate limiting, not shared secrets in client bundles.\n\n---\n\n## Project Structure\n\n```\n├── api/\n│ └── conversation.mjs # Vercel serverless function\n├── server/\n│ ├── core.mjs # Shared AI logic (prompts, DeepSeek client, schema normalization)\n│ └── deepseek-proxy.mjs # Local dev API proxy\n├── shared/\n│ ├── practitioners.json # Practitioner database\n│ └── practitioners.ts # TypeScript types\n├── src/\n│ ├── App.tsx # Main app + chat UI\n│ ├── App.css # All styles\n│ └── DebugPage.tsx # Dev-only debug view\n├── vercel.json # Vercel config (build, rewrites)\n└── vite.config.ts # Vite dev config\n```\n\n---\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `DEEPSEEK_API_KEY` | ✅ | DeepSeek API key |\n| `DEEPSEEK_MODEL` | No | Model name (default: `deepseek-chat`) |\n| `DEEPSEEK_BASE_URL` | No | API base URL (default: `https://api.deepseek.com`) |\n| `ALLOWED_ORIGIN` | For production | Your Vercel domain for CORS |\n| `TURNSTILE_SECRET_KEY` | For production | Cloudflare Turnstile secret |\n| `CMATCH_API_HOST` | Local dev only | Proxy bind host (default: `127.0.0.1`) |\n| `CMATCH_API_PORT` | Local dev only | Proxy bind port (default: `8787`) |\n\n---\n\n## Compliance Note\n\nPatient symptoms and preferences are sent to DeepSeek (third-party API). No Business Associate Agreement (BAA) or Data Processing Agreement (DPA) is in place. This is a **prototype-level deployment** — not suitable for production healthcare use without legal review.\n"}, "116228f5a5f0cb40a21e8e706cc63e246b792bac": {"t": 1785764221.5325084, "n": 5383, "h": "502abf8b15899e11", "c": "# CMatch\n\n> Chinese Medicine practitioner matching via conversational AI intake.\n\nCMatch is a prototype web application that helps patients structure their Chinese Medicine concerns into safety-aware, explainable practitioner matches. It uses a conversational interface powered by DeepSeek AI to extract structured intake data and match against a curated practitioner database.\n\n---\n\n## Architecture\n\n```\n┌─────────────┐ ┌─────────────────┐ ┌──────────────┐\n│ React SPA │────▶│ Vercel Function │────▶│ DeepSeek API│\n│ (Vite) │ │ api/conversation│ │ (LLM) │\n└─────────────┘ └─────────────────┘ └──────────────┘\n │\n ▼\n ┌──────────────┐\n │ Cloudflare │\n │ Turnstile │\n │ (bot check) │\n └──────────────┘\n```\n\n- **Frontend**: React 19 + TypeScript + Vite, vanilla CSS\n- **Backend**: Vercel serverless function (`api/conversation.mjs`)\n- **Shared core**: `server/core.mjs` — single source of truth for prompts, schema normalization, and DeepSeek API logic (used by both local dev and production)\n- **Bot protection**: Cloudflare Turnstile (invisible challenge on first interaction)\n\n---\n\n## Local Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Start local dev (Vite client + API proxy)\nnpm run dev\n\n# Or run separately\nnpm run dev:client # Vite on http://localhost:5173\nnpm run dev:api # API proxy on http://127.0.0.1:8787\n```\n\nCreate a `.env` file (see `.env.example`):\n\n```bash\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_MODEL=deepseek-chat\nALLOWED_ORIGIN=https://your-project.vercel.app\nTURNSTILE_SECRET_KEY=0x...\n```\n\n---\n\n## Deployment to Vercel\n\n### 1. Rotate your DeepSeek API key\n\nIf this repository was ever shared or the key appeared in chat history, generate a new key at [platform.deepseek.com](https://platform.deepseek.com) before deploying.\n\n### 2. Connect repository\n\n1. Go to [vercel.com/new](https://vercel.com/new)\n2. Import `bowmanlee/CMatch`\n3. Framework preset: **Vite**\n\n### 3. Environment variables\n\nAdd these in the Vercel dashboard (**Settings → Environment Variables**):\n\n| Variable | Value | Secret? |\n|----------|-------|---------|\n| `DEEPSEEK_API_KEY` | `sk-...` | ✅ Yes |\n| `DEEPSEEK_MODEL` | `deepseek-chat` | No |\n| `ALLOWED_ORIGIN` | `https://your-project.vercel.app` | No |\n| `TURNSTILE_SECRET_KEY` | `0x...` | ✅ Yes |\n\n> **Note**: `ALLOWED_ORIGIN` must match your production domain exactly. Include `https://`.\n\n### 4. Deploy\n\nClick **Deploy**. Vercel will:\n- Build the SPA (`npm run build` → `dist/`)\n- Deploy `api/conversation.mjs` as a serverless function\n- Apply SPA rewrites from `vercel.json`\n\n### 5. Add Turnstile site key to frontend\n\n1. Create a Turnstile site at [dash.cloudflare.com](https://dash.cloudflare.com) → Turnstile\n2. Copy the **Site Key**\n3. In `src/App.tsx`, set `TURNSTILE_SITE_KEY` to your site key (this is public and safe to embed)\n\n---\n\n## Security Notes\n\n- **Zero-PHI logging**: Server logs never contain user message content, schema fields, or raw LLM responses. Only metadata (model, message count, status) is logged.\n- **Debug page**: Only loads in development (`import.meta.env.DEV`). Production builds tree-shake it out completely.\n- **CORS**: Exact-match allowlist, not prefix matching. Localhost origins are hardcoded; production origin comes from `ALLOWED_ORIGIN`.\n- **Request timeout**: All DeepSeek API calls use `AbortController` with a 30-second timeout.\n- **Bot protection**: Cloudflare Turnstile verifies every API request. Invalid tokens receive HTTP 403.\n- **No client-side secrets**: The broken `X-API-Key` approach was removed. Abuse protection comes from Turnstile + edge rate limiting, not shared secrets in client bundles.\n\n---\n\n## Project Structure\n\n```\n├── api/\n│ └── conversation.mjs # Vercel serverless function\n├── server/\n│ ├── core.mjs # Shared AI logic (prompts, DeepSeek client, schema normalization)\n│ └── deepseek-proxy.mjs # Local dev API proxy\n├── shared/\n│ ├── practitioners.json # Practitioner database\n│ └── practitioners.ts # TypeScript types\n├── src/\n│ ├── App.tsx # Main app + chat UI\n│ ├── App.css # All styles\n│ └── DebugPage.tsx # Dev-only debug view\n├── vercel.json # Vercel config (build, rewrites)\n└── vite.config.ts # Vite dev config\n```\n\n---\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `DEEPSEEK_API_KEY` | ✅ | DeepSeek API key |\n| `DEEPSEEK_MODEL` | No | Model name (default: `deepseek-chat`) |\n| `DEEPSEEK_BASE_URL` | No | API base URL (default: `https://api.deepseek.com`) |\n| `ALLOWED_ORIGIN` | For production | Your Vercel domain for CORS |\n| `TURNSTILE_SECRET_KEY` | For production | Cloudflare Turnstile secret |\n| `CMATCH_API_HOST` | Local dev only | Proxy bind host (default: `127.0.0.1`) |\n| `CMATCH_API_PORT` | Local dev only | Proxy bind port (default: `8787`) |\n\n---\n\n## Compliance Note\n\nPatient symptoms and preferences are sent to DeepSeek (third-party API). No Business Associate Agreement (BAA) or Data Processing Agreement (DPA) is in place. This is a **prototype-level deployment** — not suitable for production healthcare use without legal review.\n"}, "55516f6c8f846b2723e09179ce552bf6303725ad": {"t": 1785764226.3473608, "n": 2425, "h": "5976d1f9c667a38f", "c": "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)\n\n## React Compiler\n\nThe React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:\n\n```js\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n // Other configs...\n\n // Remove tseslint.configs.recommended and replace with this\n tseslint.configs.recommendedTypeChecked,\n // Alternatively, use this for stricter rules\n tseslint.configs.strictTypeChecked,\n // Optionally, add this for stylistic rules\n tseslint.configs.stylisticTypeChecked,\n\n // Other configs...\n ],\n languageOptions: {\n parserOptions: {\n project: ['./tsconfig.node.json', './tsconfig.app.json'],\n tsconfigRootDir: import.meta.dirname,\n },\n // other options...\n },\n },\n])\n```\n\nYou can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:\n\n```js\n// eslint.config.js\nimport reactX from 'eslint-plugin-react-x'\nimport reactDom from 'eslint-plugin-react-dom'\n\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n // Other configs...\n // Enable lint rules for React\n reactX.configs['recommended-typescript'],\n // Enable lint rules for React DOM\n reactDom.configs.recommended,\n ],\n languageOptions: {\n parserOptions: {\n project: ['./tsconfig.node.json', './tsconfig.app.json'],\n tsconfigRootDir: import.meta.dirname,\n },\n // other options...\n },\n },\n])\n```\n"}}}} |