refactor(admin): remove auth migration reports

This commit is contained in:
IanShaw027
2026-04-21 17:34:18 +08:00
parent c624cce88e
commit d08757ce9e
28 changed files with 20 additions and 1884 deletions

View File

@@ -7,7 +7,6 @@ import (
"net/http/httptest"
"testing"
servermiddleware "github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
@@ -23,12 +22,6 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) {
redeemHandler := NewRedeemHandler(adminSvc, nil)
router.GET("/api/v1/admin/users", userHandler.List)
router.GET("/api/v1/admin/users/auth-identity-migration-reports/summary", userHandler.GetAuthIdentityMigrationReportSummary)
router.GET("/api/v1/admin/users/auth-identity-migration-reports", userHandler.ListAuthIdentityMigrationReports)
router.POST("/api/v1/admin/users/auth-identity-migration-reports/:id/resolve", func(c *gin.Context) {
c.Set(string(servermiddleware.ContextKeyUser), servermiddleware.AuthSubject{UserID: 99})
userHandler.ResolveAuthIdentityMigrationReport(c)
})
router.GET("/api/v1/admin/users/:id", userHandler.GetByID)
router.POST("/api/v1/admin/users/:id/auth-identities", userHandler.BindAuthIdentity)
router.POST("/api/v1/admin/users", userHandler.Create)
@@ -78,23 +71,6 @@ func TestUserHandlerEndpoints(t *testing.T) {
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/users/auth-identity-migration-reports/summary", nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/users/auth-identity-migration-reports?report_type=oidc_synthetic_email_requires_manual_recovery", nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
body, _ := json.Marshal(map[string]any{"resolution_note": "resolved by manual bind"})
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/v1/admin/users/auth-identity-migration-reports/1/resolve", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/users/1", nil)
router.ServeHTTP(rec, req)
@@ -111,7 +87,7 @@ func TestUserHandlerEndpoints(t *testing.T) {
"channel_subject": "openid-123",
},
}
body, _ = json.Marshal(bindBody)
body, _ := json.Marshal(bindBody)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/v1/admin/users/1/auth-identities", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

View File

