ALLOWED_HOSTS was missing partner.eventifyplus.com + docker internal
hostnames (eventify-backend, eventify-django). Partner Next.js
server-side authorize() fetch to /api/v1/auth/me/ was rejected with
HTTP 400 DisallowedHost, so admin "Login as Partner" redirected to
/login?error=ImpersonationFailed instead of /dashboard.
Also added `partner` FK to UserSerializer so the /me/ response exposes
the partner id the portal needs to set session.user.partnerId.
Deployed to both eventify-backend and eventify-django containers via
docker cp + HUP.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
_serialize_review() was not returning the reviewer's profile_picture URL,
so the consumer app had no field to key off and always rendered DiceBear
cartoons for every reviewer.
- Resolves r.reviewer.profile_picture.url when non-empty
- Treats default.png placeholder as no-photo (returns empty string)
- Defensive try/except around FK dereference, same pattern as user.py
Paired with mvnew consumer v1.7.8 which consumes the new field.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Partner accounts must be able to log into admin.eventifyplus.com.
ProtectedRoute empty-module redirect (frontend) handles the access
boundary — no backend login gate needed.
AdminLoginView previously accepted any valid credential regardless of
role. partner_manager / partner / partner_staff / partner_customer /
customer accounts could obtain admin JWTs and land on admin.eventifyplus.com,
where protected pages would render generic "not found" empty states.
Now returns 403 for those roles unless the user is a superuser or has an
attached StaffProfile. Writes an auth.admin_login_failed audit row with
reason=non_admin_role.
Closes gap reported for novakopro@gmail.com on /partners/3.
- SCOPE_DEFINITIONS extended with 13 new scopes across 4 categories so the
admin Roles & Permissions grid and new Base Permissions tab can grant
module-level access
- StaffProfile.SCOPE_TO_MODULE was missing 'reviews': 'reviews' — staff with
reviews.* scopes could not resolve the Reviews module in their sidebar
- NotificationSchedule CRUD views now emit AuditLog rows
(notification.schedule.created / .updated / .deleted) matching the
v1.13.0 audit coverage pattern
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- UserStatusView, EventModerationView, ReviewModerationView,
PartnerKYCReviewView: each state change now emits _audit_log()
inside the same transaction.atomic() block so the log stays
consistent with DB state on partial failure
- AuditLogMetricsView: GET /api/v1/rbac/audit-log/metrics/ returns
total/today/week/distinct_users/by_action_group; 60 s cache with
?nocache=1 bypass
- AuditLogListView: free-text search (Q over action/target/user),
page_size bounded to [1, 200]
- accounts.User.ALL_MODULES += 'audit-log';
StaffProfile.SCOPE_TO_MODULE['audit'] = 'audit-log'
- Migration 0005: composite indexes (action,-created_at) and
(target_type,target_id) on AuditLog
- admin_api/tests.py: 11 tests covering list shape, search,
page bounds, metrics shape+nocache, suspend/ban/reinstate
audit emission
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- verify_oauth2_token now passes GOOGLE_CLIENT_ID as third arg (audience check)
- fail-closed: returns 503 if GOOGLE_CLIENT_ID env var is not set
- add GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '') to settings
- replace ClerkLoginViewTests with GoogleLoginViewTests (4 cases)
- update requirements-docker.txt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Lead model in admin_api with status/priority/source/assigned_to fields
- Admin API: metrics, list, detail, update views at /api/v1/leads/
- Consumer API: public ScheduleCallView at /api/leads/schedule-call/
- RBAC: 'leads' module registered in ALL_MODULES and StaffProfile scopes
- Migration 0003_lead with indexes on status, priority, created_at, email
model_to_dict() returns event_type as an integer PK; the DHS frontend
reads ev.event_type_name to show the category badge. Added
event_type_name resolution so the carousel displays e.g. "Festivals".