package service import ( "context" "crypto/md5" "encoding/hex" "time" v1 "kra/api/kratos/admin/v1" "kra/internal/biz" "kra/pkg/auth" "github.com/go-kratos/kratos/v2/errors" "go.einride.tech/aip/fieldmask" "go.einride.tech/aip/filtering" "go.einride.tech/aip/ordering" "go.einride.tech/aip/pagination" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" ) func encodePassword(password string) string { sum := md5.Sum([]byte(password)) return hex.EncodeToString(sum[:]) } func convertAdmin(m *biz.Admin) *v1.Admin { return &v1.Admin{ Id: m.ID, Name: m.Name, Email: m.Email, Avatar: m.Avatar, Access: m.Access, CreateTime: timestamppb.New(m.CreateTime), UpdateTime: timestamppb.New(m.UpdateTime), } } // AdminService is a greeter service. type AdminService struct { v1.UnimplementedAdminServiceServer uc *biz.AdminUsecase } // NewAdminService new a greeter service. func NewAdminService(uc *biz.AdminUsecase) *AdminService { return &AdminService{uc: uc} } // Current implements auth current admin retrieval. func (s *AdminService) Current(ctx context.Context, req *emptypb.Empty) (*v1.Admin, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } admin, err := s.uc.GetAdmin(ctx, a.UserID) if err != nil { return nil, err } return convertAdmin(admin), nil } // Login implements auth login. func (s *AdminService) Login(ctx context.Context, req *v1.LoginRequest) (*v1.Admin, error) { var ( err error admin *biz.Admin ) switch v := req.Identity.(type) { case *v1.LoginRequest_Username: admin, err = s.uc.LoginByUsername(ctx, v.Username, encodePassword(req.Password)) if err != nil { return nil, err } case *v1.LoginRequest_Email: admin, err = s.uc.LoginByEmail(ctx, v.Email, encodePassword(req.Password)) if err != nil { return nil, err } default: return nil, errors.BadRequest("AUTH", "unsupported identity type") } if err := auth.SetCookie(ctx, admin.ID, admin.Access, time.Now().Add(7*24*time.Hour)); err != nil { return nil, err } return convertAdmin(admin), nil } // Logout implements auth logout. func (s *AdminService) Logout(ctx context.Context, req *emptypb.Empty) (*emptypb.Empty, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if err := s.uc.Logout(ctx, a.UserID); err != nil { return nil, err } if err := auth.DeleteCookie(ctx); err != nil { return nil, err } return &emptypb.Empty{}, nil } // CreateAdmin implements admin creation. func (s *AdminService) CreateAdmin(ctx context.Context, req *v1.CreateAdminRequest) (*v1.Admin, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if !a.HasAdminAccess() { return nil, auth.ErrForbidden } admin, err := s.uc.CreateAdmin(ctx, &biz.Admin{ Name: req.Admin.Name, Email: req.Admin.Email, Password: req.Admin.Password, Avatar: req.Admin.Avatar, Access: req.Admin.Access, }) if err != nil { return nil, err } return convertAdmin(admin), nil } // UpdateAdmin implements admin update. func (s *AdminService) UpdateAdmin(ctx context.Context, req *v1.UpdateAdminRequest) (*v1.Admin, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if !a.HasAdminAccess() { return nil, auth.ErrForbidden } // Encode password if it's not empty if req.Admin.Password != "" { req.Admin.Password = encodePassword(req.Admin.Password) } admin, err := s.GetAdmin(ctx, &v1.GetAdminRequest{Id: req.Admin.Id}) if err != nil { return nil, err } fieldmask.Update(req.UpdateMask, admin, req.Admin) updated, err := s.uc.UpdateAdmin(ctx, &biz.Admin{ ID: admin.Id, Name: admin.Name, Email: admin.Email, Password: admin.Password, Avatar: admin.Avatar, Access: admin.Access, }) if err != nil { return nil, err } return convertAdmin(updated), nil } // DeleteAdmin implements admin deletion. func (s *AdminService) DeleteAdmin(ctx context.Context, req *v1.DeleteAdminRequest) (*emptypb.Empty, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if !a.HasAdminAccess() { return nil, auth.ErrForbidden } if err := s.uc.DeleteAdmin(ctx, req.Id); err != nil { return nil, err } return &emptypb.Empty{}, nil } // GetAdmin implements admin retrieval. func (s *AdminService) GetAdmin(ctx context.Context, req *v1.GetAdminRequest) (*v1.Admin, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if !a.HasAdminAccess() { return nil, auth.ErrForbidden } admin, err := s.uc.GetAdmin(ctx, req.Id) if err != nil { return nil, err } return convertAdmin(admin), nil } // ListAdmins implements admin listing with filtering, ordering, and pagination. func (s *AdminService) ListAdmins(ctx context.Context, req *v1.ListAdminsRequest) (*v1.AdminSet, error) { a, ok := auth.FromContext(ctx) if !ok { return nil, auth.ErrUnauthorized } if !a.HasAdminAccess() { return nil, auth.ErrForbidden } declarations, err := filtering.NewDeclarations( filtering.DeclareStandardFunctions(), filtering.DeclareIdent("name", filtering.TypeString), filtering.DeclareIdent("email", filtering.TypeString), filtering.DeclareIdent("phone", filtering.TypeString), filtering.DeclareIdent("create_time", filtering.TypeTimestamp), ) if err != nil { return nil, err } filter, err := filtering.ParseFilter(req, declarations) if err != nil { return nil, err } pageToken, err := pagination.ParsePageToken(req) if err != nil { return nil, err } orderBy, err := ordering.ParseOrderBy(req) if err != nil { return nil, err } if req.PageSize <= 0 { req.PageSize = 20 } admins, err := s.uc.ListAdmins(ctx, biz.ListFilter(filter), biz.ListOrderBy(orderBy), biz.ListLimit(int(req.PageSize)), biz.ListOffset(int(pageToken.Offset)), ) if err != nil { return nil, err } adminSet := &v1.AdminSet{ Admins: make([]*v1.Admin, 0, len(admins)), } if len(admins) >= int(req.PageSize) { adminSet.NextPageToken = pageToken.Next(req).String() } for _, admin := range admins { adminSet.Admins = append(adminSet.Admins, convertAdmin(admin)) } return adminSet, nil }