forked from baron/baron-sso
orgfront 버그 픽스
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user