feat: update users create, update, login
This commit is contained in:
parent
7006a229b0
commit
4c40dd4836
|
|
@ -20,6 +20,7 @@ type UsersController interface {
|
||||||
Show(c *fiber.Ctx) error
|
Show(c *fiber.Ctx) error
|
||||||
Save(c *fiber.Ctx) error
|
Save(c *fiber.Ctx) error
|
||||||
Update(c *fiber.Ctx) error
|
Update(c *fiber.Ctx) error
|
||||||
|
Login(c *fiber.Ctx) error
|
||||||
Delete(c *fiber.Ctx) error
|
Delete(c *fiber.Ctx) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,6 +168,39 @@ func (_i *usersController) Update(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Login Users
|
||||||
|
// @Summary Login Users
|
||||||
|
// @Description API for Login Users
|
||||||
|
// @Tags Users
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param payload body request.UserLogin true "Required payload"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /users/login [post]
|
||||||
|
func (_i *usersController) Login(c *fiber.Ctx) error {
|
||||||
|
req := new(request.UserLogin)
|
||||||
|
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
loginResponse, err := _i.usersService.Login(*req)
|
||||||
|
if err != nil {
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: false,
|
||||||
|
Code: 401,
|
||||||
|
Messages: utilRes.Messages{err.Error()},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Users successfully login"},
|
||||||
|
Data: loginResponse,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Delete Users
|
// Delete Users
|
||||||
// @Summary delete Users
|
// @Summary delete Users
|
||||||
// @Description API for delete Users
|
// @Description API for delete Users
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ type UsersCreateRequest struct {
|
||||||
LastEducation string `json:"lastEducation" validate:"required"`
|
LastEducation string `json:"lastEducation" validate:"required"`
|
||||||
UserRoleId int `json:"userRoleId" validate:"required"`
|
UserRoleId int `json:"userRoleId" validate:"required"`
|
||||||
UserLevelId int `json:"userLevelId" validate:"required"`
|
UserLevelId int `json:"userLevelId" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req UsersCreateRequest) ToEntity() *entity.Users {
|
func (req UsersCreateRequest) ToEntity() *entity.Users {
|
||||||
|
|
@ -93,6 +94,11 @@ func (req UsersUpdateRequest) ToEntity() *entity.Users {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserLogin struct {
|
||||||
|
Username string `json:"username" validate:"required,lowercase"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type UsersQueryRequestContext struct {
|
type UsersQueryRequestContext struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Nerzal/gocloak/v13"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go-humas-be/app/module/users/mapper"
|
"go-humas-be/app/module/users/mapper"
|
||||||
"go-humas-be/app/module/users/repository"
|
"go-humas-be/app/module/users/repository"
|
||||||
"go-humas-be/app/module/users/request"
|
"go-humas-be/app/module/users/request"
|
||||||
"go-humas-be/app/module/users/response"
|
"go-humas-be/app/module/users/response"
|
||||||
|
"go-humas-be/config/config"
|
||||||
"go-humas-be/utils/paginator"
|
"go-humas-be/utils/paginator"
|
||||||
utilSvc "go-humas-be/utils/service"
|
utilSvc "go-humas-be/utils/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UsersService
|
// UsersService
|
||||||
type usersService struct {
|
type usersService struct {
|
||||||
Repo repository.UsersRepository
|
Repo repository.UsersRepository
|
||||||
Log zerolog.Logger
|
Log zerolog.Logger
|
||||||
|
Keycloak *config.KeycloakConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsersService define interface of IUsersService
|
// UsersService define interface of IUsersService
|
||||||
|
|
@ -21,16 +24,18 @@ type UsersService interface {
|
||||||
All(req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error)
|
All(req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error)
|
||||||
Show(id uint) (users *response.UsersResponse, err error)
|
Show(id uint) (users *response.UsersResponse, err error)
|
||||||
Save(req request.UsersCreateRequest, authToken string) (err error)
|
Save(req request.UsersCreateRequest, authToken string) (err error)
|
||||||
|
Login(req request.UserLogin) (res *gocloak.JWT, err error)
|
||||||
Update(id uint, req request.UsersUpdateRequest) (err error)
|
Update(id uint, req request.UsersUpdateRequest) (err error)
|
||||||
Delete(id uint) error
|
Delete(id uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUsersService init UsersService
|
// NewUsersService init UsersService
|
||||||
func NewUsersService(repo repository.UsersRepository, log zerolog.Logger) UsersService {
|
func NewUsersService(repo repository.UsersRepository, log zerolog.Logger, keycloak *config.KeycloakConfig) UsersService {
|
||||||
|
|
||||||
return &usersService{
|
return &usersService{
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Log: log,
|
Log: log,
|
||||||
|
Keycloak: keycloak,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,12 +69,41 @@ func (_i *usersService) Save(req request.UsersCreateRequest, authToken string) (
|
||||||
createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken)
|
createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken)
|
||||||
newReq.CreatedById = &createdBy.ID
|
newReq.CreatedById = &createdBy.ID
|
||||||
|
|
||||||
|
keycloakId, err := _i.Keycloak.CreateUser(req.Fullname, req.Email, req.Username, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newReq.KeycloakId = &keycloakId
|
||||||
|
|
||||||
return _i.Repo.Create(newReq)
|
return _i.Repo.Create(newReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (_i *usersService) Login(req request.UserLogin) (res *gocloak.JWT, err error) {
|
||||||
|
_i.Log.Info().Interface("data", req).Msg("")
|
||||||
|
|
||||||
|
loginResponse, err := _i.Keycloak.Login(req.Username, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return loginResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (_i *usersService) Update(id uint, req request.UsersUpdateRequest) (err error) {
|
func (_i *usersService) Update(id uint, req request.UsersUpdateRequest) (err error) {
|
||||||
_i.Log.Info().Interface("data", req).Msg("")
|
_i.Log.Info().Interface("data", req).Msg("")
|
||||||
return _i.Repo.Update(id, req.ToEntity())
|
newReq := req.ToEntity()
|
||||||
|
|
||||||
|
findUser, err := _i.Repo.FindOne(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.Keycloak.UpdateUser(findUser.KeycloakId, req.Fullname, req.Email)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return _i.Repo.Update(id, newReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *usersService) Delete(id uint) error {
|
func (_i *usersService) Delete(id uint) error {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ func (_i *UsersRouter) RegisterUsersRoutes() {
|
||||||
router.Get("/:id", usersController.Show)
|
router.Get("/:id", usersController.Show)
|
||||||
router.Post("/", usersController.Save)
|
router.Post("/", usersController.Save)
|
||||||
router.Put("/:id", usersController.Update)
|
router.Put("/:id", usersController.Update)
|
||||||
|
router.Post("/login", usersController.Login)
|
||||||
router.Delete("/:id", usersController.Delete)
|
router.Delete("/:id", usersController.Delete)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,12 +81,22 @@ type objectStorage = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type keycloak = struct {
|
||||||
|
Endpoint string `toml:"endpoint"`
|
||||||
|
Realm string `toml:"realm"`
|
||||||
|
ClientId string `toml:"client-id"`
|
||||||
|
ClientSecret string `toml:"client-secret"`
|
||||||
|
AdminUsername string `toml:"admin-username"`
|
||||||
|
AdminPassword string `toml:"admin-password"`
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
App app
|
App app
|
||||||
DB db
|
DB db
|
||||||
Logger logger
|
Logger logger
|
||||||
Middleware middleware
|
Middleware middleware
|
||||||
ObjectStorage objectStorage
|
ObjectStorage objectStorage
|
||||||
|
Keycloak keycloak
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig : initialize config
|
// NewConfig : initialize config
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/Nerzal/gocloak/v13"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MinioSetup struct
|
||||||
|
type KeycloakConfig struct {
|
||||||
|
Cfg *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeycloakConfig(cfg *Config) *KeycloakConfig {
|
||||||
|
keycloakSetup := &KeycloakConfig{
|
||||||
|
Cfg: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
return keycloakSetup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_keycloak *KeycloakConfig) Login(username string, password string) (*gocloak.JWT, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint)
|
||||||
|
loginResponse, err := client.Login(
|
||||||
|
ctx,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientId,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientSecret,
|
||||||
|
_keycloak.Cfg.Keycloak.Realm,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Invalid User Credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_keycloak *KeycloakConfig) CreateUser(fullname string, email string, username string, password string) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint)
|
||||||
|
token, err := client.Login(
|
||||||
|
ctx,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientId,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientSecret,
|
||||||
|
_keycloak.Cfg.Keycloak.Realm,
|
||||||
|
_keycloak.Cfg.Keycloak.AdminUsername,
|
||||||
|
_keycloak.Cfg.Keycloak.AdminPassword,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic("Something wrong with the credentials or url")
|
||||||
|
}
|
||||||
|
|
||||||
|
var group []string
|
||||||
|
group = append(group, "humas")
|
||||||
|
user := gocloak.User{
|
||||||
|
FirstName: gocloak.StringP(fullname),
|
||||||
|
LastName: gocloak.StringP(""),
|
||||||
|
Email: gocloak.StringP(email),
|
||||||
|
Enabled: gocloak.BoolP(true),
|
||||||
|
Username: gocloak.StringP(username),
|
||||||
|
Groups: &group,
|
||||||
|
}
|
||||||
|
|
||||||
|
keycloakId, err := client.CreateUser(ctx, token.AccessToken, _keycloak.Cfg.Keycloak.Realm, user)
|
||||||
|
if err != nil {
|
||||||
|
panic("Oh no!, failed to create user :(")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _keycloak.SetPassword(token.AccessToken, keycloakId, password)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return keycloakId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_keycloak *KeycloakConfig) UpdateUser(keycloakId *string, fullname string, email string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint)
|
||||||
|
token, err := client.Login(
|
||||||
|
ctx,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientId,
|
||||||
|
_keycloak.Cfg.Keycloak.ClientSecret,
|
||||||
|
_keycloak.Cfg.Keycloak.Realm,
|
||||||
|
_keycloak.Cfg.Keycloak.AdminUsername,
|
||||||
|
_keycloak.Cfg.Keycloak.AdminPassword,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic("Something wrong with the credentials or url")
|
||||||
|
}
|
||||||
|
|
||||||
|
var group []string
|
||||||
|
group = append(group, "humas")
|
||||||
|
user := gocloak.User{
|
||||||
|
ID: keycloakId,
|
||||||
|
FirstName: gocloak.StringP(fullname),
|
||||||
|
LastName: gocloak.StringP(""),
|
||||||
|
Email: gocloak.StringP(email),
|
||||||
|
Groups: &group,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.UpdateUser(ctx, token.AccessToken, _keycloak.Cfg.Keycloak.Realm, user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_keycloak *KeycloakConfig) SetPassword(token string, keycloakId string, password string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint)
|
||||||
|
|
||||||
|
err := client.SetPassword(ctx, token, keycloakId, _keycloak.Cfg.Keycloak.Realm, password, false)
|
||||||
|
if err != nil {
|
||||||
|
panic("Oh no!, failed to set password :(")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -47,3 +47,11 @@ enable = true
|
||||||
enable = false
|
enable = false
|
||||||
max = 20
|
max = 20
|
||||||
expiration_seconds = 60
|
expiration_seconds = 60
|
||||||
|
|
||||||
|
[keycloak]
|
||||||
|
endpoint = "http://103.82.242.92:8008"
|
||||||
|
realm = "humas"
|
||||||
|
client-id = "humas-app"
|
||||||
|
client-secret = "8HV15QgqvQyNmisvZBQblQi4d27zys7l"
|
||||||
|
admin-username = "admin"
|
||||||
|
admin-password = "P@ssw0rd.1"
|
||||||
|
|
@ -5568,6 +5568,57 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/users/login": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "API for Login Users",
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Login Users",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Required payload",
|
||||||
|
"name": "payload",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.UserLogin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.BadRequestError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.UnauthorizedError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.InternalServerError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/users/{id}": {
|
"/users/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -6248,6 +6299,21 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.UserLogin": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"password",
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.UserRoleAccessesCreateRequest": {
|
"request.UserRoleAccessesCreateRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
@ -6395,6 +6461,7 @@ const docTemplate = `{
|
||||||
"identityNumber",
|
"identityNumber",
|
||||||
"identityType",
|
"identityType",
|
||||||
"lastEducation",
|
"lastEducation",
|
||||||
|
"password",
|
||||||
"phoneNumber",
|
"phoneNumber",
|
||||||
"userLevelId",
|
"userLevelId",
|
||||||
"userRoleId",
|
"userRoleId",
|
||||||
|
|
@ -6426,6 +6493,9 @@ const docTemplate = `{
|
||||||
"lastEducation": {
|
"lastEducation": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"phoneNumber": {
|
"phoneNumber": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5557,6 +5557,57 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/users/login": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "API for Login Users",
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Login Users",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Required payload",
|
||||||
|
"name": "payload",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.UserLogin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.BadRequestError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.UnauthorizedError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.InternalServerError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/users/{id}": {
|
"/users/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -6237,6 +6288,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.UserLogin": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"password",
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.UserRoleAccessesCreateRequest": {
|
"request.UserRoleAccessesCreateRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
@ -6384,6 +6450,7 @@
|
||||||
"identityNumber",
|
"identityNumber",
|
||||||
"identityType",
|
"identityType",
|
||||||
"lastEducation",
|
"lastEducation",
|
||||||
|
"password",
|
||||||
"phoneNumber",
|
"phoneNumber",
|
||||||
"userLevelId",
|
"userLevelId",
|
||||||
"userRoleId",
|
"userRoleId",
|
||||||
|
|
@ -6415,6 +6482,9 @@
|
||||||
"lastEducation": {
|
"lastEducation": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"phoneNumber": {
|
"phoneNumber": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,16 @@ definitions:
|
||||||
- parentLevelId
|
- parentLevelId
|
||||||
- provinceId
|
- provinceId
|
||||||
type: object
|
type: object
|
||||||
|
request.UserLogin:
|
||||||
|
properties:
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- password
|
||||||
|
- username
|
||||||
|
type: object
|
||||||
request.UserRoleAccessesCreateRequest:
|
request.UserRoleAccessesCreateRequest:
|
||||||
properties:
|
properties:
|
||||||
isAdminEnabled:
|
isAdminEnabled:
|
||||||
|
|
@ -481,6 +491,8 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
lastEducation:
|
lastEducation:
|
||||||
type: string
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
phoneNumber:
|
phoneNumber:
|
||||||
type: string
|
type: string
|
||||||
userLevelId:
|
userLevelId:
|
||||||
|
|
@ -500,6 +512,7 @@ definitions:
|
||||||
- identityNumber
|
- identityNumber
|
||||||
- identityType
|
- identityType
|
||||||
- lastEducation
|
- lastEducation
|
||||||
|
- password
|
||||||
- phoneNumber
|
- phoneNumber
|
||||||
- userLevelId
|
- userLevelId
|
||||||
- userRoleId
|
- userRoleId
|
||||||
|
|
@ -4207,4 +4220,36 @@ paths:
|
||||||
summary: update Users
|
summary: update Users
|
||||||
tags:
|
tags:
|
||||||
- Users
|
- Users
|
||||||
|
/users/login:
|
||||||
|
post:
|
||||||
|
description: API for Login Users
|
||||||
|
parameters:
|
||||||
|
- description: Required payload
|
||||||
|
in: body
|
||||||
|
name: payload
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.UserLogin'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Login Users
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -3,6 +3,7 @@ module go-humas-be
|
||||||
go 1.21.6
|
go 1.21.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Nerzal/gocloak/v13 v13.9.0
|
||||||
github.com/arsmn/fiber-swagger/v2 v2.31.1
|
github.com/arsmn/fiber-swagger/v2 v2.31.1
|
||||||
github.com/efectn/fx-zerolog v1.1.0
|
github.com/efectn/fx-zerolog v1.1.0
|
||||||
github.com/go-playground/locales v0.14.1
|
github.com/go-playground/locales v0.14.1
|
||||||
|
|
@ -28,6 +29,7 @@ require (
|
||||||
github.com/go-openapi/jsonreference v0.20.5 // indirect
|
github.com/go-openapi/jsonreference v0.20.5 // indirect
|
||||||
github.com/go-openapi/spec v0.20.15 // indirect
|
github.com/go-openapi/spec v0.20.15 // indirect
|
||||||
github.com/go-openapi/swag v0.22.10 // indirect
|
github.com/go-openapi/swag v0.22.10 // indirect
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
|
@ -47,10 +49,13 @@ require (
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/philhofer/fwd v1.1.2 // indirect
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/rs/xid v1.5.0 // indirect
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
|
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||||
github.com/swaggo/files v1.0.1 // indirect
|
github.com/swaggo/files v1.0.1 // indirect
|
||||||
github.com/tinylib/msgp v1.1.8 // indirect
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
|
|
||||||
10
go.sum
10
go.sum
|
|
@ -1,6 +1,8 @@
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzSVZhw=
|
||||||
|
github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
||||||
|
|
@ -46,6 +48,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
||||||
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4=
|
github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4=
|
||||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||||
|
|
@ -111,6 +115,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||||
|
|
@ -120,6 +126,7 @@ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOS
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
|
@ -133,6 +140,8 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
|
||||||
|
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
|
@ -195,6 +204,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -45,6 +45,8 @@ func main() {
|
||||||
fx.Provide(middleware.NewMiddleware),
|
fx.Provide(middleware.NewMiddleware),
|
||||||
// data storage
|
// data storage
|
||||||
fx.Provide(config.NewMinio),
|
fx.Provide(config.NewMinio),
|
||||||
|
// user management
|
||||||
|
fx.Provide(config.NewKeycloakConfig),
|
||||||
// router
|
// router
|
||||||
fx.Provide(router.NewRouter),
|
fx.Provide(router.NewRouter),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue