1
0
forked from baron/baron-sso

orgfront 버그 픽스

This commit is contained in:
2026-06-10 09:36:57 +09:00
parent 28478309fa
commit c880b3c333
33 changed files with 853 additions and 130 deletions

View File

@@ -14,6 +14,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"maps"
"os"
"reflect"
@@ -22,6 +23,7 @@ import (
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
@@ -315,26 +317,14 @@ func (h *TenantHandler) ListTenants(c *fiber.Ctx) error {
}
}
findRoot := func(id string) string {
curr := id
for {
p, exists := parentMap[curr]
if !exists || p == "" {
break
}
curr = p
}
return curr
}
roots := make(map[string]bool)
for _, id := range baseTenantIDs {
roots[findRoot(id)] = true
roots[findTenantRootID(parentMap, id)] = true
}
// Filter tenants that belong to the same tree family
for _, t := range allTenants {
if roots[findRoot(t.ID)] {
if roots[findTenantRootID(parentMap, t.ID)] {
tenants = append(tenants, t)
}
}
@@ -2774,6 +2764,14 @@ func (h *TenantHandler) GetOrgChartSnapshot(c *fiber.Ctx) error {
cacheMode := strings.ToLower(strings.TrimSpace(c.Query("cache")))
cacheKey := orgChartSnapshotCacheKey(profile, c.Get("X-Tenant-ID"))
ttl := orgChartSnapshotCacheTTL()
role, userID, profileTenantID := orgChartProfileLogValues(profile)
slog.Info("orgchart snapshot request started",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"cache_mode", cacheMode,
)
if cacheMode == "redis" && h.OrgChartCache != nil {
if raw, err := h.OrgChartCache.Get(cacheKey); err == nil && strings.TrimSpace(raw) != "" {
@@ -2785,13 +2783,43 @@ func (h *TenantHandler) GetOrgChartSnapshot(c *fiber.Ctx) error {
TTLSeconds: int(ttl.Seconds()),
}
c.Set("X-Orgfront-Cache", "HIT")
slog.Info("orgchart snapshot cache hit",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"tenant_count", len(cached.Tenants),
"user_count", len(cached.Users),
)
return c.JSON(cached)
}
slog.Warn("orgchart snapshot cache payload ignored",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"error", err,
)
} else if err != nil && err != redis.Nil {
slog.Warn("orgchart snapshot cache read failed",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"error", err,
)
}
}
snapshot, err := h.buildOrgChartSnapshot(c.Context(), profile)
if err != nil {
slog.Error("orgchart snapshot build failed",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"error", err,
)
return errorJSON(c, fiber.StatusServiceUnavailable, err.Error())
}
snapshot.Cache = orgChartSnapshotCacheInfo{
@@ -2802,13 +2830,31 @@ func (h *TenantHandler) GetOrgChartSnapshot(c *fiber.Ctx) error {
if cacheMode == "redis" && h.OrgChartCache != nil {
if raw, err := json.Marshal(snapshot); err == nil {
_ = h.OrgChartCache.Set(cacheKey, string(raw), ttl)
if err := h.OrgChartCache.Set(cacheKey, string(raw), ttl); err != nil {
slog.Warn("orgchart snapshot cache write failed",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"error", err,
)
}
}
c.Set("X-Orgfront-Cache", "MISS")
} else {
c.Set("X-Orgfront-Cache", "BYPASS")
}
slog.Info("orgchart snapshot request completed",
"user_id", userID,
"role", role,
"profile_tenant_id", profileTenantID,
"tenant_header", c.Get("X-Tenant-ID"),
"cache_mode", cacheMode,
"cache_result", c.GetRespHeader("X-Orgfront-Cache"),
"tenant_count", len(snapshot.Tenants),
"user_count", len(snapshot.Users),
)
return c.JSON(snapshot)
}
@@ -2880,27 +2926,16 @@ func (h *TenantHandler) listOrgChartTenantsForProfile(ctx context.Context, profi
parentMap[tenant.ID] = *tenant.ParentID
}
}
findRoot := func(id string) string {
curr := id
for {
parentID, exists := parentMap[curr]
if !exists || parentID == "" {
return curr
}
curr = parentID
}
}
roots := make(map[string]bool)
for _, id := range baseTenantIDs {
if strings.TrimSpace(id) != "" {
roots[findRoot(id)] = true
roots[findTenantRootID(parentMap, id)] = true
}
}
tenants := make([]domain.Tenant, 0, len(allTenants))
for _, tenant := range allTenants {
if roots[findRoot(tenant.ID)] {
if roots[findTenantRootID(parentMap, tenant.ID)] {
tenants = append(tenants, tenant)
}
}
@@ -2980,6 +3015,36 @@ func orgChartSnapshotCacheKey(profile *domain.UserProfileResponse, tenantHeader
return fmt.Sprintf("orgchart:snapshot:v1:%s:%s:%s", role, userID, tenantID)
}
func orgChartProfileLogValues(profile *domain.UserProfileResponse) (string, string, string) {
if profile == nil {
return "anonymous", "anonymous", ""
}
tenantID := ""
if profile.TenantID != nil {
tenantID = strings.TrimSpace(*profile.TenantID)
}
return domain.NormalizeRole(profile.Role), strings.TrimSpace(profile.ID), tenantID
}
func findTenantRootID(parentMap map[string]string, tenantID string) string {
curr := strings.TrimSpace(tenantID)
if curr == "" {
return ""
}
visited := map[string]struct{}{}
for {
parentID := strings.TrimSpace(parentMap[curr])
if parentID == "" || parentID == curr {
return curr
}
if _, exists := visited[parentID]; exists {
return parentID
}
visited[curr] = struct{}{}
curr = parentID
}
}
func orgChartSnapshotCacheTTL() time.Duration {
const defaultTTL = 5 * time.Minute
raw := strings.TrimSpace(os.Getenv("ORGFRONT_ORGCHART_CACHE_TTL_SECONDS"))
@@ -2996,16 +3061,26 @@ func orgChartSnapshotCacheTTL() time.Duration {
func (h *TenantHandler) GetPublicOrgChart(c *fiber.Ctx) error {
token := c.Query("token")
if token == "" {
slog.Warn("public orgchart rejected missing token")
return errorJSON(c, fiber.StatusUnauthorized, "share token is required")
}
link, err := h.SharedLink.ValidateToken(c.Context(), token)
if err != nil {
slog.Warn("public orgchart token validation failed",
"token_length", len(token),
"error", err,
)
return errorJSON(c, fiber.StatusUnauthorized, err.Error())
}
allTenants, _, err := h.Service.ListTenants(c.Context(), 10000, 0, "", "")
if err != nil {
slog.Error("public orgchart tenant list failed",
"link_id", link.ID,
"tenant_id", link.TenantID,
"error", err,
)
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
@@ -3016,24 +3091,12 @@ func (h *TenantHandler) GetPublicOrgChart(c *fiber.Ctx) error {
}
}
findRoot := func(id string) string {
curr := id
for {
p, exists := parentMap[curr]
if !exists || p == "" {
break
}
curr = p
}
return curr
}
sharedRootID := findRoot(link.TenantID)
sharedRootID := findTenantRootID(parentMap, link.TenantID)
var filteredTenants []domain.Tenant
var tenantIDs []string
for _, t := range allTenants {
if findRoot(t.ID) == sharedRootID {
if findTenantRootID(parentMap, t.ID) == sharedRootID {
filteredTenants = append(filteredTenants, t)
}
}
@@ -3076,6 +3139,13 @@ func (h *TenantHandler) GetPublicOrgChart(c *fiber.Ctx) error {
tenantSummaries = append(tenantSummaries, mapTenantSummary(t))
}
slog.Info("public orgchart request completed",
"link_id", link.ID,
"tenant_id", link.TenantID,
"shared_root_id", sharedRootID,
"tenant_count", len(tenantSummaries),
"user_count", len(publicUsers),
)
return c.JSON(fiber.Map{
"tenants": tenantSummaries,
"users": publicUsers,