Commit graph

42 commits

Author SHA1 Message Date
c8ac6ae3fa Add Hub auth/login.php endpoint — fixes Web client login
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>
2026-03-27 21:41:24 +00:00
1dacefcf70 Add Hub Messages, Files, Users, Reactions, and Pins APIs
Complete backend for SprintChat Hub migration:
- Messages: send, edit, delete, list (paginated cursor), thread, search
- Files: upload (multipart), download, thumbnail, info, list
- Users: get, getByIds, search, status (online detection)
- Reactions: add, remove, list (grouped by emoji)
- Pins: pin, unpin, list (with message content)
- Channel stats: member/message/pinned/unread counts

4 new DB tables: Hub_Messages, Hub_Files, Hub_Reactions, Hub_PinnedPosts
21 new endpoints added to PUBLIC_ROUTES

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:03:14 +00:00
629c7d2cef Add Hub Channels API — CRUD endpoints for channel management
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>
2026-03-27 01:06:14 +00:00
eb033b6d4f Move phpcheck to webhook dir for access 2026-03-25 03:22:33 +00:00
f02a0d0adb Move phpcheck to api dir 2026-03-25 03:21:53 +00:00
32c2cc1381 Add pretty HTML dashboard for team tasks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:30:51 +00:00
d1630e69b2 Add team task tracker endpoints to public routes
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>
2026-03-23 19:04:15 +00:00
e3933ce0c8 Add team task tracker API endpoints
New endpoints at api/tasks/team/ for centralized bot task tracking:
- POST create.php — register a new task
- POST update.php — change status, add notes (auto-sets PausedOn/CompletedOn)
- GET active.php — list active/paused tasks, optional BotName filter
- GET list.php — full listing with filters (BotName, Status, Channel, AssignedBy, Since) and pagination

