feat: document templates with auto-fill (P1)

This commit is contained in:
m
2026-03-30 11:29:23 +02:00
17 changed files with 1568 additions and 89 deletions

View File

@@ -14,6 +14,19 @@ const (
userRoleKey contextKey = "user_role"
ipKey contextKey = "ip_address"
userAgentKey contextKey = "user_agent"
<<<<<<< HEAD
||||||| 8e65463
||||||| 82878df
userIDKey contextKey = "user_id"
tenantIDKey contextKey = "tenant_id"
=======
userIDKey contextKey = "user_id"
tenantIDKey contextKey = "tenant_id"
userRoleKey contextKey = "user_role"
>>>>>>> mai/pike/p0-role-based
=======
userRoleKey contextKey = "user_role"
>>>>>>> mai/ritchie/p1-document-templates
)
func ContextWithUserID(ctx context.Context, userID uuid.UUID) context.Context {
@@ -33,6 +46,7 @@ func TenantFromContext(ctx context.Context) (uuid.UUID, bool) {
id, ok := ctx.Value(tenantIDKey).(uuid.UUID)
return id, ok
}
<<<<<<< HEAD
func ContextWithUserRole(ctx context.Context, role string) context.Context {
return context.WithValue(ctx, userRoleKey, role)
@@ -42,6 +56,10 @@ func UserRoleFromContext(ctx context.Context) string {
role, _ := ctx.Value(userRoleKey).(string)
return role
}
||||||| 8e65463
<<<<<<< HEAD
=======
>>>>>>> mai/ritchie/p1-document-templates
func ContextWithRequestInfo(ctx context.Context, ip, userAgent string) context.Context {
ctx = context.WithValue(ctx, ipKey, ip)
@@ -62,3 +80,28 @@ func UserAgentFromContext(ctx context.Context) *string {
}
return nil
}
<<<<<<< HEAD
||||||| 8e65463
||||||| 82878df
=======
func ContextWithUserRole(ctx context.Context, role string) context.Context {
return context.WithValue(ctx, userRoleKey, role)
}
func UserRoleFromContext(ctx context.Context) string {
role, _ := ctx.Value(userRoleKey).(string)
return role
}
>>>>>>> mai/pike/p0-role-based
=======
func ContextWithUserRole(ctx context.Context, role string) context.Context {
return context.WithValue(ctx, userRoleKey, role)
}
func UserRoleFromContext(ctx context.Context) string {
role, _ := ctx.Value(userRoleKey).(string)
return role
}
>>>>>>> mai/ritchie/p1-document-templates

View File

@@ -35,6 +35,7 @@ func (tr *TenantResolver) Resolve(next http.Handler) http.Handler {
}
var tenantID uuid.UUID
ctx := r.Context()
if header := r.Header.Get("X-Tenant-ID"); header != "" {
parsed, err := uuid.Parse(header)
@@ -56,7 +57,14 @@ func (tr *TenantResolver) Resolve(next http.Handler) http.Handler {
}
tenantID = parsed
<<<<<<< HEAD
r = r.WithContext(ContextWithUserRole(r.Context(), role))
||||||| 8e65463
// Override the role from middleware with the correct one for this tenant
r = r.WithContext(ContextWithUserRole(r.Context(), role))
=======
ctx = ContextWithUserRole(ctx, role)
>>>>>>> mai/ritchie/p1-document-templates
} else {
// Default to user's first tenant
first, err := tr.lookup.FirstTenantForUser(r.Context(), userID)
@@ -70,6 +78,7 @@ func (tr *TenantResolver) Resolve(next http.Handler) http.Handler {
return
}
tenantID = *first
<<<<<<< HEAD
// Also resolve role for default tenant
role, err := tr.lookup.GetUserRole(r.Context(), userID, tenantID)
@@ -79,9 +88,21 @@ func (tr *TenantResolver) Resolve(next http.Handler) http.Handler {
return
}
r = r.WithContext(ContextWithUserRole(r.Context(), role))
||||||| 8e65463
=======
// Get role for default tenant
role, err := tr.lookup.GetUserRole(r.Context(), userID, tenantID)
if err != nil {
slog.Error("failed to get user role", "error", err, "user_id", userID, "tenant_id", tenantID)
http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
return
}
ctx = ContextWithUserRole(ctx, role)
>>>>>>> mai/ritchie/p1-document-templates
}
ctx := ContextWithTenantID(r.Context(), tenantID)
ctx = ContextWithTenantID(ctx, tenantID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

View File

@@ -86,7 +86,13 @@ func TestTenantResolver_FromHeader_NoAccess(t *testing.T) {
func TestTenantResolver_DefaultsToFirst(t *testing.T) {
tenantID := uuid.New()
<<<<<<< HEAD
tr := NewTenantResolver(&mockTenantLookup{tenantID: &tenantID, role: "owner"})
||||||| 8e65463
tr := NewTenantResolver(&mockTenantLookup{tenantID: &tenantID})
=======
tr := NewTenantResolver(&mockTenantLookup{tenantID: &tenantID, role: "associate"})
>>>>>>> mai/ritchie/p1-document-templates
var gotTenantID uuid.UUID
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {