feat: add case + party CRUD with case events (Phase 1B)

- CaseService: list (paginated, filterable), get detail (with parties,
  events, deadline count), create, update, soft-delete (archive)
- PartyService: list by case, create, update, delete
- Auto-create case_events on case creation, status change, party add,
  and case archive
- Auth middleware now resolves tenant_id from user_tenants table
- All operations scoped to tenant_id from auth context
This commit is contained in:
m
2026-03-25 13:26:50 +01:00
parent 8049ea3c63
commit f11c411147
8 changed files with 778 additions and 4 deletions

View File

@@ -8,14 +8,16 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
)
type Middleware struct {
jwtSecret []byte
db *sqlx.DB
}
func NewMiddleware(jwtSecret string) *Middleware {
return &Middleware{jwtSecret: []byte(jwtSecret)}
func NewMiddleware(jwtSecret string, db *sqlx.DB) *Middleware {
return &Middleware{jwtSecret: []byte(jwtSecret), db: db}
}
func (m *Middleware) RequireAuth(next http.Handler) http.Handler {
@@ -33,6 +35,17 @@ func (m *Middleware) RequireAuth(next http.Handler) http.Handler {
}
ctx := ContextWithUserID(r.Context(), userID)
// Resolve tenant from user_tenants
var tenantID uuid.UUID
err = m.db.GetContext(r.Context(), &tenantID,
"SELECT tenant_id FROM user_tenants WHERE user_id = $1 LIMIT 1", userID)
if err != nil {
http.Error(w, "no tenant found for user", http.StatusForbidden)
return
}
ctx = ContextWithTenantID(ctx, tenantID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}