bbfeabdaa5
Admin login-lock reset now SCANs and clears all auth:login:* keys (any scope) and surfaces redis_deleted_keys / fallback_deleted_keys back to ops-web so the operator can tell whether the cache was actually hit. Also tracks paired keys via a per-identifier index plus an unlock marker so legacy/in-flight pair locks get cleared on the next acquire. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
303 lines
7.7 KiB
Go
303 lines
7.7 KiB
Go
package transport
|
|
|
|
import (
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/ops/app"
|
|
"github.com/geo-platform/tenant-api/internal/shared/response"
|
|
)
|
|
|
|
type createAdminUserRequest struct {
|
|
Phone string `json:"phone" binding:"required"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
Password string `json:"password" binding:"required"`
|
|
PlanCode string `json:"plan_code"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
type updateAdminUserRequest struct {
|
|
Phone string `json:"phone" binding:"required"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type changeAdminUserPlanRequest struct {
|
|
PlanCode string `json:"plan_code" binding:"required"`
|
|
}
|
|
|
|
type updateAdminUserSubscriptionExpiryRequest struct {
|
|
EndAt time.Time `json:"end_at" binding:"required"`
|
|
}
|
|
|
|
type changeAdminUserRoleRequest struct {
|
|
Role string `json:"role" binding:"required"`
|
|
}
|
|
|
|
type setAdminUserKOLRequest struct {
|
|
Enabled bool `json:"enabled"`
|
|
DisplayName string `json:"display_name"`
|
|
MarketEnabled bool `json:"market_enabled"`
|
|
}
|
|
|
|
func listPlansHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
plans, err := svc.ListPlans(c.Request.Context())
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, plans)
|
|
}
|
|
}
|
|
|
|
func listRolesHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
response.Success(c, svc.ListRoles())
|
|
}
|
|
}
|
|
|
|
func listAdminUsersHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
|
|
result, err := svc.List(c.Request.Context(), app.AdminUserListInput{
|
|
Keyword: c.Query("keyword"),
|
|
Status: c.Query("status"),
|
|
Page: page,
|
|
Size: size,
|
|
})
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, result)
|
|
}
|
|
}
|
|
|
|
func createAdminUserHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var body createAdminUserRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
detail, err := svc.Create(c.Request.Context(), actorFromGin(c), app.CreateAdminUserInput{
|
|
Phone: body.Phone,
|
|
Email: body.Email,
|
|
Name: body.Name,
|
|
Password: body.Password,
|
|
PlanCode: body.PlanCode,
|
|
Role: body.Role,
|
|
})
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func getAdminUserHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
detail, err := svc.Get(c.Request.Context(), id)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func updateAdminUserHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body updateAdminUserRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
if err := svc.Update(c.Request.Context(), actorFromGin(c), id, app.UpdateAdminUserInput{
|
|
Phone: body.Phone,
|
|
Email: body.Email,
|
|
Name: body.Name,
|
|
}); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, gin.H{"id": id})
|
|
}
|
|
}
|
|
|
|
func changeAdminUserPlanHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body changeAdminUserPlanRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
detail, err := svc.ChangePlan(c.Request.Context(), actorFromGin(c), id, body.PlanCode)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func updateAdminUserSubscriptionExpiryHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body updateAdminUserSubscriptionExpiryRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
detail, err := svc.UpdateSubscriptionExpiry(c.Request.Context(), actorFromGin(c), id, body.EndAt)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func resetAdminUserPlanUsageHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
result, err := svc.ResetCurrentPlanUsage(c.Request.Context(), actorFromGin(c), id)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, result)
|
|
}
|
|
}
|
|
|
|
func changeAdminUserRoleHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body changeAdminUserRoleRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
detail, err := svc.ChangeRole(c.Request.Context(), actorFromGin(c), id, body.Role)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func setAdminUserKOLHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body setAdminUserKOLRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
detail, err := svc.SetKOL(c.Request.Context(), actorFromGin(c), id, app.SetAdminUserKOLInput{
|
|
Enabled: body.Enabled,
|
|
DisplayName: body.DisplayName,
|
|
MarketEnabled: body.MarketEnabled,
|
|
})
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, detail)
|
|
}
|
|
}
|
|
|
|
func setAdminUserStatusHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body setStatusRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
if err := svc.SetStatus(c.Request.Context(), actorFromGin(c), id, body.Status); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, gin.H{"id": id, "status": body.Status})
|
|
}
|
|
}
|
|
|
|
func resetAdminUserPasswordHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
var body resetPasswordRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
|
|
return
|
|
}
|
|
if err := svc.ResetPassword(c.Request.Context(), actorFromGin(c), id, body.Password); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, gin.H{"id": id})
|
|
}
|
|
}
|
|
|
|
func resetAdminUserLoginLockHandler(svc *app.AdminUserService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, err := parseIDParam(c)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
result, err := svc.ResetLoginLock(c.Request.Context(), actorFromGin(c), id)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, result)
|
|
}
|
|
}
|