2025-01-01 18:35:37 -05:00
# Tinker Tickets
2024-11-30 19:26:34 -05:00
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:
- **Email Integration**: Discord webhooks are the chosen notification method
- **SLA Management**: Not required for internal infrastructure use
- **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-01-23 10:39:55 -05:00
- **View Modes**: Toggle between Table view and Kanban card view
- **Collapsible Sidebar**: Click the arrow to collapse/expand the filter sidebar
2026-03-14 21:40:43 -04:00
- **Inline Ticket Preview**: Hover over ticket IDs for a quick preview popup (300ms delay)
2026-01-20 21:11:49 -05:00
- **Stats Widgets**: Clickable cards for quick filtering (Open, Critical, Unassigned, Today's tickets)
2026-01-01 19:45:49 -05:00
- **Full-Text Search**: Search across tickets, descriptions, and metadata
2026-01-20 21:11:49 -05:00
- **Advanced Search**: Date ranges, priority ranges, user filters with saved filter support
2026-01-20 17:25:54 -05:00
- **Ticket Assignment**: Assign tickets to specific users with quick-assign from dashboard
2026-01-01 19:45:49 -05:00
- **Priority Tracking**: P1 (Critical) to P5 (Minimal Impact) with color-coded indicators
- **Custom Categories**: Hardware, Software, Network, Security, General
- **Ticket Types**: Maintenance, Install, Task, Upgrade, Issue, Problem
2026-01-20 17:25:54 -05:00
- **Export**: Export selected 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-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
- **Comment Requirements**: Optional comment requirements for specific transitions
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-01-20 17:25:54 -05:00
- **@Mentions **: Tag users in comments with autocomplete
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-01-20 17:25:54 -05:00
- **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
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-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
- **Bulk Assign**: Assign multiple tickets to a user
- **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
### Admin Pages
2026-01-20 21:11:49 -05:00
Access all admin pages via the **Admin dropdown ** in the dashboard header.
2026-01-20 17:25:54 -05:00
| Route | Description |
|-------|-------------|
2026-01-20 21:11:49 -05:00
| `/admin/templates` | Create and edit ticket templates |
| `/admin/workflow` | Visual workflow transition designer |
2026-01-20 17:25:54 -05:00
| `/admin/recurring-tickets` | Manage recurring ticket schedules |
| `/admin/custom-fields` | Define custom fields per category |
| `/admin/user-activity` | View per-user activity statistics |
2026-01-20 21:11:49 -05:00
| `/admin/audit-log` | Browse all audit log entries |
2026-01-23 10:39:55 -05:00
| `/admin/api-keys` | Generate and manage API keys |
2026-01-20 17:25:54 -05:00
### Notifications
2026-01-23 22:01:20 -05:00
- **Discord Integration**: Webhook notifications for ticket creation and updates
2026-01-01 19:45:49 -05:00
- **Rich Embeds**: Color-coded priority indicators and ticket links
2026-03-14 21:40:43 -04:00
- **Dynamic URLs**: Ticket links adapt to the server hostname (set `APP_DOMAIN` in `.env` )
2026-01-23 22:01:20 -05:00
### Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `Ctrl/Cmd + E` | Toggle edit mode (ticket page) |
| `Ctrl/Cmd + S` | Save changes (ticket page) |
| `Ctrl/Cmd + K` | Focus search box (dashboard) |
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-01-28 20:27:15 -05:00
- **CSRF Protection**: Token-based protection with constant-time comparison
- **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-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-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-01-23 10:39:55 -05:00
| `users` | User accounts with groups |
| `user_preferences` | User settings |
2026-01-20 17:25:54 -05:00
| `audit_log` | Complete audit trail |
| `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 |
|----------|--------|-------------|
| `/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 |
| `/api/audit_log.php` | GET | Audit log entries (admin) |
| `/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)
│ ├── 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
│ └── user_preferences.php # GET/POST: User preferences
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-03-14 21:40:43 -04:00
│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar
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
│ │ ├── ticket.js # Ticket + comments + visibility
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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/
│ └── ResponseHelper.php # Standardized JSON responses
├── middleware/
│ ├── AuthMiddleware.php # Authelia SSO integration
│ ├── CsrfMiddleware.php # CSRF protection
│ ├── RateLimitMiddleware.php # Session + IP-based rate limiting
│ └── SecurityHeadersMiddleware.php # CSP with nonces, security headers
├── 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
│ ├── StatsModel.php # Dashboard statistics
│ ├── 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-03-14 21:40:43 -04:00
│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads
│ └── create_dependencies_table.php # Create ticket_dependencies table
├── uploads/ # File attachment storage
├── 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
│ ├── DashboardView.php # Dashboard with kanban + sidebar
│ └── TicketView.php # Ticket view with visibility editing
├── .env # Environment variables (GITIGNORED)
├── 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
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
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-02-05 10:24:00 -05:00
**Note**: `APP_DOMAIN` is required for Discord webhook ticket links to work correctly. Without it, links will default to localhost.
2026-01-23 10:39:55 -05:00
### 2. Cron Jobs
2026-01-01 19:45:49 -05:00
2026-01-20 17:25:54 -05:00
Add to crontab for recurring tickets:
```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-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-03-14 21:40:43 -04:00
mkdir -p /path/to/tinkertickets/uploads
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-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` )
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
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
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
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-03-20 10:52:59 -04:00
18. **Relative timestamps ** : Dashboard dates use `lt.time.ago()` and refresh every 60s; full date is always in the `title` attribute for hover
19. **Boot sequence ** : Shows ASCII banner then boot messages on first page visit per session (`sessionStorage.getItem('booted')` ); removed on subsequent loads
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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` .
24. **CSS utility classes ** (dashboard.css): Use `.text-green` , `.text-amber` , `.text-cyan` , `.text-danger` , `.text-open` , `.text-closed` , `.text-muted` , `.text-muted-green` , `.text-sm` , `.text-center` , `.nowrap` , `.mono` , `.fw-bold` , `.mb-1` for typography. Use `.admin-container` (1200px) or `.admin-container-wide` (1400px) for admin page max-widths. Use `.admin-header-row` for title+button header rows. Use `.admin-form-row` , `.admin-form-field` , `.admin-label` , `.admin-input` for filter forms. Use `.admin-form-actions` for filter form button groups. Use `.table-wrapper` + `td.empty-state` for tables. Use `.setting-grid-2` and `.setting-grid-3` for modal form grids. Use `.lt-modal-sm` or `.lt-modal-lg` for modal sizing.
25. **CSS utility classes ** (ticket.css): Use `.form-hint` (green helper text), `.form-hint-warning` (amber helper text), `.visibility-groups-list` (checkbox row), `.group-checkbox-label` (checkbox label) for ticket forms.
2026-03-14 21:40:43 -04:00
## File Reference
| File | Purpose |
|------|---------|
| `index.php` | Main router for all routes |
| `config/config.php` | Config loader + .env parsing |
| `api/update_ticket.php` | Ticket updates with workflow + visibility validation |
| `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 |
| `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 |
| `assets/js/dashboard.js` | Dashboard UI, kanban, sidebar, bulk actions |
| `assets/js/ticket.js` | Ticket UI, visibility editing |
| `assets/js/markdown.js` | Markdown parsing + ticket linking (XSS-safe) |
| `assets/css/dashboard.css` | Terminal styling, kanban, sidebar |
## 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 |
| CSRF Protection | Token-based with constant-time comparison (`hash_equals` ) |
| Session Security | Fixation prevention, secure cookies, session timeout |
| Rate Limiting | Session-based + IP-based (file storage) |
| File Security | Path traversal prevention, MIME type validation |
| Visibility | Enforced on ticket views, downloads, and bulk operations |
2026-01-01 19:45:49 -05:00
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