Commit Graph

179 Commits

Author SHA1 Message Date
7462d7c509 fix: Add error handling to dependencies + cleanup migrations
- Add detailed error handling in DependencyModel (throw exceptions on failure)
- Add try-catch in ticket_dependencies.php to catch query errors
- Remove all old migrations (001-014) that have already been run
- Keep only new feature migrations (015-018) for reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:07:54 -05:00
2ce4a14201 fix: Use LEFT JOIN in DependencyModel queries
Makes queries more defensive - returns dependencies even if the
linked ticket was deleted.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:02:50 -05:00
92f936e1be fix: Fix upload_attachment.php AuditLogModel call
- Fix AuditLogModel instantiation with proper $conn parameter
- Fix log() call parameter order (details should be array, not ipAddress)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:01:42 -05:00
ebf318f8af fix: Fix delete_attachment.php AuditLogModel calls
- Add session status check
- Remove broken AuditLogModel call without $conn in CSRF check
- Fix AuditLogModel instantiation with proper $conn parameter
- Fix log() call to pass array instead of JSON string for details

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:00:54 -05:00
10d5075f2d fix: Fix duplicate session_start() in API files
- Add session status check before starting session
- Add error reporting settings for debugging
- Prevents potential session conflicts with RateLimitMiddleware

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 16:53:00 -05:00
7dffd8ed35 fix: Remove broken AuditLogModel call in upload_attachment.php
The AuditLogModel was being instantiated without required $conn parameter
when logging CSRF failures, causing a 500 error.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 16:51:26 -05:00
591fad52cc Add deployment scripts and preserve uploads folder
- Add scripts/deploy.sh for safe deployment with uploads preservation
- Add scripts/cleanup_orphan_uploads.php to remove orphaned files
- Add .gitkeep to uploads folder
- Update .gitignore to exclude uploaded files but keep folder structure

