Files
warmbly/internal/api/handler/handler.go
T
Matthew Meszaros 4eb8f7babe feat: settings overhaul, avatars, RBAC, plan alignment, perf
Backend:
- Fix contact-create 500 (nil custom_fields, doubled slice, bad RETURNING SQL)
- Avatar upload: migration 000033, S3 public-read, PNG/JPG only,
  client-resized to 512px + server dimension cap (1024px)
- Pull avatar_url through user + organization repo queries

Frontend:
- Settings restructured into nested routes with a rail layout
  (/app/settings/{profile,notifications,security,members,roles,
  workspace,billing,danger}); flat Section/Row primitives replace
  the per-card rectangles; Save buttons only render when dirty
- Standalone /app/billing and /app/team removed; legacy URLs
  redirect to the settings sections; UserNav trimmed accordingly
- CRM rebuilt: Pipelines CRUD + stage editor, Deals kanban with
  HTML5 drag/drop, Tasks bucketed by due-date with inline toggle.
  Frontend models realigned with backend (Deal.name, CRMTask.status
  enum, paginated list shapes)
- Avatars: AvatarUploader component, client-side canvas resize,
  wired into Profile + Workspace settings; UserNav + OrgSwitcher
  render the uploaded image with initials fallback
- RBAC: lib/permissions.ts mirrors organization_permission.go;
  inline role picker in Members; Roles & access section shows the
  permission matrix and per-role member counts
- Audit log page at /app/audit, gated to owner+admin via canManage
- Plans aligned with warmbly-web pricing: Starter/Grow/Business/
  Enterprise via lib/plans.ts; PlanPill, billing page, sidebar
  badges and LockedSurface all read from the same catalogue
- Header PlanPill shows current plan with status-aware coloring;
  sidebar locked rows show the required-plan badge instead of a
  generic lock icon

Perf:
- QueryClient defaults (staleTime: 30s, refetchOnWindowFocus: false,
  retry: 1) — kills 3-5 round-trip storm on every navigation
- useSubscription, usePlans → staleTime: Infinity (only invalidate
  on plan-change mutations); useUser/Timezones/Orgs get long stales
  with refetchOnMount: false
- vite.config: optimizeDeps for heavy libs + server.warmup for the
  most-mounted entry pages
2026-05-23 16:04:24 +00:00

112 lines
3.8 KiB
Go

package handler
import (
"github.com/warmbly/warmbly/internal/app/admin"
"github.com/warmbly/warmbly/internal/app/advanced"
"github.com/warmbly/warmbly/internal/app/analytics"
"github.com/warmbly/warmbly/internal/app/apikey"
"github.com/warmbly/warmbly/internal/app/audit"
"github.com/warmbly/warmbly/internal/app/auth"
"github.com/warmbly/warmbly/internal/app/campaign"
"github.com/warmbly/warmbly/internal/app/contact"
"github.com/warmbly/warmbly/internal/app/crm"
"github.com/warmbly/warmbly/internal/app/email"
"github.com/warmbly/warmbly/internal/app/emailsend"
"github.com/warmbly/warmbly/internal/app/feature"
"github.com/warmbly/warmbly/internal/app/group"
"github.com/warmbly/warmbly/internal/app/organization"
"github.com/warmbly/warmbly/internal/app/ratelimit"
"github.com/warmbly/warmbly/internal/app/sequence"
"github.com/warmbly/warmbly/internal/app/socket"
"github.com/warmbly/warmbly/internal/app/stripe"
"github.com/warmbly/warmbly/internal/app/subscription"
"github.com/warmbly/warmbly/internal/app/template"
"github.com/warmbly/warmbly/internal/app/token"
"github.com/warmbly/warmbly/internal/app/trial"
"github.com/warmbly/warmbly/internal/app/tz"
"github.com/warmbly/warmbly/internal/app/unibox"
"github.com/warmbly/warmbly/internal/app/user"
"github.com/warmbly/warmbly/internal/app/warmup"
"github.com/warmbly/warmbly/internal/app/releases"
"github.com/warmbly/warmbly/internal/app/worker"
"github.com/warmbly/warmbly/internal/app/worker_orchestrator"
"github.com/warmbly/warmbly/internal/infrastructure/storage"
"github.com/warmbly/warmbly/internal/notify"
"github.com/warmbly/warmbly/internal/repository"
"github.com/warmbly/warmbly/internal/tasks"
)
type Handler struct {
AuthService auth.AuthService
TokenService token.TokenService
UserService user.UserService
EmailService email.EmailService
CampaignService campaign.CampaignService
ContactService contact.ContactService
SequenceService sequence.SequenceService
UniboxService unibox.UniboxService
FolderService group.GroupService
TagService group.GroupService
CategoryService group.GroupService
TzService tz.TzService
SocketService socket.SocketService
TasksService tasks.TasksService
// New services
APIKeyService apikey.APIKeyService
AnalyticsService analytics.AnalyticsService
AuditService audit.AuditService
RateLimitService ratelimit.RateLimitService
// Subscription & billing
SubscriptionService subscription.SubscriptionService
StripeService stripe.StripeService
// Trial & feature gates
TrialService trial.TrialService
FeatureGateService feature.FeatureGateService
WorkerAssignmentService worker.WorkerAssignmentService
// Organization & IAM
OrganizationService organization.OrganizationService
// CRM
CRMService crm.CRMService
// Email send & templates
TemplateService template.TemplateService
EmailSendService emailsend.EmailSendService
// Admin
AdminService admin.AdminService
// Worker orchestration (SSH-driven lifecycle for admin-managed workers)
WorkerOrchestrator *worker_orchestrator.Orchestrator
WorkerRepo repository.WorkerRepository
CredentialsRepo repository.CredentialsRepository
ReleasesService *releases.Service
// Notifications
EmailNotificationService notify.EmailNotificationService
// Advanced outreach controls
AdvancedService advanced.Service
// Warmup health
WarmupService warmup.Service
// Public websocket URL used by frontend clients
WebsocketURI string
// Object storage for user-uploaded artifacts (avatars, etc.).
Storage *storage.Client
// Direct repositories used by handlers that don't yet have a
// service layer (avatars, etc.). Keep narrow and add a service
// only when business logic accumulates.
UserRepo repository.UserRepository
OrgRepo repository.OrganizationRepository
}