Commit graph

77 commits

Author SHA1 Message Date
c2ebbe586b feat: pattern analysis panel — cluster by sender/IP/subject, Find Similar from dialog, counts recipients per cluster 2026-04-01 10:00:20 -04:00
0020be1dbf fix: tint phishing/sextortion rows red in table, include in high-risk summary count 2026-04-01 09:46:16 -04:00
8bc9eca3cf feat: detect sextortion/phishing by subject pattern, explain why spam score is 0, actionable remediation 2026-04-01 09:42:37 -04:00
83a92a23c9 fix: tab bar flex layout, search form single-row with flex-wrap, View button column wider 2026-04-01 09:29:52 -04:00
8e28062d85 feat: add Delivered Mail tab with message-finder search, spam scoring, analysis dialog
- New POST /api/mimecast/delivered route using message-finder/search API
- DeliveredMailTab: search by recipient, sender, subject, time range (6h–7d)
- Results table with status badge, spam score, row tinting for high/moderate risk
- Summary stats bar: total / high spam (≥10) / moderate (5-9) / clean counts
- Sort by date or spam score; filter by status (accepted/held/rejected/bounced)
- DeliveredAnalysisDialog: explains why high-score mail got through, envelope mismatch detection, actionable remediation steps (block domain, adjust policy threshold, report)
- MimecastDeliveredMessage interface + searchDeliveredMessages() method in client
2026-04-01 08:17:18 -04:00
0df5c344b5 feat: table header shows filtered count vs loaded vs API total 2026-04-01 08:11:03 -04:00
3cb312e6ec feat: live client-side recipient filtering with match count, no reload needed 2026-04-01 08:08:59 -04:00
672d8c52a6 fix: use table-fixed + inline style widths for reliable column sizing, fix truncation 2026-04-01 08:05:09 -04:00
90c60d1be7 fix: dialog release button no Check icon, add size/attachments row, add Mimecast console link 2026-04-01 07:40:49 -04:00
6c2c8ec25f fix: table layout with colgroup widths, remove Check icon (was rendering as arrow), clean policy badge colors 2026-04-01 07:38:46 -04:00
d0112ec12e fix: held mail analysis dialog layout + color scheme + policy context
- Dialog constrained to max-w-xl, max-h-85vh, overflow-y-auto (no more overflow)
- Severity uses high/medium/low with red/amber/blue (not red/yellow/green)
- Left-border accent stripe on explanation block
- 'What was already evaluated' section shows Mimecast pipeline steps per hold type
- analyzeMessage uses reasonCode for precision, detects auth codes / marketing / spam / DMARC / impersonation / malware
- Warning flag on dangerous release actions (impersonation, malware)
- Grid layout for message details instead of flex rows
2026-04-01 07:32:09 -04:00
9952365df1 feat: held mail analysis dialog + fix release check
- Add analyzeMessage() — context-aware explanations for DMARC/impersonation/spam/auth-code/malware holds
- Add MessageAnalysisDialog with severity icon, message details, explanation, and resolution options
- Analyze button per row opens the dialog; Release button inside dialog triggers release + closes
- Fix releaseHeldMessage(): treat HTTP 200 + empty fail[] as success (not release===true check)
- Remove action:'release' from payload (API doesn't need it)
2026-04-01 07:18:52 -04:00
a15946daf8 feat: held mail release button + per-tenant fetch
- Add releaseHeldMessage() to MimecastClient (POST /api/gateway/hold-release)
- Add POST /api/mimecast/held/release route
- HeldMailTab: tenant selector before load (defaults to Wulf), only fetches selected tenant
- Release button per row with spinner + optimistic removal on success
- Error shown inline under Release button if release fails
2026-04-01 07:09:42 -04:00
2f88be9ab3 fix: held mail route force-dynamic, 403 fallback, favicon 404s, error display
- export const dynamic = 'force-dynamic' on /api/mimecast/held to prevent Next.js caching
- Add AbortController timeout (20s) per request in MimecastClient.request()
- getHeldMessages: 403 fallback without admin:true flag for tenants lacking permission
- Reduce maxMessages default to 100 (10 pages) to stay within route timeout
- Show 'permission denied' tooltip in tenant badge for 403 errors
- Surface HTTP errors in HeldMailTab instead of silently failing
- Add missing favicons: sentinelone.ico, itglue.ico, mimecast.ico
2026-04-01 06:52:58 -04:00
fcdec8e38b feat: Mimecast multi-tenant held mail viewer
- migration 062: mimecast_tenants table (company_id, client_id/secret, account_code)
- Seed Wulf (CUSA13A95) + Seubert (CUSA96A181) tenants
- MimecastClient.getHeldMessages(): full pagination via meta.pagination.next cursor
  (API always returns 10/page regardless of pageSize param, totalCount in meta)
- getMimecastClientForTenant() factory for per-tenant instantiation
- GET /api/mimecast/held?tenantId=&recipient= — fetches all tenants in parallel,
  merges + sorts by date, returns per-tenant counts + combined messages[]
- Held Mail tab on /admin/sync/mimecast (on-demand load, recipient filter,
  tenant badges, policy filter dropdown, DMARC/impersonation highlighted red)
2026-03-31 22:38:22 -04:00
a98c0daf15 feat: classification labels in data browser + kiosk recurring revenue filter
- data-browser/companies: resolve classification picklist IDs to labels in
  table column and detail modal; also added to DetailModal COMPANY_GROUPS
- DetailModal: add 'classification' FieldType with color-coded badge map
- kiosk stats + activity: switch from label-based exclusion to ID-based
  allowlist (included_classifications). Only shows companies with
  classification IN (15,16,17,18,203,205,206,207,202,5,12)
  = managed service / recurring revenue tiers only
2026-03-30 14:56:02 -04:00
5f0fbb4734 feat: complete companies field sync — 4 missing columns + classification mapping
- Migration 060: add bill_to_company_location_id, impersonator_creator_resource_id,
  invoice_non_contract_items_to_parent_company, quote_email_message_id
- entity-mapper: mapCompany() now maps all 57 Autotask API fields incl. classification
- autotask.ts: Company interface expanded to cover all API fields
- data-browser/companies: add Classification column + sort to table;
  expand detail modal with classification, category, owner, territory, market_segment,
  parent_company, create_date
2026-03-27 12:22:53 -04:00
5f4e326804 feat: Separate bypass vs disabled users in Duo UI
Bypass = security risk (MFA not enforced) — shown in red, expandable panel
Disabled = locked out, no threat — shown in muted gray, separate expandable panel

- Split /api/duo/status counts into bypass and disabled separately
- /api/duo/users/flagged returns { bypass: [], disabled: [] } instead of flat list
- Overview card: only bypass triggers red warning icon (disabled does not)
- Detail page: two separate expandable sections with distinct severity styling
- Both sections include user, email, account name, enrolled status, last login, notes
- Covers all accounts (parent + children)
2026-03-27 11:41:52 -04:00
5037d64948 feat: Add bypass/disabled users panel to Duo sync page
- Created GET /api/duo/users/flagged — returns users with status bypass or disabled, joined with account name
- Clickable warning banner expands to show full user table
- Table shows: user, email, account, status badge (yellow=bypass, red=disabled), enrolled, last login, notes
- Fixed JOIN: duo_users.duo_account_id is varchar account_id, not integer id
2026-03-27 11:31:31 -04:00
72bdc6a241 feat: Add Duo Security card to /admin/sync overview + detail page
- Added Duo card to sync overview grid (category: 2FA/MFA, green)
- Shows accounts, users, phones, auth logs counts + bypass/disabled warning
- Created /admin/sync/duo detail page with:
  - Stat cards (accounts, users, phones, auth logs, groups, integrations)
  - Parent account summary
  - Child accounts table with user counts, matched company, sync time
  - Sync Now button with polling for completion
- Created GET /api/duo/status endpoint (counts + last sync + bypass count)
- Added duo.ico logo
2026-03-27 11:14:49 -04:00
a4242b81be feat: Duo Security integration — full data sync from Accounts + Admin API
Duo API Client (lib/services/duo-client.ts):
- HMAC-SHA1 request signing, GET/POST, automatic pagination
- Rate-limit handling (429 + Retry-After), configurable timeout
- Accounts API: listAccounts() via POST /accounts/v1/account/list
- Admin API: getUsers, getPhones, getGroups, getIntegrations, getAuthLogs
- Child account access: parent creds signed against child api_hostname + account_id
- Factory helpers: getDuoAccountsClient(), getDuoAdminClient()

Database (migration 058):
- 6 tables: duo_accounts, duo_users, duo_phones, duo_auth_logs, duo_groups, duo_integrations
- All with proper FKs, indexes, JSONB fields for capabilities/location/groups

Sync Service (lib/services/duo-sync-service.ts):
- syncAll(): accounts → per-child data + auth logs → parent account → company matching
- Sequential child processing to respect rate limits
- Incremental auth logs (mintime = last synced timestamp, default 30 days)
- Company matching: exact → case-insensitive containment (30/32 = 94% matched)
- Non-blocking with sync ID tracking

API Routes:
- POST/GET /api/duo/sync — trigger sync / check status
- GET /api/duo/accounts — list all accounts with stats + matched company
- GET /api/duo/accounts/[id]/users — users for a specific account
- POST /api/openclaw/sync/duo — OpenClaw trigger with API key auth

Results: 33 accounts, 832 users, 925 phones, 5927 auth logs, 46 groups, 78 integrations
2026-03-27 09:18:04 -04:00
44db9a3019 feat: add description block to mobile ticket detail with View original toggle 2026-03-23 16:15:05 -04:00
fea62382de feat: mobile nav page + suppress desktop header on /mobile/* routes
- AppNavigation returns null on /mobile/* (no more horizontal scroll)
- Mobile header: 'Pulse' title (links home) + Menu icon (links to /mobile/nav)
- /mobile/nav: full-screen nav page with touch-friendly cards
  - Mobile Views: Dashboard, Tickets, Finance (large icon cards)
  - Full Site: Quotes, Config Items, Backup, Engagement, Ticket Digest, Admin
  - Sign out button
- Bottom tab bar unchanged (Dashboard / Tickets / Finance)
2026-03-23 12:58:09 -04:00
44847ccf21 feat: QBO sync schedules (2AM + 4PM) + Sync QBO button on mobile finance page
- DB: inserted qbo-sync-2am (0 2 * * *) and qbo-sync-4pm (0 16 * * *) schedules
- Mobile finance: 'Sync QBO' button triggers POST /api/qbo/sync incremental,
  polls /api/qbo/sync GET until lastSync timestamp changes (max 90s),
  then reloads finance data
- Shows last sync timestamp below page title
- Separate refresh-only button (↻) for quick display refresh without re-syncing
- Sync status message shown during polling
2026-03-23 12:36:02 -04:00
8cd8a94412 fix: mobile ticket view - internal notes toggle (publish=2), Mail icon for email notes, showInternal state 2026-03-23 12:32:35 -04:00
5dfd68affd perf: switch ticket modal from live API to Postgres
- New GET /api/tickets/by-number/[ticketNumber]: single query with resources
  join, returns ticket + picklist labels (status/priority cached per deploy)
- /api/tickets/[id]/notes: Postgres ticket_notes + resources join (was N+1 API calls)
- /api/tickets/[id]/time-entries: Postgres time_entries + resources join (was N+1 API calls)
- Modal: replaces 2-step fetch (all tickets → by id) with single by-number lookup
2026-03-23 11:09:29 -04:00
b72fe2c70e feat: ticket modal collapsible notes and time entries
- New GET /api/tickets/[id]/notes: fetches TicketNotes from Autotask,
  enriches with creator resource names, sorted newest first
- Modal: collapsible Notes section (lazy fetch on expand, shows count badge)
- Modal: collapsible Time Entries section (lazy fetch, count + total hours in header)
- Both sections fetch once and cache for the modal session
2026-03-23 10:27:31 -04:00
0a318fb9d6 fix: ticket modal — picklist labels for status/priority, compact layout
- Ticket [id] route: resolve statusLabel/priorityLabel via getTicketStatusPicklist()
  and getTicketPriorityPicklist() with module-level cache (fetched once per deploy)
- Modal: remove subtitle, move ticket# and title into header, compact single-row
  metadata (status badge + priority badge + assigned resource), dates in 2-col grid,
  smaller buttons
2026-03-23 10:14:12 -04:00
115819bea0 fix: dashboard quotes from SalesBldr API, RMM total from datto_rmm_sites
- Replace non-existent 'quotes' DB table lookup with live SalesBldr API call
- Fix RMM total: was checking 'rmm_sites' (wrong), now uses 'datto_rmm_sites'
- Add SALESBLDR_API_URL/KEY to .env.local (were only in .env, not loaded by container)
2026-03-23 09:45:01 -04:00
414ad78c36 feat: repo commit tracking and OpenClaw notification
- Migration 058: repo_commits + openclaw_instances tables (seeded with overwatch)
- POST /api/webhooks/forgejo: receives Forgejo push events, stores commits,
  forwards HMAC-signed payload to all enabled OpenClaw instances, sends Telegram
- GET /api/openclaw/repo-commits: OpenClaw polling endpoint (filters: since, repo, branch, limit)
- GET/POST /api/admin/openclaw-instances: manage instance registry
- PATCH/DELETE /api/admin/openclaw-instances/[id]: update/remove instances
- FORGEJO_WEBHOOK_SECRET in .env.local (leave empty to skip HMAC verification)
2026-03-21 18:52:22 -04:00
679fe3871c feat: add OpenClaw read-only Datto RMM API endpoints
GET /api/openclaw/datto-rmm/sites
GET /api/openclaw/datto-rmm/devices         (filters: siteUid, online, deleted, page, limit)
GET /api/openclaw/datto-rmm/devices/[uid]
GET /api/openclaw/datto-rmm/devices/[uid]/audit  (always live)
GET /api/openclaw/datto-rmm/alerts          (filters: resolved, siteUid, deviceUid, limit)
GET /api/openclaw/datto-rmm/alerts/open

- All protected by x-openclaw-key header
- Default: queries Pulse DB (datto_rmm_devices/alerts/sites tables)
- ?live=true: proxies to Datto RMM API via DattoRMMClient
- Responses include source:'db'|'live' for data freshness awareness
2026-03-21 17:45:19 -04:00
fe9f806c50 feat: mobile — filter all queries to Wulf Managed companies only
All mobile dashboard and ticket list queries now INNER JOIN companies
on user_defined_fields->>'MSP Service Model' = 'Wulf Managed', scoping
all stats (open total, by priority, by queue, SLA, recent activity)
and ticket list to managed clients only. 349 open tickets in scope.
2026-03-18 00:08:41 -04:00
012c7bde50 feat: mobile timeline — render links as tappable [link] in notes and time entries
- Replace stripHtml() with renderContent() that:
  1. Extracts <a href="...">...</a> anchors before stripping HTML
  2. Strips remaining HTML tags cleanly
  3. Detects bare https?:// URLs in plain text
  4. Renders each as a tappable <a>[link]</a> with text-primary underline
- Collapsed note preview still shows plain text (no link clutter)
- Expanded note/time-entry body shows inline [link] elements
2026-03-18 00:05:56 -04:00
8315a46da0 fix: mobile ticket timeline — hide system/workflow notes by default
- API: marks note_type 13,91,93,94,99,101 as is_system=true
  (13=workflow rule fired, 91=workflow templates, 99=RMM system notes,
   93/94=merge/absorb, 101=other system)
- Frontend: system notes filtered out by default; 'Show system' toggle
  in timeline header reveals them
- Time entry expanded card uses bg-muted/border-border (dark-mode safe)
  instead of hard-coded bg-blue-50 which was unreadable in dark theme
- Priority labels and colors corrected to match actual Autotask values
- Status labels expanded to full set
2026-03-17 23:49:50 -04:00
5719daa84b fix: mobile dashboard priority labels now join priorities table
- API: replaced hardcoded wrong map (1=Critical etc) with JOIN on priorities table
- API: sorted by count DESC instead of priority value
- Frontend: PRIORITY_COLOR/TEXT maps updated to cover all 10 actual Autotask values (1-11)
- Middleware: added /api/mobile to publicRoutes (was getting auth-redirected)
2026-03-17 23:44:00 -04:00
5be7b22953 feat: OpenClaw external agent sync API at /api/openclaw/*
- API key auth via x-openclaw-key header (OPENCLAW_API_KEY env var)
- GET  /api/openclaw/sync/status
- POST /api/openclaw/sync/autotask/incremental
- POST /api/openclaw/sync/autotask/full
- POST /api/openclaw/sync/autotask/entity   { entities: [...] }
- POST /api/openclaw/sync/datto-rmm          { syncType: full|incremental }
- POST /api/openclaw/sync/sentinelone
- POST /api/openclaw/sync/veeam              { syncType: full|incremental }
- POST /api/openclaw/sync/zoom
- POST /api/openclaw/sync/engagement
- POST /api/openclaw/sync/qbo                { syncType: full|incremental }
- POST /api/openclaw/sync/zabbix
- POST /api/openclaw/sync/itglue
All routes bypass Better Auth middleware, delegate to existing sync services
2026-03-17 23:26:44 -04:00
25bb70cfa6 feat: Mimecast email integration — message logs, threat events, 120d retention, admin UI 2026-03-17 16:23:47 -04:00
0f1083f5b6 feat: mobile app — scaffold, ticket dashboard, finance AR overview, invoice drill-down 2026-03-17 15:31:56 -04:00
02c81bf4e4 feat: enable Entra ID authentication
- Enable Better Auth middleware (was bypassed with return NextResponse.next())
- Re-enable AuthProvider in root layout
- Add public routes for webhooks, sync, kiosk, QBO, legal, health endpoints
- Set Microsoft Entra ID credentials and production Better Auth URLs
- Hide Engagement and Admin nav sections from non-super-admins
- Fix auth DB columns: snake_case → camelCase for Better Auth compatibility
- Add twoFactorEnabled column to user table
2026-03-17 08:49:20 -04:00
b98c67482a feat: QuickBooks Online integration
- Add QBO OAuth2 client with token refresh (lib/services/qbo-client.ts)
- Add QBO sync service for invoices, payments, deposits, purchases, journal entries, reports (lib/services/qbo-sync-service.ts)
- Add QBO types (lib/types/qbo.ts)
- Add API routes: /api/qbo/auth, /api/qbo/sync, /api/qbo/disconnect
- Add /admin/qbo status and sync management page
- Add legal pages: /legal/eula, /legal/privacy (Intuit app assessment)
- Add QBO nav link under Admin
- Fix reports: remove invalid summarize_column_by, add accounting_method from Preferences API, add showrows=all&showcols=all
- Add CashFlow report type alongside P&L and BalanceSheet
- Add NoReportData check to skip empty report months
- Add intuit_tid capture in error messages
- Add redirect: follow for cluster routing
- Migration 051: qbo_tokens, qbo_invoices, qbo_payments, qbo_deposits, qbo_transactions, qbo_reports tables

Also includes earlier work:
- Ping flap suppression pipeline step
- Ticket digest reports with LLM analysis
- Zabbix WAN monitor and gap analysis
- Kiosk is_deleted filter fixes
- Datto RMM ping target enrichment
- Entity sync soft-delete detection
2026-03-17 07:39:55 -04:00
c518eefdb2 feat: Morning NOC Summary adaptive card for Teams
- Add MorningSummaryService with Zabbix aggregation and adaptive card builder
- Add webhook delivery system with Teams incoming webhooks
- Add admin UI at /admin/morning-summary for webhook/config management
- Add API routes: /send, /test, /webhooks, /webhooks/[id], /config, /history
- Register morning-summary cron job in SyncScheduler (Mon-Fri 6:30 AM)
- Add outages_only filter (Unavailable triggers only)
- Fix host resolution: use getTriggerEnabledHosts to exclude disabled hosts
- Fix resolved events: event.get value:1 scoped to window with r_eventid filter
- Remove emojis from fact rows and section headers in card
- Remove Open Zabbix button (duplicate of View Problems)
- Add migrations: morning_summary_config + morning_summaries tables
- Add outages_only column to morning_summary_config
2026-03-11 09:34:51 -04:00
19605f82aa feat: IT Glue integration, workflow engine, pipelines, Zabbix WAN, notification channels, backup status UI improvements, nav alignment fixes 2026-02-27 14:52:14 -05:00
ed6c4a8b65 feat: Add SentinelOne integration
- Add SentinelOne API client (lib/services/sentinelone-client.ts)
  - Paginated fetching for sites, agents, threats
  - JWT token auth via S1_API_URL / S1_API_TOKEN env vars

- Add SentinelOne sync service (lib/services/sentinelone-sync-service.ts)
  - Full sync: sites, agents, threats into s1_* tables
  - Sync history tracking with per-entity results

- Add DB migration 038: s1_sites, s1_agents, s1_threats,
  s1_company_mappings, s1_sync_history tables

- Add API routes:
  - POST/GET /api/sentinelone/sync
  - GET/POST/DELETE /api/sentinelone/company-mappings
  - GET /api/sentinelone/coverage (fixed Cartesian product bug)

- Add UI pages:
  - /admin/sync/sentinelone — sync admin with history + stats
  - /sentinelone/coverage — AV coverage report per site
  - /sentinelone/mappings — map S1 sites to Autotask companies

- Wire SentinelOne into admin sync overview card grid
- Add SentinelOne Sync to app navigation
- Fix docker-compose: remove explicit S1 env var entries that
  were overwriting env_file values with empty strings
2026-02-27 05:31:31 -05:00
d7c3dc7168 feat: Autotask webhook integration, TicketNotes, Datto RMM, workflow engine, Veeam agents/alarms, AI triage, misc improvements 2026-02-20 10:28:15 -05:00
3c13defacb feat: kiosk UI updates - new cards, gauge chart, performance improvements
- Updated kiosk stats API with expanded metrics
- New components: company-tickets-card, gauge-chart, service-desk-card, ticket-leaders-card
- Updated cycling-display, kpi-card, and ticker components
- Added performance.css for kiosk optimizations
- Added Wulf logo asset
2026-02-19 15:31:23 -05:00
5dc7a7e66b feat: Veeam VSPC backup integration - sync, compliance, UI
- Database: 7 Veeam tables + backup_type_udf column on configuration_items
- API Client: VSPC REST API v3 client with pagination, rate limiting, Bearer auth
- Sync Service: full/incremental sync for orgs, servers, repos, jobs, agent jobs, workloads
- Scheduler: veeam-incremental (30min) and veeam-full (daily 2AM) schedules
- Compliance Engine: cross-references Autotask config items vs Veeam workloads
- API Endpoints: backup-status, companies, workloads, jobs, repos, compliance, sync
- UI: Backup Status page with Overview + Contract Compliance tabs
- Navigation: added Backup Status link with HardDrive icon
- Docker: added VEEAM_VSPC_URL and VEEAM_VSPC_API_KEY env vars to compose
2026-02-11 21:04:28 -05:00
73a2e9b9a3 perf: optimize ticker animation and increase speed
- Added translate3d(0,0,0) for hardware acceleration
- Added font smoothing for better rendering
- Reduced default ticker speed from 60s to 30s (2x faster)
- Improved animation smoothness to reduce jitter
- Better GPU acceleration with backface-visibility and willChange
2026-02-05 07:58:33 -05:00
f49767d1eb fix: update favicon to use Wulf logo PNG format
- Added favicon.png copy of Wulf logo
- Updated metadata to use PNG favicon with multiple size options
- Should fix triangle icon issue in browser tab
- Favicon now properly displays Wulf logo
2026-02-04 08:12:35 -05:00
98199f671d feat: add Wulff Consulting logo to navigation and favicon
- Added Wulff logo (W_RGB.png) to public directory
- Updated favicon to use Wulff logo
- Added logo to upper right of navigation bar (h-10)
- Logo appears next to theme toggle
- Links to home page when clicked
2026-02-03 23:49:58 -05:00
f27b23bec8 fix: pre-populate classifications instead of syncing from Autotask API
- Autotask REST API doesn't expose ClassificationIcons endpoint
- Created migration to populate 17 standard classification icons
- Removed sync button from UI (classifications are pre-loaded)
- Includes: Tools Only, Co-Managed, Wulf 365 Essentials, Partner, etc.
- Users can now select any classification to exclude from kiosk
2026-02-03 20:54:35 -05:00