442 Commits

Author SHA1 Message Date
8b39c961b6 Remove unused get_accessible_calendar_ids import from dashboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:43:35 +08:00
0401a71fce Fix CompoundSelect chaining: use standalone union_all()
SQLAlchemy 2.0's select().union_all() returns a CompoundSelect which
cannot chain another .union_all(). Use the standalone union_all()
function to combine all three queries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:39:40 +08:00
8f087ccebf Bump InviteSearch onBlur timeout from 150ms to 200ms
Safer margin for click-through on slower devices.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:30:09 +08:00
f54ab5079e Fix QA review findings: C-01, C-02, W-01, W-02, W-04, S-01, S-02, S-03
C-01: Remove nginx rate limit on event invitations endpoint — was
      blocking GET (invitee list) on rapid event switching. Backend
      already caps at 20 invitations per event with connection validation.

C-02: respondingRef uses string prefixes (conn-, cal-, event-) instead
      of fragile numeric offsets (+100000/+200000) to prevent collisions.

W-01: get_accessible_event_scope combined into single UNION ALL query
      (3 DB round-trips → 1) for calendar IDs + invitation IDs.

W-02: Dashboard and upcoming endpoints now include is_invited,
      invitation_status, and display_calendar_id on event items.

W-04: LeaveEventDialog closes on error (.finally) instead of staying
      open when mutation rejects.

S-01: Migration 055 FK constraint gets explicit name for consistency.

S-02: InviteSearch dropdown dismisses on blur (150ms delay for clicks).

S-03: Display calendar picker shows only owned calendars, not shared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:27:01 +08:00
25830bb99e Fix calendar event color not updating after display calendar change
eventDidMount only fires once when FullCalendar first mounts a DOM element.
When event data refetches with a new calendarColor, the existing DOM element
is reused and --event-color CSS variable stays stale.

Fix: renderEventContent now uses a ref callback (syncColor) to walk up to
the parent .umbra-event element and update --event-color on every render,
ensuring background, hover, and dot colors reflect the current calendar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:13:34 +08:00
aa1ff50788 Fix display calendar: text cutoff (py-1) and force refetch on update
- Add py-1 to Select to prevent text clipping at h-8 height
- Use refetchQueries instead of invalidateQueries for calendar-events
  after display calendar update to ensure immediate visual refresh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:14:09 +08:00
d00d6d6d49 Add migration 055: display_calendar_id on event_invitations
Adds nullable FK to calendars, index, and backfills accepted/tentative
invitations with each user's default calendar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:04:50 +08:00
a68ec0e23e Add display calendar support: model, router, service, types, visibility filter
Previously unstaged changes required for the display calendar feature:
- EventInvitation model: display_calendar_id column
- Event invitations router: display-calendar PUT endpoint
- Event invitation service: display calendar update logic
- CalendarPage: respect display_calendar_id in visibility filter
- Types: display_calendar_id on CalendarEvent interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:03:22 +08:00
29c2cbbec8 Fix post-review findings: stale calendar leak, aria-label, color dot, loading state
- Add access check to display calendar batch query (Security L-01)
- Add aria-label, color dot, disabled-during-mutation, h-8 height (UI W-01/W-02/W-03/S-01)
- Add display_calendar_id to EventInvitationResponse schema (Code W-02)
- Invalidate event-invitations cache on display calendar update (Code S-03)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:01:46 +08:00
68a609ee50 Mask calendar name/color for invited events (pen test F-01)
Invitees no longer see the event owner's calendar name/color,
preventing minor information disclosure (CWE-200).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:04:13 +08:00
df857a5719 Fix QA findings: flush before notify, dedup RSVP, sa_false, validation
- C-02: flush invitations before creating notifications so invitation_id
  is available in notification data; eliminates extra pending fetch
- C-03: skip RSVP notification when status hasn't changed
- C-01: add defensive comments on update/delete endpoints
- W-01: add ge=1, le=2147483647 per-element validation on user_ids
- W-04: deduplicate invited_event_ids query via get_invited_event_ids()
- W-06: replace Python False with sa_false() in or_() clauses
- Frontend: extract resolveInvitationId helper, prefer data.invitation_id

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:01:15 +08:00
496666ec5a Fix 'calendar no longer available' for invited events
The shared-calendar removal guard checks allCalendarIds, which only
contains the user's own + shared calendars. Invited events belong to
the inviter's calendar, triggering a false positive. Skip the check
for invited events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 13:41:20 +08:00
bafda61958 Fix invited events hidden by calendar visibility filter
Invited events belong to the inviter's calendar, which doesn't exist
in the invitee's calendar list. The visibleCalendarIds filter was
removing them. Now invited events bypass this filter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 13:36:55 +08:00
0f378ad386 Add event invite actions to notification center + toast on login
- NotificationsPage: Going/Maybe/Decline buttons for event_invite notifications
- NotificationsPage: event_invite icon mapping, eager-refetch, click-to-calendar nav
- NotificationToaster: toast actionable unread notifications on first load (max 3)
  so users see pending invites/requests when they sign in

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 13:00:27 +08:00
a41b48f016 Fix TS build: remove unused isLoadingInvitees var and Select import
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 22:16:53 +08:00
8652c9f2ce Implement event invitation feature (invite, RSVP, per-occurrence override, leave)
Full-stack implementation of event invitations allowing users to invite connected
contacts to calendar events. Invitees can respond Going/Tentative/Declined, with
per-occurrence overrides for recurring series. Invited events appear on the invitee's
calendar with a Users icon indicator. LeaveEventDialog replaces delete for invited events.

Backend: Migration 054 (2 tables + notification types), EventInvitation model with
lazy="raise", service layer, dual-router (events + event-invitations), cascade on
disconnect, events/dashboard queries extended with OR for invited events.

Frontend: Types, useEventInvitations hook, InviteeSection (view list + RSVP buttons +
invite search), LeaveEventDialog, event invite toast with 3 response buttons, calendar
eventContent render with Users icon for invited events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 02:47:27 +08:00
bdfd8448b1 Remove upper date bound on starred events so future events always show
Starred events should appear in the countdown widget regardless of how
far in the future they are. The _not_parent_template filter still
excludes recurring parent templates while allowing starred children.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:59:11 +08:00
348fe8988b Merge feature/calendar-backend-optimisations into main 2026-03-15 01:46:33 +08:00
a2c1058f9c Fix QA findings: single UNION query, weekly validation, nginx docs
W-01: Consolidate get_accessible_calendar_ids to single UNION query
instead of two separate DB round-trips.
W-02: Document that nginx rate limit on /api/events applies to all
methods (30r/m generous enough for GET polling at 2r/m).
W-03: Add weekly rule validation for consistency with other rule types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:46:11 +08:00
be1fdc4551 Calendar backend optimisations: safety caps, shared calendar fix, query consolidation
Phase 1: Recurrence safety — MAX_OCCURRENCES=730 hard cap, adaptive 90-day
horizon for daily events (interval<7), RecurrenceRule cross-field validation,
ID bounds on location_id/calendar_id schemas.

Phase 2: Dashboard correctness — shared calendar events now included in
/dashboard and /upcoming via get_accessible_calendar_ids helper. Project stats
consolidated into single GROUP BY query (saves 1 DB round-trip).

Phase 3: Write performance — bulk db.add_all() for child events, removed
redundant SELECT in this_and_future delete path.

Phase 4: Frontend query efficiency — staleTime: 30_000 on calendar events
query eliminates redundant refetches on mount/view switch. Backend LIMIT 2000
safety guard on events endpoint.

Phase 5: Rate limiting — nginx limit_req zone on /api/events (30r/m) to
prevent DB flooding via recurrence amplification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:31:48 +08:00
99f70f3a41 Merge feature/calendar-visual-overhaul into main
Calendar Visual Overhaul:
- en-AU locale with 12-hour time format
- Translucent event styling via CSS custom properties
- Custom eventContent: dot+title in month, title-first in week/day
- Now-indicator pulse dot with prefers-reduced-motion
- Weekend bg neutralised for cross-browser consistency (10+ attempt RCA)
- Per-view dayHeaderFormat (weekday-only in month view)
- Side-by-side event overlap columns in week/day

Backend Performance:
- Starred events scoped to upcoming_days window
- Dashboard queries use materialized calendar ID list

QA: 1 critical (duplicate migration removed), 3 warnings fixed, reviewed clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:02:21 +08:00
050e0c7141 Fix QA findings: remove duplicate migration, formatting, static classNames
- Remove migration 054 (duplicate of 035 which already has all 3 indexes,
  including a superior partial index for starred events)
- Fix handleEventDidMount indentation and missing semicolons
- Replace eventClassNames arrow function with static UMBRA_EVENT_CLASSES array
- Correct misleading subquery comment in dashboard.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:02:07 +08:00
e12687ca6f Add calendar_events indexes and optimize dashboard queries
Migration 054: three indexes on calendar_events table:
- (calendar_id, start_datetime) for range queries
- (parent_event_id) for recurrence bulk operations
- (calendar_id, is_starred, start_datetime) for starred widget

