05/22 Enhance codes and fix bugs 3

This commit is contained in:
2026-05-22 15:19:34 -04:00
parent 12b5fd9ce0
commit 5c9a124a71
11 changed files with 665 additions and 36 deletions
+41 -3
View File
@@ -118,6 +118,8 @@ Both decorators must be applied **after** `@login_required`.
| `PasswordResetToken` | `password_reset_tokens` | SHA-256 hashed, 1-hour expiry, single-use. |
| `TicketTemplate` | `ticket_templates` | Pre-filled ticket scaffolds selectable on the new ticket form. |
| `TicketSatisfaction` | `ticket_satisfaction` | One survey per resolved ticket; token-authenticated survey URL. |
| `TicketWatcher` | `ticket_watchers` | Users subscribed to updates on a ticket. Unique `(ticket_id, user_id)`. Added migration 005. |
| `TimeEntry` | `time_entries` | IT staff time log per ticket. Stores `minutes` + optional `note`. Added migration 005. |
### Cascade Rules
@@ -128,6 +130,8 @@ Both decorators must be applied **after** `@login_required`.
- `history`
- `links_as_source` / `links_as_target` (via `TicketLink`)
- `satisfaction` (via `TicketSatisfaction`)
- `watchers` (via `TicketWatcher`)
- `time_entries` (via `TimeEntry`)
**When deleting a ticket, physical files in `UPLOAD_FOLDER` must be removed
manually before the DB delete** — SQLAlchemy cascades handle DB rows only.
@@ -169,6 +173,8 @@ manually before the DB delete** — SQLAlchemy cascades handle DB rows only.
- `GET /kb` / `GET /kb/<id>` — knowledge base
- `POST /tickets/<id>/link` / `POST /tickets/<id>/unlink/<link_id>`
- `POST /tickets/<id>/reopen`
- `POST /tickets/<id>/watch` / `POST /tickets/<id>/unwatch` — AJAX; toggle watcher subscription
- `POST /tickets/<id>/log-time` — IT staff only; AJAX-aware; logs `TimeEntry`
- `GET /canned-responses` — JSON endpoint for IT staff comment box
- `POST /kb/<id>/feedback`
- `GET/POST /survey/<token>` — public (no login required)
@@ -178,7 +184,7 @@ manually before the DB delete** — SQLAlchemy cascades handle DB rows only.
- `GET /admin/users` / `POST /admin/users/new` / `POST /admin/users/<id>/edit` / `POST /admin/users/<id>/delete`
- `GET /admin/tickets` — all tickets with search, filter, pagination
- `POST /admin/tickets/<id>/delete`**admin only**; deletes ticket + physical files
- `POST /admin/tickets/bulk-action` — actions: resolve, close, assign_me, unassign, **delete** (delete is admin-only)
- `POST /admin/tickets/bulk-action` — actions: resolve, close, assign_me, assign_to, unassign, **delete** (delete is admin-only; `assign_to` requires `assign_to_id` form field)
- `GET /admin/tickets/export` — CSV export with active filters
- `GET /admin/kb` / `GET/POST /admin/kb/new` / `GET/POST /admin/kb/<id>/edit` / `POST /admin/kb/<id>/delete` / `POST /admin/kb/<id>/publish`
- `POST /admin/kb/upload-image` — TinyMCE image upload
@@ -236,18 +242,25 @@ log_ticket_history(ticket, field_name, old_value, new_value, changed_by_id)
| `ticket_bulk_resolve` | Bulk resolve |
| `ticket_bulk_close` | Bulk close |
| `ticket_bulk_assign_me` | Bulk assign to self |
| `ticket_bulk_assign_to` | Bulk assign to specific user |
| `ticket_bulk_unassign` | Bulk unassign |
| `kb_create` / `kb_edit` / `kb_delete` | KB article CRUD |
| `kb_attachment_delete` | KB attachment removed |
| `time_log` | IT staff logs time on a ticket |
| `ticket_create_chatbot` | Ticket created via AI chatbot |
### `notification_service.py`
Functions: `notify_new_ticket`, `notify_status_change`, `notify_comment_added`,
`notify_assignment`, `send_satisfaction_survey`
`notify_assignment`, `send_satisfaction_survey`, `notify_watchers`,
`send_weekly_digest`
**Critical rule:** Always call notification functions **after** `db.session.commit()`.
Calling them before commit means they act on uncommitted state.
- **`notify_watchers(ticket, event_title, event_message, exclude_user_id=None)`** — sends in-app notifications to all `TicketWatcher` rows for a ticket. Call after commit from `update_ticket`. Pass `exclude_user_id=current_user.id` so the actor doesn't notify themselves.
- **`send_weekly_digest(app)`** — APScheduler job; runs every Monday at 08:00 UTC via `CronTrigger`. Sends a rich HTML email to all active IT staff / admin users with `email_notif=True`. Summarises new, resolved, still-open, and overdue tickets for the past 7 days.
### `sla_service.py`
- `set_due_date(ticket, app)` — called at ticket creation; reads SLA hours from `SystemSetting`
@@ -488,6 +501,9 @@ Migrations live in `migrations/versions/`. The chain is:
└── 002_add_system_settings — creates system_settings table
└── 003_render_comments — backfills comment bodies to HTML;
widens alembic_version.version_num to VARCHAR(64)
└── 004_widen_system_setting_value — system_settings.value VARCHAR(500) → TEXT
└── 005_add_watchers_and_time_entries — creates ticket_watchers
and time_entries tables
```
**Rules:**
@@ -511,10 +527,13 @@ Migrations live in `migrations/versions/`. The chain is:
| File upload validation | Magic-byte MIME inspection + extension allowlist |
| HTML sanitization | bleach on all comment bodies and KB article bodies |
| XSS in KB | bleach allowlist covers TinyMCE-produced tags; `_sanitize_kb_body()` in `admin.py` |
| XSS in JS | `_esc()` helper in `base.html` escapes all server-supplied strings before DOM insertion (notifications, toasts) |
| SQL injection | SQLAlchemy ORM parameterized queries throughout |
| Real client IP | `ProxyFix` middleware trusts one nginx hop; `log_service._get_real_ip()` reads `X-Forwarded-For` |
| Role enforcement | `@admin_required` / `@it_required` decorators; per-route checks where needed |
| Avatar/file access | All file-serve routes require `@login_required` (except logo) |
| HTTP security headers | `@app.after_request` hook in `create_app()` sets `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `X-XSS-Protection`, `Permissions-Policy` via `setdefault` (never overrides app-set headers) |
| Session cookie security | `ProductionConfig` sets `SESSION_COOKIE_SECURE=True`, `SESSION_COOKIE_HTTPONLY=True`, `SESSION_COOKIE_SAMESITE='Lax'` |
---
@@ -597,6 +616,24 @@ proxies; static files are served directly).
| Admin ticket deletion (single + bulk) | ✅ |
| Activity log viewer | ✅ |
| Configurable display timezone | ✅ |
| HTTP security headers (X-Frame-Options, CSP-adjacent, etc.) | ✅ |
| Session cookie security flags (Secure, HttpOnly, SameSite) | ✅ |
| XSS protection in JS notification/toast HTML via `_esc()` | ✅ |
| Reusable `confirmModal()` system replacing all `confirm()` dialogs | ✅ |
| Toast notifications repositioned to top-right (no FAB overlap) | ✅ |
| Page navigation progress bar (thin top bar on link clicks) | ✅ |
| Dark mode with localStorage persistence and anti-FOCT script | ✅ |
| AJAX save on IT Update Panel (no page reload) | ✅ |
| Mobile-responsive tables via `table-responsive` wrappers | ✅ |
| Employee dashboard: status progress bar + urgency flags per ticket | ✅ |
| Linked tickets shown read-only to employees on ticket detail | ✅ |
| SLA status badge on ticket detail (On track / Overdue / Completed) | ✅ |
| Template preview panel on new ticket form | ✅ |
| Bulk assign to specific IT staff member | ✅ |
| Weekly IT digest email (APScheduler, Monday 08:00 UTC) | ✅ |
| Chatbot KB context injection (top matching articles in system prompt) | ✅ |
| Ticket watchers (subscribe/unwatch, in-app notifications on update) | ✅ |
| Time tracking (IT staff log minutes per ticket, AJAX form) | ✅ |
---
@@ -608,7 +645,7 @@ proxies; static files are served directly).
---
*Last updated: 2026-04-17*
*Last updated: 2026-05-22*
## 18. Notification Architecture
@@ -717,6 +754,7 @@ When an error is reported:
| `002_add_system_settings` | Create `system_settings` table |
| `003_render_comments` | Backfill comment bodies to HTML; widen `alembic_version.version_num` to `VARCHAR(64)` |
| `004_widen_system_setting_value` | Widen `system_settings.value` from `VARCHAR(500)` to `TEXT` |
| `005_add_watchers_and_time_entries` | Create `ticket_watchers` (UniqueConstraint + index) and `time_entries` (index) tables |
## 21. Browser Tab Notification Counter (added 2026-04-17)