- PHP 90.8%
- Shell 9.2%
|
All checks were successful
Build and deploy / deploy (push) Successful in 1s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| api | ||
| cron | ||
| includes | ||
| install | ||
| lang | ||
| lib | ||
| static/fonts | ||
| templates | ||
| .gitignore | ||
| .gitkeep | ||
| .htaccess | ||
| admin.php | ||
| bootstrap.sh | ||
| config.example.php | ||
| dashboard.php | ||
| deploy.sh | ||
| entries.php | ||
| index.php | ||
| login.php | ||
| logout.php | ||
| overview.php | ||
| README.md | ||
| settings.php | ||
Urentracker
Hour tracking for Dutch freelancers (ZZP) to meet the 1225-hour annual criterion for tax deductions.
Sync your CalDAV calendar, set exclude rules by keyword, calendar name or category, add hours manually, and export to CSV. Multi-user with admin panel. Built on PHP 8.3 + MySQL, no Composer required.
Requirements
- PHP 8.3+ with extensions:
pdo_mysql,openssl,curl - MySQL 8.0+ or MariaDB 10.6+
- Web server: Apache (with
mod_rewrite) or nginx
Installation
1. Clone the repo
git clone <repo-url> /var/www/urentracker
2. Create config
cp config.example.php config.php
nano config.php
Fill in: database credentials, app URL, a random cron_token, and your preferred default language:
openssl rand -hex 32
3. Create database
mysql -u root -p <<EOF
CREATE DATABASE urentracker CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'urentracker'@'localhost' IDENTIFIED BY 'strong-password';
GRANT ALL ON urentracker.* TO 'urentracker'@'localhost';
EOF
mysql -u urentracker -p urentracker < install/schema.sql
4. Configure web server
Apache — make sure mod_rewrite is enabled and AllowOverride All is set for the document root. The .htaccess handles the rest.
nginx — use install/nginx.conf.example as a starting point.
5. Create first admin account
Visit /install/setup.php in your browser. The wizard creates the first admin account and then automatically blocks itself — nothing to delete.
6. Set up cron
Add to crontab for automatic CalDAV sync:
0 * * * * curl -sf "https://your-domain.com/cron/sync.php?token=YOUR_CRON_TOKEN"
Or via a Forgejo Actions workflow — see .forgejo/workflows/cron.yml.example.
Usage
| Page | What |
|---|---|
/dashboard.php |
Progress ring, monthly bars, recent hours |
/entries.php |
All entries, edit, add manually, CSV export |
/settings.php |
CalDAV, exclude rules, password, hours goal |
/admin.php |
Create and manage users |
Exclude rules
Three rule types to exclude calendar events from the hours criterion:
| Type | Behaviour |
|---|---|
| Keyword | Excludes events whose title contains this word (case-insensitive) |
| Calendar name | Excludes all events from a specific calendar |
| Category | Excludes based on the CATEGORIES tag in the calendar event |
Excluded entries remain visible but do not count toward the total.
CalDAV URL formats
| Server | URL |
|---|---|
| Nextcloud | https://cloud.example.com/remote.php/dav/calendars/USER/CALENDAR/ |
| Radicale | https://cal.example.com/USER/CALENDAR/ |
| Baikal | https://baikal.example.com/cal.php/calendars/USER/CALENDAR/ |
| Fastmail | https://caldav.fastmail.com/dav/principals/user/EMAIL/ |
| iCloud | https://caldav.icloud.com/ (use app-specific password) |
Configuration reference
All configuration lives in config.php (copied from config.example.php):
'db' => [
'host' => 'localhost',
'port' => 3306,
'name' => 'urentracker',
'user' => 'urentracker',
'pass' => 'your-password',
],
'app' => [
'url' => 'https://hours.example.com',
'cron_token' => 'random-secret', // openssl rand -hex 32
'session_ttl' => 28800, // 8 hours
'default_lang' => 'en', // 'en' or 'nl'
],
File structure
urentracker/
├── config.php ← Not in git, create from config.example.php
├── config.example.php
├── index.php
├── login.php / logout.php
├── dashboard.php
├── entries.php
├── settings.php
├── admin.php
├── includes/
│ ├── bootstrap.php ← DB, auth, i18n helpers
│ └── caldav.php ← CalDAV sync engine
├── templates/
│ ├── layout.php
│ └── styles.php
├── lang/
│ ├── en.php ← English strings
│ └── nl.php ← Dutch strings
├── api/
│ ├── sync.php ← Manual sync
│ └── export.php ← CSV export
├── cron/
│ └── sync.php ← Cron sync endpoint
├── install/
│ ├── schema.sql
│ ├── setup.php ← Self-locks after first use
│ └── nginx.conf.example
└── static/
└── fonts/
License
MIT