forked from baron/baron-sso
feat: implement user data CSV export with dynamic metadata columns
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"baron-sso-backend/internal/service"
|
||||
"baron-sso-backend/internal/utils"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -541,6 +542,103 @@ func (h *UserHandler) BulkCreateUsers(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (h *UserHandler) ExportUsersCSV(c *fiber.Ctx) error {
|
||||
search := strings.TrimSpace(c.Query("search"))
|
||||
companyCode := strings.TrimSpace(c.Query("companyCode"))
|
||||
|
||||
var requesterRole string
|
||||
var manageableSlugs []string
|
||||
if profile, ok := c.Locals("user_profile").(*domain.UserProfileResponse); ok {
|
||||
requesterRole = profile.Role
|
||||
if requesterRole == domain.RoleTenantAdmin {
|
||||
for _, t := range profile.ManageableTenants {
|
||||
manageableSlugs = append(manageableSlugs, strings.ToLower(t.Slug))
|
||||
}
|
||||
if profile.CompanyCode != "" {
|
||||
manageableSlugs = append(manageableSlugs, strings.ToLower(profile.CompanyCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Fetch Users using Repo for efficiency
|
||||
users, _, err := h.UserRepo.List(c.Context(), 10000, 0, search, companyCode)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, "failed to fetch users for export")
|
||||
}
|
||||
|
||||
// 2. Filter by manageable tenants if tenant_admin
|
||||
var filtered []domain.User
|
||||
if requesterRole == domain.RoleTenantAdmin {
|
||||
slugMap := make(map[string]bool)
|
||||
for _, s := range manageableSlugs {
|
||||
slugMap[s] = true
|
||||
}
|
||||
for _, u := range users {
|
||||
if slugMap[strings.ToLower(u.CompanyCode)] {
|
||||
filtered = append(filtered, u)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filtered = users
|
||||
}
|
||||
|
||||
// 3. Set CSV Headers
|
||||
c.Set("Content-Type", "text/csv")
|
||||
c.Set("Content-Disposition", "attachment; filename=users_export_"+time.Now().Format("20060102")+".csv")
|
||||
|
||||
writer := csv.NewWriter(c)
|
||||
defer writer.Flush()
|
||||
|
||||
// Header row
|
||||
header := []string{"ID", "Email", "Name", "Role", "Status", "Tenant", "Department", "Position", "JobTitle", "CreatedAt"}
|
||||
|
||||
// Collect all possible metadata keys for dynamic columns
|
||||
metaKeysMap := make(map[string]bool)
|
||||
for _, u := range filtered {
|
||||
for k := range u.Metadata {
|
||||
metaKeysMap[k] = true
|
||||
}
|
||||
}
|
||||
var metaKeys []string
|
||||
for k := range metaKeysMap {
|
||||
metaKeys = append(metaKeys, k)
|
||||
header = append(header, "Meta:"+k)
|
||||
}
|
||||
|
||||
if err := writer.Write(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Data rows
|
||||
for _, u := range filtered {
|
||||
row := []string{
|
||||
u.ID,
|
||||
u.Email,
|
||||
u.Name,
|
||||
u.Role,
|
||||
u.Status,
|
||||
u.CompanyCode,
|
||||
u.Department,
|
||||
u.Position,
|
||||
u.JobTitle,
|
||||
u.CreatedAt.Format(time.RFC3339),
|
||||
}
|
||||
// Append metadata values in order
|
||||
for _, k := range metaKeys {
|
||||
val := ""
|
||||
if v, ok := u.Metadata[k]; ok {
|
||||
val = fmt.Sprintf("%v", v)
|
||||
}
|
||||
row = append(row, val)
|
||||
}
|
||||
if err := writer.Write(row); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
UserIDs []string `json:"userIds"`
|
||||
|
||||
Reference in New Issue
Block a user