Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@

# API Keys for Medicine Search
# Copy this file to .env.local and add your actual API keys
# Copy this file to .env (or .env.local) and add your actual API keys

# Routing mode for AI/proprietary searches:
# unset / false -> client-side calls using the VITE_* keys below (keys are
Comment on lines 2 to +6
# bundled into the browser build; current default behavior)
# true -> route AI calls through Supabase Edge Functions so keys stay
# server-side (requires the functions + their secrets deployed)
VITE_USE_SECURE_API=false

# OpenAI API Key (for ChatGPT medicine search)
VITE_OPENAI_API_KEY=your_openai_api_key_here
Expand Down
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 10
groups:
all-dependencies:
patterns: ["*"]
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci --legacy-peer-deps
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
- run: npm audit --audit-level=high
- name: Generate CycloneDX SBOM
run: npx --yes @cyclonedx/cyclonedx-npm --output-format JSON --output-file sbom.cdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.json

security-scan:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@v4
- name: OSV-Scanner
uses: google/osv-scanner-action/osv-scanner-action@v1.9.1
with:
scan-args: --lockfile=package-lock.json
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@0.24.0
with:
scan-type: fs
scanners: vuln,secret,config
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.audit

temp
node_modules
Expand All @@ -23,3 +24,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
bun.lockb
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Changelog

All notable changes to this project are documented here, following
[Keep a Changelog](https://keepachangelog.com/) and [SemVer](https://semver.org/).

## [Unreleased]

### Added
- `VITE_USE_SECURE_API` toggle to route AI searches through Supabase Edge Functions
(keys stay server-side). Defaults to the existing client-side behavior.
- Vitest test suite (`npm test`) for the RxNorm/OpenFDA adapters and the secure-API toggle.
- `typecheck` script (strict TypeScript) and a working `lint` setup.
- CI workflow, Dependabot, governance files, and `docs/supabase-security.md`.
- Medical disclaimer in the README.

### Changed
- `SecureApiWrapper.isSecureApiAvailable()` now reads `VITE_USE_SECURE_API`
instead of a hardcoded `false` (default behavior unchanged).
- Enabled `strict` TypeScript in `tsconfig.app.json` and fixed the surfaced types.
- Realigned ESLint to v9 and `eslint-plugin-react-hooks` to stable `5.2.0` so
`npm run lint` works again (was crashing under a mismatched ESLint 10).
- Standardized on `package-lock.json`; removed the conflicting `bun.lockb`.

### Security
- `npm audit fix` applied — 0 known vulnerabilities (no breaking upgrades).
12 changes: 12 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Contributor Code of Conduct

This project adopts the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).

We pledge to make participation a harassment-free experience for everyone.
Be respectful, assume good intent, and keep discussion constructive.

Report unacceptable behavior via GitHub. All complaints will be reviewed and
investigated promptly and fairly.

The full text is available at
https://www.contributor-covenant.org/version/2/1/code_of_conduct/.
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Contributing

Thanks for your interest in this demo project.

## Development

```bash
npm install # the Docker/CI build uses --legacy-peer-deps
npm run dev # http://localhost:8080
npm run lint
npm run typecheck
npm test
```

## Guidelines

- Keep changes small, simple, and backward compatible — this is a demo.
- Never commit API keys; remember `VITE_*` values ship to the browser.
- Run `npm run lint`, `npm run typecheck`, `npm test`, and `npm audit` before a PR.

## Reporting issues

Use GitHub issues for bugs and feature ideas. For security reports, see
[SECURITY.md](SECURITY.md).
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# Welcome to Medicine Brand Finder

> [!WARNING]
> **Proof-of-concept / demo — not medical advice.** Results are aggregated from
> third-party data sources and AI engines and may be inaccurate, incomplete, or
> out of date. Do not use this tool for diagnosis, treatment, prescribing, or
> dispensing decisions. Always consult a qualified healthcare professional and
> official drug labeling.

## Project info

Medicine Finder uses several online free medicine APIs (in RxNorm international format or others) and other 3rd party AI engines calls and Databases, to search for an active drug medicine substance component and return all known brands, medicines, containing the specified substance, useful for finding medicines in different other countries by doctor's prescription, moving patient data from one system into another and other international usages.

The project is frontend only and would require backend in a reliable scalable version, currently being a proof-of-concept mvp.
**Live Demo (without AI API keys, real DB, and other limitations)**

**Live demo:** deployed on Railway. It runs client-side only, so the free public
medicine APIs work out of the box; AI-engine and DrugBank/ChemSpider results need
your own API keys (see below) or the optional secure backend.


