2025-01-01 18:35:37 -05:00
# Tinker Tickets
2024-11-30 19:26:34 -05:00
2026-04-14 12:54:00 -04:00
[](https://code.lotusguild.org/LotusGuild/tinker_tickets/actions?workflow=lint.yml)
2026-01-20 21:11:49 -05:00
A feature-rich PHP-based ticketing system designed for tracking and managing data center infrastructure issues with enterprise-grade workflow management and a retro terminal aesthetic.
2025-01-01 18:35:37 -05:00
2026-01-28 20:27:15 -05:00
**Documentation ** : [Wiki ](https://wiki.lotusguild.org/en/Services/service-tinker-tickets )
2026-03-14 21:40:43 -04:00
**Design System ** : [web_template ](https://code.lotusguild.org/LotusGuild/web_template ) — shared CSS, JS, and layout patterns for all LotusGuild apps
## Styling & Layout
Tinker Tickets uses the **LotusGuild Terminal Design System ** . For all styling, component, and layout documentation see:
- [`web_template/README.md` ](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/README.md ) — full component reference, CSS variables, JS API
- [`web_template/base.css` ](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.css ) — unified CSS (`.lt-*` classes)
- [`web_template/base.js` ](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.js ) — `window.lt` utilities (toast, modal, CSRF, fetch helpers)
- [`web_template/php/layout.php` ](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/php/layout.php ) — PHP base layout template
**Key conventions: **
- All `.lt-*` CSS classes come from `base.css` — do not duplicate them in `assets/css/`
- All `lt.*` JS utilities come from `base.js` — use `lt.toast` , `lt.modal` , `lt.api` , etc.
- CSP nonces: every `<script>` tag needs `nonce="<?php echo $nonce; ?>"`
- CSRF: inject `window.CSRF_TOKEN` via the nonce-protected inline script block; `lt.api.*` adds the header automatically
2026-01-28 20:27:15 -05:00
## Design Decisions
The following features are intentionally **not planned ** for this system:
2026-04-05 17:52:07 -04:00
- **Email Integration**: Matrix (hookshot webhook) is the chosen external notification method
2026-01-28 20:27:15 -05:00
- **Time Tracking**: Out of scope for current requirements
- **OAuth2/External Identity Providers**: Authelia is the only approved SSO method
2026-01-20 17:25:54 -05:00
## Core Features
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Dashboard & Ticket Management
2026-04-05 17:52:07 -04:00
- **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
2026-01-01 19:45:49 -05:00
- **Full-Text Search**: Search across tickets, descriptions, and metadata
2026-04-05 17:52:07 -04:00
- **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
2026-01-01 19:45:49 -05:00
- **Custom Categories**: Hardware, Software, Network, Security, General
- **Ticket Types**: Maintenance, Install, Task, Upgrade, Issue, Problem
2026-04-05 17:52:07 -04:00
- **Skeleton Loaders**: Loading placeholders during filter changes and data refresh
- **Export**: Export filtered tickets to CSV or JSON format
2026-01-23 10:39:55 -05:00
- **Ticket Linking**: Reference other tickets in comments using `#123456789` format
### Ticket Visibility Levels
- **Public**: All authenticated users can view the ticket
2026-03-14 21:40:43 -04:00
- **Internal**: Only users in specified groups can view the ticket (at least one group required)
2026-01-23 10:39:55 -05:00
- **Confidential**: Only the creator, assignee, and admins can view the ticket
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Workflow Management
2026-01-12 17:00:33 -05:00
- **Status Transitions**: Enforced workflow rules (Open → Pending → In Progress → Closed)
2026-04-05 17:52:07 -04:00
- **Comment Requirements**: Transitions that require a comment open an inline modal before committing the change
2026-01-20 17:25:54 -05:00
- **Workflow Designer**: Visual admin UI at `/admin/workflow` to configure transitions
2026-01-01 19:45:49 -05:00
- **Workflow Validation**: Server-side validation prevents invalid status changes
- **Admin Controls**: Certain transitions can require admin privileges
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Collaboration Features
2026-01-24 16:59:29 -05:00
- **Markdown Comments**: Full Markdown support with live preview, toolbar, and table rendering
2026-04-05 17:52:07 -04:00
- **@Mentions **: Tag users in comments with `@` autocomplete (typeahead); triggers Matrix notification to mentioned user
2026-01-24 16:59:29 -05:00
- **Comment Edit/Delete**: Comment owners and admins can edit or delete comments
- **Auto-linking**: URLs in comments are automatically converted to clickable links
2026-04-05 17:52:07 -04:00
- **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
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Ticket Templates
- **Template Management**: Admin UI at `/admin/templates` to create/edit templates
2026-01-01 19:45:49 -05:00
- **Quick Creation**: Pre-configured templates for common issues
- **Auto-fill**: Templates populate title, description, category, type, and priority
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Recurring Tickets
- **Scheduled Tickets**: Automatically create tickets on a schedule
- **Admin UI**: Manage at `/admin/recurring-tickets`
- **Flexible Scheduling**: Daily, weekly, or monthly recurrence
- **Cron Integration**: Run `cron/create_recurring_tickets.php` to process
### Custom Fields
- **Per-Category Fields**: Define custom fields for specific ticket categories
- **Admin UI**: Manage at `/admin/custom-fields`
- **Field Types**: Text, textarea, select, checkbox, date, number
- **Required Fields**: Mark fields as required for validation
2026-01-23 10:39:55 -05:00
### API Key Management
- **Admin UI**: Generate and manage API keys at `/admin/api-keys`
- **Bearer Token Auth**: Use API keys with `Authorization: Bearer YOUR_KEY` header
- **Expiration**: Optional expiration dates for keys
- **Revocation**: Revoke compromised keys instantly
2026-01-20 17:25:54 -05:00
### User Management & Authentication
2026-01-01 19:45:49 -05:00
- **SSO Integration**: Authelia authentication with LLDAP backend
- **Role-Based Access**: Admin and standard user roles
2026-01-23 10:39:55 -05:00
- **User Groups**: Groups displayed in settings modal, used for visibility
2026-04-05 17:52:07 -04:00
- **User Avatars**: JPEG avatars fetched from lldap via LDAP; cached locally (`/api/user_avatar.php` )
2026-01-20 17:25:54 -05:00
- **User Activity**: View per-user stats at `/admin/user-activity`
- **Session Management**: Secure PHP session handling with timeout
2025-01-01 18:35:37 -05:00
2026-01-20 17:25:54 -05:00
### Bulk Actions (Admin Only)
2026-01-01 19:45:49 -05:00
- **Bulk Close**: Close multiple tickets at once
2026-04-05 17:52:07 -04:00
- **Bulk Assign**: Assign multiple tickets to a user (typeahead search)
2026-01-01 19:45:49 -05:00
- **Bulk Priority**: Change priority for multiple tickets
2026-01-20 17:25:54 -05:00
- **Bulk Status**: Change status for multiple tickets
2026-01-23 10:39:55 -05:00
- **Checkbox Click Area**: Click anywhere in the checkbox cell to toggle
2026-01-20 17:25:54 -05:00
2026-04-05 17:52:07 -04:00
### 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
### 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`
### 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
2026-01-23 22:01:20 -05:00
### Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
2026-04-05 17:52:07 -04:00
| `Ctrl/Cmd + K` | Open command palette (global) |
2026-01-23 22:01:20 -05:00
| `Ctrl/Cmd + E` | Toggle edit mode (ticket page) |
| `Ctrl/Cmd + S` | Save changes (ticket page) |
2026-03-20 10:52:59 -04:00
| `N` | New ticket (dashboard) |
| `J` / `K` | Next / previous row (dashboard table) |
| `Enter` | Open selected ticket (dashboard) |
| `G` then `D` | Go to dashboard |
| `1` – `4` | Quick status change (ticket page) |
2026-01-23 22:01:20 -05:00
| `ESC` | Cancel edit / close modal |
| `?` | Show keyboard shortcuts help |
2026-01-01 19:45:49 -05:00
2026-01-20 17:25:54 -05:00
### Security Features
2026-04-05 17:52:07 -04:00
- **CSRF Protection**: Token-based protection with constant-time comparison; token rotated after each write
2026-01-28 20:27:15 -05:00
- **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
- **XSS Protection**: HTML escaped in markdown parser, CSP headers block inline scripts
2026-01-20 17:25:54 -05:00
- **Audit Logging**: Complete audit trail of all actions
2026-01-28 20:27:15 -05:00
- **Visibility Enforcement**: Access checks on ticket views, downloads, and bulk operations
- **Collision-Safe IDs**: Ticket IDs verified unique before creation
2026-01-01 19:45:49 -05:00
2026-04-05 17:52:07 -04:00
## 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 <api_key>
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`
2026-01-20 17:25:54 -05:00
## Technical Architecture
2026-01-01 19:45:49 -05:00
### Backend
- **Language**: PHP 7.4+
- **Database**: MariaDB/MySQL
- **Architecture**: MVC pattern with models, views, controllers
2026-03-14 21:40:43 -04:00
- **Authentication**: Authelia SSO with LLDAP backend
2026-01-01 19:45:49 -05:00
### Frontend
2026-01-20 21:11:49 -05:00
- **HTML5/CSS3**: Semantic markup with retro terminal styling
2026-01-01 19:45:49 -05:00
- **JavaScript**: Vanilla JS with Fetch API for AJAX
2026-01-20 17:25:54 -05:00
- **Markdown**: Custom markdown parser with toolbar
2026-01-20 21:11:49 -05:00
- **Terminal UI**: Box-drawing characters, monospace fonts, CRT effects
2026-01-23 10:39:55 -05:00
- **Mobile Responsive**: Touch-friendly controls, responsive layouts
2026-04-05 17:52:07 -04:00
- **Chart.js**: CDN-loaded on dashboard only — priority/status/category charts
- **Flatpickr**: CDN-loaded on dashboard only — date range filter pickers
2026-01-20 17:25:54 -05:00
### Database Tables
2026-03-14 21:40:43 -04:00
2026-01-20 17:25:54 -05:00
| Table | Purpose |
|-------|---------|
2026-01-23 10:39:55 -05:00
| `tickets` | Core ticket data with visibility |
2026-01-20 17:25:54 -05:00
| `ticket_comments` | Markdown-supported comments |
| `ticket_attachments` | File attachment metadata |
| `ticket_dependencies` | Ticket relationships |
2026-04-05 17:52:07 -04:00
| `ticket_watchers` | Per-user ticket subscriptions |
2026-01-23 10:39:55 -05:00
| `users` | User accounts with groups |
2026-04-05 17:52:07 -04:00
| `user_preferences` | User settings (rows per page, notification opts, notif_last_seen) |
| `audit_log` | Complete audit trail (also powers in-app notifications) |
2026-01-20 17:25:54 -05:00
| `status_transitions` | Workflow configuration |
2026-01-23 10:39:55 -05:00
| `ticket_templates` | Reusable templates |
| `recurring_tickets` | Scheduled tickets |
2026-01-20 17:25:54 -05:00
| `custom_field_definitions` | Custom field schemas |
2026-01-23 10:39:55 -05:00
| `custom_field_values` | Custom field data |
| `saved_filters` | Saved filter combinations |
2026-03-14 21:40:43 -04:00
| `bulk_operations` | Bulk operation tracking |
| `api_keys` | API key storage with hashed keys |
#### `tickets` Table Key Columns
| Column | Type | Description |
|--------|------|-------------|
| `ticket_id` | varchar(9) | Unique 9-digit identifier |
| `visibility` | enum | `public` , `internal` , `confidential` |
| `visibility_groups` | varchar(500) | Comma-separated group names (for internal) |
| `created_by` | int | Foreign key to users |
| `assigned_to` | int | Foreign key to users (nullable) |
| `updated_by` | int | Foreign key to users |
| `priority` | int | 1– 5 (1=Critical, 5=Minimal) |
| `status` | varchar(20) | Open, Pending, In Progress, Closed |
#### Indexed Columns (performance)
- `tickets` : `ticket_id` (unique), `status` , `priority` , `created_at` , `created_by` , `assigned_to` , `visibility`
- `audit_log` : `user_id` , `action_type` , `entity_type` , `created_at`
2026-01-01 19:45:49 -05:00
### API Endpoints
2026-03-14 21:40:43 -04:00
2026-01-20 17:25:54 -05:00
| Endpoint | Method | Description |
|----------|--------|-------------|
2026-04-05 17:52:07 -04:00
| `/create_ticket_api.php` | POST | Create ticket via API key (hwmonDaemon, external tools) |
2026-01-20 17:25:54 -05:00
| `/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 |
2026-03-20 21:39:02 -04:00
| `/api/clone_ticket.php` | POST | Clone an existing ticket |
2026-01-20 17:25:54 -05:00
| `/api/get_template.php` | GET | Fetch ticket template |
| `/api/get_users.php` | GET | Get user list for assignments |
| `/api/bulk_operation.php` | POST | Perform bulk operations |
2026-01-23 10:39:55 -05:00
| `/api/ticket_dependencies.php` | GET/POST/DELETE | Manage dependencies |
2026-01-20 17:25:54 -05:00
| `/api/upload_attachment.php` | GET/POST | List or upload attachments |
| `/api/export_tickets.php` | GET | Export tickets to CSV/JSON |
2026-01-23 10:39:55 -05:00
| `/api/generate_api_key.php` | POST | Generate API key (admin) |
| `/api/revoke_api_key.php` | POST | Revoke API key (admin) |
2026-03-14 21:40:43 -04:00
| `/api/delete_comment.php` | POST | Delete comment (owner/admin) |
| `/api/update_comment.php` | POST | Update comment (owner/admin) |
| `/api/delete_attachment.php` | POST/DELETE | Delete attachment |
| `/api/download_attachment.php` | GET | Download attachment (visibility checked) |
| `/api/check_duplicates.php` | GET | Check for duplicate tickets |
| `/api/manage_recurring.php` | CRUD | Recurring tickets (admin) |
| `/api/manage_templates.php` | CRUD | Templates (admin) |
| `/api/manage_workflows.php` | CRUD | Workflow rules (admin) |
2026-03-20 21:39:02 -04:00
| `/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 |
2026-04-05 17:52:07 -04:00
| `/api/notifications.php` | GET/POST | In-app notifications (bell) |
| `/api/user_avatar.php` | GET | User avatar from lldap (cached JPEG) |
2026-03-20 21:39:02 -04:00
| `/api/audit_log.php` | GET | Audit log entries (admin) |
2026-04-05 17:52:07 -04:00
| `/api/watch_ticket.php` | POST | Watch/unwatch a ticket |
2026-03-20 21:39:02 -04:00
| `/api/health.php` | GET | Health check |
2026-03-14 21:40:43 -04:00
## Project Structure
```
tinker_tickets/
├── api/
│ ├── add_comment.php # POST: Add comment
│ ├── assign_ticket.php # POST: Assign ticket to user
2026-03-20 21:39:02 -04:00
│ ├── audit_log.php # GET: Audit log entries (admin)
2026-03-20 21:47:03 -04:00
│ ├── bootstrap.php # Shared auth/setup include (not a public endpoint)
2026-03-14 21:40:43 -04:00
│ ├── bulk_operation.php # POST: Bulk operations (admin only)
│ ├── check_duplicates.php # GET: Check for duplicate tickets
2026-03-20 21:39:02 -04:00
│ ├── clone_ticket.php # POST: Clone an existing ticket
│ ├── custom_fields.php # CRUD: Custom field definitions/values (admin)
2026-03-14 21:40:43 -04:00
│ ├── delete_attachment.php # POST/DELETE: Delete attachment
│ ├── delete_comment.php # POST: Delete comment (owner/admin)
│ ├── download_attachment.php # GET: Download with visibility check
│ ├── export_tickets.php # GET: Export tickets to CSV/JSON
│ ├── generate_api_key.php # POST: Generate API key (admin)
│ ├── get_template.php # GET: Fetch ticket template
│ ├── get_users.php # GET: Get user list
2026-03-20 21:39:02 -04:00
│ ├── health.php # GET: Health check endpoint
2026-03-14 21:40:43 -04:00
│ ├── manage_recurring.php # CRUD: Recurring tickets (admin)
│ ├── manage_templates.php # CRUD: Templates (admin)
│ ├── manage_workflows.php # CRUD: Workflow rules (admin)
2026-04-05 17:52:07 -04:00
│ ├── notifications.php # GET/POST: In-app notification bell
2026-03-14 21:40:43 -04:00
│ ├── revoke_api_key.php # POST: Revoke API key (admin)
2026-03-20 21:39:02 -04:00
│ ├── saved_filters.php # CRUD: Saved filter combinations
2026-03-14 21:40:43 -04:00
│ ├── ticket_dependencies.php # GET/POST/DELETE: Ticket dependencies
│ ├── update_comment.php # POST: Update comment (owner/admin)
│ ├── update_ticket.php # POST: Update ticket (workflow validation)
2026-03-20 21:39:02 -04:00
│ ├── upload_attachment.php # GET/POST: List or upload attachments
2026-04-05 17:52:07 -04:00
│ ├── 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
2026-03-14 21:40:43 -04:00
├── assets/
│ ├── css/
2026-03-20 21:39:02 -04:00
│ │ ├── base.css # LotusGuild Terminal Design System (copied from web_template)
2026-03-14 21:40:43 -04:00
│ │ ├── dashboard.css # Dashboard + terminal styling
│ │ └── ticket.css # Ticket view styling
│ ├── js/
│ │ ├── advanced-search.js # Advanced search modal
2026-03-20 10:52:59 -04:00
│ │ ├── ascii-banner.js # ASCII art banner (rendered in boot overlay on first visit)
2026-03-20 21:39:02 -04:00
│ │ ├── base.js # LotusGuild JS utilities — window.lt (copied from web_template)
2026-04-05 17:52:07 -04:00
│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar + charts
2026-03-20 10:52:59 -04:00
│ │ ├── keyboard-shortcuts.js # Keyboard shortcuts (uses lt.keys)
2026-03-14 21:40:43 -04:00
│ │ ├── markdown.js # Markdown rendering + ticket linking (XSS-safe)
│ │ ├── settings.js # User preferences
2026-04-05 17:52:07 -04:00
│ │ ├── ticket.js # Ticket + comments + visibility + @mention
2026-03-20 20:29:58 -04:00
│ │ ├── toast.js # Deprecated shim (no longer loaded — all callers use lt.toast directly)
│ │ └── utils.js # escapeHtml (→ lt.escHtml), getTicketIdFromUrl, showConfirmModal
2026-03-14 21:40:43 -04:00
│ └── images/
│ └── favicon.png
├── config/
│ └── config.php # Config + .env loading
├── controllers/
│ ├── DashboardController.php # Dashboard with stats + filters
│ └── TicketController.php # Ticket CRUD + timeline + visibility
├── cron/
│ └── create_recurring_tickets.php # Process recurring ticket schedules
├── helpers/
2026-04-05 17:52:07 -04:00
│ ├── 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
2026-03-14 21:40:43 -04:00
├── middleware/
2026-04-05 17:52:07 -04:00
│ ├── ApiKeyAuth.php # Bearer token auth for external API (hwmonDaemon)
2026-03-14 21:40:43 -04:00
│ ├── AuthMiddleware.php # Authelia SSO integration
│ ├── CsrfMiddleware.php # CSRF protection
│ ├── RateLimitMiddleware.php # Session + IP-based rate limiting
2026-04-05 17:52:07 -04:00
│ └── SecurityHeadersMiddleware.php # CSP headers with per-request nonce generation
2026-03-14 21:40:43 -04:00
├── models/
│ ├── ApiKeyModel.php # API key generation/validation
│ ├── AuditLogModel.php # Audit logging + timeline
│ ├── BulkOperationsModel.php # Bulk operations tracking
│ ├── CommentModel.php # Comment data access
│ ├── CustomFieldModel.php # Custom field definitions/values
│ ├── DependencyModel.php # Ticket dependencies
│ ├── RecurringTicketModel.php # Recurring ticket schedules
2026-04-05 17:52:07 -04:00
│ ├── SavedFiltersModel.php # Saved filter combinations
│ ├── StatsModel.php # Dashboard statistics (cached)
2026-03-14 21:40:43 -04:00
│ ├── TemplateModel.php # Ticket templates
│ ├── TicketModel.php # Ticket CRUD + visibility + collision-safe IDs
│ ├── UserModel.php # User management + groups
│ ├── UserPreferencesModel.php # User preferences
│ └── WorkflowModel.php # Status transition workflows
├── scripts/
2026-03-20 21:39:02 -04:00
│ ├── 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
2026-04-05 17:52:07 -04:00
│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads (run manually or via cron)
2026-03-14 21:40:43 -04:00
│ └── create_dependencies_table.php # Create ticket_dependencies table
├── uploads/ # File attachment storage
2026-04-05 17:52:07 -04:00
│ └── avatars/ # lldap avatar disk cache
2026-03-14 21:40:43 -04:00
├── views/
│ ├── admin/
│ │ ├── ApiKeysView.php # API key management
│ │ ├── AuditLogView.php # Audit log browser
│ │ ├── CustomFieldsView.php # Custom field management
│ │ ├── RecurringTicketsView.php # Recurring ticket management
│ │ ├── TemplatesView.php # Template management
│ │ ├── UserActivityView.php # User activity report
│ │ └── WorkflowDesignerView.php # Workflow transition designer
│ ├── CreateTicketView.php # Ticket creation with visibility
2026-04-05 17:52:07 -04:00
│ ├── 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
2026-03-14 21:40:43 -04:00
├── .env # Environment variables (GITIGNORED)
2026-04-05 17:52:07 -04:00
├── create_ticket_api.php # External API endpoint (hwmonDaemon, API-key auth)
2026-03-14 21:40:43 -04:00
├── README.md # This file
└── index.php # Main router
```
## Workflow States
### Default Workflow
```
Open → Pending → In Progress → Closed
↑ ↑
└───────────┘
```
All states can transition to Closed (with comment).
Closed tickets can be reopened to Open or In Progress.
2026-01-20 17:25:54 -05:00
## Setup & Configuration
2026-01-01 19:45:49 -05:00
### 1. Environment Configuration
2026-01-23 22:01:20 -05:00
Copy the example file and edit with your values:
``` bash
cp .env.example .env
nano .env
```
Required environment variables:
2025-01-01 18:35:37 -05:00
``` env
2026-03-14 21:40:43 -04:00
DB_HOST = your_db_host
DB_USER = your_db_user
2026-01-01 19:45:49 -05:00
DB_PASS = your_password
DB_NAME = ticketing_system
2026-03-14 21:40:43 -04:00
APP_DOMAIN = your.domain.example
2026-01-23 22:01:20 -05:00
TIMEZONE = America/New_York
2026-01-01 19:45:49 -05:00
```
2026-04-05 17:52:07 -04:00
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.
2026-02-05 10:24:00 -05:00
2026-01-23 10:39:55 -05:00
### 2. Cron Jobs
2026-01-01 19:45:49 -05:00
2026-04-05 17:52:07 -04:00
Add to crontab for recurring tickets and optional cleanup:
2026-01-20 17:25:54 -05:00
``` bash
# Run every hour to create scheduled recurring tickets
2026-03-14 21:40:43 -04:00
0 * * * * php /path/to/tinkertickets/cron/create_recurring_tickets.php
2026-04-05 17:52:07 -04:00
# Optional: clean up orphaned uploads weekly
0 3 * * 0 php /path/to/tinkertickets/scripts/cleanup_orphan_uploads.php
2026-01-20 17:25:54 -05:00
```
2026-01-01 19:45:49 -05:00
2026-01-23 10:39:55 -05:00
### 3. File Uploads
2026-01-01 19:45:49 -05:00
2026-01-20 17:25:54 -05:00
Ensure the `uploads/` directory exists and is writable:
``` bash
2026-04-05 17:52:07 -04:00
mkdir -p /path/to/tinkertickets/uploads/avatars
2026-03-14 21:40:43 -04:00
chown www-data:www-data /path/to/tinkertickets/uploads
chmod 755 /path/to/tinkertickets/uploads
2026-01-01 19:45:49 -05:00
```
2026-01-23 10:39:55 -05:00
### 4. Authelia Integration
2026-01-01 19:45:49 -05:00
Tinker Tickets uses Authelia for SSO. User information is passed via headers:
- `Remote-User` : Username
- `Remote-Name` : Display name
- `Remote-Email` : Email address
- `Remote-Groups` : User groups (comma-separated)
2026-01-23 10:39:55 -05:00
Admin users must be in the `admin` group in LLDAP.
2026-01-01 19:45:49 -05:00
2026-04-05 17:52:07 -04:00
### 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
` ``
2026-03-14 21:40:43 -04:00
## Developer Notes
Key conventions and gotchas for working with this codebase:
1. **Session format**: ` $_SESSION['user']['user_id']` (not ` $_SESSION['user_id']`)
2. **API auth check**: Verify ` $_SESSION['user']['user_id']` exists
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`)
2026-04-05 17:52:07 -04:00
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`
2026-03-14 21:40:43 -04:00
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
2026-04-05 17:52:07 -04:00
11. **Session in APIs**: ` RateLimitMiddleware` starts the session — guard subsequent ` session_start()` calls with ` if (session_status() === PHP_SESSION_NONE)`
2026-03-14 21:40:43 -04:00
12. **Database collation**: Use ` utf8mb4_general_ci` (not ` unicode_ci`) for new tables
2026-04-05 17:52:07 -04:00
13. **Matrix URLs**: Set ` APP_DOMAIN` in ` .env` for correct ticket URLs in Matrix notifications
2026-03-14 21:40:43 -04:00
14. **Ticket ID extraction**: Use ` getTicketIdFromUrl()` helper in JS files
15. **CSP nonces**: All inline ` <script>` tags require ` nonce="<?php echo $nonce; ?>"`
16. **Visibility validation**: Internal visibility requires at least one group — validated server-side
17. **Rate limiting**: Both session-based AND IP-based limits are enforced
2026-04-05 17:52:07 -04:00
18. **Relative timestamps**: Dashboard dates use ` lt.time.ago()` and refresh every 60 s; full date is always in the ` title` attribute for hover
2026-03-20 10:52:59 -04:00
19. **Boot sequence**: Shows ASCII banner then boot messages on first page visit per session (` sessionStorage.getItem('booted')`); removed on subsequent loads
2026-03-20 20:29:58 -04:00
20. **No raw ` fetch()`**: All AJAX calls use ` lt.api.get/post/put/delete()` — never use raw ` fetch()`. The wrapper auto-adds ` Content-Type: application/json` and ` X-CSRF-Token`, auto-parses JSON, and throws on non-2xx.
21. **Confirm dialogs**: Never use browser ` confirm()`. Use ` showConfirmModal(title, message, type, onConfirm)` (defined in ` utils.js`, available on all pages). Types: ` 'warning'` | ` 'error'` | ` 'info'`.
22. **` utils.js` on all pages**: ` utils.js` is loaded by all views (including admin). It provides ` escapeHtml()`, ` getTicketIdFromUrl()`, and ` showConfirmModal()`.
23. **No ` toast.js`**: ` toast.js` is deprecated and no longer loaded by any view. Use ` lt.toast.success/error/warning/info()` directly from ` base.js`.
2026-04-05 17:52:07 -04:00
24. **Stats cache**: ` StatsModel` caches stats for 60 s. Any API that modifies ticket state must call ` (new StatsModel($conn))->invalidateCache()` after changes (bulk_operation, assign_ticket, update_ticket, clone_ticket all do this).
25. **External API (` create_ticket_api.php`)**: Uses ` ApiKeyAuth` (Bearer token), not session auth. Served directly by the web server from the document root — not through the index.php router. Includes deduplication logic to prevent duplicate hw-alert tickets within 24 h.
2026-03-14 21:40:43 -04:00
## File Reference
| File | Purpose |
|------|---------|
| ` index.php` | Main router for all routes |
2026-04-05 17:52:07 -04:00
| ` create_ticket_api.php` | External API (hwmonDaemon) — Bearer token auth, deduplication |
2026-03-14 21:40:43 -04:00
| ` config/config.php` | Config loader + .env parsing |
| ` api/update_ticket.php` | Ticket updates with workflow + visibility validation |
2026-04-05 17:52:07 -04:00
| ` api/notifications.php` | In-app notification bell — reads from audit_log |
| ` api/user_avatar.php` | LDAP avatar proxy with disk cache |
2026-03-14 21:40:43 -04:00
| ` api/download_attachment.php` | File downloads with visibility check |
| ` api/bulk_operation.php` | Bulk operations with visibility filtering |
| ` models/TicketModel.php` | Ticket CRUD, visibility filtering, collision-safe ID generation |
| ` models/ApiKeyModel.php` | API key generation and validation |
2026-04-05 17:52:07 -04:00
| ` models/StatsModel.php` | Dashboard statistics (60 s cache; invalidated on ticket changes) |
| ` middleware/ApiKeyAuth.php` | Bearer token authentication for external API |
2026-03-14 21:40:43 -04:00
| ` middleware/AuthMiddleware.php` | Authelia header parsing + session setup |
| ` middleware/CsrfMiddleware.php` | CSRF token generation and validation |
| ` middleware/SecurityHeadersMiddleware.php` | CSP headers with per-request nonce generation |
| ` middleware/RateLimitMiddleware.php` | Session + IP-based rate limiting |
2026-04-05 17:52:07 -04:00
| ` helpers/NotificationHelper.php` | Matrix hookshot webhook events |
| ` helpers/SynapseHelper.php` | Username → Matrix ID resolution via Synapse admin API |
| ` assets/js/dashboard.js` | Dashboard UI, kanban, sidebar, bulk actions, charts, command palette |
| ` assets/js/ticket.js` | Ticket UI, @mention autocomplete, lightbox, visibility editing |
2026-03-14 21:40:43 -04:00
| ` assets/js/markdown.js` | Markdown parsing + ticket linking (XSS-safe) |
2026-04-05 17:52:07 -04:00
| ` assets/css/dashboard.css` | Terminal styling, kanban, sidebar, charts, workload panel |
| ` assets/css/ticket.css` | Ticket view: SLA progress, attachment thumbnails, timeline |
2026-03-14 21:40:43 -04:00
## Security Implementations
| Feature | Implementation |
|---------|---------------|
| SQL Injection | All queries use prepared statements with parameter binding |
| XSS Prevention | HTML escaped in markdown parser; CSP with per-request nonces |
2026-04-05 17:52:07 -04:00
| CSRF Protection | Token-based with constant-time comparison (` hash_equals`); rotated on each write |
2026-03-14 21:40:43 -04:00
| Session Security | Fixation prevention, secure cookies, session timeout |
| Rate Limiting | Session-based + IP-based (file storage) |
2026-04-05 17:52:07 -04:00
| File Security | Path traversal prevention, MIME type validation, uploads ` .htaccess` blocks execution |
2026-03-14 21:40:43 -04:00
| Visibility | Enforced on ticket views, downloads, and bulk operations |
2026-04-05 17:52:07 -04:00
| API Key Auth | SHA-256 hashed keys stored in DB; Bearer token auth for external API |
2026-01-01 19:45:49 -05:00
2026-04-14 12:54:00 -04:00
## CI / CD
| Workflow | Purpose | Triggers |
|---|---|---|
| ` lint.yml` (php-lint) | phpcs PSR-12 standard | Every push and PR |
| ` lint.yml` (js-lint) | ESLint on ` assets/js/` | Every push and PR |
| ` security.yml` | ` npm audit --audit-level=high` (not applicable — no runtime npm deps) | — |
| ` deploy` job in ` lint.yml` | Calls deploy webhooks on CT132 (10.10.10.45): ` tinker-deploy` (main) or ` tinker-beta-deploy` (development) | Push to ` main` or ` development`, after both lint jobs pass |
Branch protection is enabled on ` main` — both lint jobs must pass before any PR can merge.
Lint config: ` .phpcs.xml` (PSR-12 with project-specific tweaks), ` .eslintrc.json` per directory.
2026-01-20 17:25:54 -05:00
## License
2026-01-01 19:45:49 -05:00
2026-01-20 17:25:54 -05:00
Internal use only - LotusGuild Infrastructure