Jared Vititoe 06b7a8f59b Consolidate showConfirmModal into utils.js, remove duplicate from dashboard.js
utils.js is loaded on all pages (dashboard, ticket, admin views) before dashboard.js.
Moving the canonical definition there and removing the guard + the copy in dashboard.js
eliminates the redundant redefinition on every page load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:44:46 -04:00
2026-01-01 15:40:32 -05:00

Tinker Tickets

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.

Documentation: Wiki Design System: 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:

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

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

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)
  • 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
  • Custom Categories: Hardware, Software, Network, Security, General
  • Ticket Types: Maintenance, Install, Task, Upgrade, Issue, Problem
  • Export: Export selected tickets to CSV or JSON format
  • Ticket Linking: Reference other tickets in comments using #123456789 format

Ticket Visibility Levels

  • Public: All authenticated users can view the ticket
  • Internal: Only users in specified groups can view the ticket (at least one group required)
  • Confidential: Only the creator, assignee, and admins can view the ticket

Workflow Management

  • Status Transitions: Enforced workflow rules (Open → Pending → In Progress → Closed)
  • 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
  • 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

Ticket Templates

  • Template Management: Admin UI at /admin/templates to create/edit templates
  • Quick Creation: Pre-configured templates for common issues
  • Auto-fill: Templates populate title, description, category, type, and priority

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

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

User Management & Authentication

  • 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 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 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.

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

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)

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)
N New ticket (dashboard)
J / K Next / previous row (dashboard table)
Enter Open selected ticket (dashboard)
G then D Go to dashboard
14 Quick status change (ticket page)
ESC Cancel edit / close modal
? Show keyboard shortcuts help

Security Features

  • 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
  • Audit Logging: Complete audit trail of all actions
  • Visibility Enforcement: Access checks on ticket views, downloads, and bulk operations
  • Collision-Safe IDs: Ticket IDs verified unique before creation

Technical Architecture

Backend

  • Language: PHP 7.4+
  • Database: MariaDB/MySQL
  • Architecture: MVC pattern with models, views, controllers
  • Authentication: Authelia SSO with LLDAP backend

Frontend

  • HTML5/CSS3: Semantic markup with retro terminal styling
  • JavaScript: Vanilla JS with Fetch API for AJAX
  • Markdown: Custom markdown parser with toolbar
  • Terminal UI: Box-drawing characters, monospace fonts, CRT effects
  • Mobile Responsive: Touch-friendly controls, responsive layouts

Database Tables

Table Purpose
tickets Core ticket data with visibility
ticket_comments Markdown-supported comments
ticket_attachments File attachment metadata
ticket_dependencies Ticket relationships
users User accounts with groups
user_preferences User settings
audit_log Complete audit trail
status_transitions Workflow configuration
ticket_templates Reusable templates
recurring_tickets Scheduled tickets
custom_field_definitions Custom field schemas
custom_field_values Custom field data
saved_filters Saved filter combinations
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 15 (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

API Endpoints

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
/api/clone_ticket.php POST Clone an existing ticket
/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
/api/ticket_dependencies.php GET/POST/DELETE Manage dependencies
/api/upload_attachment.php GET/POST List or upload attachments
/api/export_tickets.php GET Export tickets to CSV/JSON
/api/generate_api_key.php POST Generate API key (admin)
/api/revoke_api_key.php POST Revoke API key (admin)
/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)
/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/bootstrap.php GET Bootstrap config/user data for front-end
/api/health.php GET Health check

Project Structure

tinker_tickets/
├── api/
│   ├── add_comment.php                   # POST: Add comment
│   ├── assign_ticket.php                 # POST: Assign ticket to user
│   ├── audit_log.php                     # GET: Audit log entries (admin)
│   ├── bootstrap.php                     # GET: Bootstrap data (config/user for front-end)
│   ├── bulk_operation.php                # POST: Bulk operations (admin only)
│   ├── check_duplicates.php              # GET: Check for duplicate tickets
│   ├── clone_ticket.php                  # POST: Clone an existing ticket
│   ├── custom_fields.php                 # CRUD: Custom field definitions/values (admin)
│   ├── 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
│   ├── health.php                        # GET: Health check endpoint
│   ├── 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)
│   ├── 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
├── assets/
│   ├── css/
│   │   ├── base.css                      # LotusGuild Terminal Design System (copied from web_template)
│   │   ├── dashboard.css                 # Dashboard + terminal styling
│   │   └── ticket.css                    # Ticket view styling
│   ├── js/
│   │   ├── 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
│   │   ├── 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
│   │   ├── toast.js                      # Deprecated shim (no longer loaded — all callers use lt.toast directly)
│   │   └── utils.js                      # escapeHtml (→ lt.escHtml), getTicketIdFromUrl, showConfirmModal
│   └── 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/
│   ├── 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
│   └── 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.

Setup & Configuration

1. Environment Configuration

Copy the example file and edit with your values:

cp .env.example .env
nano .env

Required environment variables:

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.

2. Cron Jobs

Add to crontab for recurring tickets:

# Run every hour to create scheduled recurring tickets
0 * * * * php /path/to/tinkertickets/cron/create_recurring_tickets.php

3. File Uploads

Ensure the uploads/ directory exists and is writable:

mkdir -p /path/to/tinkertickets/uploads
chown www-data:www-data /path/to/tinkertickets/uploads
chmod 755 /path/to/tinkertickets/uploads

4. Authelia Integration

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)

Admin users must be in the admin group in LLDAP.

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
  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
  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.

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

License

Internal use only - LotusGuild Infrastructure

Description
A PHP-based ticketing system with a clean web interface for managing and tracking hardware, software, and network issues in Lotus Guild Cluster
Readme 6.2 MiB
Languages
PHP 57.6%
CSS 23.7%
JavaScript 18.5%
Shell 0.2%