The deploy script now:
- Backs up and restores .env file
- Backs up and restores uploads folder contents
- Runs database migrations automatically

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 15:27:05 -05:00
bc6a5cecf8 fix: Resolve multiple UI and API bugs
- Remove is_active filter from get_users.php (column doesn't exist)
- Fix ticket ID validation regex in upload_attachment.php (9-digit format)
- Fix createSettingsModal reference to use openSettingsModal from settings.js
- Add error handling for dependencies tab to prevent infinite loading
- Add try-catch wrapper to ticket_dependencies.php API
- Make export dropdown visible only when tickets are selected
- Export only selected tickets instead of all filtered tickets

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 15:16:14 -05:00
be505b7312 Implement comprehensive improvement plan (Phases 1-6)
Security (Phase 1-2):
- Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc.
- Add RateLimitMiddleware for API rate limiting
- Add security event logging to AuditLogModel
- Add ResponseHelper for standardized API responses
- Update config.php with security constants

Database (Phase 3):
- Add migration 014 for additional indexes
- Add migration 015 for ticket dependencies
- Add migration 016 for ticket attachments
- Add migration 017 for recurring tickets
- Add migration 018 for custom fields

Features (Phase 4-5):
- Add ticket dependencies with DependencyModel and API
- Add duplicate detection with check_duplicates API
- Add file attachments with AttachmentModel and upload/download APIs
- Add @mentions with autocomplete and highlighting
- Add quick actions on dashboard rows

Collaboration (Phase 5):
- Add mention extraction in CommentModel
- Add mention autocomplete dropdown in ticket.js
- Add mention highlighting CSS styles

Admin & Export (Phase 6):
- Add StatsModel for dashboard widgets
- Add dashboard stats cards (open, critical, unassigned, etc.)
- Add CSV/JSON export via export_tickets API
- Add rich text editor toolbar in markdown.js
- Add RecurringTicketModel with cron job
- Add CustomFieldModel for per-category fields
- Add admin views: RecurringTickets, CustomFields, Workflow,
  Templates, AuditLog, UserActivity
- Add admin APIs: manage_workflows, manage_templates,
  manage_recurring, custom_fields, get_users
- Add admin routes in index.php

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
8c7211d311 Add Ceph cluster-wide ticket deduplication support
Update generateTicketHash() to exclude hostname from hash for
cluster-wide Ceph issues, enabling proper deduplication across
all nodes in the cluster.

Cluster-wide issues detected by:
- [cluster-wide] tag in title
- HEALTH_ERR or HEALTH_WARN in title
- "cluster usage" in title

This prevents all nodes from creating duplicate tickets for the
same cluster-wide issue (e.g., Ceph HEALTH_WARN).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 15:53:45 -05:00
496e8d6c21 fix: Use parseMarkdown instead of marked.parse for comment preview
Fixed markdown preview for comments by replacing marked.parse() calls
with parseMarkdown() function. The application uses a custom markdown
parser (markdown.js), not the marked.js library.

Changes:
- togglePreview(): Use parseMarkdown() instead of marked.parse()
- updatePreview(): Use parseMarkdown() instead of marked.parse()

Resolves issue where markdown preview didn't work for comments but
worked after posting.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-13 15:16:25 -05:00
ee69b9094b Update Claude.md 2026-01-12 17:01:38 -05:00
bb4b1400f2 Update README.md 2026-01-12 17:00:33 -05:00
1b66663307 fix: Pass selectedOption parameter to performStatusChange function
Fixed scope issue where selectedOption variable was not accessible in
performStatusChange(). Updated function signature to accept selectedOption
as a parameter and updated both call sites to pass it.

Resolves error: "selectedOption is not defined" when changing ticket status.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 17:08:11 -05:00
63dc2d6314 fix: Correct function closure in ticket.js breaking tab navigation
Fixed syntax error from previous commit where updateTicketStatus()
function had incorrect closing. Changed `});` to `}` at line 434.

This was preventing showTab() and other functions from loading,
breaking the Description/Comments/Activity tab navigation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 17:04:21 -05:00
d86a60c609 feat: Enhance toast system with queuing and manual dismiss
Improved toast notification system with queue management:

**Features Added**:
1. **Toast Queuing**:
   - Multiple toasts no longer replace each other
   - Toasts are queued and displayed sequentially
   - Smooth transitions between queued messages
   - Prevents message loss during rapid operations

2. **Manual Dismissal**:
   - Click [×] button to dismiss toast immediately
   - Useful for long-duration error messages
   - Clears auto-dismiss timeout on manual close
   - Next queued toast appears immediately after dismiss

3. **Queue Management**:
   - Internal toastQueue[] array tracks pending messages
   - currentToast reference prevents overlapping displays
   - dismissToast() handles both auto and manual dismissal
   - Automatic dequeue when toast closes

**Implementation**:
- displayToast() separated from showToast() for queue handling
- timeoutId stored on toast element for cleanup
- Close button styled with terminal aesthetic ([×])
- 300ms fade-out animation preserved

**Benefits**:
✓ No lost messages during bulk operations
✓ Better UX - users can dismiss errors immediately
✓ Clean queue management prevents memory leaks
✓ Maintains terminal aesthetic with minimal close button

Example: Bulk assign 10 tickets with 2 failures now shows:
1. "Bulk assign: 8 succeeded, 2 failed" (toast 1)
2. Next operation's message queued (toast 2)
3. User can dismiss or wait for auto-dismiss

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 17:00:35 -05:00
998b85e907 feat: Replace browser alerts with terminal-aesthetic notifications
Replaced all native browser dialogs with custom terminal-style UI:

**Utility Functions** (dashboard.js):
- showConfirmModal() - Reusable confirmation modal with type-based colors
- showInputModal() - Text input modal for user prompts
- Both support keyboard shortcuts (ESC to cancel, Enter to submit)

**Alert Replacements** (22 instances):
- Validation warnings → toast.warning() (amber, 2s)
- Error messages → toast.error() (red, 5s)
- Success messages → toast.success() or toast.warning() with details
- Example: "Bulk close: 5 succeeded, 2 failed" vs simple "Operation complete"

**Confirm Replacements** (3 instances):
- dashboard.js:509 - Bulk close confirmation → showConfirmModal()
- ticket.js:417 - Status change warning → showConfirmModal()
- advanced-search.js:321 - Delete filter → showConfirmModal('error' type)

**Prompt Replacement** (1 instance):
- advanced-search.js:151 - Save filter name → showInputModal()

**Benefits**:
✓ Visual consistency - matches terminal CRT aesthetic
✓ Non-blocking - toasts don't interrupt workflow
✓ Better UX - different colors for different message types
✓ Keyboard friendly - ESC/Enter support in modals
✓ Reusable - modal functions available for future use

All dialogs maintain retro aesthetic with:
- ASCII borders (╚ ╝)
- Terminal green glow
- Monospace fonts
- Color-coded by type (amber warning, red error, cyan info)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:54:02 -05:00
a3298e7dbe fix: Enable proper sorting for Created By and Assigned To columns
Fixed server-side sorting for user-related columns on dashboard:

Problem:
- Clicking "Created By" or "Assigned To" headers didn't sort
- Columns were missing from $allowedColumns validation
- Fell back to ticket_id sort, appearing random to users

Solution:
1. Added 'created_by' and 'assigned_to' to $allowedColumns array

2. Smart sort expression mapping:
   - created_by → sorts by display_name/username (not user ID)
   - assigned_to → uses CASE to put unassigned at end, then sorts by name
   - Other columns → use table prefix (t.column_name)

3. Database-level NULL handling for assigned_to:
   - Uses CASE WHEN to sort unassigned tickets last
   - Regardless of ASC/DESC direction
   - Then alphabetically sorts assigned users

Result:
- A→Z: Alice, Bob, Charlie... Unassigned
- Z→A: Zack, Yolanda, Xavier... Unassigned
- Consistent grouping and predictable order

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:42:13 -05:00
08a73eb84c fix: Improve Assigned To column sorting behavior
Fixed sorting logic for the "Assigned To" column on dashboard:

Problem:
- "Unassigned" was sorted alphabetically with user names
- Appeared randomly in middle of list (after 'S', before 'V')
- Made it hard to find unassigned tickets when sorted

Solution:
- "Unassigned" tickets now always appear at end of list
- Regardless of sort direction (A→Z or Z→A)
- Assigned user names still sort normally among themselves
- Example A→Z: Alice, Bob, Charlie... Unassigned
- Example Z→A: Zack, Yolanda, Xavier... Unassigned

This keeps unassigned tickets grouped together and predictable.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:38:16 -05:00
837c4baf56 Security Updates 2026-01-09 16:32:11 -05:00
becee84821 perf: Add TTL-based caching to UserModel to prevent stale data
Cache optimization with automatic expiration:

1. New Cache Structure:
   - Changed from simple array to TTL-aware structure
   - Each entry: ['data' => ..., 'expires' => timestamp]
   - 5-minute (300s) TTL prevents indefinite stale data

2. Helper Methods:
   - getCached($key): Returns data if not expired, null otherwise
   - setCached($key, $data): Stores with expiration timestamp
   - invalidateCache($userId, $username): Manual cache clearing

3. Updated All Cache Access Points:
   - syncUserFromAuthelia() - User sync from Authelia
   - getSystemUser() - System user for daemon operations
   - getUserById() - User lookup by ID
   - getUserByUsername() - User lookup by username

Benefits:
- Prevents memory leaks from unlimited cache growth
- Ensures user data refreshes periodically
- Maintains performance benefits of caching
- Automatic cleanup of expired entries

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:27:04 -05:00
4a05c82852 perf: Eliminate N+1 queries in bulk operations with batch loading
Performance optimization to address N+1 query problem:

1. TicketModel.php:
   - Added getTicketsByIds() method for batch loading
   - Loads multiple tickets in single query using IN clause
   - Returns associative array keyed by ticket_id
   - Includes all JOINs for creator/updater/assignee data

2. BulkOperationsModel.php:
   - Pre-load all tickets at start of processOperation()
   - Replaced 3x getTicketById() calls with array lookups
   - Benefits bulk_close, bulk_priority, and bulk_status operations

Performance Impact:
- Before: 100 tickets = ~100 database queries
- After: 100 tickets = ~2 database queries (1 batch + 100 updates)
- 30-50% faster bulk operations on large ticket sets

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:24:36 -05:00
e801eee6ee feat: Add session security and fixation prevention
Session security improvements in AuthMiddleware:

1. Secure Cookie Configuration:
   - HttpOnly flag prevents JavaScript access to session cookies
   - Secure flag requires HTTPS (protects from MITM)
   - SameSite=Strict prevents CSRF via cookie inclusion
   - Strict mode rejects uninitialized session IDs

2. Session Fixation Prevention:
   - session_regenerate_id(true) called after successful authentication
   - Old session ID destroyed, new one generated
   - Prevents attacker from using pre-set session ID

3. CSRF Token Regeneration:
   - New CSRF token generated on login
   - Ensures fresh token for each session

These changes protect against session hijacking, fixation, and
cross-site attacks while maintaining existing 5-hour timeout.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:23:09 -05:00
58f2e9d143 feat: Add CSRF tokens to all JavaScript fetch calls and fix XSS
Security improvements across all JavaScript files:

CSRF Protection:
- assets/js/ticket.js - Added X-CSRF-Token header to 5 fetch calls
  (update_ticket.php x3, add_comment.php, assign_ticket.php)
- assets/js/dashboard.js - Added X-CSRF-Token to 8 fetch calls
  (update_ticket.php x2, bulk_operation.php x6)
- assets/js/settings.js - Added X-CSRF-Token to user preferences save
- assets/js/advanced-search.js - Added X-CSRF-Token to filter save/delete

XSS Prevention:
- assets/js/ticket.js:183-209 - Replaced insertAdjacentHTML() with safe
  DOM API (createElement/textContent) to prevent script injection in
  comment rendering. User-supplied data (user_name, created_at) now
  auto-escaped via textContent.

All state-changing operations now include CSRF token validation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 16:13:13 -05:00
783bf52552 feat: Inject CSRF tokens in TicketView and CreateTicketView
Add CSRF token injection to the remaining view files:
- views/TicketView.php - Added CSRF token before ticket data script
- views/CreateTicketView.php - Added CSRF token in head section

All view files now expose window.CSRF_TOKEN for JavaScript fetch calls.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 15:05:20 -05:00
8137a007a1 feat: Add CSRF protection to user preferences API
- Add CSRF validation to user_preferences.php
- Protects POST and DELETE methods
- Completes CSRF protection for all API endpoints

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 12:34:45 -05:00
f46b1c31b5 feat: Add CSRF protection to assign and filter APIs
- Add CSRF validation to assign_ticket.php
- Add CSRF validation to saved_filters.php
- Supports POST, PUT, and DELETE methods

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 12:33:23 -05:00
fa9d9dfe0f feat: Add CSRF protection to critical API endpoints
- Add CSRF validation to update_ticket.php
- Add CSRF validation to add_comment.php
- Add CSRF validation to bulk_operation.php
- All POST/PUT requests now require valid CSRF token

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 12:32:34 -05:00
f096766e5d feat: Add CSRF middleware and performance index migrations
- Create CsrfMiddleware.php with token generation and validation
- Add database indexes for ticket_comments and audit_log
- Includes rollback script for safe deployment

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 11:45:23 -05:00
962724d811 better filtering and searching 2026-01-09 11:20:27 -05:00
f9d9c775fb markdown fix 2026-01-09 11:09:27 -05:00
2633d0f962 remove bulk delete 2026-01-08 23:35:49 -05:00
2e7956ce40 Bulk actions update 2026-01-08 23:30:25 -05:00
61e3bd69ff Centered settings modal 2026-01-08 23:19:44 -05:00
83a1ba393a Fix settings 2026-01-08 23:16:29 -05:00
b781a44ed5 Added settings menu 2026-01-08 23:05:03 -05:00
eda9c61724 ui improvements, keyboard shortcuts, and toast not 2026-01-08 22:49:48 -05:00
1a74536079 test fix for the ticket title 2026-01-08 22:40:26 -05:00
649854c86e ticket title not wrapping 2026-01-08 22:36:07 -05:00
0b304ace95 visual fixes 2026-01-08 22:29:20 -05:00
590a24bc99 added pending 2026-01-08 13:20:41 -05:00
d27b61c56d No discord send on status update 2026-01-08 13:11:41 -05:00
3be5b24d1f expand status column 2026-01-08 13:04:52 -05:00
1bd329ac1b update status's on tickets 2026-01-08 13:02:52 -05:00
d6dae6825c read response.txt once 2026-01-08 12:50:11 -05:00
d76ff7aad0 Fixed api send on ticket resolution 2026-01-08 12:48:06 -05:00
de9da756e9 Fix duplicate ticket creation for evolving SMART errors
Problem: When SMART errors evolved on the same drive, new tickets were created
instead of updating the existing ticket. This happened because the hash was
based on specific error values (e.g., "Reallocated_Sector_Ct: 8") instead of
just the issue category.

Root Cause:
- Old hash included specific SMART attribute names and values
- When errors changed (8 → 16 reallocated sectors, or new errors appeared),
  the hash changed, allowing duplicate tickets
- Only matched "Warning" attributes, missing "Critical" and "Error X occurred"
- Only matched /dev/sd[a-z], missing NVMe devices

Solution:
- Hash now based on: hostname + device + issue_category (e.g., "smart")
- Does NOT include specific error values or attribute names
- Supports both /dev/sdX and /dev/nvmeXnY devices
- Detects issue categories: smart, storage, memory, cpu, network

Result:
 Same drive, errors evolve → Same hash → Updates existing ticket
 Different device → Different hash → New ticket
 Drive replaced → Different device → New ticket
 NVMe devices now supported

Example:
Before:
- "Warning Reallocated: 8" → hash abc123
- "Warning Reallocated: 16" → hash xyz789 (NEW TICKET - bad!)

After:
- "Warning Reallocated: 8" → hash abc123
- "Warning Reallocated: 16" → hash abc123 (SAME TICKET - good!)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-07 19:27:13 -05:00
80db9d76f8 subtle aesthetic updates 2026-01-07 18:49:44 -05:00
6a4d74c5ea category, status, and priority are editable on tickets. 2026-01-07 18:14:29 -05:00
cf2d596219 Sidebar with no hamburger menu 2026-01-07 17:47:11 -05:00