|
@@ -0,0 +1,514 @@
|
|
|
+
|
|
|
+<!DOCTYPE html>
|
|
|
+<html>
|
|
|
+ <head>
|
|
|
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
|
+ <title>handler: Go Coverage Report</title>
|
|
|
+ <style>
|
|
|
+ body {
|
|
|
+ background: black;
|
|
|
+ color: rgb(80, 80, 80);
|
|
|
+ }
|
|
|
+ body, pre, #legend span {
|
|
|
+ font-family: Menlo, monospace;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ #topbar {
|
|
|
+ background: black;
|
|
|
+ position: fixed;
|
|
|
+ top: 0; left: 0; right: 0;
|
|
|
+ height: 42px;
|
|
|
+ border-bottom: 1px solid rgb(80, 80, 80);
|
|
|
+ }
|
|
|
+ #content {
|
|
|
+ margin-top: 50px;
|
|
|
+ }
|
|
|
+ #nav, #legend {
|
|
|
+ float: left;
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+ #legend {
|
|
|
+ margin-top: 12px;
|
|
|
+ }
|
|
|
+ #nav {
|
|
|
+ margin-top: 10px;
|
|
|
+ }
|
|
|
+ #legend span {
|
|
|
+ margin: 0 5px;
|
|
|
+ }
|
|
|
+ .cov0 { color: rgb(192, 0, 0) }
|
|
|
+.cov1 { color: rgb(128, 128, 128) }
|
|
|
+.cov2 { color: rgb(116, 140, 131) }
|
|
|
+.cov3 { color: rgb(104, 152, 134) }
|
|
|
+.cov4 { color: rgb(92, 164, 137) }
|
|
|
+.cov5 { color: rgb(80, 176, 140) }
|
|
|
+.cov6 { color: rgb(68, 188, 143) }
|
|
|
+.cov7 { color: rgb(56, 200, 146) }
|
|
|
+.cov8 { color: rgb(44, 212, 149) }
|
|
|
+.cov9 { color: rgb(32, 224, 152) }
|
|
|
+.cov10 { color: rgb(20, 236, 155) }
|
|
|
+
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ <div id="topbar">
|
|
|
+ <div id="nav">
|
|
|
+ <select id="files">
|
|
|
+
|
|
|
+ <option value="file0">github.com/go-nunu/nunu-layout-advanced/internal/handler/handler.go (80.0%)</option>
|
|
|
+
|
|
|
+ <option value="file1">github.com/go-nunu/nunu-layout-advanced/internal/handler/user.go (55.6%)</option>
|
|
|
+
|
|
|
+ <option value="file2">github.com/go-nunu/nunu-layout-advanced/internal/repository/repository.go (8.3%)</option>
|
|
|
+
|
|
|
+ <option value="file3">github.com/go-nunu/nunu-layout-advanced/internal/repository/user.go (64.7%)</option>
|
|
|
+
|
|
|
+ <option value="file4">github.com/go-nunu/nunu-layout-advanced/internal/service/service.go (100.0%)</option>
|
|
|
+
|
|
|
+ <option value="file5">github.com/go-nunu/nunu-layout-advanced/internal/service/user.go (80.0%)</option>
|
|
|
+
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div id="legend">
|
|
|
+ <span>not tracked</span>
|
|
|
+
|
|
|
+ <span class="cov0">not covered</span>
|
|
|
+ <span class="cov8">covered</span>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div id="content">
|
|
|
+
|
|
|
+ <pre class="file" id="file0" style="display: none">package handler
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/gin-gonic/gin"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/middleware"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/pkg/log"
|
|
|
+)
|
|
|
+
|
|
|
+type Handler struct {
|
|
|
+ logger *log.Logger
|
|
|
+}
|
|
|
+
|
|
|
+func NewHandler(logger *log.Logger) *Handler <span class="cov8" title="1">{
|
|
|
+ return &Handler{
|
|
|
+ logger: logger,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+func GetUserIdFromCtx(ctx *gin.Context) string <span class="cov8" title="1">{
|
|
|
+ v, exists := ctx.Get("claims")
|
|
|
+ if !exists </span><span class="cov0" title="0">{
|
|
|
+ return ""
|
|
|
+ }</span>
|
|
|
+ <span class="cov8" title="1">return v.(*middleware.MyCustomClaims).UserId</span>
|
|
|
+}
|
|
|
+</pre>
|
|
|
+
|
|
|
+ <pre class="file" id="file1" style="display: none">package handler
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/gin-gonic/gin"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/service"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ "net/http"
|
|
|
+)
|
|
|
+
|
|
|
+type UserHandler interface {
|
|
|
+ Register(ctx *gin.Context)
|
|
|
+ Login(ctx *gin.Context)
|
|
|
+ GetProfile(ctx *gin.Context)
|
|
|
+ UpdateProfile(ctx *gin.Context)
|
|
|
+}
|
|
|
+
|
|
|
+type userHandler struct {
|
|
|
+ *Handler
|
|
|
+ userService service.UserService
|
|
|
+}
|
|
|
+
|
|
|
+func NewUserHandler(handler *Handler, userService service.UserService) UserHandler <span class="cov8" title="1">{
|
|
|
+ return &userHandler{
|
|
|
+ Handler: handler,
|
|
|
+ userService: userService,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+
|
|
|
+func (h *userHandler) Register(ctx *gin.Context) <span class="cov8" title="1">{
|
|
|
+ req := new(service.RegisterRequest)
|
|
|
+ if err := ctx.ShouldBindJSON(req); err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">if err := h.userService.Register(ctx, req); err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">resp.HandleSuccess(ctx, nil)</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (h *userHandler) Login(ctx *gin.Context) <span class="cov8" title="1">{
|
|
|
+ var req service.LoginRequest
|
|
|
+ if err := ctx.ShouldBindJSON(&req); err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">token, err := h.userService.Login(ctx, &req)
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusUnauthorized, 1, err.Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">resp.HandleSuccess(ctx, gin.H{
|
|
|
+ "accessToken": token,
|
|
|
+ })</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (h *userHandler) GetProfile(ctx *gin.Context) <span class="cov8" title="1">{
|
|
|
+ userId := GetUserIdFromCtx(ctx)
|
|
|
+ if userId == "" </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusUnauthorized, 1, "unauthorized", nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">user, err := h.userService.GetProfile(ctx, userId)
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">resp.HandleSuccess(ctx, user)</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (h *userHandler) UpdateProfile(ctx *gin.Context) <span class="cov8" title="1">{
|
|
|
+ userId := GetUserIdFromCtx(ctx)
|
|
|
+
|
|
|
+ var req service.UpdateProfileRequest
|
|
|
+ if err := ctx.ShouldBindJSON(&req); err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">if err := h.userService.UpdateProfile(ctx, userId, &req); err != nil </span><span class="cov0" title="0">{
|
|
|
+ resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
|
|
|
+ return
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">resp.HandleSuccess(ctx, nil)</span>
|
|
|
+}
|
|
|
+</pre>
|
|
|
+
|
|
|
+ <pre class="file" id="file2" style="display: none">package repository
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "fmt"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/pkg/log"
|
|
|
+ "github.com/redis/go-redis/v9"
|
|
|
+ "github.com/spf13/viper"
|
|
|
+ "gorm.io/driver/mysql"
|
|
|
+ "gorm.io/gorm"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+type Repository struct {
|
|
|
+ db *gorm.DB
|
|
|
+ rdb *redis.Client
|
|
|
+ logger *log.Logger
|
|
|
+}
|
|
|
+
|
|
|
+func NewRepository(db *gorm.DB, rdb *redis.Client, logger *log.Logger) *Repository <span class="cov8" title="1">{
|
|
|
+ return &Repository{
|
|
|
+ db: db,
|
|
|
+ rdb: rdb,
|
|
|
+ logger: logger,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+
|
|
|
+func NewDB(conf *viper.Viper) *gorm.DB <span class="cov0" title="0">{
|
|
|
+ db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{})
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ panic(err)</span>
|
|
|
+ }
|
|
|
+ <span class="cov0" title="0">return db</span>
|
|
|
+}
|
|
|
+func NewRedis(conf *viper.Viper) *redis.Client <span class="cov0" title="0">{
|
|
|
+ rdb := redis.NewClient(&redis.Options{
|
|
|
+ Addr: conf.GetString("data.redis.addr"),
|
|
|
+ Password: conf.GetString("data.redis.password"),
|
|
|
+ DB: conf.GetInt("data.redis.db"),
|
|
|
+ })
|
|
|
+
|
|
|
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ _, err := rdb.Ping(ctx).Result()
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ panic(fmt.Sprintf("redis error: %s", err.Error()))</span>
|
|
|
+ }
|
|
|
+
|
|
|
+ <span class="cov0" title="0">return rdb</span>
|
|
|
+}
|
|
|
+</pre>
|
|
|
+
|
|
|
+ <pre class="file" id="file3" style="display: none">package repository
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/model"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ "gorm.io/gorm"
|
|
|
+)
|
|
|
+
|
|
|
+type UserRepository interface {
|
|
|
+ Create(ctx context.Context, user *model.User) error
|
|
|
+ Update(ctx context.Context, user *model.User) error
|
|
|
+ GetByID(ctx context.Context, id string) (*model.User, error)
|
|
|
+ GetByUsername(ctx context.Context, username string) (*model.User, error)
|
|
|
+}
|
|
|
+
|
|
|
+type userRepository struct {
|
|
|
+ *Repository
|
|
|
+}
|
|
|
+
|
|
|
+func NewUserRepository(r *Repository) UserRepository <span class="cov8" title="1">{
|
|
|
+ return &userRepository{
|
|
|
+ Repository: r,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+func (r *userRepository) Create(ctx context.Context, user *model.User) error <span class="cov8" title="1">{
|
|
|
+ if err := r.db.Create(user).Error; err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to create user")
|
|
|
+ }</span>
|
|
|
+ <span class="cov8" title="1">return nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (r *userRepository) Update(ctx context.Context, user *model.User) error <span class="cov8" title="1">{
|
|
|
+ if err := r.db.Save(user).Error; err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to update user")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.User, error) <span class="cov8" title="1">{
|
|
|
+ var user model.User
|
|
|
+ if err := r.db.Where("user_id = ?", userId).First(&user).Error; err != nil </span><span class="cov0" title="0">{
|
|
|
+
|
|
|
+ return nil, errors.Wrap(err, "failed to get user by ID")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return &user, nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (r *userRepository) GetByUsername(ctx context.Context, username string) (*model.User, error) <span class="cov8" title="1">{
|
|
|
+ var user model.User
|
|
|
+ if err := r.db.Where("username = ?", username).First(&user).Error; err != nil </span><span class="cov0" title="0">{
|
|
|
+ if err == gorm.ErrRecordNotFound </span><span class="cov0" title="0">{
|
|
|
+ return nil, nil
|
|
|
+ }</span>
|
|
|
+ <span class="cov0" title="0">return nil, errors.Wrap(err, "failed to get user by username")</span>
|
|
|
+ }
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return &user, nil</span>
|
|
|
+}
|
|
|
+</pre>
|
|
|
+
|
|
|
+ <pre class="file" id="file4" style="display: none">package service
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/middleware"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/pkg/helper/sid"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/pkg/log"
|
|
|
+)
|
|
|
+
|
|
|
+type Service struct {
|
|
|
+ logger *log.Logger
|
|
|
+ sid *sid.Sid
|
|
|
+ jwt *middleware.JWT
|
|
|
+}
|
|
|
+
|
|
|
+func NewService(logger *log.Logger, sid *sid.Sid, jwt *middleware.JWT) *Service <span class="cov8" title="1">{
|
|
|
+ return &Service{
|
|
|
+ logger: logger,
|
|
|
+ sid: sid,
|
|
|
+ jwt: jwt,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+</pre>
|
|
|
+
|
|
|
+ <pre class="file" id="file5" style="display: none">package service
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/model"
|
|
|
+ "github.com/go-nunu/nunu-layout-advanced/internal/repository"
|
|
|
+ "github.com/golang-jwt/jwt/v5"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ "golang.org/x/crypto/bcrypt"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+type RegisterRequest struct {
|
|
|
+ Username string `json:"username" binding:"required"`
|
|
|
+ Password string `json:"password" binding:"required"`
|
|
|
+ Email string `json:"email" binding:"required,email"`
|
|
|
+}
|
|
|
+
|
|
|
+type LoginRequest struct {
|
|
|
+ Username string `json:"username" binding:"required"`
|
|
|
+ Password string `json:"password" binding:"required"`
|
|
|
+}
|
|
|
+
|
|
|
+type UpdateProfileRequest struct {
|
|
|
+ Nickname string `json:"nickname"`
|
|
|
+ Email string `json:"email" binding:"required,email"`
|
|
|
+ Avatar string `json:"avatar"`
|
|
|
+}
|
|
|
+
|
|
|
+type ChangePasswordRequest struct {
|
|
|
+ OldPassword string `json:"oldPassword" binding:"required"`
|
|
|
+ NewPassword string `json:"newPassword" binding:"required"`
|
|
|
+}
|
|
|
+
|
|
|
+type UserService interface {
|
|
|
+ Register(ctx context.Context, req *RegisterRequest) error
|
|
|
+ Login(ctx context.Context, req *LoginRequest) (string, error)
|
|
|
+ GetProfile(ctx context.Context, userId string) (*model.User, error)
|
|
|
+ UpdateProfile(ctx context.Context, userId string, req *UpdateProfileRequest) error
|
|
|
+ GenerateToken(ctx context.Context, userId string) (string, error)
|
|
|
+}
|
|
|
+
|
|
|
+type userService struct {
|
|
|
+ userRepo repository.UserRepository
|
|
|
+ *Service
|
|
|
+}
|
|
|
+
|
|
|
+func NewUserService(service *Service, userRepo repository.UserRepository) UserService <span class="cov8" title="1">{
|
|
|
+ return &userService{
|
|
|
+ userRepo: userRepo,
|
|
|
+ Service: service,
|
|
|
+ }
|
|
|
+}</span>
|
|
|
+
|
|
|
+func (s *userService) Register(ctx context.Context, req *RegisterRequest) error <span class="cov8" title="1">{
|
|
|
+ // 检查用户名是否已存在
|
|
|
+ if user, err := s.userRepo.GetByUsername(ctx, req.Username); err == nil && user != nil </span><span class="cov8" title="1">{
|
|
|
+ return errors.New("username already exists")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to hash password")
|
|
|
+ }</span>
|
|
|
+ // 生成用户ID
|
|
|
+ <span class="cov8" title="1">userId, err := s.sid.GenString()
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to generate user ID")
|
|
|
+ }</span>
|
|
|
+ // 创建用户
|
|
|
+ <span class="cov8" title="1">user := &model.User{
|
|
|
+ UserId: userId,
|
|
|
+ Username: req.Username,
|
|
|
+ Password: string(hashedPassword),
|
|
|
+ Email: req.Email,
|
|
|
+ }
|
|
|
+ if err = s.userRepo.Create(ctx, user); err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to create user")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (s *userService) Login(ctx context.Context, req *LoginRequest) (string, error) <span class="cov8" title="1">{
|
|
|
+ user, err := s.userRepo.GetByUsername(ctx, req.Username)
|
|
|
+ if err != nil || user == nil </span><span class="cov8" title="1">{
|
|
|
+ return "", errors.Wrap(err, "failed to get user by username")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return "", errors.Wrap(err, "failed to hash password")
|
|
|
+ }</span>
|
|
|
+ // 生成JWT token
|
|
|
+ <span class="cov8" title="1">token, err := s.GenerateToken(ctx, user.UserId)
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return "", errors.Wrap(err, "failed to generate JWT token")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return token, nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (s *userService) GetProfile(ctx context.Context, userId string) (*model.User, error) <span class="cov8" title="1">{
|
|
|
+ user, err := s.userRepo.GetByID(ctx, userId)
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return nil, errors.Wrap(err, "failed to get user by ID")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return user, nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (s *userService) UpdateProfile(ctx context.Context, userId string, req *UpdateProfileRequest) error <span class="cov8" title="1">{
|
|
|
+ user, err := s.userRepo.GetByID(ctx, userId)
|
|
|
+ if err != nil </span><span class="cov8" title="1">{
|
|
|
+ return errors.Wrap(err, "failed to get user by ID")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">user.Email = req.Email
|
|
|
+ user.Nickname = req.Nickname
|
|
|
+
|
|
|
+ if err = s.userRepo.Update(ctx, user); err != nil </span><span class="cov0" title="0">{
|
|
|
+ return errors.Wrap(err, "failed to update user")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return nil</span>
|
|
|
+}
|
|
|
+
|
|
|
+func (s *userService) GenerateToken(ctx context.Context, userId string) (string, error) <span class="cov8" title="1">{
|
|
|
+ // 生成JWT token
|
|
|
+ s.jwt.GenToken(userId, time.Now().Add(time.Hour*24*90))
|
|
|
+ token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
|
+ "userId": userId,
|
|
|
+ "exp": time.Now().Add(time.Hour * 24).Unix(),
|
|
|
+ }).SignedString([]byte("secret"))
|
|
|
+ if err != nil </span><span class="cov0" title="0">{
|
|
|
+ return "", errors.Wrap(err, "failed to generate JWT token")
|
|
|
+ }</span>
|
|
|
+
|
|
|
+ <span class="cov8" title="1">return token, nil</span>
|
|
|
+}
|
|
|
+</pre>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </body>
|
|
|
+ <script>
|
|
|
+ (function() {
|
|
|
+ var files = document.getElementById('files');
|
|
|
+ var visible;
|
|
|
+ files.addEventListener('change', onChange, false);
|
|
|
+ function select(part) {
|
|
|
+ if (visible)
|
|
|
+ visible.style.display = 'none';
|
|
|
+ visible = document.getElementById(part);
|
|
|
+ if (!visible)
|
|
|
+ return;
|
|
|
+ files.value = part;
|
|
|
+ visible.style.display = 'block';
|
|
|
+ location.hash = part;
|
|
|
+ }
|
|
|
+ function onChange() {
|
|
|
+ select(files.value);
|
|
|
+ window.scrollTo(0, 0);
|
|
|
+ }
|
|
|
+ if (location.hash != "") {
|
|
|
+ select(location.hash.substr(1));
|
|
|
+ }
|
|
|
+ if (!visible) {
|
|
|
+ select("file0");
|
|
|
+ }
|
|
|
+ })();
|
|
|
+ </script>
|
|
|
+</html>
|