## What technologies are used for this project?
Expand All @@ -20,8 +30,37 @@ This project is automatically and manually built with:
- Supabase/Vercel/Openrouter.ai integrations


### .env API keys, for some searches to work - or securely add backend Supabase Edge Function for them (as in current implementation)
### .env API keys, for some searches to work - or securely add backend Supabase Edge Function for them
Note: Some APIs (RxNorm, OpenFDA, EMA, WHO, ClinicalTrials, PubChem, Wikidata) are free and do not require API keys.
DeepSeek/OpenRouter offers a free tier with generous limits if you register to use it.
APIs and/or engines might need either local setup, changes of URLS, registering your API keys (locally in env and exposed, or as functions responses in supabase/deployment/etc.)

Copy `.env.example` to `.env` and fill the keys you have. See [.env.example](./.env.example) for the full list.

> [!CAUTION]
> Any `VITE_*` value is **bundled into the browser build and publicly visible**.
> By default (`VITE_USE_SECURE_API` unset/`false`) the AI/DrugBank/ChemSpider keys
> you set are exposed client-side — only use keys you are comfortable exposing, or
> set `VITE_USE_SECURE_API=true` to route those calls through the Supabase Edge
> Functions in [supabase/functions/](./supabase/functions) so the keys stay
> server-side (see [docs/supabase-security.md](./docs/supabase-security.md)).

## Run & test

```bash
npm install # (the Docker build uses --legacy-peer-deps)
npm run dev # local dev server at http://localhost:8080
npm run build # production build
npm run lint # ESLint
npm run typecheck # strict TypeScript check
npm test # Vitest unit tests (data adapters + secure-API toggle)
```

## Manual review checklist

- **config:** copy `.env.example` → `.env`; free APIs need no keys; AI keys optional; `VITE_USE_SECURE_API` toggles the secure backend path
- **run:** `npm install` → `npm run dev` (or `npm run build` + `npm run preview`)
- **examples:** search an active ingredient (e.g. "ibuprofen") to list brands/medicines
- **result:** merged, de-duplicated brand list from RxNorm/OpenFDA/EMA/etc. (+ AI engines if keys set)
- **path:** data adapters in `src/utils/api/`, secure wrapper `src/utils/api/secureApiWrapper.ts`, edge functions `supabase/functions/`

23 changes: 23 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Security Policy

> This is a proof-of-concept / demo, not a production medical service.

## Supported versions

Only the latest `main` is supported.

## Reporting a vulnerability

Please report security issues privately via GitHub Security Advisories on this
repository. Do not open public issues for undisclosed vulnerabilities.

We aim to acknowledge reports within 7 days and follow a 90-day coordinated
disclosure window.

## Notes

- The app is frontend-only. Any `VITE_*` value is bundled into the public browser
build — only set keys you are comfortable exposing, or enable the secure
Supabase Edge Function path (`VITE_USE_SECURE_API=true`). See
[docs/supabase-security.md](docs/supabase-security.md).
- `.env` is gitignored; never commit real API keys.
Binary file removed bun.lockb
Binary file not shown.
36 changes: 36 additions & 0 deletions docs/supabase-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Supabase Edge Functions — security notes

The app can route AI/proprietary searches through Supabase Edge Functions so that
provider API keys stay on the server instead of in the browser bundle.

## When this path is used

Controlled by `VITE_USE_SECURE_API` (see `.env.example`):

- **`false` / unset (default):** searches run client-side using the `VITE_*` keys.
Those keys are embedded in the static build and are publicly visible.
- **`true`:** searches call the functions in [`supabase/functions/`](../supabase/functions)
(`openai-search`, `openrouter-search`, `deepseek-search`, `perplexity-search`),
which hold the provider keys as Supabase function secrets.

## Functions and their secrets

| Function | Provider key (set as a Supabase secret) |
|---|---|
| `openai-search` | `OPENAI_API_KEY` |
| `openrouter-search` | `OPENROUTER_API_KEY` |
| `deepseek-search` | `DEEPSEEK_API_KEY` |
| `perplexity-search` | `PERPLEXITY_API_KEY` |

Set them with `supabase secrets set OPENAI_API_KEY=...` (never commit real keys).

## Hardening recommendation

The bundled functions are intended for demo use and do not enforce caller
authentication. Before exposing them publicly you should:

- require a valid Supabase JWT (verify the `Authorization` header), and/or
- add per-IP rate limiting, and restrict CORS to your own origin.

These are noted here rather than enabled by default so the demo's behavior stays
unchanged; the secure path is opt-in.
Loading
Loading