Includes schema.sql for the TeamTasks table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:00:12 +00:00
601245d969 fix: harden auth middleware — exact route matching, remove admin bypass, add cron secret
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
2026-03-23 01:43:43 +00:00
John
dde811d876 Enable magic OTP (123456) for Apple app review testing 2026-03-20 05:22:17 +00:00
John Mizerek
66e441b295 Add portal/getSettings and portal/updateSettings PHP endpoints
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>
2026-03-17 15:42:24 -07:00
John Mizerek
628172c31c Add TaskTypeID=0 to order task creation INSERTs
submitCash.php, submit.php, and webhook.php were creating kitchen
tasks without TaskTypeID, which is NOT NULL with no default. This
caused cash order submission to fail with a SQL error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 23:21:41 -07:00
John Mizerek
b5394aec7c Include PlatformFee in order detail total calculation
getDetail.php was computing total as subtotal + tax + tip, omitting the
Payfrit service fee. This caused the order detail view to show a lower
total than what the cart/checkout displayed at order time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:39:24 -07:00
John Mizerek
07abcee2fd Create kitchen task on order submission for KDS display
submitCash.php had no task creation — cash orders were invisible to KDS.
submit.php also lacked it (tab orders never hit webhook.php).
Both now create "Prepare Order #X for Table" task at StatusID=1.
submit.php includes duplicate guard since webhook.php also creates tasks
for card payments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:02:02 -07:00
John Mizerek
5d9be247c8 Add beacons/lookupByMac.php and beacons/wipe.php endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 19:13:21 -07:00
John Mizerek
04d09cfc4e Fix item prices returning as strings instead of floats in JSON
MySQL PDO returns DECIMAL/FLOAT columns as strings, causing json_encode
to emit them as JSON strings ("9.99") instead of numbers (9.99).
Android's Gson Map parsing fails the as? Number cast on strings,
defaulting to 0.0. Cast Price to (float) before building the response.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 17:24:57 -07:00
John Mizerek
8b54145d9d Add IsServiceBell flag to task types
Only Call Staff, Chat With Staff, and Pay With Cash should appear
on the customer service bell. New column distinguishes service bell
items from internal task types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:52:23 -07:00
John Mizerek
5761ed3e88 Standardize UUID format: generateUUID() now returns unhyphenated 32-char hex
- Remove vsprintf hyphenation from generateUUID() in helpers.php
- Remove redundant str_replace('-', '', ...) wrappers in callers
- Fix grants/create, tabs/open, orders/getOrCreateCart which were storing hyphenated UUIDs
- Cast prices to float in getForBuilder.php
- Uppercase auth response keys (TOKEN, USERID, FIRSTNAME)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:43:02 -07:00
John Mizerek
bb03cb0533 Cast prices to float in getForBuilder.php
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 15:06:42 -07:00
John Mizerek
6ec5f812e1 Fix: restore cd and stderr redirect for platform-images shell_exec
Got reverted during earlier merge conflict resolution. Without cd,
node can't find modules. Without stderr redirect, log lines corrupt
the JSON output causing json_decode to fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 10:19:31 -07:00
John Mizerek
4c5d8b07dc Revert og:image header extraction — header is manual upload
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 09:46:21 -07:00
John Mizerek
d7cd6774c7 Extract og:image as header during wizard discovery
- analyzeMenuUrl.php: Extract og:image and JSON-LD image during discovery, return as headerImageUrl
- downloadImages.php: Add User-Agent header, detect image format from content-type/magic bytes, update HeaderImageExtension in DB

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 09:36:43 -07:00
John Mizerek
add3842db3 Return priceMap from platform_images mode
order.online embeds displayPrice alongside images — now extracted and
returned so items missing prices from image-based menus get filled in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 02:36:01 -07:00
John Mizerek
436f83d67e Fix: use queryTimed instead of undefined $pdo for ImageExtension update
$pdo doesn't exist in this file — all DB queries use queryTimed/queryOne
from helpers.php.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 02:08:26 -07:00
John Mizerek
5d34d8378b Fix: set ImageExtension after downloading item images in saveWizard
downloadItemImage() saved files to disk but never updated the DB column,
so items appeared to have no images despite files existing on disk.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:57:39 -07:00
John Mizerek
280394f5e0 Move app from /opt to /var/www/payfrit-api (standard Linux web dir)
- 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>
2026-03-14 22:32:55 -07:00
John Mizerek
aa986507fd Remove all Lucee references — uploads now live under /opt/payfrit-api
- 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>
2026-03-14 22:26:52 -07:00
John Mizerek
28d86ba6e5 Fix production webroot path — both servers use /opt/lucee/tomcat/webapps/ROOT
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>
2026-03-14 22:19:10 -07:00
John Mizerek
4a4a098551 Fix upload paths to use Lucee webroot and accept uppercase OTP keys
Upload endpoints were saving files to PHP's DOCUMENT_ROOT instead of
the Lucee webroot where the Android app loads them from. Also fix
verifyLoginOTP and verifyOTP to accept both UUID/OTP and uuid/otp keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:04:27 -07:00
John Mizerek
3d9084d848 Add platform_images mode with stealth Playwright for order.online
- Stealth Playwright bypasses Cloudflare bot protection
- Multiple selector strategies for menu item cards
- Fuzzy name matching (exact, normalized, partial)
- Background-image extraction as fallback
- Script auto-created at /opt/playwright/platform-images.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 20:22:10 -07:00
John Mizerek
24849c01e4 Fetch contact/about page during discovery for business info
- Detect contact/about/location/hours links on main page
- Fetch contact page and extract phone, address, hours
- Phone: regex for US phone formats + tel: links
- Address: US street address pattern (number + street type)
- Hours: day + time range patterns from plain text
- Overrides bad JSON-LD data with actual contact page info

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:26:19 -07:00
John Mizerek
b48f20011d Fix address parsing: proper US format detection (Street, City, ST ZIP)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:11:41 -07:00
John Mizerek
1df69463a8 Fix discovery business info extraction
- Prefer title tag for name over JSON-LD (sites often put address in LD name)
- Parse full address string into components (addressLine1, city, state, zip)
- Handle newlines in addresses (Squarespace puts newlines in JSON-LD)
- Convert 24h hours to 12h format
- Strip country suffix from addresses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:09:54 -07:00
John Mizerek
a14213151f Extract per-menu schedule in extract_page mode
- Claude prompt now asks for menuSchedule (days/times this menu is served)
- Separates menu schedule from overall business hours
- Returns menuSchedule in response for frontend to use

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:45:05 -07:00
John Mizerek
571930ed25 Extract business info during discovery phase
- Parse JSON-LD structured data (Restaurant, FoodEstablishment, etc.)
- Extract phone from tel: links, address from og: meta tags
- Return businessInfo in discovery response so sub-pages don't need it

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:34:55 -07:00
John Mizerek
4ac13de09d Add discovery + multi-page extract modes for setup wizard
- Discovery mode: quick Playwright crawl returns detected menu sub-pages
- Extract_page mode: processes single menu page through Claude individually
- More aggressive HTML stripping: removes SVG, nav, footer, form, attributes
- Increased truncation limit from 100KB to 200KB for generic fallback path
- Enables interactive wizard flow: discover → confirm → extract each page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:03:54 -07:00
John Mizerek
552c404cf6 Server-side address parsing: split combined address into components
Claude returns address as one string with country suffix.
Now strips "United States/USA", extracts ZIP, state, splits
address line and city server-side before sending to wizard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:45:34 -07:00
John Mizerek
a7464545df Support multiple menus in wizard import (Brunch/Lunch/Dinner)
- Updated Claude prompt to detect separate menus vs categories
- Added platformImageMap and subPagesVisited parsing from Playwright
- Bumped Playwright wait from 5s to 10s for sub-page crawling
- saveWizard.php creates separate Menus rows and assigns categories/items
  to the correct menu based on each item's "menu" field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:29:38 -07:00
John Mizerek
08ef54976f Port Twilio SMS integration from CFML to PHP
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>
2026-03-14 16:02:34 -07:00
John Mizerek
4d806d4e1e Port admin, cron, and receipt endpoints from CFML to PHP
- admin/quickTasks: list, create, save, delete
- admin/scheduledTasks: list, save, delete, toggle, run, runDue
- cron: expireStaleChats, expireTabs
- receipt: public order receipt page (no auth, UUID-secured)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:57:25 -07:00
John Mizerek
bd913bb46d Fix PUBLIC_ROUTES case-sensitivity bug in runAuth (strtolower path vs mixed-case routes) 2026-03-14 15:11:04 -07:00
John Mizerek
1f81d98c52 Initial PHP API migration from CFML
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.
2026-03-14 14:26:59 -07:00