@@ -17,7 +17,6 @@ type stubAdminService struct {
proxies []service.Proxy
proxyCounts []service.ProxyWithAccountCount
redeems []service.RedeemCode
migrationReports []service.AuthIdentityMigrationReport
boundAuthIdentity *service.AdminBindAuthIdentityInput
boundAuthIdentityFor int64
createdAccounts []*service.CreateAccountInput
@@ -134,15 +133,6 @@ func newStubAdminService() *stubAdminService {
proxies: []service.Proxy{proxy},
proxyCounts: []service.ProxyWithAccountCount{{Proxy: proxy, AccountCount: 1}},
redeems: []service.RedeemCode{redeem},
migrationReports: []service.AuthIdentityMigrationReport{
{
ID: 1,
ReportType: "oidc_synthetic_email_requires_manual_recovery",
ReportKey: "u-1",
Details: map[string]any{"user_id": 1},
CreatedAt: now,
},
},
}
}
@@ -193,30 +183,6 @@ func (s *stubAdminService) GetUserUsageStats(ctx context.Context, userID int64,
return map[string]any{"user_id": userID}, nil
}
func (s *stubAdminService) ListAuthIdentityMigrationReports(ctx context.Context, reportType string, page, pageSize int) ([]service.AuthIdentityMigrationReport, int64, error) {
if reportType == "" {
return s.migrationReports, int64(len(s.migrationReports)), nil
}
filtered := make([]service.AuthIdentityMigrationReport, 0, len(s.migrationReports))
for _, report := range s.migrationReports {
if strings.EqualFold(report.ReportType, reportType) {
filtered = append(filtered, report)
}
}
return filtered, int64(len(filtered)), nil
}
func (s *stubAdminService) GetAuthIdentityMigrationReportSummary(ctx context.Context) (*service.AuthIdentityMigrationReportSummary, error) {
summary := &service.AuthIdentityMigrationReportSummary{
ByType: map[string]int64{},
}
for _, report := range s.migrationReports {
summary.Total++
summary.ByType[report.ReportType]++
}
return summary, nil
}
func (s *stubAdminService) BindUserAuthIdentity(ctx context.Context, userID int64, input service.AdminBindAuthIdentityInput) (*service.AdminBoundAuthIdentity, error) {
s.boundAuthIdentityFor = userID
copied := input
@@ -263,20 +229,6 @@ func (s *stubAdminService) BindUserAuthIdentity(ctx context.Context, userID int6
return result, nil
}
func (s *stubAdminService) ResolveAuthIdentityMigrationReport(ctx context.Context, reportID, resolvedByUserID int64, resolutionNote string) (*service.AuthIdentityMigrationReport, error) {
now := time.Now().UTC()
for i := range s.migrationReports {
if s.migrationReports[i].ID != reportID {
continue
}
s.migrationReports[i].ResolvedAt = &now
s.migrationReports[i].ResolvedByUserID = &resolvedByUserID
s.migrationReports[i].ResolutionNote = resolutionNote
return &s.migrationReports[i], nil
}
return nil, nil
}
func (s *stubAdminService) ListGroups(ctx context.Context, page, pageSize int, platform, status, search string, isExclusive *bool, sortBy, sortOrder string) ([]service.Group, int64, error) {
return s.groups, int64(len(s.groups)), nil
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
servermiddleware "github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
@@ -83,10 +82,6 @@ type BindUserAuthIdentityChannelRequest struct {
Metadata map[string]any `json:"metadata"`
}
type ResolveAuthIdentityMigrationReportRequest struct {
ResolutionNote string `json:"resolution_note"`
}
// List handles listing all users with pagination
// GET /api/v1/admin/users
// Query params:
@@ -193,31 +188,6 @@ func (h *UserHandler) GetByID(c *gin.Context) {
response.Success(c, dto.UserFromServiceAdmin(user))
}
// GetAuthIdentityMigrationReportSummary returns aggregate migration report counts.
// GET /api/v1/admin/users/auth-identity-migration-reports/summary
func (h *UserHandler) GetAuthIdentityMigrationReportSummary(c *gin.Context) {
summary, err := h.adminService.GetAuthIdentityMigrationReportSummary(c.Request.Context())
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, summary)
}
// ListAuthIdentityMigrationReports returns paginated auth identity migration reports.
// GET /api/v1/admin/users/auth-identity-migration-reports
func (h *UserHandler) ListAuthIdentityMigrationReports(c *gin.Context) {
page, pageSize := response.ParsePagination(c)
reportType := strings.TrimSpace(c.Query("report_type"))
reports, total, err := h.adminService.ListAuthIdentityMigrationReports(c.Request.Context(), reportType, page, pageSize)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Paginated(c, reports, total, page, pageSize)
}
// BindAuthIdentity manually binds a canonical auth identity to a user.
// POST /api/v1/admin/users/:id/auth-identities
func (h *UserHandler) BindAuthIdentity(c *gin.Context) {
@@ -257,40 +227,6 @@ func (h *UserHandler) BindAuthIdentity(c *gin.Context) {
response.Success(c, result)
}
// ResolveAuthIdentityMigrationReport marks a migration report as resolved.
// POST /api/v1/admin/users/auth-identity-migration-reports/:id/resolve
func (h *UserHandler) ResolveAuthIdentityMigrationReport(c *gin.Context) {
reportID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid report ID")
return
}
subject, ok := servermiddleware.GetAuthSubjectFromContext(c)
if !ok || subject.UserID <= 0 {
response.Unauthorized(c, "Authentication required")
return
}
var req ResolveAuthIdentityMigrationReportRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
report, err := h.adminService.ResolveAuthIdentityMigrationReport(
c.Request.Context(),
reportID,
subject.UserID,
req.ResolutionNote,
)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, report)
}
// Create handles creating a new user
// POST /api/v1/admin/users
func (h *UserHandler) Create(c *gin.Context) {

View File

@@ -29,7 +29,6 @@ func TestUserHandlerListIncludesActivityFieldsAndSortParams(t *testing.T) {
Username: "activity-user",
Role: service.RoleUser,
Status: service.StatusActive,
LastLoginAt: &lastLoginAt,
LastActiveAt: &lastActiveAt,
LastUsedAt: &lastUsedAt,
CreatedAt: lastLoginAt.Add(-24 * time.Hour),
@@ -57,7 +56,6 @@ func TestUserHandlerListIncludesActivityFieldsAndSortParams(t *testing.T) {
Code int `json:"code"`
Data struct {
Items []struct {
LastLoginAt *time.Time `json:"last_login_at"`
LastActiveAt *time.Time `json:"last_active_at"`
LastUsedAt *time.Time `json:"last_used_at"`
} `json:"items"`
@@ -66,7 +64,6 @@ func TestUserHandlerListIncludesActivityFieldsAndSortParams(t *testing.T) {
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &resp))
require.Equal(t, 0, resp.Code)
require.Len(t, resp.Data.Items, 1)
require.WithinDuration(t, lastLoginAt, *resp.Data.Items[0].LastLoginAt, time.Second)
require.WithinDuration(t, lastActiveAt, *resp.Data.Items[0].LastActiveAt, time.Second)
require.WithinDuration(t, lastUsedAt, *resp.Data.Items[0].LastUsedAt, time.Second)
}
@@ -86,7 +83,6 @@ func TestUserHandlerGetByIDIncludesActivityFields(t *testing.T) {
Username: "detail-user",
Role: service.RoleUser,
Status: service.StatusActive,
LastLoginAt: &lastLoginAt,
LastActiveAt: &lastActiveAt,
LastUsedAt: &lastUsedAt,
CreatedAt: lastLoginAt.Add(-24 * time.Hour),
@@ -107,14 +103,12 @@ func TestUserHandlerGetByIDIncludesActivityFields(t *testing.T) {
var resp struct {
Code int `json:"code"`
Data struct {
LastLoginAt *time.Time `json:"last_login_at"`
LastActiveAt *time.Time `json:"last_active_at"`
LastUsedAt *time.Time `json:"last_used_at"`
} `json:"data"`
}
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &resp))
require.Equal(t, 0, resp.Code)
require.WithinDuration(t, lastLoginAt, *resp.Data.LastLoginAt, time.Second)
require.WithinDuration(t, lastActiveAt, *resp.Data.LastActiveAt, time.Second)
require.WithinDuration(t, lastUsedAt, *resp.Data.LastUsedAt, time.Second)
}