From 01f2dac2d6b5d3cfc3d37851324df2bda112f8d1 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sun, 5 Apr 2026 17:52:07 -0400 Subject: [PATCH] Fix session_start guards, add missing API routes, rewrite README - Added session_status() === PHP_SESSION_NONE guard to six API files (custom_fields, revoke_api_key, manage_templates, generate_api_key, get_template, manage_recurring) that called bare session_start() after RateLimitMiddleware had already started the session - Registered /api/notifications.php and /api/user_avatar.php in index.php router (were missing, served only by direct file access) - Complete README rewrite: remove all Discord references (Matrix/hookshot is the only external notification method), add hwmonDaemon API docs, document all TDS v1.2 features (kanban, charts, SLA, command palette, notification bell, watcher avatars, @mention, etc.), fix keyboard shortcuts table, add Matrix/LDAP env vars to setup section Co-Authored-By: Claude Sonnet 4.6 --- README.md | 230 +++++++++++++++++++++++++++++---------- api/custom_fields.php | 2 +- api/generate_api_key.php | 2 +- api/get_template.php | 2 +- api/manage_recurring.php | 2 +- api/manage_templates.php | 2 +- api/revoke_api_key.php | 2 +- index.php | 8 ++ 8 files changed, 184 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 0b2997a..496d862 100644 --- a/README.md +++ b/README.md @@ -23,25 +23,28 @@ Tinker Tickets uses the **LotusGuild Terminal Design System**. For all styling, ## Design Decisions The following features are intentionally **not planned** for this system: -- **Email Integration**: Discord webhooks are the chosen notification method -- **SLA Management**: Not required for internal infrastructure use +- **Email Integration**: Matrix (hookshot webhook) is the chosen external notification method - **Time Tracking**: Out of scope for current requirements - **OAuth2/External Identity Providers**: Authelia is the only approved SSO method ## Core Features ### Dashboard & Ticket Management -- **View Modes**: Toggle between Table view and Kanban card view -- **Collapsible Sidebar**: Click the arrow to collapse/expand the filter sidebar -- **Inline Ticket Preview**: Hover over ticket IDs for a quick preview popup (300ms delay) -- **Stats Widgets**: Clickable cards for quick filtering (Open, Critical, Unassigned, Today's tickets) +- **View Modes**: Toggle between Table view and Kanban card view (drag-and-drop status changes) +- **Right Drawer Preview**: Click any ticket title to open a quick-preview panel without navigating away +- **Stats Widgets**: Clickable cards for quick filtering (Open, Critical, Unassigned, Today's tickets) with live trend indicators +- **Charts**: Priority distribution donut, status breakdown donut, and category bar chart (Chart.js, CDN) +- **Team Workload**: Collapsible panel showing open ticket count per assignee with progress bars - **Full-Text Search**: Search across tickets, descriptions, and metadata -- **Advanced Search**: Date ranges, priority ranges, user filters with saved filter support -- **Ticket Assignment**: Assign tickets to specific users with quick-assign from dashboard -- **Priority Tracking**: P1 (Critical) to P5 (Minimal Impact) with color-coded indicators +- **Advanced Search**: Date ranges (Flatpickr), priority ranges, user filters +- **Saved Filters**: Save and recall filter presets; quick-switch pills above the table +- **Column Visibility**: Toggle which dashboard table columns are shown; persisted in localStorage +- **Ticket Assignment**: Assign tickets to specific users with typeahead search +- **Priority Tracking**: P1 (Critical) to P5 (Minimal Impact) with color-coded indicators and status dots - **Custom Categories**: Hardware, Software, Network, Security, General - **Ticket Types**: Maintenance, Install, Task, Upgrade, Issue, Problem -- **Export**: Export selected tickets to CSV or JSON format +- **Skeleton Loaders**: Loading placeholders during filter changes and data refresh +- **Export**: Export filtered tickets to CSV or JSON format - **Ticket Linking**: Reference other tickets in comments using `#123456789` format ### Ticket Visibility Levels @@ -51,19 +54,24 @@ The following features are intentionally **not planned** for this system: ### Workflow Management - **Status Transitions**: Enforced workflow rules (Open → Pending → In Progress → Closed) +- **Comment Requirements**: Transitions that require a comment open an inline modal before committing the change - **Workflow Designer**: Visual admin UI at `/admin/workflow` to configure transitions - **Workflow Validation**: Server-side validation prevents invalid status changes - **Admin Controls**: Certain transitions can require admin privileges -- **Comment Requirements**: Optional comment requirements for specific transitions ### Collaboration Features - **Markdown Comments**: Full Markdown support with live preview, toolbar, and table rendering -- **@Mentions**: Tag users in comments with autocomplete +- **@Mentions**: Tag users in comments with `@` autocomplete (typeahead); triggers Matrix notification to mentioned user - **Comment Edit/Delete**: Comment owners and admins can edit or delete comments - **Auto-linking**: URLs in comments are automatically converted to clickable links -- **File Attachments**: Upload files to tickets with drag-and-drop support -- **Ticket Dependencies**: Link tickets as blocks/blocked-by/relates-to/duplicates -- **Activity Timeline**: Complete audit trail of all ticket changes +- **File Attachments**: Upload files to tickets with drag-and-drop; image attachments display as thumbnails with lightbox zoom +- **Ticket Cloning**: Duplicate any ticket with a single click; auto-links as `relates_to` +- **Ticket Dependencies**: Link tickets as blocks / blocked-by / relates-to / duplicates +- **Duplicate Detection**: Similarity check on ticket title surfaces potential duplicates with one-click linking +- **Activity Timeline**: Full `lt-timeline` audit trail — color-coded by event type (status, comment, assign, attach) +- **Watcher Avatars**: Avatar group shows who is watching a ticket; tooltip lists all names +- **SLA Timer**: P1/P2 tickets display a live elapsed-time banner with progress bar (P1 = 8 h, P2 = 24 h, P3 = 72 h) +- **Priority Alert Banner**: P1 shows a sticky error banner; P2 shows a warning banner — dismissible per session ### Ticket Templates - **Template Management**: Admin UI at `/admin/templates` to create/edit templates @@ -92,40 +100,44 @@ The following features are intentionally **not planned** for this system: - **SSO Integration**: Authelia authentication with LLDAP backend - **Role-Based Access**: Admin and standard user roles - **User Groups**: Groups displayed in settings modal, used for visibility +- **User Avatars**: JPEG avatars fetched from lldap via LDAP; cached locally (`/api/user_avatar.php`) - **User Activity**: View per-user stats at `/admin/user-activity` - **Session Management**: Secure PHP session handling with timeout ### Bulk Actions (Admin Only) - **Bulk Close**: Close multiple tickets at once -- **Bulk Assign**: Assign multiple tickets to a user +- **Bulk Assign**: Assign multiple tickets to a user (typeahead search) - **Bulk Priority**: Change priority for multiple tickets - **Bulk Status**: Change status for multiple tickets - **Checkbox Click Area**: Click anywhere in the checkbox cell to toggle -### Admin Pages -Access all admin pages via the **Admin dropdown** in the dashboard header. +### In-App Notifications +- **Notification Bell**: Header bell icon with unread count badge; polls every 60 s +- **Notification Sources**: Ticket assigned to you, comment on your ticket, status change on watched ticket, @mention +- **Mark All Read**: Click the bell or "Mark all read" to clear the badge +- **Powered by audit_log**: No extra table — notifications are derived from existing audit trail -| Route | Description | -|-------|-------------| -| `/admin/templates` | Create and edit ticket templates | -| `/admin/workflow` | Visual workflow transition designer | -| `/admin/recurring-tickets` | Manage recurring ticket schedules | -| `/admin/custom-fields` | Define custom fields per category | -| `/admin/user-activity` | View per-user activity statistics | -| `/admin/audit-log` | Browse all audit log entries | -| `/admin/api-keys` | Generate and manage API keys | +### Matrix Notifications (hookshot) +- **Ticket Created**: Fires when any ticket is created (manual or via API) +- **Status Changed**: Fires on every status transition +- **@Mentions**: Mentioned users receive a direct Matrix notification +- **Assignment**: Optional — set `MATRIX_NOTIFY_ASSIGNMENTS=1` to enable +- **Comments**: Optional — set `MATRIX_NOTIFY_COMMENTS=1` to enable +- **Watcher Alerts**: Watchers receive Matrix notifications on status changes (resolved via Synapse Admin API) +- **Rich Payloads**: JSON payloads sent to hookshot generic webhook; format ticket links using `APP_DOMAIN` -### Notifications -- **Discord Integration**: Webhook notifications for ticket creation and updates -- **Rich Embeds**: Color-coded priority indicators and ticket links -- **Dynamic URLs**: Ticket links adapt to the server hostname (set `APP_DOMAIN` in `.env`) +### Command Palette (Ctrl+K) +- **Global Access**: Available on every page via `Ctrl+K` or `⌘K` button in header +- **Quick Navigation**: Dashboard, New Ticket, My Tickets, admin pages +- **Recent Tickets**: Last 5 viewed tickets (stored in localStorage) +- **Filter Shortcuts**: Apply common filters directly from palette ### Keyboard Shortcuts | Shortcut | Action | |----------|--------| +| `Ctrl/Cmd + K` | Open command palette (global) | | `Ctrl/Cmd + E` | Toggle edit mode (ticket page) | | `Ctrl/Cmd + S` | Save changes (ticket page) | -| `Ctrl/Cmd + K` | Focus search box (dashboard) | | `N` | New ticket (dashboard) | | `J` / `K` | Next / previous row (dashboard table) | | `Enter` | Open selected ticket (dashboard) | @@ -135,7 +147,7 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. | `?` | Show keyboard shortcuts help | ### Security Features -- **CSRF Protection**: Token-based protection with constant-time comparison +- **CSRF Protection**: Token-based protection with constant-time comparison; token rotated after each write - **Rate Limiting**: Session-based AND IP-based rate limiting to prevent abuse - **Security Headers**: CSP with nonces (no unsafe-inline), X-Frame-Options, X-Content-Type-Options - **SQL Injection Prevention**: All queries use prepared statements with parameter binding @@ -144,6 +156,31 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. - **Visibility Enforcement**: Access checks on ticket views, downloads, and bulk operations - **Collision-Safe IDs**: Ticket IDs verified unique before creation +## Automated Ticket Creation (hwmonDaemon) + +[hwmonDaemon](https://code.lotusguild.org/LotusGuild/hwmonDaemon) runs on all servers and creates tickets automatically for hardware/health issues. It calls the **standalone API endpoint** at the document root: + +``` +POST /create_ticket_api.php +Authorization: Bearer +Content-Type: application/json + +{ + "title": "[hostname][auto][production][hardware][single-node] SMART issues on /dev/sda", + "description": "...", + "priority": "2", + "category": "Hardware", + "type": "Issue" +} +``` + +**Key behaviours:** +- Authenticated via `Authorization: Bearer` header — API key stored in `/etc/hwmonDaemon/.env` +- **Deduplication**: Generates a SHA-256 hash from the issue category, hostname, and device; rejects duplicate tickets within 24 hours +- Cluster-wide issues (Ceph health, etc.) deduplicate across all nodes (hostname excluded from hash) +- Matrix notification sent automatically after ticket creation +- API key must be generated at `/admin/api-keys`; the key goes in hwmonDaemon's `/etc/hwmonDaemon/.env` as `TICKET_API_KEY` + ## Technical Architecture ### Backend @@ -158,6 +195,8 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. - **Markdown**: Custom markdown parser with toolbar - **Terminal UI**: Box-drawing characters, monospace fonts, CRT effects - **Mobile Responsive**: Touch-friendly controls, responsive layouts +- **Chart.js**: CDN-loaded on dashboard only — priority/status/category charts +- **Flatpickr**: CDN-loaded on dashboard only — date range filter pickers ### Database Tables @@ -167,9 +206,10 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. | `ticket_comments` | Markdown-supported comments | | `ticket_attachments` | File attachment metadata | | `ticket_dependencies` | Ticket relationships | +| `ticket_watchers` | Per-user ticket subscriptions | | `users` | User accounts with groups | -| `user_preferences` | User settings | -| `audit_log` | Complete audit trail | +| `user_preferences` | User settings (rows per page, notification opts, notif_last_seen) | +| `audit_log` | Complete audit trail (also powers in-app notifications) | | `status_transitions` | Workflow configuration | | `ticket_templates` | Reusable templates | | `recurring_tickets` | Scheduled tickets | @@ -201,6 +241,7 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. | Endpoint | Method | Description | |----------|--------|-------------| +| `/create_ticket_api.php` | POST | Create ticket via API key (hwmonDaemon, external tools) | | `/api/update_ticket.php` | POST | Update ticket with workflow validation | | `/api/assign_ticket.php` | POST | Assign ticket to user | | `/api/add_comment.php` | POST | Add comment to ticket | @@ -224,7 +265,10 @@ Access all admin pages via the **Admin dropdown** in the dashboard header. | `/api/custom_fields.php` | CRUD | Custom field definitions/values (admin) | | `/api/saved_filters.php` | CRUD | Saved filter combinations | | `/api/user_preferences.php` | GET/POST | User preferences | +| `/api/notifications.php` | GET/POST | In-app notifications (bell) | +| `/api/user_avatar.php` | GET | User avatar from lldap (cached JPEG) | | `/api/audit_log.php` | GET | Audit log entries (admin) | +| `/api/watch_ticket.php` | POST | Watch/unwatch a ticket | | `/api/health.php` | GET | Health check | ## Project Structure @@ -251,13 +295,16 @@ tinker_tickets/ │ ├── manage_recurring.php # CRUD: Recurring tickets (admin) │ ├── manage_templates.php # CRUD: Templates (admin) │ ├── manage_workflows.php # CRUD: Workflow rules (admin) +│ ├── notifications.php # GET/POST: In-app notification bell │ ├── revoke_api_key.php # POST: Revoke API key (admin) │ ├── saved_filters.php # CRUD: Saved filter combinations │ ├── ticket_dependencies.php # GET/POST/DELETE: Ticket dependencies │ ├── update_comment.php # POST: Update comment (owner/admin) │ ├── update_ticket.php # POST: Update ticket (workflow validation) │ ├── upload_attachment.php # GET/POST: List or upload attachments -│ └── user_preferences.php # GET/POST: User preferences +│ ├── user_avatar.php # GET: LDAP avatar proxy with disk cache +│ ├── user_preferences.php # GET/POST: User preferences +│ └── watch_ticket.php # POST: Watch/unwatch a ticket ├── assets/ │ ├── css/ │ │ ├── base.css # LotusGuild Terminal Design System (copied from web_template) @@ -267,11 +314,11 @@ tinker_tickets/ │ │ ├── advanced-search.js # Advanced search modal │ │ ├── ascii-banner.js # ASCII art banner (rendered in boot overlay on first visit) │ │ ├── base.js # LotusGuild JS utilities — window.lt (copied from web_template) -│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar +│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar + charts │ │ ├── keyboard-shortcuts.js # Keyboard shortcuts (uses lt.keys) │ │ ├── markdown.js # Markdown rendering + ticket linking (XSS-safe) │ │ ├── settings.js # User preferences -│ │ ├── ticket.js # Ticket + comments + visibility +│ │ ├── ticket.js # Ticket + comments + visibility + @mention │ │ ├── toast.js # Deprecated shim (no longer loaded — all callers use lt.toast directly) │ │ └── utils.js # escapeHtml (→ lt.escHtml), getTicketIdFromUrl, showConfirmModal │ └── images/ @@ -284,12 +331,17 @@ tinker_tickets/ ├── cron/ │ └── create_recurring_tickets.php # Process recurring ticket schedules ├── helpers/ -│ └── ResponseHelper.php # Standardized JSON responses +│ ├── CacheHelper.php # File-based cache (stats, avatars) +│ ├── Database.php # Centralized mysqli connection +│ ├── NotificationHelper.php # Matrix hookshot webhook events +│ ├── SynapseHelper.php # Resolves usernames → Matrix IDs via Synapse admin API +│ └── UrlHelper.php # Canonical ticket URLs using APP_DOMAIN ├── middleware/ +│ ├── ApiKeyAuth.php # Bearer token auth for external API (hwmonDaemon) │ ├── AuthMiddleware.php # Authelia SSO integration │ ├── CsrfMiddleware.php # CSRF protection │ ├── RateLimitMiddleware.php # Session + IP-based rate limiting -│ └── SecurityHeadersMiddleware.php # CSP with nonces, security headers +│ └── SecurityHeadersMiddleware.php # CSP headers with per-request nonce generation ├── models/ │ ├── ApiKeyModel.php # API key generation/validation │ ├── AuditLogModel.php # Audit logging + timeline @@ -298,7 +350,8 @@ tinker_tickets/ │ ├── CustomFieldModel.php # Custom field definitions/values │ ├── DependencyModel.php # Ticket dependencies │ ├── RecurringTicketModel.php # Recurring ticket schedules -│ ├── StatsModel.php # Dashboard statistics +│ ├── SavedFiltersModel.php # Saved filter combinations +│ ├── StatsModel.php # Dashboard statistics (cached) │ ├── TemplateModel.php # Ticket templates │ ├── TicketModel.php # Ticket CRUD + visibility + collision-safe IDs │ ├── UserModel.php # User management + groups @@ -307,9 +360,10 @@ tinker_tickets/ ├── scripts/ │ ├── add_closed_at_column.php # Migration: add closed_at column to tickets │ ├── add_comment_updated_at.php # Migration: add updated_at column to ticket_comments -│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads +│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads (run manually or via cron) │ └── create_dependencies_table.php # Create ticket_dependencies table ├── uploads/ # File attachment storage +│ └── avatars/ # lldap avatar disk cache ├── views/ │ ├── admin/ │ │ ├── ApiKeysView.php # API key management @@ -320,9 +374,12 @@ tinker_tickets/ │ │ ├── UserActivityView.php # User activity report │ │ └── WorkflowDesignerView.php # Workflow transition designer │ ├── CreateTicketView.php # Ticket creation with visibility -│ ├── DashboardView.php # Dashboard with kanban + sidebar -│ └── TicketView.php # Ticket view with visibility editing +│ ├── DashboardView.php # Dashboard with kanban + sidebar + charts +│ ├── layout_footer.php # Shared footer (notification polling, boot sequence) +│ ├── layout_header.php # Shared header (nav, command palette, theme toggle) +│ └── TicketView.php # Ticket view with timeline, SLA, watcher avatars ├── .env # Environment variables (GITIGNORED) +├── create_ticket_api.php # External API endpoint (hwmonDaemon, API-key auth) ├── README.md # This file └── index.php # Main router ``` @@ -355,26 +412,60 @@ DB_HOST=your_db_host DB_USER=your_db_user DB_PASS=your_password DB_NAME=ticketing_system -DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... APP_DOMAIN=your.domain.example TIMEZONE=America/New_York ``` -**Note**: `APP_DOMAIN` is required for Discord webhook ticket links to work correctly. Without it, links will default to localhost. +Matrix notification variables (all optional): +```env +# hookshot generic webhook URL — send events to Matrix room +MATRIX_WEBHOOK_URL=https://matrix.lotusguild.org/_hookshot/webhook/... + +# Comma-separated Matrix user IDs to @mention on new tickets / status changes +MATRIX_NOTIFY_USERS=@jared:matrix.lotusguild.org,@ops:matrix.lotusguild.org + +# Matrix homeserver domain (used to build Matrix user IDs from LLDAP usernames) +MATRIX_DOMAIN=matrix.lotusguild.org + +# Synapse internal URL and admin token (used to resolve usernames → Matrix IDs for watcher DMs) +SYNAPSE_ADMIN_URL=http://10.10.10.29:8008 +SYNAPSE_ADMIN_TOKEN=your_synapse_admin_token + +# Optional: send Matrix notification on comments and/or assignments +MATRIX_NOTIFY_COMMENTS=0 +MATRIX_NOTIFY_ASSIGNMENTS=1 +``` + +LDAP/avatar variables (optional): +```env +LDAP_ENABLED=true +LDAP_HOST=10.10.10.39 +LDAP_PORT=3890 +LDAP_BIND_DN=uid=tinker-tickets,ou=people,dc=example,dc=com +LDAP_BIND_PW=your_bind_password +LDAP_BASE_DN=dc=example,dc=com +LDAP_USER_BASE=ou=people,dc=example,dc=com +AVATAR_CACHE_TTL=3600 +``` + +**Note**: `APP_DOMAIN` is required for Matrix webhook ticket links to work correctly. Without it, links will default to localhost. ### 2. Cron Jobs -Add to crontab for recurring tickets: +Add to crontab for recurring tickets and optional cleanup: ```bash # Run every hour to create scheduled recurring tickets 0 * * * * php /path/to/tinkertickets/cron/create_recurring_tickets.php + +# Optional: clean up orphaned uploads weekly +0 3 * * 0 php /path/to/tinkertickets/scripts/cleanup_orphan_uploads.php ``` ### 3. File Uploads Ensure the `uploads/` directory exists and is writable: ```bash -mkdir -p /path/to/tinkertickets/uploads +mkdir -p /path/to/tinkertickets/uploads/avatars chown www-data:www-data /path/to/tinkertickets/uploads chmod 755 /path/to/tinkertickets/uploads ``` @@ -389,6 +480,16 @@ Tinker Tickets uses Authelia for SSO. User information is passed via headers: Admin users must be in the `admin` group in LLDAP. +### 5. hwmonDaemon API Key + +1. Go to `/admin/api-keys` and generate a new key named e.g. "hwmonDaemon" +2. Copy the displayed key (shown only once) +3. On each monitored server, create `/etc/hwmonDaemon/.env`: + ```env + TICKET_API_KEY=your_generated_key + TICKET_API_URL=http://10.10.10.45/create_ticket_api.php + ``` + ## Developer Notes Key conventions and gotchas for working with this codebase: @@ -398,46 +499,54 @@ Key conventions and gotchas for working with this codebase: 3. **Admin check**: `$_SESSION['user']['is_admin'] ?? false` 4. **Config path**: `config/config.php` (not `config/db.php`) 5. **Comments table**: `ticket_comments` (not `comments`) -6. **CSRF**: Required for all POST/DELETE requests via `X-CSRF-Token` header -7. **Cache busting**: Use `?v=YYYYMMDD` query params on JS/CSS files +6. **CSRF**: Required for all POST/DELETE requests via `X-CSRF-Token` header; bootstrap.php rotates token and returns it in `csrf_token` field of all `apiRespond()` responses +7. **Cache busting**: `ASSET_VERSION` is auto-computed from asset file mtimes; override with `ASSET_VERSION=` in `.env` 8. **Ticket linking**: Use `#123456789` in markdown-enabled comments 9. **User groups**: Stored in `users.groups` as comma-separated values 10. **API routing**: All API endpoints must be registered in `index.php` router -11. **Session in APIs**: `RateLimitMiddleware` starts the session — do not call `session_start()` again +11. **Session in APIs**: `RateLimitMiddleware` starts the session — guard subsequent `session_start()` calls with `if (session_status() === PHP_SESSION_NONE)` 12. **Database collation**: Use `utf8mb4_general_ci` (not `unicode_ci`) for new tables -13. **Discord URLs**: Set `APP_DOMAIN` in `.env` for correct ticket URLs in Discord notifications +13. **Matrix URLs**: Set `APP_DOMAIN` in `.env` for correct ticket URLs in Matrix notifications 14. **Ticket ID extraction**: Use `getTicketIdFromUrl()` helper in JS files 15. **CSP nonces**: All inline `