Commit Graph

1213 Commits

Author SHA1 Message Date
Scott Idem
7f9666dc1e fix: authenticate_passcode — priority ordering, full role flags, per-role TTL, min_length 2026-04-10 11:53:58 -04:00
Scott Idem
ea25bf78d4 import: map marketing consent CSV column to event_badge.agree_to_tc and allow_tracking 2026-04-07 19:59:51 -04:00
Scott Idem
c837d465ca chore: remove temporary debug logging from event_badge_methods
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 17:10:39 -04:00
Scott Idem
2659047d24 fix: sql_update record_id missing on Vision ID models — update path now works
All create_update_*_v4 functions for event_badge, event_person,
event_person_profile, event_presenter, and event_presentation were
calling sql_update without record_id. Vision ID models use Optional[str]
IDs with a root_validator that strips integer values, so the serialized
dict contained no id key and sql_update could not identify the row.

Fix: pass record_id=<integer_id> explicitly to sql_update in all five
functions. Also fix walrus-operator false-negative: None return from
sql_update (0 rows affected — record unchanged) is not an error and
should not abort sub-object cascade; use explicit `is False` check.

Also broadens Axonius badge_type_code mapping to substring match so
future ticket name variants still resolve correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 16:50:04 -04:00
Scott Idem
18374f855f event: Zoom CSV import — temporary Axonius badge_type_code mapping (attendee/sponsor) 2026-04-07 15:36:23 -04:00
Scott Idem
e5acefe8f6 model: event_badge_template — treat other_json as Json to match DB 2026-04-07 13:27:30 -04:00
Scott Idem
082163b5df Spaces gone 2026-04-07 13:03:27 -04:00
Scott Idem
e35fdb4f67 event: Zoom CSV import — finalize mapping and cleanup (staged changes) 2026-04-07 13:02:34 -04:00
Scott Idem
02a2be7275 event: ensure event_id preserved on event_person insert by converting to id_random when available 2026-04-07 13:00:49 -04:00
Scott Idem
eba3456b7b model: event_badge_template — add background_image_path and cfg_json fields 2026-04-07 13:00:49 -04:00
Scott Idem
987b552157 event: Zoom CSV import — check for existing event_person by event_id+external_id before create; handle duplicates 2026-04-07 11:41:54 -04:00
Scott Idem
7ad158883a event: Zoom CSV import — force registrant email as external_id; ignore placeholder Unique identifier 2026-04-07 11:35:28 -04:00
Scott Idem
2b608d7a1a event: Zoom CSV import — default Axonius badge template 21 (temporary) 2026-04-07 11:23:36 -04:00
Scott Idem
535fc9f2b5 event: Zoom CSV import — use email as fallback external_id; populate address/phone fields 2026-04-07 10:58:08 -04:00
Scott Idem
8e9fb88e5a General file clean up. 2026-04-02 17:10:35 -04:00
Scott Idem
42eaa6676e Version bump just because. 2026-04-02 16:51:34 -04:00
Scott Idem
b5c50fd116 Changed the expiration time from 1 hour to 2 hours. 2026-04-02 15:57:36 -04:00
Scott Idem
2a1f270db6 feat(jitsi): add JWT token E2E test suite and improve api.py comments
- Add tests/e2e/test_e2e_jitsi_token.py: verifies moderator/attendee claims,
  room isolation, input validation, and exp claim correctness
- Update Jitsi section comment in api.py with actionable secret rotation TODO
  (must update JWT_APP_SECRET here AND in dgr_zone_jitsi .env, then restart
  prosody + jicofo)
2026-04-02 12:57:44 -04:00
Scott Idem
ebc5db96da fix(jitsi): allow non-moderators to request Jitsi tokens
Removed the 403 guard that blocked non-moderators. is_moderator is
already passed through to the token payload, so participants get
"moderator": false as expected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:46:27 -04:00
Scott Idem
153c2ce6dd models: add default_qry_str to event, session, presenter models 2026-03-31 16:24:25 -04:00
Scott Idem
9faf22d841 models: add default_qry_str to Journal_Entry_Base for API responses 2026-03-31 16:18:17 -04:00
Scott Idem
293f447a1c chore(site_domain): flesh out TODO stubs in legacy lookup routes
Uncommented and completed access_key + referrer handling in
lookup_site_domain_fqdn() and the GET /site/domain/fqdn/{fqdn} route.
These routes are disabled in registry.py and not currently active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 14:48:28 -04:00
Scott Idem
4629e1ec63 feat(site_domain): restore access_key enforcement for FQDN lookups
- api_crud_v3: strip falsy access_key values; restrict keyless queries
  to public domains (both site_access_key and site_domain_access_key
  must be NULL/empty); 75-line recursive block replaced with ~16 lines
- lib_sql_search: expand virtual 'access_key' field into priority SQL —
  site_access_key first, site_domain_access_key as fallback
- cms.py: add site_domain_access_key to site_domain searchable_fields
- docs: update frontend guide with access key behavior and examples
- e2e test: expand to cover all valid/invalid access key scenarios (15/15)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 14:46:33 -04:00
Scott Idem
1f9cbb0a1f Commit remaining changes: logging upgrades and E2E test 2026-03-27 11:47:35 -04:00
Scott Idem
7f87f32b70 Add INFO logging for nested parent-resolution and add E2E nested-create test for event_badge 2026-03-27 11:30:08 -04:00
Scott Idem
687472f4e3 feat(user): V3 action endpoints + auth bug fixes (19/19 + 22/22 tests)
New router: /v3/action/user/ (api_v3_actions_user.py)
  - POST /authenticate  — credentials in body (not query params; security fix)
  - POST /verify_password
  - POST /{user_id}/change_password  — optional current-password verification
  - GET  /{user_id}/new_auth_key
  - GET  /{user_id}/email_auth_key_url
  Registered in registry.py under /v3/action/user with V3 AccountContext auth.

Bug fixes (from audit in previous session):
  - user.py: fix broken @router.get decorator (authenticate was unreachable)
  - user.py + user_methods.py: fix AttributeError id_random → id (Vision ID)
  - user_models.py: add fields_to_exclude_from_db to User_New_Base; narrow
    collision prevention to self-reference IDs only
  - user_models.py: pre-inject hashed password in root_validator(pre=True) so
    exclude_unset=True in CRUD POST handler includes it (was writing NULL)
  - api_crud_v3.py: move sanitize_payload + account_id injection to after
    model validation (fixes FK integer collision with Vision ID constraints)

Docs: GUIDE__AE_API_V3_for_Frontend.md — new Section 7 with full migration
  table (legacy → V3), request/response docs for all 5 action endpoints,
  and V3 CRUD search equivalents for the 3 lookup routes.

Tests: tests/e2e/test_e2e_v3_user_action_routes.py — 19 tests, 19/19 pass.
  Legacy tests/e2e/test_e2e_v3_user_auth_routes.py — 22/22 still pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:54:09 -04:00
Scott Idem
91434968f7 docs+site_domain: Add guidance for restoring access_key validation in site_domain lookup; stage recent user/auth changes and frontend guide updates 2026-03-25 19:33:53 -04:00
Scott Idem
6bde236633 fix(crud): extend Vision ID safety net to all response paths
- Extracted apply_vision_id_fix() helper to lib_api_crud_v3.py — single
  source of truth for the fix that ensures {obj_type}_id in responses is
  always the random string, never the DB integer.
- Applied to all response-returning paths in api_crud_v3.py:
  GET single, GET list, POST search, POST create, PATCH update.
- Applied to all response-returning paths in api_crud_v3_nested.py:
  GET child list, POST search, POST create, GET single child, PATCH child.
- Removed duplicate get_child_obj and patch_child_obj route handlers in
  api_crud_v3_nested.py — FastAPI silently routes to only the first
  matching handler, so the second definitions were unreachable dead code.

Covers all 23 V3 CRUD models still using the old integer-alias pattern.
The archive_content model was already migrated to Vision IDs; this fix
ensures every other model gets correct responses without individual migration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 18:35:21 -04:00
Scott Idem
cffde249d3 fix(models): migrate Archive_Content_Base to Vision ID pattern
- Replace integer `id` (alias archive_content_id) with Vision string fields:
  `id: Optional[str]` and `archive_content_id: Optional[str]` — both always
  hold the random string ID, never the DB integer.
- Add `root_validator(pre=True)` (map_v3_ids) that maps id_random /
  archive_content_id_random → id and archive_content_id, with collision
  prevention to reject any integer that arrives in these fields.
- Remove old `archive_content_id_lookup` integer validator (superseded by
  sanitize_payload + root_validator).
- Keep `id_random` (alias archive_content_id_random) in responses for
  backward compatibility; add id, archive_content_id, id_random to
  fields_to_exclude_from_db so they never appear in INSERT/UPDATE payloads.

Generic CRUD layer safety net (post_obj + post_child_obj):
- After building resp_data on create, swap any integer {obj_type}_id with
  the corresponding {obj_type}_id_random value — catches models not yet
  migrated to Vision IDs.
- Fix return_obj=False fallback to return obj_id as the random string.

Docs: add Section 3D to GUIDE__AE_API_V3_for_Frontend.md documenting the
Vision ID convention — {obj_type}_id is always the random string; the
_id_random suffix is a legacy artifact that frontend code should phase out.

Fixes: POST /v3/crud/archive/{id}/archive_content/ returning integer ID,
breaking the subsequent PATCH flow (422 min_length validation failure).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 17:40:27 -04:00
Scott Idem
9d5f2c8cea Version update 2026-03-25 13:26:11 -04:00
Scott Idem
b9742cfcd8 feat(routers): migrate hosted_file hash lookup to V3 actions
Ported the legacy '/hosted_file/hash/{hash}' endpoint to the V3 actions router.
The new endpoint '/v3/action/hosted_file/hash/{hosted_file_hash}' supports:
- ID Vision: returns random string IDs instead of internal integers
- Local Check: verifies physical file existence on disk (check_for_local=True)
- Deduplication: enables frontend to check for existing files before upload

Also added PROJECT document for AE Hosted Files migration tracking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:05:09 -04:00
Scott Idem
b55b7ea81d refactor(routers): add DeprecationParams to legacy active endpoints
Tags remaining live-but-deprecated routes so every call logs a warning,
giving visibility before the next round of removals.

- registry.py: add DeprecationParams to importing and user routers
- api.py: add DeprecationParams to /request_jwt and /temp_token individually
- user.py: inherits deprecation warning via registry router-level dependency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 19:33:31 -04:00
Scott Idem
8eb699efe5 refactor(routers): comment out legacy endpoints across multiple routers
Disabled legacy routes that are superseded by V3 equivalents. Code is
commented out (not deleted) pending final verification and cleanup pass.

- registry.py: remove sql, lookup (/lu), websockets, websockets_redis;
  clean up dead imports (contact, event_person, etc.)
- data_store.py: comment out legacy CRUD and code-lookup endpoints;
  keep V3 code-lookup routes active; add TODO for action path rename
- api.py: comment out Api_Base CRUD, get_id (internal ID leak),
  and sql_test (debug) endpoints
- aether_cfg.py: comment out legacy Flask cfg endpoint
- user.py: comment out legacy user endpoints
- util_email.py: minor cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 19:22:45 -04:00
Scott Idem
c9ec3d7ea1 revert(lookup): restore PARTITION BY group; tests now track data fix
Reverts the PARTITION BY name change — group is the correct dedup key.
Partitioning by name broke country deduplication (two US records both
survived, causing Svelte each_key_duplicate on alpha_2_code='US').

