The web client calls auth/login to authenticate users, but this endpoint
was missing from the Hub API. Creates:
- api/hub/auth/login.php: password-based auth with token generation
- Hub_Users table: stores bcrypt password hashes and session tokens
- Auto-provisions on first login (creates credentials for existing agents)
- Adds route to PUBLIC_ROUTES in helpers.php
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoints under /api/hub/channels/:
- create.php: Create channel with type (public/private/direct), auto-add creator as owner
- list.php: List channels with filters (type, agent membership, archived, pagination)
- get.php: Get channel by ID or Name, includes member list
- update.php: Update display name, purpose, archive status (admin/owner only)
- delete.php: Hard-delete channel (owner only), FK cascade removes members
- members.php: List channel members with agent info
- join.php: Join public channels (private requires invite)
- leave.php: Leave channel (owners blocked from leaving)
Database: Hub_Channels + Hub_ChannelMembers tables with FK cascade.
Task #59 (T51-Sub1)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bot-to-bot endpoints don't have user tokens, so they need to
bypass auth middleware.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Switch str_contains() to exact match ($path === $route) in PUBLIC_ROUTES check
to prevent substring-based route bypass attacks.
2. Remove blanket /api/admin/ bypass that was letting all admin endpoints through
without authentication.
3. Add requireCronSecret() — cron/scheduled task endpoints now require a valid
X-Cron-Secret header matching the PAYFRIT_CRON_SECRET env var. Uses
hash_equals() for timing-safe comparison. Applied to:
- cron/expireStaleChats.php
- cron/expireTabs.php
- api/admin/scheduledTasks/runDue.php
Rewrites the last two production-critical CFM endpoints for the biz.payfrit.com
Lucee removal project. Both endpoints follow the existing helpers.php patterns
with queryTimed/queryOne and are added to PUBLIC_ROUTES.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Moved directory on both dev and biz servers
- Updated nginx configs on both servers
- Added appRoot() helper, uploadsRoot() uses it
- No more hardcoded /opt/payfrit-api paths in codebase
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Moved uploads from Lucee webroot to /opt/payfrit-api/uploads/
- Updated nginx on both dev and biz to alias /uploads/ to new path
- Replaced luceeWebroot() with uploadsRoot() helper
- Temp files now use /opt/payfrit-api/temp/
- No more /opt/lucee or /var/www/biz.payfrit.com references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added luceeWebroot() helper to avoid repeating the path. The previous
fix incorrectly used /var/www/biz.payfrit.com for production, but both
dev and biz use the same Lucee webroot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add sendSMS() to helpers.php using Twilio REST API with cURL,
credentials loaded from config/twilio.json. Wire into sendOTP,
loginOTP, and sendLoginOTP endpoints, replacing TODO stubs.
SMS is auto-skipped on dev environments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete port of all 163 API endpoints from Lucee/CFML to PHP 8.3.
Shared helpers in api/helpers.php (DB, auth, request/response, security).
PDO prepared statements throughout. Same JSON response shapes as CFML.