Dashboard: replaced correlated subquery with single materialized
list fetch for user_calendar_ids in both /dashboard and /upcoming
handlers — eliminates 2 redundant subquery evaluations per request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 00:45:36 +08:00
3e738b18d4 Scope starred events to upcoming_days window
Starred events query had no upper date bound — a starred recurring
event would fill all 5 countdown slots with successive occurrences
beyond the user's configured range. Now capped to upcoming_cutoff_dt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 00:33:23 +08:00
e630832e76 Fix weekend header cells showing different background in Firefox
FC applies its own weekend background to header <th> elements too.
Force weekend header cells to use the same hsl(0 0% 8% / 0.65) as
weekday headers with !important to override FC's built-in styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:14:21 +08:00
a0533ee0a7 Remove weekend background tint — cross-browser compositing unreliable
After 10+ attempts, semi-transparent HSL values on near-black backgrounds
produce visible teal artifacts in Firefox due to compositor divergence.
Weekday/weekend frames now use identical --fc-neutral-bg-color. FC's own
weekend td background is neutralised with transparent !important.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:30:08 +08:00
a0ccaaa4bc Reduce weekend frame tint: 10% was too aggressive, use 9% lightness
hsl(0 0% 10% / 0.65) was visibly too bright vs weekday hsl(0 0% 8% / 0.65)
in Firefox. Reduced to hsl(0 0% 9% / 0.65) — 1% bump, subtle but present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:18:48 +08:00
f5ed64b7da Fix Firefox weekend tint: use absolute HSL values instead of rgba overlay
Firefox composites rgba(255,255,255,0.05) differently against the
fc-daygrid-day-frame's --fc-neutral-bg-color background, producing a
visible mismatch. Switched to absolute HSL values that match the base
pattern:
- Month frame: hsl(0 0% 10% / 0.65) — same alpha as neutral-bg but
  slightly lighter (10% vs 8% lightness)
- Timegrid cols: hsl(0 0% 5.5%) — slightly above page bg (3.9%)

Cross-browser consistent since no alpha compositing is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:09:57 +08:00
29c91cd706 Fix weekend header mismatch and wrong date format in day headers
Header mismatch: Removed weekend tint from column headers — the white
overlay replaced the standard header bg (hsl 0 0% 8% / 0.65), creating
a non-flush look. Weekend differentiation now comes from body cells only.

Date format: dayHeaderFormat was applied globally, causing month view
headers to show dates like "Sat 10/1" instead of just "Sat". Moved to
per-view formats: month shows weekday only, week shows weekday + d/m,
day shows full weekday + day + month name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:57:17 +08:00
d959803985 Fix weekend tint not rendering: replace color-mix() with rgba()
autoprefixer was silently stripping color-mix() during the PostCSS
build pipeline, causing the weekend tint background rules to produce
no output in the deployed CSS bundle. Replaced the three weekend
tint color-mix() calls with equivalent rgba(255,255,255,0.05) which
autoprefixer passes through unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 14:19:20 +08:00
744fe2c224 Fix calendar weekend tint: target fc-daygrid-day-frame not td
FC6 renders an fc-daygrid-day-frame div inside every daygrid td, painted
with --fc-neutral-bg-color (hsl 0 0% 8% / 0.65). This opaque-ish layer sits
on top of the td background, completely hiding any rgba white overlay applied
to the td itself. Previous attempts set the tint on the td — it was never
visible because the frame covered it.