Root cause is bad seed data in lu_v3_time_zone: group='United States'
for 13 US/* zones and group='Europe' for 63 Europe/* zones instead of
group=name. A separate DB UPDATE is required to fix those rows.

Tests updated to assert:
- No duplicate alpha_2_code in country list (PARTITION BY group regression)
- All 13 US/* and Europe/* spot-check zones present (pending DB data fix)
- priority-only timezone count == 72 (pending DB data fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 17:31:30 -04:00
Scott Idem
ccf2f30e11 fix(lookup): partition dedup by name instead of group
ROW_NUMBER() was partitioning by `group`, collapsing all 12 US/* timezones
(which share group="United States") down to a single record. Partitioning
by `name` correctly deduplicates by timezone identity while still preserving
the object > account > global override hierarchy.

Priority-only list now returns the expected 72 entries. Adds a regression
test asserting all 12 US/* timezones are present in the full list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:46:47 -04:00
Scott Idem
308a7f296f fix(errors): classify 1364 as database_schema with actionable message
Parses the field name from the MariaDB error and returns a clear
"Schema mismatch: column 'X' is NOT NULL..." message instead of the
raw DB string. Consistent with how 1054/1146 (unknown column/table)
are already handled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 18:39:50 -04:00
Scott Idem
6b25cf9c6d feat: add Novi AMS → Mailman 3 cron-based mirror sync bridge (IDAA)
Implements a full proof-of-concept for syncing IDAA's Novi AMS membership
groups to Mailman 3 mailing lists via a cron-triggered reconciliation approach.

Key changes:
- methods: rewrote sync engine around confirmed Novi API shape — group-based
  member fetch (/groups/{guid}/members + /customers/{uuid}), respects
  Active=false and UnsubscribeFromEmails=true flags
- methods: mirror_novi_group_to_mailman_list() diffs Novi group against
  Mailman roster and subscribes/unsubscribes accordingly (full mirror)
- methods: mirror_all_configured_mappings() iterates novi_mailman_sync
  config array in IDAA site cfg_json — this is the cron target
- router: replaced old /sync endpoint with POST /sync (all mappings) and
  POST /sync/group/{guid} (single mapping); removed webhook endpoint
  (sync is cron-based, not event-driven)
- router: added GET/POST/DELETE endpoints for list member inspection
  and manual subscribe/unsubscribe
- tests: two new e2e scripts covering connection checks and full member
  lifecycle; old webhook integration test archived

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:36:32 -04:00
Scott Idem
29579fd9f1 feat: add V3 action endpoint for event exhibit tracking export
- New router: app/routers/api_v3_actions_event_exhibit.py
  - GET /v3/action/event_exhibit/{exhibit_id}/tracking_export
  - Full V3 auth (x-aether-api-key + account context)
  - Multi-tenant ownership check via check_account_access
  - Permission gate: leads_api_access flag OR manager-level access
  - Returns CSV or XLSX file attachment (return_file=false for JSON)
  - Flattens responses_json custom Q&A columns; strips HTML from exhibitor_notes
  - Exports all records regardless of hidden/enabled state

- Registered in registry.py under prefix /v3/action/event_exhibit

- New E2E test: tests/e2e/test_e2e_v3_action_event_exhibit_tracking_export.py
  - 7/7 tests passing against dev-api.oneskyit.com

- Docs: GUIDE__AE_API_V3_for_Frontend.md — new Section 7 covering endpoint
  usage, columns, leads_api_access dual-purpose (3rd-party API + UI export gate)

- Docs: tests/README.md — added test to table and when-to-run matrix
2026-03-16 16:50:32 -04:00
Scott Idem
eaa18a1d45 fix(nested-crud): re-inject parent FK after model serialization to prevent 1364 errors
Root cause: child model root_validators (Vision ID anti-leakage guard) strip
integer IDs before they can be serialized into the INSERT dict, causing MariaDB
to reject the INSERT with 'Field does not have a default value' (1364).

Fix: re-inject resolved_parent_id into data_to_insert after validated_obj.dict()
in post_child_obj(). This is safe — the integer was already verified against the
DB before model validation.

Affected (were all broken since ~2026-01-27):
  - journal/{id}/journal_entry/
  - event/{id}/event_session/
  - event/{id}/event_person/
  - event/{id}/event_registration/
  - event/{id}/event_presenter/
  - event/{id}/event_presentation/
  - event/{id}/event_location/
  - event/{id}/event_track/
  - event/{id}/event_device/
  - event/{id}/event_abstract/
  - event/{id}/event_badge/ (different symptom: NULL FK)

Tests: add nested create lifecycle regression tests to test_e2e_v3_demo_parity.py
  - POST + Vision check + DELETE for journal/journal_entry and event/event_session
  - All 9 checks passing (7s)

Docs: update tests/README.md with accurate demo_parity description and
  a 'When to Run Tests' matrix to prevent future gaps in coverage.
2026-03-16 12:39:45 -04:00
Scott Idem
ee28a4f26e fix: set case_sensitive=False in config to ensure environment variables are correctly injected on Linode/Staging. 2026-03-11 22:35:22 -04:00
Scott Idem
32b519c507 V3 WebSocket: wire auth dependency, add heartbeat presence refresh, update frontend guide (wss://, auth query params, schema clarifications) 2026-03-11 15:21:19 -04:00
Scott Idem
44fa28fab3 Robust delete: handle filesystem unlink errors in hosted file action 2026-03-11 15:01:41 -04:00
Scott Idem
a20c436013 Migrate clip/convert to V3 actions; add background clip support, redirect legacy route; update frontend guide 2026-03-11 14:51:08 -04:00
Scott Idem
fbbc186af0 feat: add convert_file endpoint to v3 actions hosted_file router
Exposes GET /v3/action/hosted_file/{id}/convert_file using AccountContext
(v3 auth pattern) alongside the legacy /hosted_file/ route. Accepts
link_to_type, link_to_id, filename_no_ext, and to_type query params.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:26:40 -04:00
Scott Idem
03be0ac062 Version bump to 3.00.01 2026-03-11 09:37:41 -04:00
Scott Idem
3111ed5f22 fix: Correct Novi API auth header and field names in Mailman bridge
- Auth: ApiKey header → Authorization: Basic (confirmed from IDAA Jitsi code)
- Member fields: confirmed PascalCase (FirstName, LastName, Email) from Novi API
- email.replace(' ', '+') to match Jitsi's sanitization pattern
- Bulk member list endpoint marked TODO pending confirmation
- Response unwrapping handles Results/Members/value/array shapes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 19:26:36 -04:00
Scott Idem
f1c8958a7a feat: Scaffold Novi-Mailman Bridge integration
- app/methods/e_novi_mailman_methods.py: full sync engine, per-member
  sync helper, webhook handler, and Mailman 3 REST subscribe/unsubscribe
- app/routers/api_v3_actions_e_novi_mailman.py: test_connection, list
  inspection, full sync trigger, and Novi webhook receiver endpoints
- registry.py: registered at /v3/action/e_novi_mailman
- TODO: marked as scaffolded, pending Novi field verification + data_store setup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 19:16:16 -04:00
Scott Idem
32560d2257 feat: Operational hardening — healthcheck, config refactor, requirements lock
- Add GET /health route (DB + Redis ping, 200/503) with Dockerfile HEALTHCHECK directive
- Replace config.py stub with real pydantic BaseSettings reading directly from env vars;
  remove external config file mount from docker-compose
- Add requirements.lock (pip freeze snapshot for bit-identical builds)
- Untrack config.py globally but allow app/config.py via .gitignore negation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:44:58 -04:00
Scott Idem
89e12b9f97 fix: Resolve ID Vision conflicts and validation errors in Event Exhibit Tracking
- Modified 'sanitize_payload' to ignore 'external_person_id', preventing incorrect lookup attempts for email/passcode fields.
- Refined 'Event_Exhibit_Tracking_Base' to allow 'Union[int, str]' for relational IDs, bypassing string-length validation for internal integers.
- Adjusted root validator to preserve relational integers during POST/PUT operations while still stripping primary/account IDs for Vision-compliant READ views.
- Aligned model configuration with other V3 objects for consistency.
2026-03-03 17:08:34 -05:00
Scott Idem
403b543ed2 Added the duplex field and some other minor clean up 2026-03-02 19:49:19 -05:00