- PHP 100%
| data | ||
| favicon | ||
| lang | ||
| .gitignore | ||
| .htaccess | ||
| api.php | ||
| config.php.sample | ||
| favicon.ico | ||
| index.php | ||
| README.de.md | ||
| README.md | ||
MedTracker
A simple, self-hosted, mobile-first, multi-user medication tracker. Runs on any stock PHP web server. All data is stored in plain, human-readable text files — no database required.
Features
- Multi-User with secure password hashing. Each user has their own login, data directory, schedule and history. Passwords are stored as bcrypt hashes in
data/users.json. - Persistent Login. "Stay logged in" checkbox on the login form. Uses a secure, HttpOnly remember-me cookie (30 days). Sessions persist across browser restarts until explicit logout.
- Lowercase Usernames. Usernames are always normalised to lowercase on login and user creation.
- Admin Panel. Users flagged as admin get an extra tab to add, edit, rename, and delete other users. One user is designated as super admin in
config.phpand cannot be demoted or deleted from the UI. - Admin User Switching. Admins see a global user picker below the tab bar to view and manage any user's data — Today, Schedule, Meds, and History all switch to the selected user.
- Per-User Settings. Admins can configure per-user defaults in the user edit form:
- Default Startup User — which user is pre-selected when the admin logs in.
- Default History View — Day / Week / Month / Year (default: Week). Also saved automatically when the user clicks a period button in the History tab.
- Read-Only Users. Users with the
isReadOnlyflag see only the History and Meds tabs (read-only). A configurableviewUserslist controls which other users' data a read-only user can see. - Self-Service Password Change. Any logged-in user can change their own password from the user dropdown in the top-right.
- Multi-Language UI. English, German, and French (EN / DE / FR). Auto-detects the browser's
Accept-Languageon first visit; manual switcher in the header overrides. - Mobile-First Design. The interface is optimised for a smartphone in portrait orientation.
- Medication Management. Add, edit, and delete medications. Two types:
unit(pills / pieces) — supports fractional doses (1/4,1/2,3/4,1 1/2, …).liquid— free-text amounts like15ml,20 drops.
- Visual Weekly Schedule Editor. Set per-day, per-slot dosages. Each slot has its own time-of-day picker. A per-slot "every day" checkbox propagates the slot's meds to every weekday when saving.
- Schedule Versioning. When the schedule is changed, the old line is archived with a trailing date in
schedule.txt. This preserves what was scheduled on each day for accurate history display and backfill. Note: The schedule can only be changed once per day (per weekday); a second change on the same day overwrites the first. - Configurable Time Format. Auto / 24-hour / 12-hour (AM/PM), stored per user.
- Daily "Today" View. Lists everything scheduled for the current day, grouped by time slot, with one-tap
Mark as Taken/Revertbuttons. - History with Period Selection. History view offers Day / Week / Month / Year periods with prev/next navigation.
- Day view: full detail with per-slot taken/missing breakdown.
- Week / Month / Year: collapsed date lines with colour coding — green (all taken), yellow (incomplete), red (none taken). Click any date to expand its detail.
- CSV Export: download the currently displayed period as a CSV file.
- Automatic History Backfill. When a user logs in, any missing days between their earliest history entry and today are automatically filled with "not taken" entries using the schedule that was active on each day. This means days without login still appear correctly in the history.
- File-Based. Everything lives under
data/as plain text — no database required.
Roles
| Role | Today | Schedule | Meds | History | Admin |
|---|---|---|---|---|---|
| Super admin | ✅ | ✅ | ✅ edit | ✅ | ✅ (protected) |
| Admin | ✅ | ✅ | ✅ edit | ✅ | ✅ |
| Regular user | ✅ | ✅ | ✅ edit | ✅ (own) | ❌ |
| Read-only | ❌ | ❌ | ✅ view | ✅ (allowed users) | ❌ |
Server Requirements
- A web server with PHP support (PHP 7.4 or newer). Apache, Nginx, Caddy, or lighttpd all work.
- Not compatible with static-only servers — all backend logic runs in PHP.
Installation
-
Copy files to a directory served by your web server, e.g.
/var/www/html/med/. -
Permissions. The PHP process must be able to write to
data/:chown -R www-data:www-data /path/to/med/data chmod -R u+rwX,go+rX /path/to/med/data -
Create your configuration.
cp config.php.sample config.phpThen edit
config.php:- Set
'super_admin' => 'your_login' - Optionally edit the default seed users inside
load_users()
- Set
-
First login. Open the app URL. On the first request,
data/users.jsonis created from the seed users. Log in as the super admin, then use the Admin tab to add real users.
Install on Android (Hermit WebApp)
MedTracker exposes a Web App Manifest and a full set of icons (16 – 512 px). Install via Hermit — Lite Apps Browser.
Data File Format
meds.txt
One med per line: id;short;full;type or id;short;full;type;YYYY-MM-DD for archived meds. The trailing date means the med was deactivated on that date. Archived meds still appear in history but not in the active med list. IDs are never reused.
schedule.txt
One row per weekday: day;<slot1>;<slot2>;… where each slot is 0 or medId:amount[,medId:amount,…].
Historical lines have a trailing date: day;<slot1>;…;2026-05-19. This means the line was replaced starting on that date. The schedule can only be changed once per day (per weekday); changing it archives the old active line with the next occurrence of that weekday as the archive date.
history.txt
One row per day: date;day;<slot1>;<slot2>;…;meta_times. Slot-item suffix is o (taken) or n (missed).
users.json
{
"admin": {
"realname": "Administrator",
"password": "$2y$...hashed...",
"isAdmin": true,
"isReadOnly": false,
"viewUsers": ["*"]
}
}
prefs.json
Per-user preferences stored in each user's data directory:
{
"lang": "de",
"time_format": "de",
"default_startup_user": "",
"default_history_view": "week"
}
tokens.json
Remember-me tokens stored in data/tokens.json (auto-managed, protected by data/.htaccess).
Security notes
- Passwords: bcrypt hashes, never plaintext.
config.phpis in.gitignore— don't commit secrets.- Use HTTPS (required for remember-me cookies).
data/should not be web-browsable — keep the provideddata/.htaccess.
Versioning & updates
- Version is shown top-right as
v<major>.<minor>.<patch>. - Update = pull + deploy; no migrations required.
- The current version is v1.25.0.