Fix: apply 5% white color-mix overlay directly to fc-daygrid-day-frame for
month view, and !important on fc-timegrid-col for week/day view.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 14:05:31 +08:00
d9b5868343 Fix weekend tint double-stacking: remove fc-daygrid-day-frame rule
Both the <td> and its child fc-daygrid-day-frame had the 3% white overlay,
causing the frame area to compound to ~6% while td edges stayed at 3%.
This created an uneven "not flush" pattern. The td rule alone is sufficient.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:56:30 +08:00
3ead9cd25a Fix weekend tint: replace grayscale with 3% white overlay
RCA finding: grayscale tints are imperceptible on near-black (#0a0a0a)
backgrounds. Deltas of 3-5 RGB units fall below human JND threshold
and OLED panels can clip them to identical output via gamma compression.

Changed from hsl(0 0% 5%) to hsl(0 0% 100% / 0.03) — a semi-transparent
white overlay that composites additively for visible contrast.

See .claude/context/RCA/rca-calendarbg.md for full investigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:21:24 +08:00
ebeaefe0c5 Fix calendar weekend tint contrast and dot event margin
- Weekend bg raised from hsl(0 0% 2%) to hsl(0 0% 5%) across all 4 rules
  (day cells, col headers, timegrid cols, daygrid-day-frame) so the tint is
  visually distinct against the #0a0a0a page background
- Reduced .fc-daygrid-event-dot margin from default 4px each side to
  0 2px 0 0 on umbra dot events, tightening the gap between dot and title

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 07:50:01 +08:00
e18c94cd83 Fix weekend tint visibility, dot spacing, and event FOUC
Weekend tint: hsl(0 0% 6%) was lighter than page bg #0a0a0a (imperceptible).
Changed to hsl(0 0% 2%) = #050505 for visible darkening. Added rule for
fc-daygrid-day-frame to paint above FC6 internal layers.

Dot spacing: Reduced padding from 1px 4px to 1px 2px for tighter edge gap.

FOUC fix: Moved umbra-event class from eventDidMount (post-paint) to
eventClassNames (synchronous pre-mount). eventDidMount now only sets
the --event-color CSS custom property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:43:33 +08:00
d6f5975fb9 Add dot indicator to timed month events in custom eventContent
eventContent replaces FC's default inner markup including the dot span.
Render a manual fc-daygrid-event-dot with border-color: var(--event-color).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:35:40 +08:00
40d0bb336c Merge fix: weekend tint cutoff and missing event dots 2026-03-13 02:34:34 +08:00
2a850ad8fd Fix calendar weekend tint cutoff and missing month-view event dots
- index.css: add explicit .fc-col-header-cell.fc-day-sat/sun rules with
  !important to override the generic header background, and cover
  .fc-timegrid-col weekend cells so the tint reaches all views
- CalendarPage.tsx: render .fc-daygrid-event-dot manually in the timed
  month-view eventContent branch — FC's eventContent hook replaces the
  entire default inner content including the dot span, so the CSS target
  had nothing to paint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:33:43 +08:00
0e35d473eb Refine calendar events: dot-only month timed, title-first week, no left border
- Month timed events: dot + title only, hover reveals translucent card
- Month all-day events: keep translucent fill
- Time right-aligned in month view (ml-auto)
- Week/day view: title on top, time underneath for better scanning
- Remove 2px left accent border from all events
- Set color:'transparent' on FC event data to prevent inline style conflicts
- Recurring repeat icon preserved in all views

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:24:20 +08:00
dec2c5d526 Fix event colors: remove inline backgroundColor/borderColor from event data
The previous commit failed to remove inline color props due to CRLF line
endings. FullCalendar was still setting inline styles that override CSS.
calendarColor is now correctly in extendedProps for the eventDidMount callback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:09:14 +08:00
c473e7e235 Calendar visual overhaul: translucent events, AU locale, typography hierarchy
- Import en-AU locale (object, not string) for proper date format (day/month)
- Add 12-hour time format (9:00 AM), side-by-side overlap columns
- Replace flat opaque event rectangles with translucent color-mix fills (12% opacity)
- Add 2px left accent border per calendar color via CSS custom property
- Implement eventContent render hook with typography hierarchy (time secondary, title primary)
- Add recurring event indicator (Repeat icon) next to recurring event titles
- Add now-indicator 6px pulse dot with prefers-reduced-motion respect
- Add subtle weekend column tint (hsl 0 0% 6%)
- Mobile: hide time spans in month view custom eventContent
- Update +more popover to inherit translucent event styling
- Document calendar event patterns in stylesheet.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:02:21 +08:00
652be41da4 Merge fix/category-filter-position into main
Fix category chips rendering in wrong position (between search and add button).
Chips now appear inline after the Categories toggle pill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:27:20 +08:00
85a9882d26 Fix category chips appearing in wrong position
Category chips were rendering as a separate flex row that got pushed to the
far right (between search and add button). Flatten the layout so chips appear
inline immediately after the Categories toggle pill, separated by a divider.

Remove redundant wrapper divs from TodosPage, PeoplePage, LocationsPage —
CategoryFilterBar now owns its own flex-1 sizing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:22:28 +08:00
bb5cbfa4b3 Merge optimize/docker-and-performance into main
Docker: .dockerignore, entrypoint.sh (PID 1), pinned images, network
segmentation, resource limits, npm ci, frontend healthcheck, DRY proxy config.

Backend: single JOIN auth, async Argon2id, settings cache, batch reorder,
bulk ntfy dedup, permission JOIN, cascade batch, collapsed admin COUNTs,
connection pool tuning, composite indexes (calendar_members, ntfy_sent).

Frontend: lazy-loaded routes, vendor chunk splitting, date-scoped calendar
events (87% payload reduction), 30s poll interval, clock isolation,
conditional shared-calendar polling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:15:40 +08:00
e270a2f63d Fix team review findings: reactive shared-calendar gate + ReorderItem hardening
- Convert hasSharingRef from useRef to useState in useCalendars so
  refetchInterval reacts immediately when sharing is detected (P-01)
- Add extra="forbid" to ReorderItem schema to prevent mass-assignment (S-03)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:50:02 +08:00
a94485b138 Address code review findings across all phases
Phase 1 fixes:
- W-01: Add start_period: 30s to backend healthcheck for migration window
- W-03: Narrow .dockerignore *.md to specific files (preserve alembic/README)

Phase 2 fixes:
- C-01: Wrap Argon2id calls in totp.py (disable, regenerate, backup verify,
  backup store) — missed in initial AC-2 pass
- S-01: Extract async wrappers (ahash_password, averify_password,
  averify_password_with_upgrade) into services/auth.py, refactor all
  callers to use them instead of manual run_in_executor boilerplate
- W-01: Fix ntfy dedup regression — commit per category instead of per-user
  to preserve dedup records if a later category fails

Phase 4 fixes:
- C-01: Fix optimistic drag-and-drop cache key to include date range
- C-02: Replace toISOString() with format() to avoid UTC date shift in
  visible range calculation
- W-02: Initialize visibleRange from current month to eliminate unscoped
  first fetch + immediate refetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:19:33 +08:00
2ab7121e42 Phase 4: Frontend performance optimizations
- AW-2: Scope calendar events fetch to visible date range via start/end
  query params, leveraging existing backend support
- AW-3: Reduce calendar events poll from 5s to 30s (personal organiser
  doesn't need 12 API calls/min)
- AS-4: Gate shared-calendar polling on hasSharedCalendars — saves 12
  wasted API calls/min for personal-only users
- AS-2: Lazy-load all route components with React.lazy() — only
  AdminPortal was previously lazy, now all 10 routes are code-split
- AS-1: Add Vite manualChunks to split FullCalendar (~400KB), React,
  TanStack Query, and UI libs into separate cacheable chunks
- AS-3: Extract clockNow into isolated ClockDisplay memo component —
  prevents all 8 dashboard widgets from re-rendering every minute

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:12:33 +08:00
846019d5c1 Phase 3: Backend queries and indexes optimization
- AW-1: Add composite index on calendar_members(user_id, status) for the
  hot shared-calendar polling query
- AS-6: Add composite index on ntfy_sent(user_id, sent_at) for dedup lookups
- AW-5: Combine get_user_permission into single LEFT JOIN query instead of
  2 sequential queries (called twice per event edit)
- AC-5: Batch cascade_on_disconnect — single GROUP BY + bulk UPDATE instead
  of N per-calendar checks when a connection is severed
- AW-6: Collapse admin dashboard 5 COUNT queries into single conditional
  aggregation using COUNT().filter()
- AC-3: Cache get_current_settings in request.state to avoid redundant
  queries when multiple dependencies need settings in the same request

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:08:45 +08:00
1f2083ee61 Phase 2: Backend critical path optimizations
- AC-1: Merge get_current_user into single JOIN query (session + user in
  one round-trip instead of two sequential queries per request)
- AC-2: Wrap all Argon2id hash/verify calls in run_in_executor to avoid
  blocking the async event loop (~150ms per operation)
- AW-7: Add connection pool config (pool_size=10, pool_pre_ping=True,
  pool_recycle=1800) to prevent connection exhaustion under load
- AC-4: Batch-fetch tasks in reorder_tasks with IN clause instead of
  N sequential queries during Kanban drag operations
- AW-4: Bulk NtfySent inserts with single commit per user instead of
  per-notification commits in the dispatch job

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:05:54 +08:00
dbad9c69b3 Phase 1: Docker infrastructure optimization
- Add .dockerignore for backend and frontend (DC-1: eliminates node_modules/
  and .env from build context)
- Delete start.sh with --reload flag (DC-2: superseded by Dockerfile CMD)
- Create entrypoint.sh with exec uvicorn (DW-5: proper PID 1 signal handling)
- Pin base images to patch-level tags (DW-1: reproducible builds)
- Reorder Dockerfile: create appuser before COPY, use --chown (DW-2)
- Switch to npm ci for lockfile-enforced installs (DW-3)
- Add network segmentation: backend_net + frontend_net (DW-4: db unreachable
  from frontend container)
- Add deploy.resources limits to all services (DW-6: OOM protection)
- Refactor proxy-params.conf to include security headers, deduplicate from
  nginx.conf location blocks (DW-7)
- Add image/svg+xml to gzip_types (DS-1)
- Add wget healthcheck for frontend service (DS-2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:03:46 +08:00