* ci: prevent duplicate test runs - tests only on PRs, inline tests for builds * docs: add testing and CI/CD documentation * security: fix CodeQL vulnerabilities (SSRF, XSS, rate limiting) - Add URL validation to prevent SSRF attacks on notification endpoints - Block private IPs (10.x, 172.16-31.x, 192.168.x, 169.254.x) - Block localhost and internal hostnames - Only allow HTTP/HTTPS protocols - Add HTML escaping for medication names in email templates (XSS) - Add stricter rate limiting for auth routes (5 req/15min for login/register) - Add SSRF protection tests (405 tests total) * security: add rate limiting to remaining auth routes * chore: add CodeQL config to suppress rate-limit false positives Rate limiting IS implemented via @fastify/rate-limit plugin: - Global: 100 req/min (index.ts) - Auth routes: 5-10 req/min via config.rateLimit option CodeQL doesn't recognize Fastify's plugin-based rate limiting pattern. * ci: switch to CodeQL Advanced Setup - Add custom codeql.yml workflow - Configure to use codeql-config.yml - Exclude js/missing-rate-limiting rule (false positive) Rate limiting is implemented via @fastify/rate-limit plugin * ci: add explicit permissions to workflows Fixes CodeQL 'Workflow does not contain permissions' warnings. Sets minimal 'contents: read' at top level. * ci: add manual trigger to CodeQL workflow * ci: add explicit permissions to all workflow jobs * build(deps): bump esbuild, @vitest/coverage-v8 and vitest in /backend Bumps [esbuild](https://github.com/evanw/esbuild) to 0.27.2 and updates ancestor dependencies [esbuild](https://github.com/evanw/esbuild), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). These dependencies need to be updated together. Updates `esbuild` from 0.21.5 to 0.27.2 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.21.5...v0.27.2) Updates `@vitest/coverage-v8` from 2.1.9 to 4.0.16 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/coverage-v8) Updates `vitest` from 2.1.9 to 4.0.16 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/vitest) --- updated-dependencies: - dependency-name: esbuild dependency-version: 0.27.2 dependency-type: indirect - dependency-name: "@vitest/coverage-v8" dependency-version: 4.0.16 dependency-type: direct:development - dependency-name: vitest dependency-version: 4.0.16 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> * docs: add GitHub issue templates - Bug report template with deployment type, browser info, logs - Feature request template with affected area, priority - Config with link to discussions and README - Optimize test.yml to skip tests for non-code changes * Initial plan * Remove database schema duplication by creating shared schema-sql.ts module Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Refactor frontend date formatting to eliminate duplication Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * docs: Add branch protection warning and PR workflow to instructions * ci: remove paths filter from test workflow to fix branch protection * fix: add .js extension to schema-sql imports for ESM compatibility (#15) * feat: add setting to skip reminders for taken doses - Add skipRemindersForTakenDoses setting to database schema - Extend settings API to save and load new setting - Update intake reminder scheduler to filter taken doses - Add frontend toggle in settings with i18n (EN/DE) - Only check doses from today (timezone-aware) - Update all test schemas with new field - All 405 tests passing * feat: add repeat reminders for missed doses - Add repeatRemindersEnabled and reminderRepeatIntervalMinutes settings - Refactor intake reminder state from array to object with sendCount tracking - Update scheduler to send repeated reminders at configurable intervals - Only remind for today's doses (timezone-aware filtering) - Add frontend toggle and interval input (5-480 minutes) in settings - Maintain backward compatibility for old state file format - Update all test schemas and assertions - All 406 tests passing * feat: add nagging reminders with max limit and ENV defaults - Add maxNaggingReminders setting to limit repeat reminders (1-20) - Add ENV defaults for all user settings (DEFAULT_*) - Add ALTER TABLE migrations for backward compatibility - Add smtpConfigured/shoutrrrConfigured to health endpoint - Fix Push toggle to allow enabling without existing URL - Disable skip/repeat toggles when no notifications enabled - Add Pocket ID button to registration page - Add getTodaysIntakes() for repeat reminder logic - Update translations (en/de) for new settings - Add comprehensive tests for new features --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
MedAssist-ng
Never run out of your medications again.
A medication tracking and planning app with stock monitoring, intake schedules, and reminder notifications.
🤖 AI-Generated Code
This app was 100% coded with Claude Opus 4.5. Use at your own risk.
⚠️ Disclaimer
Your health is your responsibility. This app may contain bugs. Follow your doctor's instructions closely, keep track of your medication supply, and plan ahead for reordering.
Think of this app as a helpful tool, but make all health decisions independently!
Features
Smart Inventory
- Track exact stock: packs, blisters, and loose pills
- Display remaining days of supply
- Automatic calculation based on intake schedule
Flexible Schedules
- Daily, weekly, or custom intervals per medication
- Independent schedules for each medication
Stock Alerts & Reminders
- Notifications before stock runs out
- Configurable warning thresholds
- Intake reminders via push notifications
Trip Planner
- Calculate how many pills you need for a trip or date range
- Plan ahead for vacations, business trips, or hospital stays
Multi-Person Support
- Manage medications for multiple people
- Share schedules via link. Recipients can mark doses as taken, you see it live
Notifications
- Email via SMTP
- Push notifications via ntfy, Gotify, Telegram, Discord (Shoutrrr)
- Supports both stock warnings and intake reminders
Privacy & Security
- Fully self-hosted
- SSO via OIDC (Authelia, Authentik, Pocket ID, Keycloak)
- Non-root containers
- Dark mode included 😎
Getting Started
The easiest way to deploy MedAssist-ng is with Docker Compose:
git clone https://github.com/DanielVolz/medassist-ng.git
cd medassist-ng
cp .env.example .env
docker compose up -d
Open http://localhost:4174 and start tracking your medications.
Configuration
All configuration is done via environment variables in .env. Copy .env.example to get started.
General
| Variable | Default | Description |
|---|---|---|
PUID |
1000 |
User ID for container file permissions |
PGID |
1000 |
Group ID for container file permissions |
PORT |
3000 |
Backend API port |
CORS_ORIGINS |
http://localhost:4174 |
Allowed origins for CORS |
LOG_LEVEL |
info |
Log verbosity (debug, info, warn, error) |
TZ |
Europe/Berlin |
Timezone for scheduled reminders |
Authentication
| Variable | Default | Description |
|---|---|---|
AUTH_ENABLED |
false |
Enable user authentication |
REGISTRATION_ENABLED |
false |
Allow new user registrations |
JWT_SECRET |
— | Access token signing key (required if auth enabled) |
REFRESH_SECRET |
— | Refresh token signing key (required if auth enabled) |
COOKIE_SECRET |
— | Cookie signing key (required if auth enabled) |
ACCESS_TOKEN_TTL_MINUTES |
15 |
Access token lifetime |
REFRESH_TOKEN_TTL_DAYS |
7 |
Refresh token lifetime |
Generate secrets with: openssl rand -hex 32
OIDC / SSO
| Variable | Default | Description |
|---|---|---|
OIDC_ENABLED |
false |
Enable OIDC authentication |
OIDC_ISSUER_URL |
— | OIDC provider URL |
OIDC_CLIENT_ID |
— | Client ID from OIDC provider |
OIDC_CLIENT_SECRET |
— | Client secret from OIDC provider |
OIDC_REDIRECT_URI |
— | Callback URL |
OIDC_SCOPES |
openid profile email |
Scopes to request |
OIDC_USERNAME_CLAIM |
preferred_username |
Claim for username |
OIDC_AUTO_CREATE_USERS |
true |
Auto-create users on first SSO login |
OIDC_PROVIDER_NAME |
SSO |
Name shown on login button |
Email (SMTP)
| Variable | Default | Description |
|---|---|---|
SMTP_HOST |
— | SMTP server hostname |
SMTP_PORT |
587 |
SMTP server port |
SMTP_USER |
— | SMTP username |
SMTP_PASS |
— | SMTP password |
SMTP_TOKEN |
— | OAuth2/App token (takes precedence over password) |
SMTP_FROM |
— | Sender email address |
SMTP_SECURE |
false |
Use TLS |
Reminders
| Variable | Default | Description |
|---|---|---|
REMINDER_DAYS_BEFORE |
7 |
Days before stock runs out to send reminder |
REMINDER_HOUR |
6 |
Hour to send daily reminders (24h format) |
REMINDER_MINUTES_BEFORE |
15 |
Minutes before intake to send reminder |
EXPIRY_WARNING_DAYS |
30 |
Days before expiry to show warning |
Development
docker compose -f docker-compose.dev.yml up
- Frontend:
http://localhost:5173(hot reload) - Backend:
http://localhost:3000
Acknowledgements
This project was inspired by MedAssist by njic.

